anentrypoint-design 0.0.322 → 0.0.324

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 (45) hide show
  1. package/README.md +22 -2
  2. package/app-shell.css +24 -4782
  3. package/colors_and_type.css +43 -0
  4. package/dist/247420.css +74 -3
  5. package/dist/247420.js +16 -16
  6. package/package.json +14 -8
  7. package/src/bootstrap.js +10 -1
  8. package/src/components/chat-message-parts.js +355 -0
  9. package/src/components/chat.js +23 -320
  10. package/src/components/data-density.js +15 -10
  11. package/src/components/files-modals.js +14 -2
  12. package/src/components/files.js +5 -1
  13. package/src/components/freddie/helpers.js +46 -8
  14. package/src/css/app-shell/base.css +157 -0
  15. package/src/css/app-shell/catalog-theme.css +144 -0
  16. package/src/css/app-shell/chat-basic.css +12 -0
  17. package/src/css/app-shell/chat-polish.css +459 -0
  18. package/src/css/app-shell/data-density.css +150 -0
  19. package/src/css/app-shell/files.css +709 -0
  20. package/src/css/app-shell/hero-content.css +407 -0
  21. package/src/css/app-shell/kits-appended.css +361 -0
  22. package/src/css/app-shell/loading-alerts.css +120 -0
  23. package/src/css/app-shell/panel-row.css +177 -0
  24. package/src/css/app-shell/primitives.css +197 -0
  25. package/src/css/app-shell/responsive.css +266 -0
  26. package/src/css/app-shell/responsive2-workspace.css +465 -0
  27. package/src/css/app-shell/row-print.css +75 -0
  28. package/src/css/app-shell/sidebar-misc.css +79 -0
  29. package/src/css/app-shell/states-interactions.css +696 -0
  30. package/src/css/app-shell/topbar.css +319 -0
  31. package/src/deck-stage.js +6 -4
  32. package/src/highlight.js +20 -4
  33. package/src/index.js +15 -5
  34. package/src/kits/os/freddie/pages-chat.js +1 -1
  35. package/src/kits/os/freddie/pages-core.js +1 -1
  36. package/src/kits/os/freddie/pages-os.js +1 -1
  37. package/src/kits/os/freddie/pages-tools.js +1 -1
  38. package/src/kits/os/wm.js +38 -0
  39. package/src/kits/slides/deck-stage-overlay.js +68 -0
  40. package/src/kits/slides/deck-stage-state.js +81 -0
  41. package/src/kits/slides/deck-stage-style.js +117 -0
  42. package/src/kits/slides/deck-stage.js +167 -0
  43. package/src/kits/slides/index.js +5 -0
  44. package/src/markdown.js +89 -11
  45. package/src/kits/os/freddie/helpers.js +0 -33
@@ -0,0 +1,465 @@
1
+ /* ============================================================
2
+ Responsive Perfection — Drawer rail, ultrawide cap, mobile compact
3
+ ============================================================ */
4
+
5
+ /* Tablet + Mobile: side rail becomes off-canvas drawer (≤900px) */
6
+ /* Sidebar drawer collapse keys on the SHELL width (@container on .app, which is
7
+ container-type:inline-size) — NOT the viewport. Full-page (freddie) the shell
8
+ == viewport so this is identical to the old @media(max-width:900px); embedded
9
+ in a thebird WM window the drawer + hamburger appear when the WINDOW is narrow
10
+ even on a wide desktop. Drawer/scrim/toggle are absolute (anchored to .app), so
11
+ they overlay the window, not the page. */
12
+ @container (max-width: 900px) {
13
+ .app-body,
14
+ .app-body.no-side { grid-template-columns: 1fr !important; }
15
+ .app-side-shell {
16
+ position: absolute;
17
+ /* Full-height overlay drawer: the merged .app-chrome wraps and its real
18
+ height drifts from --app-topbar-h, so anchoring below it misplaces the
19
+ drawer. Overlay the chrome instead (z above --z-header), like ws drawers. */
20
+ left: 0; top: 0;
21
+ height: 100%;
22
+ width: 280px; max-width: 80%;
23
+ transform: translateX(-100%);
24
+ transition: transform var(--dur-base, 240ms) var(--ease, ease);
25
+ /* Above the sticky chrome (z-header): scrim > chrome > content budget. */
26
+ z-index: calc(var(--z-header, 100) + 2);
27
+ background: var(--bg);
28
+ border-right: 1px solid color-mix(in oklab, var(--fg) 8%, transparent);
29
+ overflow-y: auto;
30
+ overscroll-behavior: contain;
31
+ }
32
+ /* Lock the content scroller while the drawer is open so touch pans don't
33
+ scroll the page behind the scrim. */
34
+ .app-body.side-open .app-main { overflow: hidden; }
35
+ .app-side-shell.open,
36
+ .app-side.open,
37
+ .app-body.side-open .app-side-shell { transform: translateX(0); }
38
+ .app-side {
39
+ flex-direction: column !important;
40
+ flex-wrap: nowrap !important;
41
+ gap: var(--space-2);
42
+ padding: var(--space-3);
43
+ font-size: var(--fs-sm);
44
+ }
45
+ .app-side a {
46
+ flex: 0 0 auto; min-width: 0;
47
+ grid-template-columns: 18px 1fr auto;
48
+ padding: 10px 12px;
49
+ }
50
+ .app-side .group { width: 100%; }
51
+ /* Keep the crumb on mobile — apps mount contextual controls (model picker,
52
+ actions, live status) in crumb-right, so hiding it strands them. Collapse
53
+ the breadcrumb trail/leaf instead and let the right cluster wrap. */
54
+ .app-crumb { display: flex; flex-wrap: wrap; gap: var(--space-2); padding: 8px 12px; }
55
+ .app-crumb > .sep, .app-crumb > span:not(.crumb-right):not(.leaf) { display: none; }
56
+ .app-crumb .crumb-right { margin-left: 0; }
57
+ .chat-head { padding: var(--space-2) var(--space-3) var(--space-2); margin-bottom: var(--space-2); }
58
+ .app-main { padding-left: var(--space-3); padding-right: var(--space-3); }
59
+ }
60
+
61
+ /* Drawer scrim — absolute so it covers THIS shell, shown when drawer open and
62
+ the shell is narrow (@container on .app). */
63
+ .app-side-scrim {
64
+ display: none;
65
+ position: absolute; inset: 0;
66
+ background: color-mix(in oklab, var(--bg) 60%, transparent);
67
+ /* Above the sticky chrome so nav behind an open drawer is not clickable. */
68
+ z-index: calc(var(--z-header, 100) + 1);
69
+ }
70
+ @container (max-width: 900px) {
71
+ .app-body.side-open .app-side-scrim { display: block; }
72
+ }
73
+
74
+ /* Nav toggle (hamburger) — hidden when the shell is wide, shown when the shell
75
+ itself is narrow (@container on .app). Absolute so it sits in THIS shell's
76
+ topbar, not pinned to the viewport corner. */
77
+ .app-side-toggle {
78
+ display: none;
79
+ position: absolute; top: calc((var(--app-topbar-h) - 44px) / 2); left: 10px;
80
+ /* Above the sticky topbar/chrome (z-header) and the drawer/scrim so the
81
+ translucent blurred chrome never paints over or intercepts the toggle. */
82
+ z-index: calc(var(--z-header, 100) + 3);
83
+ /* 44x44 minimum hit area (WCAG 2.5.5 / Apple HIG) - 36px is below the
84
+ reliable-tap threshold on touch. */
85
+ width: 44px; height: 44px;
86
+ align-items: center; justify-content: center;
87
+ font-size: 18px; line-height: 1;
88
+ background: var(--bg-2); color: var(--fg);
89
+ border: 1px solid var(--rule, color-mix(in oklab, var(--fg) 12%, transparent));
90
+ border-radius: var(--r-1, 6px);
91
+ cursor: pointer;
92
+ }
93
+ .app-side-toggle:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
94
+ @container (max-width: 900px) {
95
+ .app-side-toggle { display: inline-flex; }
96
+ .app-topbar .brand { margin-left: 44px; }
97
+ /* Merged-chrome layout (.app-chrome > .app-crumb) puts the breadcrumb, not
98
+ the topbar brand, at the left edge where the absolute-positioned toggle
99
+ sits — without this the crumb text renders directly under the button. */
100
+ .app-chrome > .app-crumb { margin-left: 44px; }
101
+ }
102
+
103
+ /* Desktop: the app shell is exactly viewport-height and contains its own
104
+ scroll, so only .app-main (and the side rail) scroll — never the page too.
105
+ Pinning .app to 100dvh + overflow:hidden is what prevents the double
106
+ scrollbar that a min-height:100vh shell + inner overflow:auto produces.
107
+ .app-body fills the gap between topbar/crumb and status via flex:1 rather
108
+ than a hand-computed calc() that drifts from the real chrome heights. */
109
+ @media (min-width: 901px) {
110
+ .app { height: 100dvh; min-height: 0; overflow: hidden; }
111
+ /* Embedded shell (freddie WM window) fills its container, not the viewport. */
112
+ .fd-root .app { height: 100%; overflow: visible; }
113
+ .app-body { min-height: 0; flex: 1 1 auto; }
114
+ .app-side-shell { overflow-y: auto; min-height: 0; }
115
+ .app-main { overflow-y: auto; min-height: 0; }
116
+ }
117
+
118
+ /* Mobile (≤480) status bar compact; hide tail item */
119
+ @media (max-width: 480px) {
120
+ .app-status {
121
+ font-size: var(--fs-micro);
122
+ padding: 4px var(--space-2);
123
+ gap: var(--space-2);
124
+ }
125
+ .app-status > :last-child:not(:only-child) { display: none; }
126
+ }
127
+
128
+ /* Tiny phone (≤360) topbar compact */
129
+ @media (max-width: 360px) {
130
+ .app-topbar {
131
+ padding: 8px var(--space-2);
132
+ font-size: var(--fs-xs);
133
+ gap: var(--space-2);
134
+ }
135
+ .app-topbar nav,
136
+ .app-topbar > :last-child { display: none; }
137
+ .brand { font-size: var(--fs-tiny); }
138
+ /* Slightly narrower drawer on tiny phones; max-width:80% remains the guard. */
139
+ .app-side-shell { width: 260px; }
140
+ }
141
+
142
+ /* ----------------------------------------------------------------------------
143
+ WorkspaceShell — Claude-Desktop / cowork three-column app shell.
144
+ Columns: [ws-rail] [ws-sessions?] [ws-content] [ws-pane?]
145
+ Collapse via .ws-rail-collapsed / .ws-pane-collapsed classes on .ws-shell.
146
+ All sizing/colour from kit tokens; no hardcoded palette.
147
+ ---------------------------------------------------------------------------- */
148
+ .ws-shell {
149
+ /* Fluid chrome columns: shrink on small laptops, breathe on ultrawide, so the
150
+ ~848px of fixed chrome no longer eats a constant slab. An inline --ws-*-w
151
+ written by a resize drag overrides these (inline wins), pinning the width. */
152
+ --ws-rail-w: clamp(200px, 16vw, 260px);
153
+ --ws-rail-w-collapsed: 60px;
154
+ --ws-sessions-w: clamp(248px, 22vw, 360px);
155
+ --ws-pane-w: clamp(288px, 24vw, 420px);
156
+ display: grid;
157
+ grid-template-columns: var(--ws-rail-w) var(--ws-sessions-w) 1fr var(--ws-pane-w);
158
+ grid-template-areas: "rail sessions content pane";
159
+ height: 100%;
160
+ min-height: 0;
161
+ background: var(--bg);
162
+ color: var(--fg);
163
+ }
164
+ .ws-shell.ws-no-sessions { grid-template-columns: var(--ws-rail-w) 1fr var(--ws-pane-w); grid-template-areas: "rail content pane"; }
165
+ .ws-shell.ws-no-pane.ws-no-sessions { grid-template-columns: var(--ws-rail-w) 1fr; grid-template-areas: "rail content"; }
166
+ .ws-shell.ws-no-pane:not(.ws-no-sessions) { grid-template-columns: var(--ws-rail-w) var(--ws-sessions-w) 1fr; grid-template-areas: "rail sessions content"; }
167
+ /* G1 stable frame: when a tab has no pane but asks to keep the column geometry
168
+ (ws-no-pane + ws-pane-collapsed), KEEP the 4-track grid with the pane track
169
+ at width 0 instead of collapsing to a 3-track layout - so the shell does not
170
+ re-flow its column count on tab switch. The pane track stays present (0px),
171
+ so chat/history/files/live/settings share one geometry. */
172
+ .ws-shell.ws-no-pane.ws-pane-collapsed:not(.ws-no-sessions) { grid-template-columns: var(--ws-rail-w) var(--ws-sessions-w) 1fr var(--ws-pane-w); grid-template-areas: "rail sessions content pane"; }
173
+ .ws-shell.ws-no-pane.ws-pane-collapsed.ws-no-sessions { grid-template-columns: var(--ws-rail-w) 1fr var(--ws-pane-w); grid-template-areas: "rail content pane"; }
174
+ .ws-shell.ws-rail-collapsed { --ws-rail-w: var(--ws-rail-w-collapsed); }
175
+ .ws-shell.ws-pane-collapsed { --ws-pane-w: 0px; }
176
+ /* Sessions column desktop collapse (parity with the pane collapse), reclaiming
177
+ its width for a full-width thread/grid. */
178
+ .ws-shell.ws-sessions-collapsed { --ws-sessions-w: 0px; }
179
+ .ws-sessions-collapsed .ws-sessions { overflow: hidden; border-right: none; }
180
+ .ws-sessions-collapsed .ws-sessions > * { display: none; }
181
+ /* Column resize handles: a thin keyboard-accessible separator pinned to the
182
+ right edge of each chrome track; drag (or ArrowLeft/Right) writes a clamped
183
+ inline --ws-*-w. */
184
+ .ws-resizer { grid-row: 1 / -1; align-self: stretch; justify-self: end; width: 8px; margin-right: -4px; z-index: 5; cursor: col-resize; background: transparent; border: none; padding: 0; touch-action: none; }
185
+ .ws-resizer::after { content: ""; display: block; width: 2px; height: 100%; margin: 0 auto; background: transparent; transition: background var(--dur-snap) var(--ease); }
186
+ .ws-resizer:hover::after, .ws-resizer:focus-visible::after { background: var(--accent); }
187
+ .ws-resizer:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: -2px; }
188
+ .ws-resizer.ws-resizer-rail { grid-column: 1; }
189
+ .ws-resizer.ws-resizer-sessions { grid-column: 2; }
190
+ .ws-resizer.ws-resizer-pane { grid-column: 3; justify-self: end; }
191
+ /* A resizer must vanish once its TRACK is removed by the staging media blocks,
192
+ else dragging it writes a --ws-*-w var that no longer affects the now-fixed
193
+ drawer (a dead handle). Mirror the 1480 / 1100 / 900 staging exactly. */
194
+ @media (max-width: 1480px) { .ws-resizer.ws-resizer-pane { display: none; } }
195
+ @media (max-width: 1100px) { .ws-resizer.ws-resizer-sessions { display: none; } }
196
+ @media (max-width: 900px) { .ws-resizer { display: none; } }
197
+ /* A resizer must also vanish once its TRACK is desktop-collapsed: a 0px (pane/
198
+ sessions) or 60px icon-only (rail) track has no width to drag, and a drag would
199
+ write an INLINE --ws-*-w that overrides the collapse var (inline wins), fighting
200
+ the collapse state. Mirror the breakpoint guards for the collapse classes. */
201
+ .ws-rail-collapsed .ws-resizer-rail,
202
+ .ws-sessions-collapsed .ws-resizer-sessions,
203
+ .ws-pane-collapsed .ws-resizer-pane { display: none; }
204
+ /* Coarse-pointer hit target for hybrid touch laptops above the drawer breakpoint
205
+ (the 8px sliver is un-grabbable by touch). */
206
+ @media (pointer: coarse) { .ws-resizer { width: 16px; margin-right: -8px; } }
207
+ /* Ease the rail/pane collapse. Track COUNT stays stable on collapse (only a
208
+ width var flips), so grid-template-columns is animatable; reduced-motion
209
+ drops it to an instant swap. */
210
+ @media (prefers-reduced-motion: no-preference) {
211
+ .ws-shell { transition: grid-template-columns var(--dur-base) var(--ease); }
212
+ }
213
+ /* Ultrawide: cap the shell so the 1fr content track doesn't stretch to
214
+ unreadable measures while the fixed side columns pin to their clamps. */
215
+ @media (min-width: 1920px) {
216
+ .ws-shell { max-width: 1920px; margin-inline: auto; }
217
+ }
218
+
219
+ /* Rail (left nav) */
220
+ .ws-rail {
221
+ grid-area: rail;
222
+ position: relative;
223
+ display: flex;
224
+ flex-direction: column;
225
+ min-height: 0;
226
+ background: var(--bg-2);
227
+ border-right: var(--bw-hair) solid var(--bg-3);
228
+ overflow: hidden;
229
+ transition: width var(--dur-base) var(--ease);
230
+ }
231
+ .ws-rail-toggle {
232
+ position: absolute; top: var(--space-2); right: var(--space-2);
233
+ width: 36px; height: 36px;
234
+ display: inline-flex; align-items: center; justify-content: center;
235
+ background: transparent; border: none; color: var(--fg-2);
236
+ border-radius: var(--r-1); cursor: pointer;
237
+ }
238
+ .ws-rail-toggle:hover { background: var(--bg-3); color: var(--fg); }
239
+ .ws-rail-toggle:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
240
+ .ws-rail-inner { display: flex; flex-direction: column; min-height: 0; flex: 1; padding: var(--space-3) var(--space-2); gap: var(--space-2); }
241
+ .ws-rail-head { display: flex; align-items: center; min-height: 36px; padding: 0 var(--space-2); }
242
+ .ws-rail-brand { font-family: var(--ff-body); font-weight: 600; font-size: var(--fs-body); color: var(--fg); white-space: nowrap; overflow: hidden; }
243
+ .ws-rail-action {
244
+ display: flex; align-items: center; gap: var(--space-2);
245
+ padding: var(--space-2) var(--space-3); margin: var(--space-1) 0;
246
+ background: var(--accent); color: var(--accent-fg, var(--bg));
247
+ border: none; border-radius: var(--r-1); cursor: pointer;
248
+ font-family: var(--ff-body); font-size: var(--fs-sm); font-weight: 600;
249
+ min-height: 40px; white-space: nowrap; overflow: hidden;
250
+ }
251
+ .ws-rail-action:hover { filter: brightness(0.92); }
252
+ .ws-rail-action:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
253
+ .ws-rail-nav { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
254
+ .ws-rail-item {
255
+ display: flex; align-items: center; gap: var(--space-2);
256
+ width: 100%; padding: var(--space-2); min-height: 44px;
257
+ background: transparent; border: none; color: var(--fg-2);
258
+ border-radius: var(--r-1); cursor: pointer; text-align: left;
259
+ font-family: var(--ff-body); font-size: var(--fs-sm); white-space: nowrap; overflow: hidden;
260
+ }
261
+ .ws-rail-item:hover { background: var(--bg-3); color: var(--fg); }
262
+ .ws-rail-item:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: -2px; }
263
+ .ws-rail-item.active { background: var(--accent-tint); color: var(--fg); box-shadow: inset 2px 0 0 var(--accent); }
264
+ .ws-rail-item-label { flex: 1; overflow: hidden; text-overflow: ellipsis; }
265
+ .ws-rail-item-count { font-family: var(--ff-mono); font-size: var(--fs-tiny); color: var(--fg-3); }
266
+ .ws-rail-item-flag { width: 6px; height: 6px; border-radius: 50%; flex: none; }
267
+ .ws-rail-item-flag.tone-flame { background: var(--flame); box-shadow: 0 0 0 2px color-mix(in oklab, var(--flame) 38%, transparent); }
268
+ .ws-rail-foot { margin-top: auto; padding-top: var(--space-2); border-top: var(--bw-hair) solid var(--bg-3); }
269
+
270
+ /* Collapsed rail: icon-only — hide text labels, center icons, drop toggle's far-right pin */
271
+ .ws-rail-collapsed .ws-rail-brand,
272
+ .ws-rail-collapsed .ws-rail-action-label,
273
+ .ws-rail-collapsed .ws-rail-item-label,
274
+ .ws-rail-collapsed .ws-rail-item-count { display: none; }
275
+ .ws-rail-collapsed .ws-rail-item,
276
+ .ws-rail-collapsed .ws-rail-action { justify-content: center; gap: 0; }
277
+ .ws-rail-collapsed .ws-rail-head { justify-content: center; }
278
+
279
+ /* Sessions column */
280
+ .ws-sessions {
281
+ grid-area: sessions;
282
+ min-height: 0; display: flex; flex-direction: column;
283
+ /* Rail and pane both sit on --bg-2 (a recessed plane framing the elevated
284
+ --bg content column); sessions used flat --bg instead, so the
285
+ conversation list and the chat thread read as the SAME surface with only
286
+ a 1px hairline between them - the flattest, most "one big beige box"
287
+ tell in the shell. --bg-2 restores the three-plane depth (rail/sessions
288
+ recessed either side, content elevated in the middle) the design
289
+ already establishes everywhere else. */
290
+ background: var(--bg-2); border-right: var(--bw-hair) solid var(--bg-3);
291
+ overflow: hidden;
292
+ }
293
+ .ws-no-sessions .ws-sessions { display: none; }
294
+
295
+ /* Content column */
296
+ .ws-content { grid-area: content; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
297
+ /* Crumb band is the top chrome of the content column: a stable height so the
298
+ top edge aligns with the rail head, and a left gutter matching .ws-main so
299
+ the trail text is not flush against the rail border. */
300
+ .ws-crumb { flex: 0 0 auto; min-height: calc(var(--app-crumb-h) + var(--space-3)); padding: 0 var(--space-5); display: flex; align-items: center; gap: var(--space-1); border-bottom: var(--bw-hair) solid var(--bg-3); }
301
+ .ws-crumb-main { flex: 1 1 auto; min-width: 0; }
302
+ /* Content column gutter. Claude-Code-class calm: file grid / dashboard / event
303
+ list / history get a generous, consistent inner gutter instead of butting
304
+ against the 1px column borders. The chat tab opts out via .ws-main--flush
305
+ because the chat thread carries its own --measure gutter. */
306
+ .ws-main { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; padding: var(--space-4) var(--space-5); }
307
+ .ws-main.ws-main--flush { padding: 0; }
308
+ .ws-main:focus { outline: none; }
309
+
310
+ /* Right context pane */
311
+ .ws-pane {
312
+ grid-area: pane; position: relative;
313
+ min-height: 0; display: flex; flex-direction: column;
314
+ background: var(--bg-2); border-left: var(--bw-hair) solid var(--bg-3);
315
+ overflow: hidden; transition: width var(--dur-base) var(--ease);
316
+ overscroll-behavior: contain;
317
+ }
318
+ .ws-pane-collapsed .ws-pane { width: 0; border-left: none; }
319
+ .ws-pane-collapsed .ws-pane > * { display: none; }
320
+ @media (pointer: coarse) {
321
+ .ws-rail-toggle { width: 44px; height: 44px; }
322
+ .ws-drawer-toggle { width: 44px; height: 44px; }
323
+ .ws-desktop-toggle { width: 44px; height: 44px; }
324
+ }
325
+
326
+ /* Drawer toggles and the scrim are hidden by default and revealed by the staged
327
+ media blocks BELOW - these base rules must precede those blocks in source
328
+ order: at equal specificity the later rule wins regardless of media, so a
329
+ trailing display:none silently kills every drawer affordance (shipped bug:
330
+ the mobile drawers were dead because these lines used to sit last). */
331
+ .ws-drawer-toggle { display: none; align-items: center; justify-content: center; width: 40px; height: 40px; border: none; background: none; color: var(--fg-2); cursor: pointer; border-radius: var(--r-1); }
332
+ /* Desktop-only chrome toggle (sessions collapse): inverse of the mobile drawer-toggle. */
333
+ .ws-desktop-toggle { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: none; background: none; color: var(--fg-2); cursor: pointer; border-radius: var(--r-1); }
334
+ .ws-desktop-toggle:hover { background: var(--bg-2); color: var(--fg); }
335
+ .ws-desktop-toggle:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
336
+ @media (max-width: 900px) { .ws-desktop-toggle { display: none; } }
337
+ /* The pane collapse toggle is meaningless once the pane TRACK is dropped (the
338
+ pane becomes a mobile overlay drawer at <=1480px, reached via its own
339
+ drawer-toggle), so hide this crumb control past that breakpoint. */
340
+ @media (max-width: 1480px) { .ws-pane-toggle { display: none; } }
341
+ @media (max-width: 480px) { .ds-247420 .ws-crumb { padding-left: var(--space-2); padding-right: var(--space-2); } }
342
+ .ws-scrim { display: none; }
343
+
344
+ /* Responsive: the columns yield to the CONTENT in stages - the main column is
345
+ the product, so the right pane gives way first, the sessions list second,
346
+ and only below 900px does the rail itself drop to a fixed icon strip. Yielded
347
+ columns stay reachable as overlay drawers (.ws-pane-open / .ws-sessions-open,
348
+ toggled from the crumb bar) over a shared fade scrim. Before this staging the
349
+ 4-track grid survived down to 901px and crushed the chat thread to ~245px
350
+ while the 320px context pane kept its full width. */
351
+
352
+ /* Stage 1 (<=1480px): drop the pane TRACK; the context pane becomes a right
353
+ overlay drawer. */
354
+ @media (max-width: 1480px) {
355
+ .ws-shell,
356
+ .ws-shell.ws-no-pane,
357
+ .ws-shell.ws-pane-collapsed,
358
+ .ws-shell.ws-no-pane.ws-pane-collapsed:not(.ws-no-sessions) {
359
+ grid-template-columns: var(--ws-rail-w) var(--ws-sessions-w) minmax(0, 1fr);
360
+ grid-template-areas: "rail sessions content";
361
+ }
362
+ .ws-shell.ws-no-sessions,
363
+ .ws-shell.ws-no-pane.ws-no-sessions,
364
+ .ws-shell.ws-no-pane.ws-pane-collapsed.ws-no-sessions {
365
+ grid-template-columns: var(--ws-rail-w) minmax(0, 1fr);
366
+ grid-template-areas: "rail content";
367
+ }
368
+ /* Drawer width is a literal: --ws-pane-w is 0px while pane-collapsed, and a
369
+ desktop-collapsed pane must still open as a full drawer. */
370
+ .ws-pane {
371
+ position: fixed; inset: 0 0 0 auto;
372
+ width: min(340px, 85vw);
373
+ z-index: 42; transform: translateX(110%);
374
+ transition: transform var(--dur-base) var(--ease);
375
+ }
376
+ .ws-shell.ws-pane-open .ws-pane { transform: translateX(0); width: min(340px, 85vw); }
377
+ .ws-shell.ws-pane-open.ws-pane-collapsed .ws-pane { border-left: var(--bw-hair) solid var(--bg-3); }
378
+ .ws-shell.ws-pane-open.ws-pane-collapsed .ws-pane > * { display: revert; }
379
+ .ws-pane-drawer-toggle { display: inline-flex; }
380
+ /* Scrim is always present from this stage down so it FADES with the drawer
381
+ instead of hard-appearing; pointer-events gate keeps it inert while closed. */
382
+ .ws-scrim { display: block; opacity: 0; pointer-events: none; position: fixed; inset: 0; z-index: 41; background: color-mix(in srgb, var(--fg) 38%, transparent); transition: opacity var(--dur-base) var(--ease); }
383
+ .ws-shell.ws-sessions-open .ws-scrim, .ws-shell.ws-pane-open .ws-scrim { opacity: 1; pointer-events: auto; }
384
+ }
385
+
386
+ /* Stage 2 (<=1100px): drop the sessions TRACK; the conversation list becomes a
387
+ left overlay drawer. */
388
+ @media (max-width: 1100px) {
389
+ .ws-shell,
390
+ .ws-shell.ws-no-pane,
391
+ .ws-shell.ws-pane-collapsed,
392
+ .ws-shell.ws-no-sessions,
393
+ .ws-shell.ws-no-pane.ws-no-sessions,
394
+ .ws-shell.ws-no-pane.ws-pane-collapsed:not(.ws-no-sessions),
395
+ .ws-shell.ws-no-pane.ws-pane-collapsed.ws-no-sessions {
396
+ grid-template-columns: var(--ws-rail-w) minmax(0, 1fr);
397
+ grid-template-areas: "rail content";
398
+ }
399
+ .ws-sessions {
400
+ position: fixed; inset: 0 auto 0 0;
401
+ /* Immune to the desktop collapse var (--ws-sessions-w:0px persisted from
402
+ a desktop session would open an invisible 0-width drawer). */
403
+ width: min(max(var(--ws-sessions-w), 280px), 80vw);
404
+ z-index: 42; transform: translateX(-110%);
405
+ transition: transform var(--dur-base) var(--ease);
406
+ border-right: var(--bw-hair) solid var(--bg-3);
407
+ overscroll-behavior: contain;
408
+ }
409
+ .ws-shell.ws-sessions-open .ws-sessions { transform: translateX(0); }
410
+ /* Drawer mode ignores the desktop collapsed state: children stay rendered
411
+ and the border returns, so a persisted collapse can't blank the drawer. */
412
+ .ws-shell.ws-sessions-collapsed .ws-sessions > * { display: revert; }
413
+ .ws-shell.ws-sessions-collapsed .ws-sessions { border-right: var(--bw-hair) solid var(--bg-3); }
414
+ .ws-sessions-drawer-toggle { display: inline-flex; }
415
+ }
416
+
417
+ /* Stage 3 (<=900px): single content column; the rail drops to a fixed icon-only
418
+ strip. Labels hide UNCONDITIONALLY here - a 60px rail showing clipped label
419
+ text ('age', 'Ne') is the worst of both worlds. */
420
+ @media (max-width: 900px) {
421
+ .ws-shell,
422
+ .ws-shell.ws-no-sessions,
423
+ .ws-shell.ws-no-pane,
424
+ .ws-shell.ws-pane-collapsed,
425
+ .ws-shell.ws-no-pane.ws-no-sessions,
426
+ .ws-shell.ws-no-pane.ws-pane-collapsed:not(.ws-no-sessions),
427
+ .ws-shell.ws-no-pane.ws-pane-collapsed.ws-no-sessions {
428
+ grid-template-columns: minmax(0, 1fr);
429
+ grid-template-areas: "content";
430
+ }
431
+ .ws-rail { position: fixed; inset: 0 auto 0 0; width: var(--ws-rail-w-collapsed); z-index: 40; }
432
+ .ws-rail .ws-rail-brand,
433
+ .ws-rail .ws-rail-action-label,
434
+ .ws-rail .ws-rail-item-label,
435
+ .ws-rail .ws-rail-item-count,
436
+ .ws-rail .ds-theme-toggle-label { display: none; }
437
+ .ws-rail .ws-rail-item, .ws-rail .ws-rail-action { justify-content: center; gap: 0; }
438
+ .ws-rail .ws-rail-head { justify-content: center; }
439
+ /* Expand/collapse is meaningless on the icon strip. */
440
+ .ws-rail-toggle { display: none; }
441
+ .ws-rail-foot { display: flex; justify-content: center; }
442
+ /* The 1100px rule's closed-state translateX(-110%) was sized for left:0;
443
+ once left shifts to the collapsed rail width, -110% no longer clears the
444
+ viewport and leaves a sliver of the "closed" drawer visible on screen. */
445
+ .ws-sessions { inset: 0 auto 0 var(--ws-rail-w-collapsed); transform: translateX(calc(-100% - var(--ws-rail-w-collapsed))); }
446
+ .ws-shell.ws-sessions-open .ws-sessions { transform: translateX(0); }
447
+ .ws-content { grid-area: content; padding-left: var(--ws-rail-w-collapsed); }
448
+ /* The Crumb's narrow collapse elsewhere keys on the .app @container, which
449
+ does not exist inside WorkspaceShell - mirror it here so the crumb bar
450
+ cannot overflow a narrow viewport (trail hides, leaf ellipsizes, the
451
+ right cluster wraps under instead of pushing past the edge). */
452
+ .ws-crumb { padding-top: 6px; padding-bottom: 6px; }
453
+ .ws-crumb .app-crumb { flex-wrap: wrap; min-width: 0; }
454
+ .ws-crumb .app-crumb > .sep, .ws-crumb .app-crumb > span:not(.crumb-right):not(.leaf) { display: none; }
455
+ .ws-crumb .app-crumb .leaf { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
456
+ .ws-crumb .app-crumb .crumb-right { margin-left: 0; flex-wrap: wrap; min-width: 0; display: inline-flex; }
457
+ }
458
+ .ws-drawer-toggle:hover { background: var(--bg-2); color: var(--fg); }
459
+ .ws-drawer-toggle:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
460
+
461
+ @media (prefers-reduced-motion: reduce) {
462
+ .ws-shell, .ws-rail, .ws-pane, .ws-sessions, .ws-scrim { transition: none; }
463
+ .ws-resizer::after { transition: none; }
464
+ }
465
+
@@ -0,0 +1,75 @@
1
+ /* ============================================================
2
+ Row title highlight, expanded-row actions, filter pills.
3
+ ============================================================ */
4
+
5
+ /* Highlighted match inside a Row title (Row `highlight` prop). */
6
+ .ds-hl { background: color-mix(in oklab, var(--accent) 25%, transparent); color: inherit; border-radius: var(--r-hair); }
7
+
8
+ /* Action strip rendered inside an EXPANDED Row (Row `actions` prop). The
9
+ buttons stop propagation so they never fire the row onClick. */
10
+ .row-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); width: 100%; padding-top: var(--space-2); }
11
+ .row-act {
12
+ display: inline-flex; align-items: center; justify-content: center;
13
+ padding: 4px 10px; min-height: 28px;
14
+ background: var(--bg-2); border: var(--bw-hair) solid var(--bg-3); border-radius: var(--r-1);
15
+ color: var(--fg-2); font-family: var(--ff-body); font-size: var(--fs-tiny); cursor: pointer;
16
+ transition: background var(--dur-snap) var(--ease), color var(--dur-snap) var(--ease);
17
+ }
18
+ .row-act:hover { background: var(--bg-3); color: var(--fg); }
19
+ .row-act:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
20
+
21
+ /* FilterPills - a group of pill toggle buttons (aria-pressed). */
22
+ .ds-filter-pills { display: flex; flex-wrap: wrap; gap: var(--space-2); }
23
+ .ds-filter-pill {
24
+ display: inline-flex; align-items: center; justify-content: center;
25
+ padding: 4px 12px; min-height: 28px;
26
+ background: var(--bg-2); border: var(--bw-hair) solid var(--bg-3); border-radius: var(--r-pill);
27
+ color: var(--fg-2); font-family: var(--ff-body); font-size: var(--fs-tiny); cursor: pointer;
28
+ transition: background var(--dur-snap) var(--ease), color var(--dur-snap) var(--ease);
29
+ }
30
+ .ds-filter-pill:hover { background: var(--bg-3); color: var(--fg); }
31
+ .ds-filter-pill.active { background: var(--accent-tint); color: var(--fg); border-color: var(--accent); }
32
+ .ds-filter-pill:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
33
+
34
+ /* Touch floor for the new small controls. */
35
+ @media (pointer: coarse) {
36
+ .row-act, .ds-filter-pill { min-height: 44px; }
37
+ }
38
+
39
+ /* Disabled file actions (read-only row / in-flight mutation) stay visible but inert. */
40
+ .ds-file-act:disabled { opacity: .45; cursor: default; }
41
+ .ds-file-act:disabled:hover { background: transparent; color: var(--fg-3); }
42
+ .ds-file-act:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
43
+
44
+ /* ============================================================
45
+ Print — linearize WorkspaceShell so main content prints in full.
46
+ The multi-column CSS grid with overflow:auto regions clips at
47
+ the on-screen height in print/PDF; drop all chrome and let
48
+ .ws-main expand to its natural document height.
49
+ ============================================================ */
50
+ @media print {
51
+ .ws-shell { display: block; }
52
+ .ws-rail,
53
+ .ws-sessions,
54
+ .ws-pane,
55
+ .ws-crumb,
56
+ .app-status { display: none; }
57
+ .ws-main { overflow: visible; height: auto; }
58
+ /* AppShell: unpin the viewport-height frame so content flows across pages,
59
+ and drop all chrome (it prints as page furniture otherwise). */
60
+ .app { height: auto; min-height: 0; overflow: visible; display: block; }
61
+ .app-body { display: block; }
62
+ .app-main { overflow: visible; height: auto; display: block; max-width: 100%; }
63
+ .app-topbar, .app-chrome, .app-crumb, .app-side-shell, .app-side-scrim,
64
+ .app-side-toggle, .btn-fab, .skip-link, .ds-marquee { display: none; }
65
+ /* Modals/preview: print the content, not a blurred overlay clipped to one page. */
66
+ .ds-modal-backdrop { position: static; background: none; backdrop-filter: none; padding: 0; }
67
+ .ds-modal, .ds-modal-preview { max-width: 100%; max-height: none; box-shadow: none; }
68
+ .ds-modal-body, .ds-preview-body, .ds-preview-code, .ds-preview-text { overflow: visible; }
69
+ .ds-modal-actions, .ds-preview-actions { display: none; }
70
+ /* Data-density: fixed-height log clips; bg-only tiles lose their boundary. */
71
+ .ds-live-log { height: auto; max-height: none; overflow: visible; }
72
+ .ds-stat, .kpi-card { border: 1px solid var(--rule); }
73
+ .ds-session-row { break-inside: avoid; }
74
+ }
75
+
@@ -0,0 +1,79 @@
1
+ /* ============================================================
2
+ Sidebar polish: hide zero-count badges; tonal active state;
3
+ monospace count chips.
4
+ ============================================================ */
5
+ .app-side .count {
6
+ padding: 1px 8px; font-size: var(--fs-micro); font-weight: 700;
7
+ font-family: var(--ff-mono); letter-spacing: 0.04em;
8
+ min-width: 18px; text-align: center;
9
+ }
10
+ .app-side a:has(> .count:empty) > .count,
11
+ .app-side .count:empty { display: none; }
12
+ .app-side a .glyph { font-family: var(--ff-mono); font-size: 13px; font-weight: 600; color: var(--fg-3); text-align: center; }
13
+ .app-side a.active .glyph { color: var(--accent-ink); }
14
+
15
+ /* -- select primitive --------------------------------------------------- */
16
+ .ds-select {
17
+ width: 100%;
18
+ min-width: 0;
19
+ min-height: 32px; /* shared control floor with .btn/.btn-primary/.btn-ghost */
20
+ padding: 10px 30px 10px 14px; /* right gap clears the chevron */
21
+ background: var(--bg-2);
22
+ border: 0;
23
+ border-radius: var(--r-2);
24
+ font-family: inherit;
25
+ font-size: var(--fs-sm);
26
+ color: var(--fg);
27
+ /* Clip long option text gracefully - a select that cuts 'Claude Sonnet
28
+ (latest' mid-parenthesis reads as broken; ellipsis reads as truncated. */
29
+ text-overflow: ellipsis;
30
+ white-space: nowrap;
31
+ overflow: hidden;
32
+ appearance: none;
33
+ background-image: linear-gradient(45deg, transparent 50%, var(--fg-3) 50%),
34
+ linear-gradient(135deg, var(--fg-3) 50%, transparent 50%);
35
+ background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%;
36
+ background-size: 5px 5px, 5px 5px;
37
+ background-repeat: no-repeat;
38
+ cursor: pointer;
39
+ }
40
+ .ds-select:focus-visible { box-shadow: var(--focus-ring-inset); }
41
+ .ds-select:hover { background-color: var(--bg-3); }
42
+
43
+ /* -- chat composer autogrow polish (sizing lives in the primary block) -- */
44
+ .chat-composer textarea {
45
+ transition: height var(--dur-snap) var(--ease);
46
+ }
47
+ .chat-composer textarea:disabled {
48
+ opacity: 0.5;
49
+ cursor: not-allowed;
50
+ }
51
+
52
+ /* -- mobile responsive: chat -------------------------------------------- */
53
+ @media (max-width: 768px) {
54
+ .chat-stack { max-width: 100%; }
55
+ .chat-msg { padding: 4px 0; }
56
+ .chat-msg:hover { padding: 4px 0; margin: 0; border-radius: 0; background: transparent; }
57
+ .chat-bubble { font-size: var(--fs-sm); padding: 10px 14px; }
58
+ .chat-avatar { width: 28px; height: 28px; font-size: 12px; }
59
+ .chat-composer { padding: 8px; }
60
+ .chat-composer textarea { padding: 10px 12px; }
61
+ }
62
+
63
+ /* --- landscape orientation: reduce vertical space for chrome + composer --- */
64
+ @media (max-height: 500px) and (orientation: landscape) {
65
+ /* Keep horizontal padding: 0 put the textarea/focus ring flush on the edge. */
66
+ .chat-composer { padding: 6px var(--space-2); }
67
+ .chat-composer textarea { min-height: 36px; max-height: 96px; }
68
+ .chat-composer .chat-composer-hint,
69
+ .chat-composer .chat-composer-context { display: none; }
70
+ /* Compact the shell chrome: ~114px of topbar+crumb+status is a third of a
71
+ 360px-tall landscape fold. */
72
+ :root { --app-topbar-h: 40px; --app-crumb-h: 24px; }
73
+ .app-chrome { padding: 4px var(--pad-x); }
74
+ .app-status { display: none; }
75
+ .ws-crumb { min-height: 32px; }
76
+ .ds-hero { padding: var(--space-4) 0 var(--space-3); }
77
+ .ds-hero-title { font-size: clamp(28px, 6cqi, 56px); }
78
+ }
79
+