anentrypoint-design 0.0.414 → 0.0.415

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 (102) hide show
  1. package/dist/247420.js +62 -12
  2. package/dist/app-shell.css +40 -594
  3. package/dist/colors_and_type.css +607 -47
  4. package/dist/index.html +227 -277
  5. package/dist/preview/buttons.html +39 -28
  6. package/dist/preview/colors-core.html +18 -8
  7. package/dist/preview/colors-lore.html +26 -17
  8. package/dist/preview/colors-semantic.html +24 -18
  9. package/dist/preview/dateline.html +6 -5
  10. package/dist/preview/dropzone.html +4 -3
  11. package/dist/preview/file-grid.html +10 -9
  12. package/dist/preview/file-row.html +14 -13
  13. package/dist/preview/file-toolbar.html +4 -3
  14. package/dist/preview/file-viewer.html +4 -3
  15. package/dist/preview/header.html +26 -24
  16. package/dist/preview/icons-unicode.html +8 -5
  17. package/dist/preview/index-row.html +17 -9
  18. package/dist/preview/inputs.html +4 -3
  19. package/dist/preview/manifesto.html +34 -17
  20. package/dist/preview/motion-default.js +77 -57
  21. package/dist/preview/rules.html +4 -3
  22. package/dist/preview/spacing.html +13 -12
  23. package/dist/preview/stamps-lore.html +5 -4
  24. package/dist/preview/stamps.html +4 -3
  25. package/dist/preview/theme-ink.html +7 -6
  26. package/dist/preview/type-display.html +14 -5
  27. package/dist/preview/type-mono.html +6 -5
  28. package/dist/preview/type-prose.html +12 -10
  29. package/dist/preview/type-scale.html +4 -3
  30. package/dist/preview/wordmarks.html +10 -7
  31. package/dist/site/content/globals/site.yaml +5 -3
  32. package/dist/site/content/pages/freddie.yaml +2 -2
  33. package/dist/site/content/pages/home.yaml +55 -55
  34. package/dist/site/theme.mjs +96 -348
  35. package/dist/slides/index.html +13 -13
  36. package/dist/src/bootstrap.js +10 -1
  37. package/dist/src/components/chat.js +43 -195
  38. package/dist/src/components/community.js +18 -166
  39. package/dist/src/components/content.js +30 -210
  40. package/dist/src/components/files-modals.js +14 -106
  41. package/dist/src/components/files.js +15 -116
  42. package/dist/src/components/freddie/helpers.js +53 -8
  43. package/dist/src/components/freddie.js +35 -25
  44. package/dist/src/components/shell.js +16 -114
  45. package/dist/src/components/theme-toggle.js +69 -25
  46. package/dist/src/components.js +92 -13
  47. package/dist/src/deck-stage.js +6 -4
  48. package/dist/src/highlight.js +51 -7
  49. package/dist/src/index.js +58 -12
  50. package/dist/src/kits/os/about-app.js +2 -3
  51. package/dist/src/kits/os/app-panes.css +46 -70
  52. package/dist/src/kits/os/browser-app.js +28 -1
  53. package/dist/src/kits/os/files-app.js +7 -0
  54. package/dist/src/kits/os/freddie/pages-chat.js +90 -75
  55. package/dist/src/kits/os/freddie/pages-core.js +9 -9
  56. package/dist/src/kits/os/freddie/pages-os.js +8 -8
  57. package/dist/src/kits/os/freddie/pages-tools.js +14 -14
  58. package/dist/src/kits/os/freddie/routes.js +21 -19
  59. package/dist/src/kits/os/freddie-dashboard.css +42 -21
  60. package/dist/src/kits/os/freddie-dashboard.js +6 -6
  61. package/dist/src/kits/os/icons.js +38 -16
  62. package/dist/src/kits/os/index.js +11 -0
  63. package/dist/src/kits/os/launcher.css +26 -10
  64. package/dist/src/kits/os/launcher.js +3 -0
  65. package/dist/src/kits/os/shell.js +72 -108
  66. package/dist/src/kits/os/terminal-app.js +1 -1
  67. package/dist/src/kits/os/theme.css +1330 -150
  68. package/dist/src/kits/os/wm.css +68 -15
  69. package/dist/src/kits/os/wm.js +93 -17
  70. package/dist/src/markdown.js +121 -12
  71. package/dist/src/motion.js +18 -2
  72. package/dist/src/page-html.js +62 -134
  73. package/dist/src/theme.js +106 -13
  74. package/dist/src/web-components/ds-chat.js +80 -9
  75. package/dist/ui_kits/aicat/app.js +54 -6
  76. package/dist/ui_kits/aicat/index.html +2 -0
  77. package/dist/ui_kits/blog/index.html +33 -17
  78. package/dist/ui_kits/chat/app.js +90 -22
  79. package/dist/ui_kits/chat/index.html +2 -0
  80. package/dist/ui_kits/community/app.js +86 -24
  81. package/dist/ui_kits/community/index.html +1 -0
  82. package/dist/ui_kits/dashboard/app.js +123 -34
  83. package/dist/ui_kits/dashboard/index.html +1 -0
  84. package/dist/ui_kits/docs/index.html +43 -33
  85. package/dist/ui_kits/error_404/app.js +78 -23
  86. package/dist/ui_kits/file_browser/README.md +1 -1
  87. package/dist/ui_kits/file_browser/app.js +45 -10
  88. package/dist/ui_kits/file_browser/index.html +1 -0
  89. package/dist/ui_kits/gallery/app.js +130 -51
  90. package/dist/ui_kits/homepage/app.js +156 -138
  91. package/dist/ui_kits/project_page/app.js +142 -125
  92. package/dist/ui_kits/project_page/index.html +2 -1
  93. package/dist/ui_kits/search/app.js +52 -15
  94. package/dist/ui_kits/settings/app.js +166 -39
  95. package/dist/ui_kits/signin/app.js +175 -49
  96. package/dist/ui_kits/signin/index.html +1 -0
  97. package/dist/ui_kits/slide_deck/app.js +83 -49
  98. package/dist/ui_kits/system_primer/app.js +57 -40
  99. package/dist/ui_kits/terminal/app.js +160 -43
  100. package/package.json +1 -1
  101. package/src/page-html/client-script.js +40 -11
  102. package/src/page-html/page-styles.js +22 -1
@@ -1,26 +1,45 @@
1
+ /* Fonts are declared once in the canonical theme (colors_and_type.css /
2
+ vendor/fonts.css), which every .ds-247420 consumer loads first. No @import
3
+ here — a second Inter-only import risked a weight-set FOUT mismatch. */
4
+
5
+ /* The OS shell namespaces its surfaces as --os-* but those are pure ALIASES of
6
+ the canonical semantic tokens from colors_and_type.css. colors_and_type.css
7
+ is always loaded before this sheet for any .ds-247420 consumer, so the
8
+ canonical tokens are guaranteed present and the alias fallbacks are dropped —
9
+ a fallback here would silently re-introduce theme drift (a baked light value
10
+ that ignores [data-theme="ink"]). One theme drives both token namespaces. */
1
11
  :root {
2
- --os-accent: var(--panel-accent, #3F8A4A);
3
- --os-accent-2: var(--panel-accent-2, #2B6B36);
4
- --os-accent-soft: var(--panel-select, #D8ECCB);
5
- --os-bg-0: var(--panel-0, #F5F0E4);
6
- --os-bg-1: var(--panel-1, #FBF6EB);
7
- --os-bg-2: var(--panel-2, #F0E9DA);
8
- --os-bg-3: var(--panel-3, #E3DAC7);
9
- --os-fg: var(--panel-text, #1F1B16);
10
- --os-fg-2: var(--panel-text-2, #5A5246);
11
- --os-fg-3: var(--panel-text-3, #857B6C);
12
- --os-red: var(--warn, #FF6B4A);
13
- --os-amber: var(--sun, #FFD86B);
14
- --os-green: var(--green, #3F8A4A);
15
- --os-radius: var(--r-2, 10px);
16
- --os-radius-sm: var(--r-1, 6px);
12
+ /* --ff-ui is the OS-shell alias for the canonical body font; --ff-display
13
+ and --ff-mono are already canonical (colors_and_type.css), referenced
14
+ directly below — do not self-redefine them here (cycle). */
15
+ --ff-ui: var(--ff-body);
16
+ /* --os-accent is used predominantly as TEXT/icon/border in the OS chrome,
17
+ so it tracks the readable --accent-ink tone (the electric lead is too
18
+ light for text on paper). --os-accent-fill is the saturated lead for the
19
+ rare solid fill; --os-accent-soft is the tint. */
20
+ --os-accent: var(--accent-ink);
21
+ --os-accent-fill: var(--accent);
22
+ --os-accent-2: var(--accent-bright, var(--accent));
23
+ --os-accent-soft: var(--accent-tint);
24
+ --os-bg-0: var(--bg);
25
+ --os-bg-1: var(--bg-2);
26
+ --os-bg-2: var(--bg-3);
27
+ --os-bg-3: var(--panel-3);
28
+ --os-fg: var(--fg);
29
+ --os-fg-2: var(--fg-2);
30
+ --os-fg-3: var(--fg-3);
31
+ --os-red: var(--warn);
32
+ --os-amber: var(--sun);
33
+ --os-green: var(--green);
34
+ --os-radius: var(--r-2);
35
+ --os-radius-sm: var(--r-1);
17
36
  --os-bar-h: 44px;
18
37
  --os-bar-h-mobile: 52px;
19
38
  --os-rail-w: 64px;
20
39
  --os-tap: 44px;
21
- --os-font: var(--ff-ui, 'Nunito', sans-serif);
22
- --os-display: var(--ff-display, 'Archivo Black', 'Archivo', sans-serif);
23
- --os-mono: var(--ff-mono, 'JetBrains Mono', ui-monospace, monospace);
40
+ --os-font: var(--ff-ui);
41
+ --os-display: var(--ff-display);
42
+ --os-mono: var(--ff-mono);
24
43
  }
25
44
 
26
45
  html, body {
@@ -32,13 +51,15 @@ html, body {
32
51
  }
33
52
 
34
53
  .os-menubar, .os-taskbar {
35
- background: var(--os-bg-2);
54
+ background: color-mix(in oklab, var(--os-bg-2) 88%, transparent);
55
+ backdrop-filter: blur(10px);
56
+ -webkit-backdrop-filter: blur(10px);
36
57
  border: none;
37
58
  color: var(--os-fg);
38
- font: 13px var(--os-font);
59
+ font: var(--fs-tiny) var(--os-font);
39
60
  height: var(--os-bar-h);
40
- padding: 0 12px;
41
- gap: 6px;
61
+ padding: 0 var(--space-2-75);
62
+ gap: var(--space-1-75);
42
63
  box-shadow: none;
43
64
  display: flex;
44
65
  align-items: center;
@@ -48,17 +69,22 @@ html, body {
48
69
  .os-menubar > *, .os-taskbar > * { flex-shrink: 0; }
49
70
  .os-menubar .os-spacer { flex: 1 1 auto; min-width: 0; }
50
71
  .os-menubar .os-tray { margin-left: auto; }
72
+ /* Many open windows must scroll within the bar, not overflow it. Mobile
73
+ already had this; promote it to all widths so a crowded desktop taskbar
74
+ scrolls horizontally instead of pushing buttons off-screen. */
75
+ .os-taskbar { overflow-x: auto; overflow-y: hidden; scrollbar-width: none; }
76
+ .os-taskbar::-webkit-scrollbar { display: none; }
51
77
 
52
78
  .os-brand {
53
79
  color: var(--os-fg);
54
80
  font-family: var(--os-display);
55
81
  font-weight: 700;
56
82
  letter-spacing: -0.01em;
57
- font-size: 15px;
83
+ font-size: var(--fs-sm);
58
84
  margin-right: 14px;
59
85
  display: inline-flex;
60
86
  align-items: center;
61
- gap: 8px;
87
+ gap: var(--space-2);
62
88
  text-transform: lowercase;
63
89
  }
64
90
  .os-brand::before {
@@ -77,12 +103,12 @@ html, body {
77
103
  cursor: pointer;
78
104
  font: inherit;
79
105
  font-family: var(--os-font);
80
- font-size: 13px;
81
- border-radius: 999px;
106
+ font-size: var(--fs-tiny);
107
+ border-radius: var(--r-pill, 999px);
82
108
  transition: background 80ms ease, color 80ms ease;
83
109
  display: inline-flex;
84
110
  align-items: center;
85
- gap: 6px;
111
+ gap: var(--space-1-75);
86
112
  outline: none;
87
113
  text-transform: lowercase;
88
114
  }
@@ -97,42 +123,59 @@ html, body {
97
123
  background: var(--os-bg-1);
98
124
  border: none;
99
125
  border-radius: var(--r-3, 18px);
100
- padding: 8px;
126
+ padding: var(--space-2, 8px);
101
127
  min-width: 220px;
102
- top: calc(var(--os-bar-h) + 4px);
103
- left: 8px;
104
- z-index: 9500;
128
+ top: calc(var(--os-bar-h) + var(--space-1, 4px));
129
+ left: var(--space-2, 8px);
130
+ z-index: var(--z-dropdown);
105
131
  display: none;
106
132
  flex-direction: column;
107
- gap: 2px;
133
+ gap: var(--space-hair);
108
134
  pointer-events: auto;
135
+ /* Long app registries must scroll, not extend past the viewport. */
136
+ max-height: calc(100vh - var(--os-bar-h) - 16px);
137
+ max-height: calc(100dvh - var(--os-bar-h) - env(safe-area-inset-top, 0px) - 16px);
138
+ overflow-y: auto;
139
+ overscroll-behavior: contain;
109
140
  }
110
141
  .os-menu.open { display: flex; }
111
142
  .os-menu .os-btn {
112
143
  width: 100%;
113
144
  text-align: left;
114
145
  border-radius: var(--os-radius-sm);
115
- padding: 8px 10px;
146
+ padding: var(--space-2) var(--space-2-5);
116
147
  color: var(--os-fg);
117
148
  justify-content: flex-start;
118
149
  }
119
150
  .os-menu .os-btn:hover { background: var(--panel-hover, var(--os-bg-2)); color: var(--os-fg); }
120
151
 
152
+ /* Collapsible "System" apps group inside the apps menu. thebird's os-shell.js
153
+ * builds the group structurally and toggles only the list's display; the static
154
+ * layout (column flow + indent + arrow alignment) lives here so no visual rule
155
+ * leaks back into thebird JS (zero-design-CSS contract). */
156
+ .os-menu-group { display: flex; flex-direction: column; gap: var(--space-hair); }
157
+ .os-menu-system-toggle { display: flex; align-items: center; gap: var(--space-1-75); }
158
+ .os-menu-system-arrow { display: inline-flex; align-items: center; transition: transform 120ms ease; }
159
+ .os-menu-system-arrow .ds-icon { width: 14px; height: 14px; }
160
+ .os-menu-system-list { display: none; flex-direction: column; padding-left: 14px; }
161
+ .os-menu-system-toggle[aria-expanded="true"] + .os-menu-system-list { display: flex; }
162
+ @media (prefers-reduced-motion: reduce) { .os-menu-system-arrow { transition: none; } }
163
+
121
164
  .os-clock {
122
165
  color: var(--os-fg-2);
123
166
  font-variant-numeric: tabular-nums;
124
167
  font-family: var(--os-mono);
125
- padding: 0 10px;
126
- font-size: 12px;
168
+ padding: 0 var(--space-2-5);
169
+ font-size: var(--fs-micro);
127
170
  }
128
171
 
129
172
  .os-task {
130
173
  border: none;
131
174
  background: var(--os-bg-1);
132
175
  color: var(--os-fg-2);
133
- border-radius: 999px;
134
- padding: 6px 14px;
135
- font-size: 12px;
176
+ border-radius: var(--r-pill, 999px);
177
+ padding: var(--space-1-75) 14px;
178
+ font-size: var(--fs-micro);
136
179
  font-family: var(--os-font);
137
180
  transition: background 80ms ease, color 80ms ease, box-shadow 80ms ease;
138
181
  max-width: 200px;
@@ -143,7 +186,10 @@ html, body {
143
186
  text-overflow: ellipsis;
144
187
  text-transform: lowercase;
145
188
  }
189
+ .os-task { min-height: 28px; }
146
190
  .os-task:hover { background: var(--panel-hover, var(--os-bg-2)); color: var(--os-fg); }
191
+ /* Empty taskbar reads as an intentional strip, not a stray blurred bar. */
192
+ .os-task-empty { font-size: var(--fs-nano); color: var(--os-fg-3); padding: 0 var(--space-2-5); font-family: var(--os-font); }
147
193
  .os-task.focused {
148
194
  background: var(--panel-select, var(--os-accent-soft));
149
195
  color: var(--os-fg);
@@ -151,78 +197,93 @@ html, body {
151
197
  box-shadow: inset 4px 0 0 var(--os-accent);
152
198
  }
153
199
 
154
- .wm-win {
155
- background: var(--os-bg-1) !important;
156
- border: none !important;
157
- border-radius: var(--r-3, 18px) !important;
158
- box-shadow: none !important;
200
+ /* Window-chrome overrides of wm.css.
201
+ *
202
+ * wm.css is loaded by the consumer as a SEPARATE raw sheet (it is not part of
203
+ * the dist bundle), so its load order relative to this file is not guaranteed.
204
+ * These rules therefore carry the `.ds-247420` scope class: at (0,2,0) they
205
+ * beat wm.css's bare (0,1,0) `.wm-*` rules on SPECIFICITY, which is order-
206
+ * independent — replacing the `!important` wall that previously did the same
207
+ * job by force. Do not un-scope these; the scope IS the override mechanism. */
208
+ .ds-247420 .wm-win {
209
+ background: var(--os-bg-1);
210
+ border: none;
211
+ border-radius: var(--r-3);
212
+ box-shadow: none;
159
213
  overflow: hidden;
160
214
  }
161
- .wm-win.wm-focused {
162
- border: none !important;
163
- box-shadow: inset 4px 0 0 var(--os-accent) !important;
215
+ .ds-247420 .wm-win.wm-focused {
216
+ border: none;
217
+ box-shadow: inset var(--space-1) 0 0 var(--os-accent);
164
218
  }
165
219
 
166
- .wm-bar {
167
- background: var(--os-bg-2) !important;
168
- border: none !important;
169
- padding: 8px 12px !important;
170
- gap: 10px !important;
220
+ .ds-247420 .wm-bar {
221
+ background: var(--os-bg-2);
222
+ border: none;
223
+ padding: var(--space-2) var(--space-2-75);
224
+ gap: var(--space-2-5);
171
225
  }
172
- .wm-title {
173
- font: 13px var(--os-font) !important;
174
- color: var(--os-fg-2) !important;
226
+ .ds-247420 .wm-title {
227
+ font: var(--fs-tiny) var(--os-font);
228
+ color: var(--os-fg-2);
175
229
  letter-spacing: 0.005em;
176
230
  text-align: center;
177
231
  font-weight: 600;
178
232
  text-transform: lowercase;
179
233
  }
180
- .wm-btns { gap: 6px !important; order: -1; }
181
- .wm-btn {
182
- width: 22px !important;
183
- height: 22px !important;
184
- border-radius: var(--r-1, 6px) !important;
185
- border: none !important;
186
- background: var(--os-bg-3) !important;
187
- color: var(--os-fg-2) !important;
188
- padding: 0 !important;
234
+ .ds-247420 .wm-btns { gap: var(--space-1-75); order: -1; }
235
+ .ds-247420 .wm-btn {
236
+ width: 22px;
237
+ height: 22px;
238
+ border-radius: var(--r-1);
239
+ border: none;
240
+ background: var(--os-bg-3);
241
+ color: var(--os-fg-2);
242
+ padding: 0;
189
243
  cursor: pointer;
190
- font: 600 12px var(--os-mono) !important;
191
- line-height: 1 !important;
192
- display: inline-flex !important;
244
+ font: 600 var(--fs-micro) var(--os-mono);
245
+ line-height: 1;
246
+ display: inline-flex;
193
247
  align-items: center;
194
248
  justify-content: center;
195
249
  transition: background 100ms ease, color 100ms ease;
196
250
  }
197
- .wm-btn:hover { background: var(--panel-hover, var(--os-bg-2)) !important; color: var(--os-fg) !important; }
251
+ .ds-247420 .wm-btn:hover { background: var(--panel-hover, var(--os-bg-2)); color: var(--os-fg); }
198
252
 
199
- .wm-resize {
200
- background: transparent !important;
201
- width: 14px !important;
202
- height: 14px !important;
253
+ .ds-247420 .wm-resize {
254
+ background: transparent;
255
+ width: 14px;
256
+ height: 14px;
203
257
  color: var(--os-fg-3);
204
258
  opacity: 0.4;
205
- font: 10px var(--os-mono);
206
- display: flex;
207
- align-items: flex-end;
208
- justify-content: flex-end;
209
- line-height: 1;
210
259
  }
211
- .wm-resize::after { content: '\25E2'; }
212
- .wm-body { background: var(--os-bg-1) !important; }
260
+ /* Resize grip via CSS gradient (matches wm.css); no decorative content glyph. */
261
+ .wm-resize::after {
262
+ content: '';
263
+ position: absolute;
264
+ right: 3px;
265
+ bottom: 3px;
266
+ width: 8px;
267
+ height: 8px;
268
+ background:
269
+ linear-gradient(135deg, transparent 0 45%, currentColor 45% 55%, transparent 55% 100%),
270
+ linear-gradient(135deg, transparent 0 70%, currentColor 70% 80%, transparent 80% 100%);
271
+ }
272
+ .ds-247420 .wm-body { background: var(--os-bg-1); }
213
273
 
214
274
  .os-side-rail {
215
275
  position: fixed;
216
- left: 0; top: 0; bottom: 0;
217
- width: var(--os-rail-w);
276
+ left: 0; top: var(--os-bar-h); bottom: var(--os-bar-h);
277
+ padding-left: env(safe-area-inset-left, 0px);
278
+ width: calc(var(--os-rail-w) + env(safe-area-inset-left, 0px));
218
279
  background: var(--os-bg-2);
219
280
  border: none;
220
281
  display: none;
221
282
  flex-direction: column;
222
283
  align-items: center;
223
- padding: 12px 0;
224
- gap: 6px;
225
- z-index: 9100;
284
+ padding: var(--space-2, 8px) 0;
285
+ gap: var(--space-1, 4px);
286
+ z-index: var(--z-dock);
226
287
  pointer-events: auto;
227
288
  }
228
289
  .os-rail-btn {
@@ -246,7 +307,7 @@ html, body {
246
307
  position: fixed;
247
308
  inset: 0;
248
309
  background: var(--os-bg-0);
249
- z-index: 9700;
310
+ z-index: var(--z-modal);
250
311
  pointer-events: auto;
251
312
  display: none;
252
313
  flex-direction: column;
@@ -259,13 +320,13 @@ html, body {
259
320
  .os-drawer.open { display: flex; opacity: 1; transform: translateY(0); }
260
321
  .os-drawer-head {
261
322
  display: flex; align-items: center; justify-content: space-between;
262
- padding: 12px 16px;
323
+ padding: var(--space-2, 8px) var(--space-3, 16px);
263
324
  border: none;
264
325
  height: 56px;
265
326
  box-sizing: border-box;
266
327
  background: var(--os-bg-2);
267
328
  }
268
- .os-drawer-title { color: var(--os-fg); font-family: var(--os-display); font-size: 22px; font-weight: 700; letter-spacing: -0.01em; text-transform: lowercase; }
329
+ .os-drawer-title { color: var(--os-fg); font-family: var(--os-display); font-size: var(--fs-xl); font-weight: 700; letter-spacing: -0.01em; text-transform: lowercase; }
269
330
  .os-drawer-close {
270
331
  width: 44px; height: 44px;
271
332
  border: none; background: transparent;
@@ -279,20 +340,24 @@ html, body {
279
340
  .os-drawer-close .ic svg { width: 22px; height: 22px; fill: none; stroke: currentColor; }
280
341
  .os-drawer-grid {
281
342
  display: grid;
282
- grid-template-columns: repeat(3, 1fr);
283
- gap: 12px;
284
- padding: 16px;
343
+ grid-template-columns: repeat(auto-fill, minmax(clamp(92px, 24vw, 140px), 1fr));
344
+ gap: var(--space-3, 16px);
345
+ padding: var(--space-3, 16px);
285
346
  overflow-y: auto;
286
347
  flex: 1;
287
348
  align-content: start;
349
+ max-width: 720px;
350
+ margin: 0 auto;
351
+ width: 100%;
352
+ box-sizing: border-box;
288
353
  }
289
354
  .os-drawer-tile {
290
- display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
291
- padding: 18px 8px;
355
+ display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-2, 8px);
356
+ padding: var(--space-3, 16px) var(--space-2, 8px);
292
357
  background: var(--os-bg-1);
293
358
  border: none;
294
359
  color: var(--os-fg);
295
- font: 13px var(--os-font);
360
+ font: var(--fs-tiny) var(--os-font);
296
361
  min-height: 100px;
297
362
  cursor: pointer;
298
363
  transition: background 80ms ease;
@@ -307,62 +372,72 @@ html, body {
307
372
  .os-drawer-tile .lbl { color: var(--os-fg); font-weight: 600; }
308
373
 
309
374
  .os-spacer { flex: 1 1 auto; }
310
- .os-tray { display: flex; align-items: center; gap: 6px; }
311
- .os-instances { display: flex; gap: 4px; margin-left: 8px; max-width: 280px; overflow-x: auto; overflow-y: hidden; scrollbar-width: thin; padding: 0 4px; }
375
+ .os-tray { display: flex; align-items: center; gap: var(--space-1-75); }
376
+ .os-instances { display: flex; gap: var(--space-1); margin-left: var(--space-2); flex: 0 1 40vw; max-width: 40vw; overflow-x: auto; overflow-y: hidden; scrollbar-width: thin; padding: 0 var(--space-1); -webkit-mask-image: linear-gradient(to right, transparent, black 12px, black calc(100% - 12px), transparent); mask-image: linear-gradient(to right, transparent, black 12px, black calc(100% - 12px), transparent); }
312
377
  .os-instances::-webkit-scrollbar { height: 4px; }
313
- .os-instances .os-btn { flex: 0 0 auto; min-width: 36px; padding: 2px 8px; font-family: var(--os-mono); font-size: 11px; }
378
+ .os-instances .os-btn { flex: 0 0 auto; min-width: 36px; padding: var(--space-hair) var(--space-2); font-family: var(--os-mono); font-size: var(--fs-nano); }
314
379
  .os-instances .os-btn.active { background: var(--panel-select, var(--os-accent-soft)); color: var(--os-fg); border-color: var(--panel-accent, var(--os-accent)); box-shadow: inset 0 0 0 1px var(--panel-accent, var(--os-accent)); }
315
380
  .os-menubar [data-role="home"] { display: none; }
316
- .os-root { position: fixed; inset: 0; display: flex; flex-direction: column; pointer-events: none; z-index: 8000; }
381
+ .os-root { position: fixed; inset: 0; display: flex; flex-direction: column; pointer-events: none; z-index: calc(var(--z-dock) + 2); }
317
382
  .os-menubar, .os-taskbar { pointer-events: auto; flex: 0 0 auto; }
318
383
  .os-taskbar { margin-top: auto; }
319
- .wm-root { top: var(--os-bar-h) !important; bottom: var(--os-bar-h) !important; inset: var(--os-bar-h) 0 var(--os-bar-h) 0 !important; }
320
-
321
- .app-pane { padding: 16px 18px; font: 14px var(--os-font); color: var(--os-fg); line-height: 1.55; overflow: auto; height: 100%; box-sizing: border-box; background: var(--os-bg-1); }
322
- .app-pane h2 { margin: 0 0 10px 0; color: var(--os-fg); font-family: var(--os-display); font-size: 22px; font-weight: 700; letter-spacing: -0.01em; line-height: 1.15; }
323
- .app-pane p { margin: 0 0 10px 0; color: var(--os-fg-2); }
324
- .app-pane ul { padding-left: 18px; margin: 6px 0 12px; color: var(--os-fg-2); }
325
- .app-pane li { padding: 2px 0; }
326
- .app-pane code { font: 12px var(--os-mono); background: var(--os-bg-2); padding: 1px 6px; border-radius: var(--r-1, 6px); color: var(--os-fg); }
384
+ /* Desktop default: the window layer sits BETWEEN the menubar and taskbar.
385
+ * Scoped (0,2,0) to beat wm.css's bare `.wm-root{inset:0}` by specificity
386
+ * rather than `!important`. The brand "bars overlay" block further below
387
+ * re-asserts inset:0 for .ds-247420 at equal specificity + later source order. */
388
+ .ds-247420 .wm-root { inset: var(--os-bar-h) 0 var(--os-bar-h) 0; }
389
+
390
+ .app-pane { padding: var(--space-3, 16px); font: var(--fs-sm, 14px) var(--os-font); color: var(--os-fg); line-height: var(--lh-base, 1.55); overflow: auto; height: 100%; box-sizing: border-box; background: var(--os-bg-1); }
391
+ .app-pane h2 { margin: 0 0 var(--space-2, 8px) 0; color: var(--os-fg); font-family: var(--os-display); font-size: var(--fs-xl, 21px); font-weight: 700; letter-spacing: -0.01em; line-height: var(--lh-snug, 1.2); }
392
+ .app-pane p { margin: 0 0 var(--space-2, 8px) 0; color: var(--os-fg-2); }
393
+ .app-pane ul { padding-left: var(--space-3, 16px); margin: var(--space-1, 4px) 0 var(--space-3, 16px); color: var(--os-fg-2); }
394
+ .app-pane li { padding: var(--space-1, 4px) 0; }
395
+ .app-pane code { font: var(--fs-tiny, 12px) var(--os-mono); background: var(--os-bg-2); padding: 1px var(--space-1-75); border-radius: var(--r-1, 6px); color: var(--os-fg); }
327
396
  .app-pane a { color: var(--os-accent); text-decoration: none; }
328
397
  .app-pane a:hover { text-decoration: underline; text-underline-offset: 2px; }
329
- .app-pane .meta { color: var(--os-fg-3); font-size: 12px; }
330
- .app-pane.mono { font: 12.5px var(--os-mono); padding: 12px; background: var(--os-bg-1); }
331
- .app-pane.mono .row { padding: 5px 8px; cursor: pointer; border-radius: var(--r-1, 6px); }
398
+ .app-pane .meta { color: var(--os-fg-3); font-size: var(--fs-tiny, 12px); }
399
+ .app-pane.mono { font: var(--fs-tiny, 12px) var(--os-mono); padding: var(--space-2, 8px); background: var(--os-bg-1); }
400
+ .app-pane.mono[data-component="monitor-app"] { white-space: pre-wrap; line-height: 1.7; }
401
+ .app-pane.mono .row { padding: var(--space-1) var(--space-2); cursor: pointer; border-radius: var(--r-1, 6px); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
332
402
  .app-pane.mono .row:hover { background: var(--panel-hover, var(--os-bg-2)); }
333
- .app-pane.mono .head { color: var(--os-accent); margin-bottom: 8px; padding-bottom: 6px; font-weight: 600; letter-spacing: 0.02em; }
334
- .app-pane.mono pre { background: var(--os-bg-2); padding: 10px 12px; margin: 8px 0 0 0; max-height: 220px; overflow: auto; white-space: pre-wrap; font: 11.5px var(--os-mono); border-radius: var(--r-1, 6px); color: var(--os-fg); border: none; }
335
- .app-shell-pane { margin: 0; padding: 8px; font: 12px var(--os-mono); color: var(--os-fg); background: var(--os-bg-0); height: 100%; overflow: auto; white-space: pre-wrap; box-sizing: border-box; }
403
+ .app-pane.mono .row.selected { background: var(--panel-select, var(--os-accent-soft)); box-shadow: inset 4px 0 0 var(--os-accent); }
404
+ .app-pane.mono .head { color: var(--os-accent); margin-bottom: var(--space-2); padding-bottom: var(--space-2); font-weight: 600; letter-spacing: 0.02em; border-bottom: 1px solid var(--os-bg-3); }
405
+ .app-pane.mono pre { background: var(--os-bg-2); padding: var(--space-2-5) var(--space-2-75); margin: var(--space-2) 0 0 0; max-height: 220px; overflow: auto; white-space: pre-wrap; font: var(--fs-nano) var(--os-mono); border-radius: var(--r-1, 6px); color: var(--os-fg); border: none; }
406
+ .app-shell-pane { margin: 0; padding: var(--space-2); font: var(--fs-micro) var(--os-mono); color: var(--os-fg); background: var(--os-bg-0); height: 100%; overflow: auto; white-space: pre-wrap; box-sizing: border-box; }
336
407
  .app-canvas { width: 100%; height: 100%; background: var(--os-bg-0); display: block; cursor: default; }
337
- .app-canvas.x-display { background: #0b0d10; }
408
+ .app-canvas.x-display { background: #0b0d10; } /* intentional: fake CRT/terminal black screen, not a theme surface */
338
409
  .app-frame { width: 100%; height: 100%; border: 0; background: var(--os-bg-0); }
339
410
  .app-iframe { width: 100%; height: 100%; border: 0; display: block; }
340
- .app-iframe.web { background: #ffffff; }
341
- .app-text-cursor { animation: app-cursor-blink 0.5s step-end infinite; }
342
- @keyframes app-cursor-blink { 50% { opacity: 0; } }
411
+ .app-iframe.web { background: #ffffff; } /* intentional: white canvas for embedded external web content that expects white */
412
+ .app-text-cursor { }
413
+
414
+ @media (prefers-reduced-motion: no-preference) {
415
+ .app-text-cursor { animation: app-cursor-blink 0.5s step-end infinite; }
416
+ @keyframes app-cursor-blink { 50% { opacity: 0; } }
417
+ }
343
418
  @media (max-width: 1023px) and (min-width: 768px) {
344
419
  .os-side-rail { display: flex; }
345
- .wm-root { left: var(--os-rail-w) !important; }
346
- .os-menubar { padding-left: calc(var(--os-rail-w) + 8px); }
347
- .os-taskbar { padding-left: calc(var(--os-rail-w) + 8px); }
420
+ .ds-247420 .wm-root { left: calc(var(--os-rail-w) + env(safe-area-inset-left, 0px)); }
421
+ .os-menubar { padding-left: calc(var(--os-rail-w) + env(safe-area-inset-left, 0px) + var(--space-2, 8px)); }
422
+ .os-taskbar { padding-left: calc(var(--os-rail-w) + env(safe-area-inset-left, 0px) + var(--space-2, 8px)); }
348
423
  .os-menubar .os-instances { display: flex; }
349
- .os-menu { left: calc(var(--os-rail-w) + 8px); }
424
+ .os-menu { left: calc(var(--os-rail-w) + env(safe-area-inset-left, 0px) + var(--space-2, 8px)); }
350
425
  }
351
426
 
352
427
  @media (max-width: 767px) {
353
428
  :root { --os-bar-h: var(--os-bar-h-mobile); }
354
429
  body { overscroll-behavior: none; }
355
- .os-menubar [data-role="home"] { display: inline-flex; min-width: var(--os-tap); min-height: var(--os-tap); padding: 0 12px; }
430
+ .os-menubar [data-role="home"] { display: inline-flex; min-width: var(--os-tap); min-height: var(--os-tap); padding: 0 var(--space-2-75); }
356
431
  .os-menubar [data-role="home"] .ic svg { width: 22px; height: 22px; }
357
- .os-menubar { font-size: 14px; padding: 0 8px; padding-top: env(safe-area-inset-top, 0); height: calc(var(--os-bar-h) + env(safe-area-inset-top, 0)); }
432
+ .os-menubar { font-size: var(--fs-xs); padding: env(safe-area-inset-top, 0) max(8px, env(safe-area-inset-right, 0px)) 0 max(8px, env(safe-area-inset-left, 0px)); height: calc(var(--os-bar-h) + env(safe-area-inset-top, 0)); }
358
433
  .os-menubar [data-role="apps"] { display: none; }
359
- .os-menubar .os-brand { font-size: 13px; margin-right: 6px; }
434
+ .os-menubar .os-brand { font-size: var(--fs-tiny); margin-right: var(--space-1-75); }
360
435
  .os-menubar [data-role="add"] { display: none; }
361
436
  .os-menubar .os-instances { display: none; }
362
437
  .os-clock { display: none; }
363
438
  .os-taskbar {
364
- padding: 6px 8px calc(6px + env(safe-area-inset-bottom, 0));
365
- gap: 6px;
439
+ padding: 6px max(8px, env(safe-area-inset-right, 0px)) calc(6px + env(safe-area-inset-bottom, 0)) max(8px, env(safe-area-inset-left, 0px));
440
+ gap: var(--space-1-75);
366
441
  overflow-x: auto;
367
442
  overflow-y: hidden;
368
443
  flex-wrap: nowrap;
@@ -376,37 +451,1142 @@ html, body {
376
451
  min-width: 120px;
377
452
  min-height: 44px;
378
453
  max-width: none;
379
- padding: 10px 14px;
454
+ padding: var(--space-2-5) 14px;
380
455
  scroll-snap-align: start;
381
- font-size: 13px;
456
+ font-size: var(--fs-tiny);
382
457
  }
383
- .os-task.focused { background: var(--panel-select, var(--os-accent-soft)); box-shadow: inset 4px 0 0 var(--os-accent); }
384
- .os-side-rail { display: none !important; }
385
- .os-menu { left: 8px; right: 8px; min-width: 0; top: calc(var(--os-bar-h) + env(safe-area-inset-top, 0) + 4px); }
386
- .wm-root {
387
- top: calc(var(--os-bar-h) + env(safe-area-inset-top, 0)) !important;
388
- bottom: calc(var(--os-bar-h) + env(safe-area-inset-bottom, 0)) !important;
389
- inset: calc(var(--os-bar-h) + env(safe-area-inset-top, 0)) 0 calc(var(--os-bar-h) + env(safe-area-inset-bottom, 0)) 0 !important;
458
+ .os-task.focused { background: var(--panel-select, var(--os-accent-soft)); box-shadow: inset var(--space-1) 0 0 var(--os-accent); }
459
+ .os-side-rail { display: none; }
460
+ .os-menu { left: var(--space-2); right: var(--space-2); min-width: 0; top: calc(var(--os-bar-h) + env(safe-area-inset-top, 0) + var(--space-1)); }
461
+ .ds-247420 .wm-root {
462
+ inset: calc(var(--os-bar-h) + env(safe-area-inset-top, 0)) 0 calc(var(--os-bar-h) + env(safe-area-inset-bottom, 0)) 0;
390
463
  }
391
- .wm-win {
392
- position: absolute !important;
393
- left: 0 !important; top: 0 !important;
394
- width: 100% !important; height: 100% !important;
395
- border: none !important;
396
- border-radius: 0 !important;
397
- box-shadow: none !important;
464
+ /* LOAD-BEARING !important: wm.css's own mobile block declares
465
+ * `.wm-win{left/top/width/height: ... !important}` (wm.css:157-165). An
466
+ * !important declaration can only be beaten by another !important one, so
467
+ * scoping alone cannot win here — these five must keep the flag. The
468
+ * non-!important properties in this rule do not, and were dropped. */
469
+ .ds-247420 .wm-win {
470
+ position: absolute;
471
+ left: 0 !important;
472
+ top: 0 !important;
473
+ width: 100% !important;
474
+ height: 100% !important;
475
+ border: none;
476
+ /* bare 0, not --r-0 (4px): a full-screen window has square corners.
477
+ * The radius lint intentionally does not flag unitless zero. */
478
+ border-radius: 0;
479
+ box-shadow: none;
398
480
  }
399
- .wm-bar {
400
- padding: 6px 10px !important;
401
- height: 36px !important;
402
- gap: 6px !important;
403
- cursor: default !important;
404
- touch-action: pan-y !important;
481
+ .ds-247420 .wm-bar {
482
+ padding: var(--space-1-75) var(--space-2-5);
483
+ height: 36px;
484
+ gap: var(--space-1-75);
485
+ cursor: default;
486
+ touch-action: pan-y;
405
487
  pointer-events: none;
406
488
  }
407
- .wm-bar .wm-title { pointer-events: auto; font-size: 13px !important; }
408
- .wm-bar .wm-btns { pointer-events: auto; }
409
- .wm-btns .wm-btn:nth-child(1), .wm-btns .wm-btn:nth-child(2) { display: none !important; }
410
- .wm-btns .wm-btn:nth-child(3) { width: 44px !important; height: 44px !important; border-radius: var(--r-1, 6px) !important; background: var(--os-bg-3) !important; color: var(--os-fg) !important; }
411
- .wm-resize { display: none !important; }
489
+ .ds-247420 .wm-bar .wm-title { pointer-events: auto; font-size: var(--fs-tiny); }
490
+ .ds-247420 .wm-bar .wm-btns { pointer-events: auto; }
491
+ .ds-247420 .wm-btns .wm-btn:nth-child(1), .ds-247420 .wm-btns .wm-btn:nth-child(2) { display: none; }
492
+ .ds-247420 .wm-btns .wm-btn:nth-child(3) { width: 44px; height: 44px; border-radius: var(--r-1); background: var(--os-bg-3); color: var(--os-fg); }
493
+ .ds-247420 .wm-resize, .ds-247420 .wm-edge { display: none; }
494
+ }
495
+
496
+ /* ---- data-attr theme/accent: deferred to the canonical theme ----
497
+ * The OS shell no longer redefines surface or accent colors per theme. The
498
+ * --os-* aliases above consume the canonical semantic tokens (--bg/--fg/
499
+ * --accent), and colors_and_type.css already drives [data-theme] (paper/ink/
500
+ * auto, with color-scheme + --danger tuning) and [data-accent] (green/purple/
501
+ * mascot). Re-declaring the os surface or accent tokens per theme here only
502
+ * re-introduced drift (ink vs canonical ink; purple vs canonical purple).
503
+ * Density bar-height is genuinely OS-shell-specific, so it stays;
504
+ * --os-accent-tint maps to the canonical accent tint. */
505
+ :root { --os-accent-tint: var(--accent-tint); }
506
+ :root[data-density="compact"] { --os-bar-h: 28px; --os-tile-pad: 6px; --os-gap: 6px; }
507
+ :root[data-density="comfortable"] { --os-bar-h: 36px; --os-tile-pad: 10px; --os-gap: 10px; }
508
+ :root[data-density="spacious"] { --os-bar-h: 44px; --os-tile-pad: 14px; --os-gap: 14px; }
509
+
510
+ /* ============================================================
511
+ * thebird brand layer (migrated from thebird's local thebird-brand.css
512
+ * 2026-05-26). thebird ships ZERO design CSS — every rule below lives here
513
+ * upstream and is delivered via the published package. Scoped to .ds-247420
514
+ * (thebird's OS root carries that class). Brand identity: Inter + green accent
515
+ * + uniform 34px bars + Windows-style right-side window controls.
516
+ *
517
+ * NOTE: this no longer globally overrides --paper. A global :root override
518
+ * silently mutated the base theme for EVERY portfolio consumer (canonical
519
+ * --paper is #F6F5F1; thebird wanted #F5F0E4). The warm-paper variant is now a
520
+ * named preset [data-theme="thebird"] a consumer opts into, leaving the base
521
+ * theme untouched. The [data-theme="thebird"] COLOR preset now lives in the
522
+ * canonical colors_and_type.css (a first-class swappable theme); only the
523
+ * OS-shell chrome (bar geometry, typography alias) stays here scoped to
524
+ * .ds-247420.
525
+ * ============================================================ */
526
+ .ds-247420 {
527
+ --ff-ui: var(--ff-body);
528
+ /* uniform bar height: 22px button + 6px+6px padding = 34px.
529
+ * LOAD-BEARING !important: `:root[data-density="compact"|"comfortable"|
530
+ * "spacious"]` (this file, "data-attr theme/accent" block above) redefines
531
+ * --os-bar-h at (0,2,0) — HIGHER than this rule's (0,1,0) — on the same
532
+ * <html> element. Without the flag the density presets silently override
533
+ * the brand's uniform 34px bar geometry. */
534
+ --os-bar-h: 34px !important;
535
+ --os-bar-h-mobile: 34px !important;
536
+ --os-dock-h: 34px !important;
537
+ /* Brand identity, scoped so it never leaks to other portfolio consumers.
538
+ * The base theme's auto/paper/ink presets let --accent drift to the bright
539
+ * --green-2 (#3A9A34) in ink mode; thebird's brand accent is the deep green
540
+ * #247420 (--green) in EVERY resolved mode, and its paper is the warm
541
+ * #F5F0E4 variant. Re-assert both here rather than forcing the consumer to
542
+ * opt into [data-theme="thebird"], which the OS boots past with
543
+ * data-theme="auto". --accent-bright is pinned too so the ink-mode
544
+ * --accent: var(--accent-bright, ...) chain still resolves to brand green.
545
+ * LOAD-BEARING !important: colors_and_type.css defines --accent on
546
+ * `[data-accent="acid"|"green"|...]` and `[data-theme="ink"]` — attribute
547
+ * selectors at (0,1,0), the SAME specificity as this `.ds-247420` class
548
+ * rule, on the same <html> element. colors_and_type.css is always loaded
549
+ * first, but equal specificity + later source order would still let a
550
+ * [data-accent] preset win. The flag is the only thing pinning brand green. */
551
+ --accent: var(--green) !important;
552
+ --accent-bright: var(--green) !important;
553
+ --paper: #F5F0E4;
554
+ }
555
+ /* Window titlebar matches the 34px bar height; clamp so menubar/taskbar/titlebar
556
+ * heights are uniform. */
557
+ .ds-247420 .wm-bar { height: 34px; min-height: 34px; box-sizing: border-box; padding-top: var(--space-1); padding-bottom: var(--space-1); }
558
+ /* Bars overlay windows: .wm-root fills full viewport, menubar/taskbar paint on top.
559
+ * Tilde key hides them via .bars-hidden on <html>.
560
+ * Wins over the desktop `.ds-247420 .wm-root{inset:var(--os-bar-h)...}` above by
561
+ * equal specificity + later source order (both are now (0,2,0)) — no flag needed. */
562
+ .ds-247420 .wm-root { inset: 0; }
563
+ /* With bars overlaying the wm-root, a maximized window's 34px titlebar would
564
+ * sit exactly under the 34px menubar (untappable) and its body under the
565
+ * taskbar; keep maximized windows between the bars on desktop/tablet. The
566
+ * mobile band re-asserts its own contract in the responsive layer below. */
567
+ @media (min-width: 768px) {
568
+ /* LOAD-BEARING !important: wm.css declares `.wm-win.wm-max{top:0!important;
569
+ * height:100%!important}` (wm.css:137-143). Only another !important can beat
570
+ * an !important, regardless of specificity. */
571
+ .ds-247420 .wm-win.wm-max {
572
+ top: var(--os-bar-h) !important;
573
+ height: calc(100% - (var(--os-bar-h) * 2)) !important;
574
+ }
575
+ }
576
+ /* Instance filtering hides off-instance windows via class, not inline style.
577
+ * LOAD-BEARING !important: must beat wm.css's `.wm-win.wm-max{...!important}`
578
+ * geometry and any display set by the window's own state classes — a hidden
579
+ * window must stay hidden no matter which layout rule matched it. */
580
+ .ds-247420 .wm-win.wm-inst-hidden,
581
+ .wm-win.wm-inst-hidden { display: none !important; }
582
+ /* LOAD-BEARING !important: `.os-menubar`/`.os-taskbar` carry `display:flex`
583
+ * from rules at equal-or-higher specificity in this sheet and in the responsive
584
+ * bands below; the tilde-key hide must beat all of them unconditionally. */
585
+ html.bars-hidden .os-menubar,
586
+ html.bars-hidden .os-taskbar { display: none !important; }
587
+ .ds-247420 .os-task { border-radius: var(--r-pill, 999px); }
588
+ .os-brand { font-family: var(--ff-display); font-weight: 700; }
589
+
590
+ /* thebird windows use Windows/Linux-style right-side controls (upstream theme
591
+ * sets .wm-btns order:-1 for a macOS look; thebird overrides to natural DOM
592
+ * order so controls render on the right). */
593
+ .ds-247420 .wm-btns { order: 0; }
594
+
595
+ /* OS-shell z-index layering: .os-root above .wm-root so its .os-menu dropdowns
596
+ * paint above windows. */
597
+ .ds-247420 .os-root { z-index: calc(var(--z-dock) + 2); }
598
+ .ds-247420 .os-menubar { z-index: calc(var(--z-dock) + 1); position: relative; }
599
+ .ds-247420 .os-taskbar { z-index: calc(var(--z-dock) + 1); position: relative; }
600
+ .ds-247420 .os-menu { z-index: var(--z-dropdown); }
601
+ .ds-247420 .wm-root { z-index: var(--z-window); }
602
+ .ds-247420 .launcher-dock { z-index: var(--z-dock); }
603
+
604
+ /* Touch/gesture hardening. */
605
+ html.ds-247420 { touch-action: pan-x pan-y; overscroll-behavior: none; -webkit-text-size-adjust: 100%; }
606
+ .ds-247420 .os-root, .ds-247420 .wm-root { touch-action: none; }
607
+ .ds-247420 .wm-body { overscroll-behavior: contain; touch-action: auto; }
608
+ .ds-247420 .wm-bar, .ds-247420 .wm-resize { -webkit-touch-callout: none; user-select: none; }
609
+ .ds-247420 .os-menubar, .ds-247420 .os-taskbar, .ds-247420 .launcher-dock { user-select: none; -webkit-user-select: none; }
610
+
611
+ /* Aero-snap drag preview. */
612
+ .ds-247420 .wm-snap-preview {
613
+ position: fixed;
614
+ pointer-events: none;
615
+ display: none;
616
+ z-index: calc(var(--z-window) + 1);
617
+ background: color-mix(in oklab, var(--os-accent) 20%, transparent);
618
+ border: 2px solid var(--os-accent);
619
+ border-radius: var(--r-0);
620
+ /* Animate only geometry, never `all` — `transition: all` animated display/
621
+ * opacity too, producing a one-frame ghost on show (jank-pass fix). */
622
+ transition: left 80ms ease, top 80ms ease, width 80ms ease, height 80ms ease;
623
+ }
624
+ @media (prefers-reduced-motion: reduce) {
625
+ .ds-247420 .wm-snap-preview { transition: none; }
626
+ }
627
+
628
+ /* Alt+Tab window switcher overlay. */
629
+ .ds-247420 .wm-switcher {
630
+ position: fixed;
631
+ left: 50%;
632
+ top: 50%;
633
+ transform: translate(-50%, -50%);
634
+ padding: var(--space-3) var(--space-3-5);
635
+ background: var(--os-bg-2);
636
+ border: 1px solid var(--os-accent);
637
+ border-radius: var(--r-0);
638
+ z-index: var(--z-modal);
639
+ font-family: var(--ff-ui);
640
+ color: var(--os-fg);
641
+ min-width: 280px;
642
+ box-shadow: var(--shadow-overlay);
643
+ }
644
+ .ds-247420 .wm-switcher-item {
645
+ padding: var(--space-2) var(--space-2-75);
646
+ border-radius: var(--r-0);
647
+ display: flex;
648
+ align-items: center;
649
+ gap: var(--space-2-5);
650
+ }
651
+ .ds-247420 .wm-switcher-item.active {
652
+ background: var(--os-accent);
653
+ color: var(--paper);
654
+ }
655
+
656
+ /* freddie-chat font family (upstream still ships Nunito; brand is Inter). */
657
+ .ds-247420 freddie-chat,
658
+ .ds-247420 freddie-chat .chat-bubble,
659
+ .ds-247420 freddie-chat .chat-meta,
660
+ .ds-247420 freddie-chat .chat-head {
661
+ font-family: var(--ff-ui);
412
662
  }
663
+ .ds-247420 freddie-chat .chat-bubble pre,
664
+ .ds-247420 freddie-chat .chat-bubble code {
665
+ font-family: var(--ff-mono, ui-monospace, Menlo, Consolas, monospace);
666
+ }
667
+ .ds-247420 freddie-chat .chat-head {
668
+ font-family: var(--ff-display, system-ui, sans-serif);
669
+ font-weight: 700;
670
+ }
671
+
672
+ /* Session-switch crossfade. */
673
+ .ds-247420 .wm-win { transition: opacity 140ms ease; }
674
+ .tb-switching .ds-247420 .wm-win { opacity: 0.4; }
675
+ @media (prefers-reduced-motion: reduce) {
676
+ .ds-247420 .wm-win { transition: none; }
677
+ .tb-switching .ds-247420 .wm-win { opacity: 1; }
678
+ }
679
+
680
+ /* ---- Session-UX surfaces: chips + modals + empty state ---- */
681
+ .ds-247420 .os-instances { display: flex; gap: var(--space-1-75); align-items: center; flex: 0 1 auto; min-width: 0; height: var(--os-bar-h, 34px); overflow-x: auto; overflow-y: hidden; padding: 0 var(--space-1); scrollbar-width: none; }
682
+ .ds-247420 .os-instances::-webkit-scrollbar { display: none; }
683
+
684
+ .ds-247420 .tb-sess-chip {
685
+ display: inline-flex; align-items: center; gap: var(--space-1-75);
686
+ height: 22px; padding: 0 var(--space-1-75) 0 var(--space-2);
687
+ border-radius: var(--r-pill, 999px);
688
+ background: color-mix(in oklab, var(--fg) 6%, transparent);
689
+ color: var(--fg);
690
+ font-family: var(--ff-ui); font-size: var(--fs-nano); font-weight: 500;
691
+ cursor: pointer;
692
+ border: 1px solid transparent;
693
+ transition: background 120ms ease, border-color 120ms ease;
694
+ user-select: none; flex: 0 0 auto;
695
+ }
696
+ .ds-247420 .tb-sess-chip:hover { background: color-mix(in oklab, var(--fg) 12%, transparent); }
697
+ .ds-247420 .tb-sess-chip.active {
698
+ background: color-mix(in oklab, var(--os-accent) 18%, transparent);
699
+ border-color: var(--os-accent);
700
+ color: var(--fg);
701
+ }
702
+ .ds-247420 .tb-sess-chip.active .tb-sess-dot { background: var(--os-accent); box-shadow: 0 0 0 2px color-mix(in oklab, var(--os-accent) 30%, transparent); }
703
+ .ds-247420 .tb-sess-dot { width: 6px; height: 6px; border-radius: 50%; background: color-mix(in oklab, var(--fg) 40%, transparent); flex: 0 0 6px; }
704
+ .ds-247420 .tb-sess-label { max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
705
+ .ds-247420 .tb-sess-badge {
706
+ display: inline-flex; align-items: center; justify-content: center;
707
+ min-width: 16px; height: 14px; padding: 0 var(--space-1);
708
+ border-radius: var(--r-0);
709
+ background: color-mix(in oklab, var(--fg) 14%, transparent);
710
+ font-size: var(--fs-pico); font-weight: 600; line-height: 1;
711
+ }
712
+ .ds-247420 .tb-sess-chip.active .tb-sess-badge { background: color-mix(in oklab, var(--os-accent) 30%, transparent); }
713
+ .ds-247420 .tb-sess-x {
714
+ width: 16px; height: 16px; line-height: 14px; text-align: center;
715
+ border: 0; padding: 0; background: transparent;
716
+ border-radius: 50%; cursor: pointer; color: inherit;
717
+ font-size: var(--fs-xs); font-weight: 600;
718
+ opacity: 0.55; transition: opacity 120ms ease, background 120ms ease;
719
+ }
720
+ .ds-247420 .tb-sess-chip:hover .tb-sess-x { opacity: 1; }
721
+ .ds-247420 .tb-sess-x:hover { background: color-mix(in oklab, var(--danger) 30%, transparent); color: var(--on-color); }
722
+
723
+ .ds-247420 .tb-sess-add {
724
+ display: inline-flex; align-items: center; gap: var(--space-1);
725
+ height: 22px; padding: 0 var(--space-2-5);
726
+ border: 1px dashed color-mix(in oklab, var(--fg) 30%, transparent);
727
+ background: transparent;
728
+ border-radius: var(--r-pill, 999px);
729
+ font-family: var(--ff-ui); font-size: var(--fs-nano); font-weight: 500;
730
+ color: var(--fg);
731
+ cursor: pointer;
732
+ margin-left: var(--space-1-75);
733
+ transition: border-color 120ms ease, background 120ms ease;
734
+ }
735
+ .ds-247420 .tb-sess-add:hover {
736
+ border-color: var(--os-accent);
737
+ background: color-mix(in oklab, var(--os-accent) 8%, transparent);
738
+ }
739
+ .ds-247420 .tb-sess-add-plus { font-size: var(--fs-tiny); line-height: 1; }
740
+ .ds-247420 .tb-sess-add-lbl { line-height: 1; }
741
+
742
+ .tb-sess-overlay {
743
+ position: fixed; inset: 0; z-index: var(--z-modal);
744
+ background: var(--scrim);
745
+ display: flex; align-items: center; justify-content: center;
746
+ animation: tb-sess-fade 120ms ease;
747
+ }
748
+ @keyframes tb-sess-fade { from { opacity: 0; } to { opacity: 1; } }
749
+ .tb-sess-modal {
750
+ background: var(--bg);
751
+ color: var(--fg);
752
+ border-radius: var(--r-1);
753
+ box-shadow: var(--shadow-overlay);
754
+ min-width: 320px; max-width: 480px;
755
+ border: 1px solid color-mix(in oklab, var(--fg) 12%, transparent);
756
+ overflow: hidden;
757
+ font-family: var(--ff-ui);
758
+ }
759
+ .tb-sess-modal-head { display: flex; align-items: center; justify-content: space-between; padding: var(--space-2-75) var(--space-3); border-bottom: 1px solid color-mix(in oklab, var(--fg) 8%, transparent); }
760
+ .tb-sess-modal-title { font-weight: 600; font-size: var(--fs-xs); }
761
+ .tb-sess-modal-x { background: transparent; border: 0; font-size: var(--fs-xl); line-height: 1; cursor: pointer; color: inherit; padding: 0 var(--space-1); }
762
+ .tb-sess-modal-body { padding: var(--space-3); display: flex; flex-direction: column; gap: var(--space-2); }
763
+ .tb-sess-modal-lbl { font-size: var(--fs-nano); font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; opacity: 0.7; }
764
+ .tb-sess-modal-input {
765
+ width: 100%; box-sizing: border-box;
766
+ height: 32px; padding: 0 var(--space-2-5);
767
+ border: 1px solid color-mix(in oklab, var(--fg) 20%, transparent);
768
+ border-radius: var(--r-0);
769
+ background: color-mix(in oklab, var(--fg) 6%, var(--bg));
770
+ color: inherit;
771
+ font-family: inherit; font-size: var(--fs-tiny);
772
+ }
773
+ .tb-sess-modal-input:focus { outline: none; border-color: var(--os-accent); box-shadow: 0 0 0 3px color-mix(in oklab, var(--os-accent) 20%, transparent); }
774
+ .tb-sess-modal-hint { font-size: var(--fs-nano); opacity: 0.7; }
775
+ .tb-sess-modal-msg { font-size: var(--fs-tiny); }
776
+ .tb-sess-modal-foot { display: flex; justify-content: flex-end; gap: var(--space-2); padding: var(--space-2-75) var(--space-3); border-top: 1px solid color-mix(in oklab, var(--fg) 8%, transparent); }
777
+ .tb-sess-modal-btn {
778
+ height: 28px; padding: 0 14px;
779
+ border-radius: var(--r-0); border: 1px solid color-mix(in oklab, var(--fg) 20%, transparent);
780
+ background: transparent; color: inherit;
781
+ font-family: inherit; font-size: var(--fs-micro); font-weight: 500; cursor: pointer;
782
+ transition: background 120ms ease, border-color 120ms ease;
783
+ }
784
+ .tb-sess-modal-btn:hover { background: color-mix(in oklab, var(--fg) 6%, transparent); }
785
+ .tb-sess-modal-btn.primary { background: var(--os-accent); color: var(--on-color); border-color: var(--os-accent); }
786
+ .tb-sess-modal-btn.primary:hover { background: color-mix(in oklab, var(--os-accent) 80%, var(--ink)); }
787
+ .tb-sess-modal-btn.danger { background: var(--danger); color: var(--on-color); border-color: var(--danger); }
788
+ .tb-sess-modal-btn.danger:hover { background: color-mix(in oklab, var(--danger) 80%, var(--ink)); }
789
+
790
+ .tb-sess-empty {
791
+ position: fixed; inset: 0; z-index: var(--z-window);
792
+ display: flex; align-items: center; justify-content: center;
793
+ pointer-events: none;
794
+ }
795
+ .tb-sess-empty-card {
796
+ pointer-events: auto;
797
+ background: var(--bg);
798
+ color: var(--fg);
799
+ border: 1px solid color-mix(in oklab, var(--fg) 14%, transparent);
800
+ border-radius: var(--r-2);
801
+ box-shadow: var(--shadow-3);
802
+ padding: var(--space-5) 40px; text-align: center;
803
+ font-family: var(--ff-ui);
804
+ max-width: 420px;
805
+ }
806
+ .tb-sess-empty-title { font-size: var(--fs-lg); font-weight: 600; margin-bottom: var(--space-1-75); }
807
+ .tb-sess-empty-sub { font-size: var(--fs-tiny); opacity: 0.7; margin-bottom: var(--space-3-5); }
808
+ .tb-sess-empty-cta {
809
+ height: 40px; padding: 0 var(--space-4);
810
+ background: var(--os-accent); color: var(--on-color);
811
+ border: 0; border-radius: var(--r-pill, 999px);
812
+ font-family: inherit; font-size: var(--fs-xs); font-weight: 600;
813
+ cursor: pointer;
814
+ transition: transform 120ms ease, box-shadow 120ms ease;
815
+ }
816
+ .tb-sess-empty-cta:hover { transform: translateY(-1px); box-shadow: 0 4px 16px color-mix(in oklab, var(--os-accent) 40%, transparent); }
817
+
818
+ /* ---- Workspaces app — full-window session manager card grid ---- */
819
+ .ds-247420 .tb-sessions-app { display: flex; flex-direction: column; height: 100%; font-family: var(--ff-ui); color: var(--fg); background: var(--bg); }
820
+ .ds-247420 .tb-sessions-bar { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-2-5) 14px; border-bottom: 1px solid color-mix(in oklab, var(--fg) 10%, transparent); }
821
+ .ds-247420 .tb-sessions-title { font-weight: 600; font-size: var(--fs-xs); }
822
+ .ds-247420 .tb-sessions-count { font-size: var(--fs-micro); opacity: 0.6; margin-left: var(--space-1); }
823
+ .ds-247420 .tb-sessions-spacer { flex: 1; }
824
+ .ds-247420 .tb-sessions-btn { height: 28px; padding: 0 var(--space-2-75); border-radius: var(--r-0); border: 1px solid color-mix(in oklab, var(--fg) 20%, transparent); background: transparent; color: inherit; font-size: var(--fs-micro); font-weight: 500; cursor: pointer; font-family: inherit; }
825
+ .ds-247420 .tb-sessions-btn:hover { background: color-mix(in oklab, var(--fg) 6%, transparent); }
826
+ .ds-247420 .tb-sessions-btn.primary { background: var(--os-accent); color: var(--on-color); border-color: var(--os-accent); }
827
+ .ds-247420 .tb-sessions-btn.danger { background: var(--danger); color: var(--on-color); border-color: var(--danger); }
828
+ .ds-247420 .tb-sessions-btn:disabled { opacity: 0.4; cursor: not-allowed; }
829
+ .ds-247420 .tb-sessions-grid { flex: 1; overflow: auto; padding: 14px; display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--space-2-75); align-content: start; }
830
+ .ds-247420 .tb-sessions-card { padding: var(--space-2-75); border-radius: var(--r-1, 10px); border: 1px solid color-mix(in oklab, var(--fg) 12%, transparent); background: color-mix(in oklab, var(--fg) 3%, transparent); display: flex; flex-direction: column; gap: var(--space-2); transition: border-color 120ms ease, transform 120ms ease; }
831
+ .ds-247420 .tb-sessions-card:hover { border-color: var(--os-accent); }
832
+ .ds-247420 .tb-sessions-card.active { border-color: var(--os-accent); box-shadow: 0 0 0 2px color-mix(in oklab, var(--os-accent) 30%, transparent); }
833
+ .ds-247420 .tb-sessions-card-head { display: flex; align-items: center; gap: var(--space-2); }
834
+ .ds-247420 .tb-sessions-card-check { width: 14px; height: 14px; cursor: pointer; flex: 0 0 14px; }
835
+ .ds-247420 .tb-sessions-card-name { font-weight: 600; font-size: var(--fs-tiny); flex: 1; outline: none; padding: var(--space-hair) var(--space-1); border-radius: var(--r-0); }
836
+ .ds-247420 .tb-sessions-card-name[contenteditable="true"] { background: color-mix(in oklab, var(--fg) 8%, transparent); }
837
+ .ds-247420 .tb-sessions-card-id { font-family: var(--os-mono, monospace); font-size: var(--fs-pico); opacity: 0.5; }
838
+ .ds-247420 .tb-sessions-card-stats { font-size: var(--fs-nano); opacity: 0.7; display: flex; gap: var(--space-2-75); }
839
+ .ds-247420 .tb-sessions-card-actions { display: flex; gap: var(--space-1-75); margin-top: var(--space-1); }
840
+ .ds-247420 .tb-sessions-card-actions button { flex: 1; height: 24px; font-size: var(--fs-nano); padding: 0 var(--space-2); border-radius: var(--r-0); border: 1px solid color-mix(in oklab, var(--fg) 16%, transparent); background: transparent; color: inherit; cursor: pointer; font-family: inherit; }
841
+ .ds-247420 .tb-sessions-card-actions button:hover { background: color-mix(in oklab, var(--fg) 6%, transparent); }
842
+ .ds-247420 .tb-sessions-card-actions button.danger:hover { background: var(--danger); color: var(--on-color); border-color: var(--danger); }
843
+ .ds-247420 .tb-sessions-empty-mid { padding: 40px; text-align: center; opacity: 0.6; font-size: var(--fs-tiny); }
844
+
845
+ /* ---- prefers-contrast: more — strengthen borders + focus so the translucent
846
+ chrome stays legible under forced/high-contrast user settings. ---- */
847
+ @media (prefers-contrast: more) {
848
+ .ds-247420 .wm-win { border-color: var(--fg); }
849
+ .ds-247420 .wm-bar { border-bottom: 1px solid var(--fg); }
850
+ .ds-247420 .os-menubar,
851
+ .ds-247420 .os-taskbar { border-color: var(--fg); }
852
+ .ds-247420 .tb-sess-chip,
853
+ .ds-247420 .tb-sessions-card,
854
+ .ds-247420 .tb-sessions-btn { border-color: var(--fg); }
855
+ .ds-247420 :focus-visible { outline: 2px solid var(--fg); outline-offset: 2px; }
856
+ }
857
+
858
+ /* ---- print: a web-OS desktop has no meaningful print form. Suppress the
859
+ live chrome so a Ctrl+P does not waste pages on translucent panels. ---- */
860
+ /* LOAD-BEARING !important throughout this block. Print must defeat two things
861
+ * no selector can outrank: (1) wm.css's `.wm-win.wm-max{left/top/width/height
862
+ * !important}` and its mobile `.wm-win{...!important}` band, and (2) the
863
+ * INLINE styles createWM()/renderWindow() write directly onto .wm-win/.wm-canvas
864
+ * for drag, resize and pan (element style attribute beats every stylesheet
865
+ * rule). Flattening the desktop to static flow for print is only possible by
866
+ * force. Do not remove. */
867
+ @media print {
868
+ .os-menubar, .os-taskbar, .wm-snap-preview,
869
+ .tb-sess-overlay, .tb-switching, .wm-switcher { display: none !important; }
870
+ .wm-root, .wm-canvas { position: static !important; transform: none !important; overflow: visible !important; }
871
+ .wm-win { position: static !important; box-shadow: none !important; border: 1px solid var(--fg) !important; page-break-inside: avoid; margin: 0 0 var(--space-2-75); width: auto !important; height: auto !important; }
872
+ .wm-win.wm-min { display: none !important; }
873
+ }
874
+
875
+ /* ============================================================================
876
+ * Relocated from thebird (2026-05-30) — moves design CSS out of docs/*.js
877
+ * modules to enforce the zero-design-CSS-in-thebird rule.
878
+ * Source files: docs/chat-config.js, docs/fsbrowse-app.js, docs/wm.js,
879
+ * docs/apps.js (todo-app), docs/freddie-keys.js.
880
+ * ============================================================================ */
881
+
882
+ /* ---- chat-config strip (was: docs/chat-config.js ensureStyles) ---- */
883
+ .ds-247420 .wm-body:has(> .freddie-chat-wrap) { display: flex; flex-direction: column; padding: 0; }
884
+ .ds-247420 .freddie-chat-wrap { position: absolute; inset: 0; display: flex; flex-direction: column; min-height: 0; }
885
+ .ds-247420 .freddie-chat-wrap > freddie-chat { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
886
+ .ds-247420 .cc-strip { flex: 0 0 auto; font-size: var(--fs-micro); border-bottom: 1px solid color-mix(in oklab, var(--fg) 16%, transparent); }
887
+ .ds-247420 .cc-toggle { all: unset; cursor: pointer; display: inline-flex; align-items: center; gap: .4em; padding: .35em .7em; color: var(--fg, inherit); opacity: .75; }
888
+ .ds-247420 .cc-toggle:hover { opacity: 1; }
889
+ .ds-247420 .cc-toggle.cc-open { opacity: 1; font-weight: 600; }
890
+ .ds-247420 .cc-body { padding: .5em .7em .7em; display: flex; flex-direction: column; gap: .5em; max-height: 42vh; max-height: 42dvh; overflow: auto; }
891
+ .ds-247420 .cc-body[hidden] { display: none; }
892
+ .ds-247420 .cc-row { display: flex; gap: .6em; flex-wrap: wrap; }
893
+ .ds-247420 .cc-row > .cc-field { flex: 1 1 140px; min-width: 0; }
894
+ .ds-247420 .cc-field { display: flex; flex-direction: column; gap: .2em; }
895
+ .ds-247420 .cc-field-label { font-size: var(--fs-nano); opacity: .65; text-transform: lowercase; }
896
+ .ds-247420 .cc-strip select,
897
+ .ds-247420 .cc-strip input { font: inherit; font-size: var(--fs-micro); padding: .3em .4em; width: 100%; box-sizing: border-box; background: var(--panel-1, transparent); color: var(--fg, inherit); border: 1px solid color-mix(in oklab, var(--fg) 18%, transparent); border-radius: var(--r-0); }
898
+ .ds-247420 .cc-acp-mode-wrap { display: flex; align-items: center; gap: .4em; }
899
+ .ds-247420 .cc-acp-mode-wrap select { flex: 1; }
900
+ .ds-247420 .cc-acp-status { opacity: .5; font-size: var(--fs-tiny); }
901
+ .ds-247420 .cc-acp-status.cc-reachable { opacity: 1; color: var(--os-accent, #247420); }
902
+ .ds-247420 .cc-details > summary { cursor: pointer; opacity: .7; padding: .2em 0; }
903
+ .ds-247420 .cc-skills,
904
+ .ds-247420 .cc-plugins { display: flex; flex-wrap: wrap; gap: .3em .8em; padding: .3em 0; }
905
+ .ds-247420 .cc-check { display: inline-flex; align-items: center; gap: .3em; cursor: pointer; }
906
+ .ds-247420 .cc-check input { width: auto; }
907
+ .ds-247420 .cc-empty { opacity: .5; font-style: italic; }
908
+
909
+ /* ---- fsbrowse files app (was: docs/fsbrowse-app.js ensureStyles) ---- */
910
+ .ds-247420 .fsb-root { position: absolute; inset: 0; display: flex; flex-direction: column; min-height: 0; font-size: var(--fs-tiny); color: var(--fg, inherit); }
911
+ .ds-247420 .fsb-bar { flex: 0 0 auto; display: flex; align-items: center; gap: .4em; padding: .45em .6em; border-bottom: 1px solid color-mix(in oklab, var(--fg) 16%, transparent); flex-wrap: wrap; }
912
+ .ds-247420 .fsb-crumbs { flex: 1; display: flex; align-items: center; gap: .2em; min-width: 0; overflow: auto; white-space: nowrap; }
913
+ .ds-247420 .fsb-crumb { cursor: pointer; opacity: .75; text-decoration: none; color: inherit; }
914
+ .ds-247420 .fsb-crumb:hover { opacity: 1; text-decoration: underline; }
915
+ .ds-247420 .fsb-crumb-sep { opacity: .4; }
916
+ .ds-247420 .fsb-btn { all: unset; cursor: pointer; padding: .3em .55em; border: 1px solid color-mix(in oklab, var(--fg) 18%, transparent); border-radius: var(--r-0); opacity: .85; }
917
+ .ds-247420 .fsb-btn:hover { opacity: 1; background: color-mix(in oklab, var(--fg) 6%, transparent); }
918
+ .ds-247420 .fsb-list { flex: 1; min-height: 0; overflow: auto; padding: .3em 0; }
919
+ .ds-247420 .fsb-row { display: flex; align-items: center; gap: .6em; padding: .35em .7em; cursor: pointer; border: 0; }
920
+ .ds-247420 .fsb-row:hover { background: color-mix(in oklab, var(--fg) 6%, transparent); }
921
+ .ds-247420 .fsb-row:focus-visible { outline: 2px solid var(--os-accent); outline-offset: -2px; background: color-mix(in oklab, var(--fg) 6%, transparent); }
922
+ .ds-247420 .fsb-icon { width: 1.3em; text-align: center; flex: 0 0 auto; }
923
+ .ds-247420 .fsb-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
924
+ .ds-247420 .fsb-size { opacity: .55; font-size: var(--fs-nano); font-variant-numeric: tabular-nums; flex: 0 0 auto; }
925
+ .ds-247420 .fsb-actions { display: flex; gap: .3em; flex: 0 0 auto; opacity: 0; }
926
+ /* Reveal row actions on hover OR keyboard focus-within OR coarse-pointer (touch),
927
+ * so they aren't hover-only dead controls for keyboard/touch users. */
928
+ .ds-247420 .fsb-row:hover .fsb-actions,
929
+ .ds-247420 .fsb-row:focus-within .fsb-actions { opacity: 1; }
930
+ @media (hover: none) { .ds-247420 .fsb-actions { opacity: 1; } }
931
+ .ds-247420 .fsb-act { all: unset; cursor: pointer; padding: .1em .35em; border-radius: var(--r-0); opacity: .7; font-size: var(--fs-micro); }
932
+ .ds-247420 .fsb-act:hover { opacity: 1; background: color-mix(in oklab, var(--fg) 12%, transparent); }
933
+ .ds-247420 .fsb-empty { opacity: .5; padding: 1.5em; text-align: center; font-style: italic; }
934
+ .ds-247420 .fsb-view { flex: 1; min-height: 0; overflow: auto; margin: 0; padding: .7em; white-space: pre-wrap; word-break: break-word; font: var(--fs-micro)/1.5 var(--os-mono, ui-monospace, monospace); background: var(--panel-1, transparent); }
935
+ .ds-247420 .fsb-dropping { outline: 2px dashed var(--os-accent, #247420); outline-offset: -6px; }
936
+
937
+ /* ---- wm canvas (infinite zoom/pan surface — was: docs/desktop-camera.js canvas.style.cssText) ----
938
+ * .wm-root is the fixed clipping viewport; .wm-canvas is the inner transformed
939
+ * layer that carries translate()/scale() updates per camera tick. Compositor-
940
+ * promoted via will-change:transform so per-frame pan/zoom does GPU-only work. */
941
+ .ds-247420 .wm-canvas { position: absolute; inset: 0; transform-origin: 0 0; will-change: transform; }
942
+
943
+ /* ---- wm context menu (was: docs/wm.js showWindowMenu cssText) ---- */
944
+ /* No var() fallbacks here: colors_and_type.css is guaranteed loaded first for
945
+ * every .ds-247420 consumer (see the file header), and the old `#1a1a1a` /
946
+ * `#fff` fallbacks were baked DARK values that would have rendered white text
947
+ * on a near-white panel in paper mode had they ever fired. */
948
+ .ds-247420 .wm-context-menu { position: fixed; background: var(--panel-2); border: 1px solid color-mix(in oklab, var(--fg) 18%, transparent); border-radius: var(--r-0); z-index: var(--z-dropdown); font-family: var(--ff-ui); color: var(--fg); min-width: 140px; box-shadow: 0 var(--space-2) var(--space-5) rgba(0, 0, 0, .35); padding: var(--space-1); }
949
+ .ds-247420 .wm-context-menu-item { display: block; width: 100%; padding: 7px var(--space-2-5); border: none; border-radius: var(--r-0); background: none; color: inherit; font-family: inherit; text-align: left; cursor: pointer; font-size: var(--fs-tiny); }
950
+ .ds-247420 .wm-context-menu-item:hover { background: color-mix(in oklab, var(--fg) 8%, transparent); }
951
+
952
+ /* ---- todo app (was: docs/apps.js todo-app cssText) ---- */
953
+ .ds-247420 .tb-todo-bar { display: flex; gap: var(--space-1-75); margin-bottom: var(--space-2); }
954
+ .ds-247420 .tb-todo-input { flex: 1; padding: var(--space-1-5) var(--space-2); border: 1px solid color-mix(in oklab, var(--fg) 18%, transparent); border-radius: var(--r-0); font: inherit; background: var(--panel-1, transparent); color: var(--fg, inherit); }
955
+ .ds-247420 .tb-todo-list { list-style: none; padding: 0; margin: var(--space-1-75) 0; }
956
+ .ds-247420 .tb-todo-footer { display: flex; gap: var(--space-2); align-items: center; margin-top: var(--space-1-75); font-size: var(--fs-nano); }
957
+ .ds-247420 .tb-todo-status { margin-top: var(--space-2-5); font-size: var(--fs-nano); color: color-mix(in oklab, var(--fg) 55%, transparent); padding: var(--space-1-75) var(--space-2); background: var(--panel-1, transparent); border-radius: var(--r-0); min-height: 14px; }
958
+ .ds-247420 .tb-todo-item { display: flex; gap: var(--space-2); align-items: center; padding: var(--space-1-5) var(--space-1-75); border-bottom: 1px solid color-mix(in oklab, var(--fg) 8%, transparent); }
959
+ .ds-247420 .tb-todo-text { flex: 1; }
960
+ .ds-247420 .tb-todo-text.is-done { text-decoration: line-through; color: color-mix(in oklab, var(--fg) 45%, transparent); }
961
+ .ds-247420 .tb-todo-del { padding: var(--space-hair) var(--space-2); }
962
+
963
+ /* ---- freddie keys panel (was: docs/freddie-keys.js cssText) ---- */
964
+ .ds-247420 .fk-root { padding: var(--space-2-75); overflow: auto; height: 100%; font-family: var(--os-mono, monospace); font-size: var(--fs-micro); }
965
+ .ds-247420 .fk-status { margin-top: var(--space-2); color: color-mix(in oklab, var(--fg) 60%, transparent); min-height: 1.2em; }
966
+ .ds-247420 .fk-row { display: grid; grid-template-columns: 140px 1fr auto auto; gap: var(--space-1-75); align-items: center; margin: var(--space-hair) 0; }
967
+ .ds-247420 .fk-input { font-family: var(--os-mono, monospace); font-size: var(--fs-nano); padding: var(--space-hair) var(--space-1); }
968
+ .ds-247420 .fk-title { font-weight: 600; margin-bottom: var(--space-2); }
969
+ .ds-247420 .fk-nim-wrap { display: flex; gap: var(--space-1-75); align-items: center; margin: var(--space-1) 0 var(--space-2-5) 0; }
970
+ .ds-247420 .fk-nim-in { font-family: var(--os-mono, monospace); font-size: var(--fs-nano); padding: var(--space-hair) var(--space-1); flex: 1; }
971
+ .ds-247420 .fk-badge { font-size: var(--fs-pico); padding: var(--space-hair) var(--space-1-75); border: 1px solid color-mix(in oklab, var(--fg) 18%, transparent); border-radius: var(--r-hair); color: var(--os-accent, #247420); }
972
+ .ds-247420 .fk-agent-title,
973
+ .ds-247420 .fk-host-title { margin-top: 14px; font-size: var(--fs-nano); color: color-mix(in oklab, var(--fg) 60%, transparent); }
974
+ .ds-247420 .fk-summary { font-size: var(--fs-pico); color: color-mix(in oklab, var(--fg) 60%, transparent); margin: var(--space-1-75) 0; }
975
+
976
+ /* ============================================================================
977
+ * Freddie chat redesign (2026-05-30) — extends ds-chat / freddie-chat with
978
+ * agent-loop chrome: tool-call cards, thinking indicator, system/centered
979
+ * roles, suggestion chips on empty state, cancel-while-generating button.
980
+ * Drives the {role: 'tool'|'system'|'thinking'} + parts:[{kind:'tool'|...}]
981
+ * shape that freddie's agent machine now produces.
982
+ * ============================================================================ */
983
+
984
+ /* Centered chrome for system/tool/thinking — full row, no avatar column. */
985
+ .ds-247420 .chat-msg.centered { grid-template-columns: 1fr; padding: var(--space-1) 12%; }
986
+ .ds-247420 .chat-msg.centered .chat-stack { align-items: center; }
987
+ .ds-247420 .chat-msg.system .chat-bubble { background: transparent; border: 0; font-style: italic; opacity: 0.65; font-size: var(--fs-micro); padding: var(--space-1) var(--space-2); text-align: center; max-width: 100%; }
988
+
989
+ /* Tool-call card — collapsible details with status icon + name + label. */
990
+ .ds-247420 .chat-tool { display: block; background: var(--panel-1, color-mix(in oklab, var(--fg) 4%, transparent)); border: 1px solid color-mix(in oklab, var(--fg) 14%, transparent); border-radius: var(--r-0); padding: 0; max-width: min(640px, 92%); overflow: hidden; font-size: var(--fs-micro); }
991
+ .ds-247420 .chat-tool[open] { background: var(--panel-1, color-mix(in oklab, var(--fg) 6%, transparent)); }
992
+ .ds-247420 .chat-tool.tool-error { border-color: color-mix(in oklab, var(--danger, #c0392b) 60%, transparent); }
993
+ .ds-247420 .chat-tool.tool-done { border-color: color-mix(in oklab, var(--os-accent, #247420) 35%, transparent); }
994
+ .ds-247420 .chat-tool-head { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-1-75) var(--space-2-5); cursor: pointer; list-style: none; user-select: none; }
995
+ .ds-247420 .chat-tool-head::-webkit-details-marker { display: none; }
996
+ .ds-247420 .chat-tool-icon { display: inline-flex; width: 14px; height: 14px; align-items: center; justify-content: center; font-family: var(--os-mono, monospace); font-size: var(--fs-micro); opacity: 0.7; }
997
+ .ds-247420 .chat-tool.tool-error .chat-tool-icon { color: var(--danger, #c0392b); opacity: 1; }
998
+ .ds-247420 .chat-tool.tool-done .chat-tool-icon { color: var(--os-accent, #247420); opacity: 1; }
999
+ .ds-247420 .chat-tool.tool-running .chat-tool-icon { animation: chat-spin 1.4s linear infinite; }
1000
+ @keyframes chat-spin { to { transform: rotate(360deg); } }
1001
+ .ds-247420 .chat-tool-name { font-family: var(--os-mono, monospace); font-weight: 600; font-size: var(--fs-micro); }
1002
+ .ds-247420 .chat-tool-label { opacity: 0.7; font-size: var(--fs-nano); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1003
+ .ds-247420 .chat-tool-status { font-size: var(--fs-pico); opacity: 0.55; text-transform: uppercase; letter-spacing: 0.05em; margin-left: auto; }
1004
+ .ds-247420 .chat-tool-body { padding: var(--space-1) var(--space-2-5) var(--space-2-5); display: flex; flex-direction: column; gap: var(--space-2); border-top: 1px solid color-mix(in oklab, var(--fg) 8%, transparent); }
1005
+ .ds-247420 .chat-tool-section { display: flex; flex-direction: column; gap: var(--space-half); }
1006
+ .ds-247420 .chat-tool-section-label { font-size: var(--fs-pico); opacity: 0.5; text-transform: lowercase; letter-spacing: 0.05em; }
1007
+ .ds-247420 .chat-tool-pre { margin: 0; padding: var(--space-1-75) var(--space-2); background: color-mix(in oklab, var(--fg) 4%, transparent); border-radius: var(--r-0); max-height: 200px; overflow: auto; font: var(--fs-nano)/1.45 var(--os-mono, ui-monospace, monospace); white-space: pre-wrap; word-break: break-word; }
1008
+ .ds-247420 .chat-tool-pre.is-error { color: var(--danger, #c0392b); }
1009
+
1010
+ /* Thinking indicator — typing dots + label, transient (assistant-side). */
1011
+ .ds-247420 .chat-thinking { display: inline-flex; align-items: center; gap: var(--space-2); background: transparent; border: 0; padding: var(--space-1) var(--space-2); opacity: 0.7; font-size: var(--fs-micro); }
1012
+ .ds-247420 .chat-thinking-dots { display: inline-flex; gap: var(--space-half); }
1013
+ .ds-247420 .chat-thinking-dots span { width: 5px; height: 5px; border-radius: 50%; background: var(--fg); opacity: 0.4; animation: chat-thinking-bounce 1.2s infinite ease-in-out; }
1014
+ .ds-247420 .chat-thinking-dots span:nth-child(2) { animation-delay: 0.15s; }
1015
+ .ds-247420 .chat-thinking-dots span:nth-child(3) { animation-delay: 0.3s; }
1016
+ @keyframes chat-thinking-bounce { 0%, 80%, 100% { transform: scale(0.8); opacity: 0.4; } 40% { transform: scale(1); opacity: 1; } }
1017
+ @media (prefers-reduced-motion: reduce) {
1018
+ .ds-247420 .chat-thinking-dots span { animation: none; opacity: 0.7; }
1019
+ .ds-247420 .chat-tool.tool-running .chat-tool-icon { animation: none; }
1020
+ }
1021
+
1022
+ /* Empty-state suggestion chips — clickable prompts. */
1023
+ .ds-247420 .chat-empty-suggestions { display: flex; flex-wrap: wrap; gap: var(--space-1-75); justify-content: center; margin-top: var(--space-2-75); max-width: 480px; }
1024
+ .ds-247420 .chat-empty-suggestion { all: unset; cursor: pointer; font-size: var(--fs-micro); padding: var(--space-1-75) var(--space-2-5); border: 1px solid color-mix(in oklab, var(--fg) 18%, transparent); border-radius: var(--r-pill, 999px); background: var(--panel-1, transparent); transition: background 120ms ease, border-color 120ms ease; }
1025
+ .ds-247420 .chat-empty-suggestion:hover { background: color-mix(in oklab, var(--os-accent, #247420) 10%, transparent); border-color: var(--os-accent, #247420); }
1026
+ .ds-247420 .chat-empty-suggestion:focus-visible { outline: 2px solid var(--os-accent, #247420); outline-offset: 2px; }
1027
+
1028
+ /* Cancel-while-generating button replaces send. */
1029
+ .ds-247420 .chat-composer .send.cancel { background: var(--danger); color: var(--on-color); }
1030
+ .ds-247420 .chat-composer .send.cancel:hover { background: color-mix(in oklab, var(--danger, #c0392b) 80%, black); }
1031
+
1032
+ /* ============================================================================
1033
+ * Jank pass 2 (2026-05-30) — outer-layer scroll containment + iframe gesture
1034
+ * blocking + backdrop blur. Compositor-promotion on .wm-win drag/resize was
1035
+ * already addressed in pass 1 (commit 7e057b6); these are the rules pass 1
1036
+ * did not surface.
1037
+ * ============================================================================ */
1038
+
1039
+ /* Scroll containment on the desktop frame too — pass 1 only set
1040
+ * overscroll-behavior on .wm-body. Touch-trackpad two-finger pan over the
1041
+ * desktop could leak to document.body in some Chromium versions, briefly
1042
+ * rubber-banding the page while the camera also panned. */
1043
+ .ds-247420 .wm-root,
1044
+ .ds-247420 .wm-canvas { overscroll-behavior: contain; }
1045
+
1046
+ /* Block iframe pointer-events while a wm window is being dragged/resized so
1047
+ * mid-gesture cursor crossings into an iframe child don't swallow the drag
1048
+ * (the iframe captures pointerover and the outer wm pointermove stops firing).
1049
+ * .wm-dragging / .wm-resizing classes are toggled by docs/wm.js for gesture
1050
+ * duration only. */
1051
+ .ds-247420 .wm-dragging iframe,
1052
+ .ds-247420 .wm-resizing iframe,
1053
+ .ds-247420 body:has(.wm-win.wm-dragging) iframe,
1054
+ .ds-247420 body:has(.wm-win.wm-resizing) iframe { pointer-events: none; }
1055
+
1056
+ /* Subtle backdrop blur behind modal overlays — depth cue without compounding
1057
+ * paint cost since the blur layer is GPU-composited. Respect reduced-motion
1058
+ * by falling back to plain dim. */
1059
+ .ds-247420 .tb-sess-overlay { backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
1060
+ @media (prefers-reduced-motion: reduce) {
1061
+ .ds-247420 .tb-sess-overlay { backdrop-filter: none; -webkit-backdrop-filter: none; }
1062
+ }
1063
+
1064
+ /* -----------------------------------------------------------------------
1065
+ * Freddie chat — jank pass 3 (2026-05-31)
1066
+ * Pins layout, spacing, focus, scrollbar, role-distinct bubbles, focus ring,
1067
+ * overflow-wrap, empty-state typography, composer chrome, suggestion list.
1068
+ * Visual code lives in this kit; thebird only wires state.
1069
+ * ----------------------------------------------------------------------- */
1070
+
1071
+ .ds-247420 freddie-chat,
1072
+ .ds-247420 ds-chat,
1073
+ .ds-247420 freddie-chat.chat,
1074
+ .ds-247420 ds-chat.chat {
1075
+ display: flex;
1076
+ flex-direction: column;
1077
+ min-height: 0;
1078
+ height: 100%;
1079
+ overflow: hidden;
1080
+ /* zero the legacy `.chat` card treatment (app-shell.css chat-polish.css,
1081
+ which the bundle scopes to `.ds-247420 .chat` = (0,2,0)) leaking onto the
1082
+ host element, which also carries the `.chat` class. The
1083
+ `freddie-chat.chat` / `ds-chat.chat` compound selectors here are (0,2,1) —
1084
+ strictly higher — so these win on SPECIFICITY alone, independent of which
1085
+ sheet loads first. That is why no !important is needed. */
1086
+ margin: 0;
1087
+ padding: 0;
1088
+ gap: 0;
1089
+ max-width: none;
1090
+ background: transparent;
1091
+ border-radius: 0;
1092
+ }
1093
+ .ds-247420 freddie-chat > .chat,
1094
+ .ds-247420 ds-chat > .chat {
1095
+ flex: 1 1 auto;
1096
+ min-height: 0;
1097
+ display: flex;
1098
+ flex-direction: column;
1099
+ overflow: hidden;
1100
+ margin: 0;
1101
+ padding: 0;
1102
+ gap: 0;
1103
+ }
1104
+
1105
+ .ds-247420 .chat-head {
1106
+ display: flex;
1107
+ align-items: center;
1108
+ gap: var(--space-2);
1109
+ padding: var(--space-2) var(--space-2-75);
1110
+ border-bottom: 1px solid color-mix(in oklab, var(--fg) 10%, transparent);
1111
+ min-height: 38px;
1112
+ flex: 0 0 auto;
1113
+ background: var(--panel-0, transparent);
1114
+ }
1115
+ .ds-247420 .chat-head .dot {
1116
+ width: 8px;
1117
+ height: 8px;
1118
+ border-radius: 50%;
1119
+ background: var(--os-accent, #247420);
1120
+ flex-shrink: 0;
1121
+ }
1122
+ .ds-247420 .chat-head .ds-chat-title {
1123
+ font-size: var(--fs-tiny);
1124
+ font-weight: 600;
1125
+ margin: 0;
1126
+ white-space: nowrap;
1127
+ overflow: hidden;
1128
+ text-overflow: ellipsis;
1129
+ flex-shrink: 0;
1130
+ }
1131
+ .ds-247420 .chat-head .sub {
1132
+ font-size: var(--fs-micro);
1133
+ opacity: 0.7;
1134
+ white-space: nowrap;
1135
+ overflow: hidden;
1136
+ text-overflow: ellipsis;
1137
+ min-width: 0;
1138
+ flex: 1 1 auto;
1139
+ }
1140
+ .ds-247420 .chat-head .spread { flex: 0; }
1141
+
1142
+ .ds-247420 .chat-thread {
1143
+ flex: 1 1 auto;
1144
+ min-height: 0;
1145
+ overflow-y: auto;
1146
+ overflow-x: hidden;
1147
+ padding: var(--space-2-75);
1148
+ display: flex;
1149
+ flex-direction: column;
1150
+ gap: var(--space-1-75);
1151
+ scrollbar-width: thin;
1152
+ scrollbar-color: color-mix(in oklab, var(--fg) 22%, transparent) transparent;
1153
+ scroll-behavior: smooth;
1154
+ overscroll-behavior: contain;
1155
+ }
1156
+ .ds-247420 .chat-thread::-webkit-scrollbar { width: 8px; }
1157
+ .ds-247420 .chat-thread::-webkit-scrollbar-track { background: transparent; }
1158
+ .ds-247420 .chat-thread::-webkit-scrollbar-thumb {
1159
+ background: color-mix(in oklab, var(--fg) 18%, transparent);
1160
+ border-radius: var(--r-0);
1161
+ }
1162
+ .ds-247420 .chat-thread::-webkit-scrollbar-thumb:hover {
1163
+ background: color-mix(in oklab, var(--fg) 30%, transparent);
1164
+ }
1165
+ @media (prefers-reduced-motion: reduce) {
1166
+ .ds-247420 .chat-thread { scroll-behavior: auto; }
1167
+ }
1168
+
1169
+ .ds-247420 .chat-msg {
1170
+ display: grid;
1171
+ grid-template-columns: 28px 1fr;
1172
+ gap: var(--space-2);
1173
+ align-items: start;
1174
+ }
1175
+ .ds-247420 .chat-msg.you {
1176
+ grid-template-columns: 1fr 28px;
1177
+ justify-items: end;
1178
+ }
1179
+ .ds-247420 .chat-msg.you .chat-stack { align-items: flex-end; }
1180
+ .ds-247420 .chat-msg.centered {
1181
+ grid-template-columns: 1fr;
1182
+ justify-items: center;
1183
+ padding: var(--space-hair) 12%;
1184
+ }
1185
+
1186
+ .ds-247420 .chat-avatar {
1187
+ width: 28px;
1188
+ height: 28px;
1189
+ border-radius: 50%;
1190
+ background: color-mix(in oklab, var(--os-accent, #247420) 18%, transparent);
1191
+ color: var(--fg);
1192
+ display: inline-flex;
1193
+ align-items: center;
1194
+ justify-content: center;
1195
+ font-size: var(--fs-micro);
1196
+ font-weight: 600;
1197
+ text-transform: lowercase;
1198
+ flex-shrink: 0;
1199
+ user-select: none;
1200
+ }
1201
+ .ds-247420 .chat-msg.you .chat-avatar {
1202
+ background: var(--os-accent, #247420);
1203
+ color: var(--on-color);
1204
+ }
1205
+
1206
+ .ds-247420 .chat-stack {
1207
+ display: flex;
1208
+ flex-direction: column;
1209
+ gap: var(--space-half);
1210
+ min-width: 0;
1211
+ align-items: flex-start;
1212
+ }
1213
+
1214
+ .ds-247420 .chat-bubble {
1215
+ background: var(--panel-1, color-mix(in oklab, var(--fg) 5%, transparent));
1216
+ color: var(--fg);
1217
+ border: 1px solid color-mix(in oklab, var(--fg) 8%, transparent);
1218
+ border-radius: var(--r-1);
1219
+ padding: var(--space-2) var(--space-2-75);
1220
+ font-size: var(--fs-xs);
1221
+ line-height: 1.5;
1222
+ max-width: min(720px, 86%);
1223
+ overflow-wrap: anywhere;
1224
+ word-break: break-word;
1225
+ }
1226
+ .ds-247420 .chat-msg.you .chat-bubble {
1227
+ background: var(--os-accent, #247420);
1228
+ color: var(--on-color);
1229
+ border-color: color-mix(in oklab, var(--os-accent, #247420) 60%, black);
1230
+ border-bottom-right-radius: 4px;
1231
+ }
1232
+ .ds-247420 .chat-msg.them .chat-bubble {
1233
+ border-bottom-left-radius: 4px;
1234
+ }
1235
+ .ds-247420 .chat-bubble + .chat-bubble { margin-top: var(--space-1); }
1236
+ .ds-247420 .chat-bubble pre,
1237
+ .ds-247420 .chat-bubble code { font-family: var(--os-mono, ui-monospace, monospace); font-size: var(--fs-micro); }
1238
+ .ds-247420 .chat-bubble pre {
1239
+ margin: var(--space-1-75) 0 0;
1240
+ padding: var(--space-2) var(--space-2-5);
1241
+ background: color-mix(in oklab, var(--fg) 6%, transparent);
1242
+ border-radius: var(--r-0);
1243
+ max-height: 320px;
1244
+ overflow: auto;
1245
+ white-space: pre;
1246
+ scrollbar-width: thin;
1247
+ }
1248
+ .ds-247420 .chat-msg.you .chat-bubble pre {
1249
+ background: color-mix(in oklab, black 25%, transparent);
1250
+ color: var(--on-color);
1251
+ }
1252
+ .ds-247420 .chat-bubble a {
1253
+ color: inherit;
1254
+ text-decoration: underline;
1255
+ text-decoration-thickness: 1px;
1256
+ text-underline-offset: 2px;
1257
+ overflow-wrap: anywhere;
1258
+ }
1259
+
1260
+ .ds-247420 .chat-meta {
1261
+ font-size: var(--fs-nano);
1262
+ opacity: 0.55;
1263
+ display: inline-flex;
1264
+ align-items: center;
1265
+ gap: var(--space-1-75);
1266
+ padding: 0 var(--space-1);
1267
+ }
1268
+ .ds-247420 .chat-msg.you .chat-meta { flex-direction: row-reverse; }
1269
+
1270
+ .ds-247420 .chat-empty {
1271
+ flex: 1 1 auto;
1272
+ display: flex;
1273
+ flex-direction: column;
1274
+ align-items: center;
1275
+ justify-content: center;
1276
+ text-align: center;
1277
+ padding: var(--space-5) var(--space-4);
1278
+ color: var(--fg);
1279
+ gap: var(--space-1);
1280
+ }
1281
+ .ds-247420 .chat-empty-title {
1282
+ font-size: var(--fs-lg);
1283
+ font-weight: 600;
1284
+ margin: 0;
1285
+ letter-spacing: -0.01em;
1286
+ }
1287
+ .ds-247420 .chat-empty-sub {
1288
+ font-size: var(--fs-tiny);
1289
+ opacity: 0.65;
1290
+ margin: 0;
1291
+ max-width: 420px;
1292
+ line-height: 1.5;
1293
+ }
1294
+
1295
+ .ds-247420 .chat-composer {
1296
+ display: flex;
1297
+ flex-direction: column;
1298
+ gap: var(--space-1-75);
1299
+ padding: var(--space-2) var(--space-2-5);
1300
+ border-top: 1px solid color-mix(in oklab, var(--fg) 10%, transparent);
1301
+ background: var(--panel-0, transparent);
1302
+ flex: 0 0 auto;
1303
+ }
1304
+ .ds-247420 .chat-composer textarea {
1305
+ font: var(--fs-xs)/1.45 var(--os-ui, system-ui), sans-serif;
1306
+ background: var(--panel-1, color-mix(in oklab, var(--fg) 5%, transparent));
1307
+ color: var(--fg);
1308
+ border: 1px solid color-mix(in oklab, var(--fg) 14%, transparent);
1309
+ border-radius: var(--r-0);
1310
+ padding: var(--space-2) var(--space-2-5);
1311
+ resize: none;
1312
+ outline: none;
1313
+ width: 100%;
1314
+ min-height: 40px;
1315
+ max-height: 200px;
1316
+ box-sizing: border-box;
1317
+ transition: border-color 120ms ease;
1318
+ }
1319
+ .ds-247420 .chat-composer textarea:hover {
1320
+ border-color: color-mix(in oklab, var(--fg) 22%, transparent);
1321
+ }
1322
+ .ds-247420 .chat-composer textarea:focus-visible {
1323
+ outline: 2px solid var(--os-accent, #247420);
1324
+ outline-offset: 0;
1325
+ border-color: color-mix(in oklab, var(--os-accent, #247420) 60%, transparent);
1326
+ }
1327
+ .ds-247420 .chat-composer-toolbar {
1328
+ display: flex;
1329
+ align-items: center;
1330
+ gap: var(--space-1);
1331
+ }
1332
+ .ds-247420 .composer-btn {
1333
+ all: unset;
1334
+ cursor: pointer;
1335
+ display: inline-flex;
1336
+ align-items: center;
1337
+ justify-content: center;
1338
+ width: 32px;
1339
+ height: 32px;
1340
+ border-radius: var(--r-0);
1341
+ color: var(--fg);
1342
+ opacity: 0.65;
1343
+ transition: background-color 120ms ease, opacity 120ms ease;
1344
+ }
1345
+ .ds-247420 .composer-btn:hover {
1346
+ background: color-mix(in oklab, var(--fg) 6%, transparent);
1347
+ opacity: 1;
1348
+ }
1349
+ .ds-247420 .composer-btn:focus-visible {
1350
+ outline: 2px solid var(--os-accent, #247420);
1351
+ outline-offset: 2px;
1352
+ opacity: 1;
1353
+ }
1354
+ .ds-247420 .chat-composer .send {
1355
+ all: unset;
1356
+ cursor: pointer;
1357
+ margin-left: auto;
1358
+ width: 36px;
1359
+ height: 32px;
1360
+ border-radius: var(--r-0);
1361
+ background: var(--os-accent, #247420);
1362
+ color: var(--on-color);
1363
+ display: inline-flex;
1364
+ align-items: center;
1365
+ justify-content: center;
1366
+ transition: background 120ms ease, transform 60ms ease;
1367
+ }
1368
+ .ds-247420 .chat-composer .send:hover {
1369
+ background: color-mix(in oklab, var(--os-accent, #247420) 88%, black);
1370
+ }
1371
+ .ds-247420 .chat-composer .send:active { transform: scale(0.96); }
1372
+ .ds-247420 .chat-composer .send:focus-visible {
1373
+ outline: 2px solid var(--os-accent, #247420);
1374
+ outline-offset: 2px;
1375
+ }
1376
+ .ds-247420 .chat-composer .send:disabled,
1377
+ .ds-247420 .chat-composer .send[disabled] {
1378
+ opacity: 0.35;
1379
+ cursor: not-allowed;
1380
+ background: color-mix(in oklab, var(--fg) 25%, transparent);
1381
+ }
1382
+ .ds-247420 .chat-composer.is-disabled {
1383
+ opacity: .55;
1384
+ pointer-events: none;
1385
+ }
1386
+
1387
+ .ds-247420 .ds-icon { --ds-icon-stroke: 1.7; }
1388
+
1389
+ .ds-247420 .aicat-face {
1390
+ font-family: var(--os-mono, ui-monospace, monospace);
1391
+ font-size: var(--fs-nano);
1392
+ line-height: 1.2;
1393
+ margin: 0;
1394
+ }
1395
+
1396
+ /* gm memory browser — recall hits rendered as interactive, prunable cards
1397
+ (thebird's gm app, docs/apps.js recall panel). */
1398
+ .ds-247420 .gm-recall-hits {
1399
+ display: flex;
1400
+ flex-direction: column;
1401
+ gap: var(--space-1-75);
1402
+ margin: var(--space-2) 0;
1403
+ }
1404
+ .ds-247420 .gm-recall-hit {
1405
+ border: 1px solid color-mix(in oklab, var(--fg) 14%, transparent);
1406
+ border-radius: var(--r-0);
1407
+ padding: var(--space-2) var(--space-2-5);
1408
+ background: color-mix(in oklab, var(--bg) 92%, var(--fg) 2%);
1409
+ }
1410
+ .ds-247420 .gm-recall-hit.is-pruned {
1411
+ opacity: 0.45;
1412
+ text-decoration: line-through;
1413
+ }
1414
+ .ds-247420 .gm-recall-meta {
1415
+ font-family: var(--os-mono, ui-monospace, monospace);
1416
+ font-size: var(--fs-nano);
1417
+ color: color-mix(in oklab, var(--fg) 60%, transparent);
1418
+ margin-bottom: var(--space-1);
1419
+ }
1420
+ .ds-247420 .gm-recall-text {
1421
+ font-size: var(--fs-tiny);
1422
+ line-height: 1.4;
1423
+ white-space: pre-wrap;
1424
+ word-break: break-word;
1425
+ }
1426
+ .ds-247420 .gm-recall-acts {
1427
+ display: flex;
1428
+ justify-content: flex-end;
1429
+ margin-top: var(--space-1-75);
1430
+ }
1431
+ .ds-247420 .gm-recall-forget {
1432
+ font-size: var(--fs-nano);
1433
+ padding: var(--space-hair) var(--space-2);
1434
+ }
1435
+
1436
+ /* ============================================================
1437
+ * thebird RESPONSIVE PERFECTION layer (desk / tablet / mobile).
1438
+ * Appended LAST so the .ds-247420-scoped rules here win by SOURCE ORDER at
1439
+ * equal (0,2,0) specificity over the earlier brand overrides
1440
+ * (.ds-247420 .wm-root{inset:0} at the "bars overlay" block,
1441
+ * .ds-247420 .wm-bar{height:34px}, the unconditional
1442
+ * .ds-247420 .os-instances{display:flex}) that were silently defeating the
1443
+ * @media(max-width:767px)/(768-1023px) contracts. Every rule below is a
1444
+ * measured fix for a confirmed responsive-audit finding.
1445
+ *
1446
+ * Because every competing rule is now equally scoped, source order alone
1447
+ * decides — so this layer needs no !important except where it must beat
1448
+ * wm.css's own !important declarations or wm.js's inline styles.
1449
+ *
1450
+ * View classes: desktop >1023px (free-float windows, full menubar);
1451
+ * tablet 768..1023px (side-rail, wm-root left-offset); mobile <=767px AND
1452
+ * short landscape phones (full-screen windows, collapsed menubar, bottom
1453
+ * scroll-snap taskbar). Touch sizing is driven by (pointer:coarse) so it
1454
+ * also covers the landscape-phone band that width-only queries miss.
1455
+ * ============================================================ */
1456
+
1457
+ /* --- mobile bar height: the kit intends a touch-friendly 52px; the brand
1458
+ * layer pinned --os-bar-h-mobile to 34px which left no room for 44px tap
1459
+ * targets in the bar. Restore 52px on mobile/touch. --- */
1460
+ @media (max-width: 767px), (max-height: 500px) and (orientation: landscape) {
1461
+ /* LOAD-BEARING !important: same reason as the brand block's --os-bar-h —
1462
+ * `:root[data-density="…"]` redefines --os-bar-h at (0,2,0), higher than
1463
+ * this (0,1,0) class rule on the same <html> element. */
1464
+ .ds-247420 { --os-bar-h-mobile: 52px !important; --os-bar-h: 52px !important; }
1465
+ }
1466
+
1467
+ /* --- mobile + landscape-phone full-screen windows. The earlier
1468
+ * .ds-247420 .wm-root{inset:0!important} forced windows over the bars so the
1469
+ * titlebar close button landed under the menubar (occluded, untappable) and
1470
+ * window content underlapped the taskbar. Re-assert the bar-inset at scoped
1471
+ * specificity so windows sit BETWEEN the bars. --- */
1472
+ @media (max-width: 767px), (max-height: 500px) and (orientation: landscape) {
1473
+ .ds-247420 .wm-root {
1474
+ inset: calc(var(--os-bar-h) + env(safe-area-inset-top, 0px)) 0
1475
+ calc(var(--os-bar-h) + env(safe-area-inset-bottom, 0px)) 0;
1476
+ }
1477
+ /* LOAD-BEARING !important on the geometry four: wm.css's mobile band
1478
+ * (wm.css:157-165) declares left/top/width/height with !important, and
1479
+ * wm.js writes left/top/width/height as INLINE styles during drag/resize.
1480
+ * Neither can be beaten by specificity — only by !important. The remaining
1481
+ * properties here need no flag. */
1482
+ .ds-247420 .wm-win {
1483
+ position: absolute;
1484
+ left: 0 !important; top: 0 !important;
1485
+ width: 100% !important; height: 100% !important;
1486
+ border: none;
1487
+ /* bare 0: full-screen window has square corners (not --r-0's 4px) */
1488
+ border-radius: 0;
1489
+ box-shadow: none;
1490
+ }
1491
+ /* titlebar must host a 44px close button -> 44px bar (beats the 34px brand
1492
+ * clamp by later source order at equal specificity) */
1493
+ .ds-247420 .wm-bar {
1494
+ height: 44px; min-height: 44px;
1495
+ padding: 0 var(--space-2-5); align-items: center;
1496
+ pointer-events: none;
1497
+ }
1498
+ .ds-247420 .wm-bar .wm-title { pointer-events: auto; }
1499
+ .ds-247420 .wm-bar .wm-btns { pointer-events: auto; }
1500
+ /* hide min+max on mobile full-screen; keep close at 44x44 fully inside the bar */
1501
+ .ds-247420 .wm-btns .wm-btn:nth-child(1),
1502
+ .ds-247420 .wm-btns .wm-btn:nth-child(2) { display: none; }
1503
+ /* LOAD-BEARING !important on width/height: wm.css's `@media (pointer:coarse)`
1504
+ * sets `.wm-btn{min-width:44px;min-height:44px}` and this must also beat the
1505
+ * (0,3,0) `.ds-247420 .wm-btns .wm-btn` coarse-pointer rule below, which is
1506
+ * later in source order. Colors/radius need no flag. */
1507
+ .ds-247420 .wm-btns .wm-btn:nth-child(3) {
1508
+ width: 44px !important; height: 44px !important;
1509
+ border-radius: var(--r-1);
1510
+ background: var(--os-bg-3); color: var(--os-fg);
1511
+ }
1512
+ .ds-247420 .wm-resize, .ds-247420 .wm-edge { display: none; }
1513
+ /* collapse the menubar workspace cluster on mobile. `.os-menubar
1514
+ * .os-instances` is (0,3,0) here, beating the unconditional (0,2,0)
1515
+ * `.ds-247420 .os-instances{display:flex}` on specificity. */
1516
+ .ds-247420 .os-menubar .os-instances,
1517
+ .ds-247420 .os-instances { display: none; }
1518
+ .ds-247420 .os-menubar [data-role="add"],
1519
+ .ds-247420 .tb-sess-add { display: none; }
1520
+ }
1521
+
1522
+ /* --- landscape-phone (short + wide + touch): also hide the side-rail that the
1523
+ * 768-1023 tablet band would otherwise show, and force the mobile menubar. --- */
1524
+ @media (max-height: 500px) and (orientation: landscape) and (pointer: coarse) {
1525
+ .ds-247420 .os-side-rail { display: none; }
1526
+ }
1527
+
1528
+ /* --- tablet (768..1023): the side-rail offset for .wm-root was defeated by
1529
+ * the scoped inset:0; re-asserted here at equal (0,2,0) specificity but later
1530
+ * source order, which is enough now that the earlier rule is also scoped.
1531
+ * `inset` alone carries the left offset — the separate `left` declaration it
1532
+ * used to be paired with was redundant with this shorthand. --- */
1533
+ @media (min-width: 768px) and (max-width: 1023px) {
1534
+ .ds-247420 .wm-root {
1535
+ inset: 0 0 0 var(--os-rail-w);
1536
+ }
1537
+ }
1538
+
1539
+ /* --- universal touch tap-target floor (pointer:coarse covers tablets AND
1540
+ * landscape phones that width-only queries miss). Every interactive control
1541
+ * the audit measured below 44px is lifted to the --os-tap (44px) token. --- */
1542
+ @media (pointer: coarse) {
1543
+ /* window chrome */
1544
+ .ds-247420 .wm-btns .wm-btn { min-width: var(--os-tap, 44px); min-height: var(--os-tap, 44px); }
1545
+ .ds-247420 .wm-resize { width: var(--os-tap, 44px); height: var(--os-tap, 44px); }
1546
+ /* taskbar tasks (also widen and snap on the whole touch band, not mobile-only) */
1547
+ .ds-247420 .os-task {
1548
+ min-height: var(--os-tap, 44px); min-width: 120px;
1549
+ padding: var(--space-2-5) 14px; scroll-snap-align: start;
1550
+ }
1551
+ .ds-247420 .os-taskbar { scroll-snap-type: x proximity; }
1552
+ /* menubar workspace controls (when shown on tablet) */
1553
+ .ds-247420 .os-menubar .tb-sess-x { min-width: var(--os-tap, 44px); min-height: var(--os-tap, 44px); }
1554
+ .ds-247420 .tb-sess-chip { min-height: var(--os-tap, 44px); }
1555
+ .ds-247420 .tb-sess-add { min-height: var(--os-tap, 44px); }
1556
+ /* modal controls */
1557
+ .ds-247420 .tb-sess-modal-input { height: var(--os-tap, 44px); min-height: var(--os-tap, 44px); }
1558
+ .ds-247420 .tb-sess-modal-btn { min-height: var(--os-tap, 44px); }
1559
+ .ds-247420 .tb-sess-modal-x { min-width: var(--os-tap, 44px); min-height: var(--os-tap, 44px); }
1560
+ /* files-app rows + per-row actions */
1561
+ .ds-247420 .fsb-row { min-height: var(--os-tap, 44px); }
1562
+ .ds-247420 .fsb-act {
1563
+ min-width: var(--os-tap, 44px); min-height: var(--os-tap, 44px);
1564
+ display: inline-flex; align-items: center; justify-content: center;
1565
+ }
1566
+ /* chat composer controls */
1567
+ .ds-247420 .chat-composer .send { width: var(--os-tap, 44px); height: var(--os-tap, 44px); }
1568
+ .ds-247420 .cc-toggle { min-height: var(--os-tap, 44px); }
1569
+ }
1570
+
1571
+ /* --- files-app row actions must reveal on coarse pointers too (the existing
1572
+ * trigger keyed only on hover:none, which fails on hybrid touch+hover
1573
+ * tablets that also report a hover-capable pointer). --- */
1574
+ @media (hover: none), (pointer: coarse) {
1575
+ .ds-247420 .fsb-actions { opacity: 1; }
1576
+ }
1577
+
1578
+ /* --- modals: a content-tall card had no max-height and the overlay did not
1579
+ * scroll, so the footer (primary action) was stranded off-canvas. Cap the
1580
+ * card to the visible viewport, scroll the body, honor safe-area. --- */
1581
+ .ds-247420 .tb-sess-overlay {
1582
+ overflow-y: auto;
1583
+ padding: max(12px, env(safe-area-inset-top, 0px)) max(12px, env(safe-area-inset-right, 0px))
1584
+ max(12px, env(safe-area-inset-bottom, 0px)) max(12px, env(safe-area-inset-left, 0px));
1585
+ }
1586
+ .ds-247420 .tb-sess-modal { max-height: calc(100vh - 24px); max-height: calc(100dvh - 24px); display: flex; flex-direction: column; }
1587
+ .ds-247420 .tb-sess-modal-body { overflow-y: auto; min-height: 0; }
1588
+
1589
+ /* --- browser-pane: the generic .app-pane 16px prose padding stranded the
1590
+ * iframe 16px on every side; the browser pane is full-bleed. --- */
1591
+ /* (0,3,0) beats the generic `.app-pane` (0,1,0) prose padding on specificity. */
1592
+ .ds-247420 .app-pane.browser-app { padding: 0; }