anentrypoint-design 1.0.32 → 1.0.34

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anentrypoint-design",
3
- "version": "1.0.32",
3
+ "version": "1.0.34",
4
4
  "description": "247420 design system SDK — webjsx + modified ripple-ui, single-file ESM bundle for reproducible use of the AnEntrypoint design.",
5
5
  "type": "module",
6
6
  "main": "./dist/247420.js",
@@ -256,7 +256,7 @@ export function mountCommunityApp(root, adapter = {}) {
256
256
  // id + tabindex match AppShell()'s contract so the skip link
257
257
  // above actually lands somewhere; this app builds its own shell
258
258
  // and so inherited neither.
259
- h('main', { class: 'app-main', id: 'app-main', tabindex: '0', onclick: () => { if (s.mobileMenuOpen && A.closeMobileMenu) A.closeMobileMenu(); } },
259
+ h('main', { class: 'app-main ds-app-surface', id: 'app-main', tabindex: '0', onclick: () => { if (s.mobileMenuOpen && A.closeMobileMenu) A.closeMobileMenu(); } },
260
260
  // The channel name is the page title, but it lived only in
261
261
  // the topbar brand span, leaving the document with no
262
262
  // heading at all. sr-only because the topbar and chat-head
@@ -95,6 +95,12 @@ export function ChatMessage({ role, who = 'them', avatar, text, parts, time, typ
95
95
  if (time) metaItems.push(h('span', { class: 't', key: 'ti' }, time));
96
96
  if (tickNode) metaItems.push(tickNode);
97
97
  const meta = metaItems.length ? h('div', { class: 'chat-meta' }, ...metaItems) : null;
98
+ // Incoming turns show the name above the bubble, level with the avatar's
99
+ // top edge, so the two read as one identity unit -- the way the 'you'
100
+ // side already pairs its avatar with its own bubble. Placing meta after
101
+ // the bubble (as for 'you', where only time/receipt trail below) left an
102
+ // incoming message's name floating above the PREVIOUS message's bubble
103
+ // instead of its own, with a bare avatar disc stranded beside its bubble.
98
104
  // Per-message actions (copy / retry / edit) — a hover-revealed control row
99
105
  // below the bubble, the way Claude-Desktop surfaces message-level actions.
100
106
  // Each action is { label, icon, onClick, title }. Kept icon-only with an
@@ -132,7 +138,9 @@ export function ChatMessage({ role, who = 'them', avatar, text, parts, time, typ
132
138
  const roleLabel = isFlat
133
139
  ? h('div', { class: 'chat-role', key: '_role' }, resolvedWho === 'you' ? t('chat.roleYou', 'You') : (name || t('chat.roleAssistant', 'Assistant')))
134
140
  : null;
135
- const stack = h('div', { class: 'chat-stack' }, roleLabel, ...bodyNodes, reactionRow, actionRow, meta);
141
+ const stack = resolvedWho === 'them'
142
+ ? h('div', { class: 'chat-stack' }, roleLabel, meta, ...bodyNodes, reactionRow, actionRow)
143
+ : h('div', { class: 'chat-stack' }, roleLabel, ...bodyNodes, reactionRow, actionRow, meta);
136
144
  // Centered roles (system/tool/thinking) skip the avatar column entirely so
137
145
  // the bubble owns the full row — the chrome reads as out-of-band signal,
138
146
  // not a participant turn.
@@ -14,7 +14,7 @@ export function Panel({ title, count, right, style = '', class: className = '',
14
14
  // a screen-reader heading-jump can actually find each section, not just
15
15
  // the page's single top-level h1.
16
16
  const headingTag = 'h' + headingLevel;
17
- return h('div', { class: cls, style, ...(id ? { id } : {}) },
17
+ return h('div', { class: cls, style: style || null, ...(id ? { id } : {}) },
18
18
  title != null ? h('div', { class: 'panel-head' },
19
19
  h(headingTag, { class: 'panel-title' }, title),
20
20
  right != null ? right : (count != null ? h('span', { class: 'ds-badge' }, String(count)) : null)
@@ -164,7 +164,7 @@ function isStatusCollapsed() {
164
164
  try { return localStorage.getItem(STATUS_COLLAPSE_KEY) === '1'; } catch (_) { return false; }
165
165
  }
166
166
  function toggleStatusCollapsed(fromEl) {
167
- const app = fromEl && fromEl.closest && fromEl.closest('.app');
167
+ const app = fromEl && fromEl.closest && fromEl.closest('.app, .ws-shell');
168
168
  const bar = app && app.querySelector('.app-status');
169
169
  if (!bar) return;
170
170
  const next = !bar.classList.contains('is-collapsed');
@@ -185,7 +185,7 @@ export function Status({ left = [], right = [] } = {}) {
185
185
  // ref runs on mount, before paint, so there's no expanded-then-collapse flash.
186
186
  const syncLiveVar = (el) => {
187
187
  if (!el) return;
188
- const app = el.closest('.app');
188
+ const app = el.closest('.app, .ws-shell');
189
189
  if (app) app.style.setProperty('--app-status-h-live', collapsed ? 'var(--space-4)' : 'var(--app-status-h)');
190
190
  };
191
191
  return h('footer', { class: 'app-status' + (collapsed ? ' is-collapsed' : ''), role: 'contentinfo', ref: syncLiveVar },
@@ -145,7 +145,7 @@ export function Glyph({ children, color, size = 'base', label } = {}) {
145
145
  }
146
146
 
147
147
  export function Heading({ level = 1, children, style = '', class: className = '', 'aria-level': ariaLevel }) {
148
- return h('h' + level, { class: className || null, style, 'aria-level': ariaLevel != null ? String(ariaLevel) : null }, children);
148
+ return h('h' + level, { class: className || null, style: style || null, 'aria-level': ariaLevel != null ? String(ariaLevel) : null }, children);
149
149
  }
150
150
 
151
151
  export function Lede({ children }) {
@@ -120,7 +120,7 @@ export function WorkspaceShell({ rail, sessions, main, pane, crumb, status, narr
120
120
  onclick: (e) => toggleWsDrawer('pane', null, e.currentTarget),
121
121
  }, Icon('page')) : null)
122
122
  : null,
123
- h('main', { class: 'ws-main' + (narrow ? ' narrow' : '') + (mainFlush ? ' ws-main--flush' : ''), id: 'ws-main', tabindex: '-1' },
123
+ h('main', { class: 'ws-main ds-app-surface' + (narrow ? ' narrow' : '') + (mainFlush ? ' ws-main--flush' : ''), id: 'ws-main', tabindex: '-1' },
124
124
  ...(Array.isArray(main) ? main : [main])),
125
125
  status || null),
126
126
  // Optional right context pane. Its desktop collapse toggle now lives in
@@ -143,6 +143,15 @@ h3 + .lede, h3 + .t-lede,
143
143
  margin-top: var(--space-2);
144
144
  }
145
145
 
146
+ /* .app-main.narrow widens well past prose measure on wide no-sidebar
147
+ viewports on purpose (topbar.css) so full-width siblings like .post-meta/
148
+ h1/.post-index use the room. .lede/.ds-prose keep their reading-measure
149
+ caps (38ch/--measure) but stayed left edge-anchored inside that wider
150
+ column, turning the freed width into one-sided dead whitespace on the
151
+ right of the article body instead of being absorbed as margin. */
152
+ .app-main.narrow > .lede, .app-main.narrow > .t-lede,
153
+ .app-main.narrow > .ds-prose { margin-left: auto; margin-right: auto; }
154
+
146
155
  .lede + .post-body, .t-lede + .post-body,
147
156
  .lede + .ds-prose, .t-lede + .ds-prose {
148
157
  margin-top: var(--space-4);
@@ -46,7 +46,7 @@
46
46
  .chat-empty-title { font-size: var(--fs-sm); margin: 0 0 var(--space-1); }
47
47
  .chat-empty-sub { font-size: var(--fs-tiny); margin: 0; opacity: .82; } /* WCAG-verified 2026 restyle: .7 blended --fg-3 to 3.72:1 on dark --bg (#1a1a1a); .82 clears 4.53:1 */
48
48
 
49
- .chat-msg { display: flex; gap: var(--space-2-75); align-items: flex-start; padding: var(--space-1-75) 0; position: relative; min-width: 0; }
49
+ .chat-msg { display: flex; flex-shrink: 0; gap: var(--space-2-75); align-items: flex-start; padding: var(--space-1-75) 0; position: relative; min-width: 0; }
50
50
  .chat-msg.you { flex-direction: row-reverse; }
51
51
  .chat-msg.you .chat-stack { align-items: flex-end; }
52
52
  .chat-msg:hover { background: color-mix(in oklab, var(--fg) 4%, transparent); padding: var(--space-1-75) 0; margin: 0; border-radius: var(--r-0); transition: background var(--dur-base) var(--ease); }
@@ -613,6 +613,23 @@
613
613
  text-align: center;
614
614
  margin: 0;
615
615
  }
616
+ /* Reference's Projects/General sidebar switcher: a two-pill row sitting
617
+ above the tree, spaced off the tree below it like a real section header,
618
+ not flush against it. Tighter side padding than --pad-x (round 4 widened
619
+ that to 48px) -- two pills at natural width need more of the 256px rail
620
+ than a 48px-each-side gutter leaves, so this row alone uses --space-3 to
621
+ stay a single non-wrapping row like the reference, without touching the
622
+ shared --pad-x token every other sidebar section still uses. */
623
+ .aicat-side-tabs {
624
+ padding: var(--space-3) var(--space-3) var(--space-2);
625
+ flex-wrap: nowrap;
626
+ }
627
+ .ds-side-empty-note {
628
+ padding: var(--space-2) var(--pad-x);
629
+ color: var(--fg-3);
630
+ font-size: var(--fs-sm);
631
+ margin: 0;
632
+ }
616
633
 
617
634
  /* ============================================================
618
635
  Composer attachment-detected badge, context line, hint, toolbar,
@@ -51,7 +51,10 @@
51
51
  .ds-skel::after {
52
52
  content: ''; position: absolute; inset: 0;
53
53
  background: linear-gradient(90deg, transparent, var(--bg-3), transparent);
54
- transform: translateX(-100%); animation: ds-skel-shimmer 1.2s infinite;
54
+ transform: translateX(-100%);
55
+ }
56
+ @media (prefers-reduced-motion: no-preference) {
57
+ .ds-skel::after { animation: ds-skel-shimmer 1.2s infinite; }
55
58
  }
56
59
  .ds-skel-icon { width: 22px; height: 22px; }
57
60
  .ds-skel-title { height: 12px; width: 60%; }
@@ -375,6 +378,7 @@
375
378
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
376
379
  }
377
380
  .ds-crumb-seg:hover { background: var(--bg-2); color: var(--fg); }
381
+ .ds-crumb-seg:focus-visible { outline: none; box-shadow: var(--focus-ring-inset); }
378
382
  .ds-crumb-seg.leaf { color: var(--fg); font-weight: 600; }
379
383
  .ds-crumb-sep { color: var(--fg-3); opacity: 0.6; user-select: none; }
380
384
 
@@ -546,7 +550,11 @@
546
550
  progress) — a sliding marquee bar reads as "working", not stuck at 0%. */
547
551
  .ds-upload-bar.indeterminate .ds-upload-fill {
548
552
  width: 40%; transition: none;
549
- animation: ds-upload-marquee 1.1s ease-in-out infinite;
553
+ }
554
+ @media (prefers-reduced-motion: no-preference) {
555
+ .ds-upload-bar.indeterminate .ds-upload-fill {
556
+ animation: ds-upload-marquee 1.1s ease-in-out infinite;
557
+ }
550
558
  }
551
559
  @keyframes ds-upload-marquee {
552
560
  0% { transform: translateX(-100%); }
@@ -100,6 +100,16 @@
100
100
  @container (max-width: 900px) {
101
101
  .ds-hero { padding: var(--space-7) 0 var(--space-6); }
102
102
  }
103
+ /* .ds-hero already supplies its own --space-8 (96px) trailing padding, same
104
+ "gap owned by one side only" pattern as * + .ds-section below. A .panel
105
+ right after it still added its own --space-6-scaled top margin PLUS its
106
+ panel-head top padding on top of that -- measured live, the stat block sat
107
+ ~138px from "currently shipping" (96 hero pad + panel's own lead) versus
108
+ the ~42px any other block gets ahead of a .panel, and well past the 96px
109
+ .ds-section itself uses for its "more space above than below" lead. Zero
110
+ the panel's own top margin here so the hero's trailing padding is the only
111
+ contributor, matching every other panel/section transition on the page. */
112
+ .ds-hero + .panel { margin-top: 0; }
103
113
  .ds-chat-title { margin: 0; font-size: inherit; }
104
114
 
105
115
  /* ============================================================
@@ -172,14 +182,16 @@
172
182
  .ds-marquee-track {
173
183
  display: inline-flex; gap: var(--space-5);
174
184
  white-space: nowrap; will-change: transform;
175
- animation: ds-marquee-run 28s linear infinite;
185
+ }
186
+ @media (prefers-reduced-motion: no-preference) {
187
+ .ds-marquee-track { animation: ds-marquee-run 28s linear infinite; }
188
+ .ds-marquee:hover .ds-marquee-track,
189
+ .ds-marquee:focus-within .ds-marquee-track,
190
+ .ds-marquee:active .ds-marquee-track { animation-play-state: paused; }
176
191
  }
177
192
  .ds-marquee-run { display: inline-flex; align-items: center; gap: var(--space-5); }
178
193
  .ds-marquee-item { display: inline-flex; align-items: center; gap: var(--space-5); }
179
194
  .ds-marquee-sep { color: var(--accent-ink); }
180
- .ds-marquee:hover .ds-marquee-track,
181
- .ds-marquee:focus-within .ds-marquee-track,
182
- .ds-marquee:active .ds-marquee-track { animation-play-state: paused; }
183
195
  @keyframes ds-marquee-run {
184
196
  from { transform: translateX(0); }
185
197
  to { transform: translateX(-50%); }
@@ -388,7 +388,18 @@
388
388
  }
389
389
  .ds-prim-panel { padding: clamp(10px, 2.5vw, 14px) clamp(10px, 3vw, 18px); display: flex; flex-direction: column; gap: 18px; }
390
390
  .ds-prim-row { display: flex; gap: var(--space-2-5); flex-wrap: wrap; align-items: center; }
391
+ /* collab overlay specimens (LiveCursorOverlay/RemoteSelectionRings/
392
+ RecentEditHighlightFlash) are `position:absolute; inset:0`, meant to
393
+ overlay their own demo row -- without a positioned ancestor closer than
394
+ the ancestor `.panel` (panel-row.css), they escape to cover the whole
395
+ panel including its title. Scope them to this row. */
396
+ .ds-prim-row-collab { position: relative; min-height: 64px; }
391
397
  .ds-prim-label { flex: 0 0 64px; font-family: var(--ff-mono); font-size: var(--fs-nano); color: var(--fg-3); }
398
+ /* Trailing status readouts (e.g. "none pressed yet") share .ds-prim-label's
399
+ type/color but are free-length prose, not the fixed-width leading row
400
+ label -- the shared 64px flex-basis wrapped them into a cramped 3-line
401
+ stack. Give status text room to sit on one line instead. */
402
+ .ds-prim-label-status { flex: 0 1 auto; white-space: nowrap; }
392
403
 
393
404
  /* == appended: content-site == */
394
405
  /* Home landing wrappers (site/theme.mjs) — replace banned inline styles. */
@@ -475,8 +486,8 @@
475
486
  need a clearly larger interval between them than within them, or the whole
476
487
  form reads as one undifferentiated column. Margin-collapse-free (flow root
477
488
  siblings) — first/last are zeroed so the panel body sets the outer edge. */
478
- .ds-field-block { display: flex; flex-direction: column; gap: var(--space-1-75); margin: calc(var(--space-4) * var(--density)) 0 0; }
479
- .ds-settings-body > .ds-field-block:first-child { margin-top: 0; }
489
+ .ds-field-block, .ds-field-wrap { display: flex; flex-direction: column; gap: var(--space-1-75); margin: calc(var(--space-4) * var(--density)) 0 0; }
490
+ .ds-settings-body > .ds-field-block:first-child, .ds-settings-body > .ds-field-wrap:first-child { margin-top: 0; }
480
491
  /* Field width is a hint about expected input length. A name or handle stretched
481
492
  across a 1000px desktop column tells the user to type a paragraph and leaves
482
493
  the eye no left-to-right anchor; capping the control (not the row) keeps the
@@ -555,7 +566,7 @@
555
566
  then has to scroll. `auto` collapses when the pane is short, centres when tall. */
556
567
  .ds-auth-col { width: 100%; max-width: 440px; display: flex; flex-direction: column; gap: var(--space-2); margin: auto 0; min-width: 0; }
557
568
  /* 18px ceiling: no tier between --space-3 (16) and --space-3-5 (20). */
558
- .ds-auth-form { padding: clamp(var(--space-2-75), 3vw, 18px); display: flex; flex-direction: column; gap: var(--space-2-5); }
569
+ .ds-auth-form { display: flex; flex-direction: column; gap: var(--space-2-5); }
559
570
  .ds-auth-sent { align-items: center; text-align: center; }
560
571
  /* 32px is an ICON size, not a type size: this is the confirmation mark above
561
572
  the "check your mail" copy, sized to the 440px auth column, and the --fs-*
@@ -762,18 +773,19 @@
762
773
  /* min-height is a floor, not the intended size — .chat-kit-page's flex-column
763
774
  splits leftover height between this (flex:1 1 0%, "leftover") and the
764
775
  pattern-notes panel below it (flex:0 1 auto, i.e. sized to its own content
765
- first). On a short viewport the panel's natural content height can consume
766
- the entire column budget, squeezing this flex-basis:0% sibling to a real,
767
- rendered, fully-populated-with-messages 0px — the thread is never empty,
768
- it is invisible. Inside .chat itself, .chat-head and .chat-composer are
769
- also flex:0-ish naturally-sized siblings of .chat-thread (flex:1 1 0%,
770
- chat-polish.css), so the floor has to clear their combined natural height
771
- (~55px head + ~78px composer, chrome + gap) with real room left for the
772
- thread to still show at least a couple of message bubbles rather than
773
- collapsing to a sliver between them — 30vh was not enough on a short
774
- viewport. It still grows past this floor via flex:1 whenever the column
775
- has room. */
776
- .ds-chat-layout { display: flex; flex-direction: column; flex: 1; min-height: max(30vh, 420px); gap: var(--space-3); }
776
+ first). A hard px floor here (this used to read max(30vh, 420px)) can
777
+ exceed .chat-kit-page's own available height on a short/laptop viewport —
778
+ 420px plus the panel's natural content height overflows the column
779
+ instead of yielding to it, so .chat-thread's internal overflow-y:auto
780
+ never engages and the last couple of messages are stranded below the
781
+ fold with no reachable scroll region (the thread is never empty, it is
782
+ invisible). min-height:0 here — same as .chat/.chat-thread already use —
783
+ lets flex:1 actually shrink this to whatever .chat-kit-page has left,
784
+ so .chat-thread (flex:1, min-height:0, overflow-y:auto) is the thing that
785
+ scrolls, not the page. It still grows past 0 via flex:1 whenever the
786
+ column has room, and 30vh remains the width->grid breakpoint's intent via
787
+ plain flex growth, not a floor that can outgrow its container. */
788
+ .ds-chat-layout { display: flex; flex-direction: column; flex: 1; min-height: 0; gap: var(--space-3); }
777
789
  .ds-chat-layout > .chat { min-width: 0; }
778
790
  .ds-chat-detail { display: none; }
779
791
  @media (min-width: 1100px) {
@@ -962,9 +974,14 @@
962
974
  color: var(--fg-3);
963
975
  line-height: 1.3;
964
976
  }
965
- /* Message body claims the full second line of the wrapping body row. */
977
+ /* Message body claims the full second line of the wrapping body row. Capped
978
+ to a readable measure -- unlike .chat-bubble this is a flat list row, not
979
+ a two-sided bubble, but an uncapped prose line stretches the full pane
980
+ width (~970px), which reads as dead paper beside every short message
981
+ rather than an intentionally spacious flat list. */
966
982
  .ds-community-msg-text {
967
983
  flex: 1 0 100%;
984
+ max-width: 70ch;
968
985
  margin: 0;
969
986
  min-width: 0;
970
987
  color: var(--fg-2);
@@ -188,7 +188,11 @@
188
188
  border-radius: 50%;
189
189
  background: var(--sky);
190
190
  transform: translateY(-50%);
191
- animation: chip-live-pulse 1.6s ease-in-out infinite;
191
+ }
192
+ @media (prefers-reduced-motion: no-preference) {
193
+ .chip.tone-live::before {
194
+ animation: chip-live-pulse 1.6s ease-in-out infinite;
195
+ }
192
196
  }
193
197
  @keyframes chip-live-pulse {
194
198
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 color-mix(in oklab, var(--sky) 50%, transparent); }
@@ -317,3 +321,31 @@
317
321
  .ds-tooltip { transition: none; }
318
322
  }
319
323
 
324
+ /* -- Menubar -------------------------------------------------------------
325
+ Row of closed triggers (mirrors .ds-dp-trigger's affordance); without
326
+ this the wrapper and its item divs fall back to block-level UA defaults,
327
+ stacking the triggers vertically and rendering them as filled buttons
328
+ abutting any adjacent status text. */
329
+ .ds-menubar { display: inline-flex; align-items: center; gap: var(--space-1); }
330
+ .ds-menubar-item { display: inline-flex; }
331
+ .ds-menubar-trigger {
332
+ display: inline-flex;
333
+ align-items: center;
334
+ min-height: 32px;
335
+ padding: var(--space-1-5) var(--space-2-5);
336
+ border: 0;
337
+ border-radius: var(--r-2);
338
+ background: var(--bg-2);
339
+ color: var(--fg);
340
+ font-family: inherit;
341
+ font-size: var(--fs-sm);
342
+ cursor: pointer;
343
+ transition: background-color var(--dur-snap) var(--ease), color var(--dur-snap) var(--ease);
344
+ }
345
+ .ds-menubar-trigger:hover { background: var(--bg-3); }
346
+ .ds-menubar-trigger:focus-visible { box-shadow: var(--focus-ring-inset); outline: none; }
347
+ .ds-menubar-trigger.is-open { background: var(--bg-3); }
348
+ @media (prefers-reduced-motion: reduce) {
349
+ .ds-menubar-trigger { transition: none; }
350
+ }
351
+
@@ -77,6 +77,9 @@ button:not(:disabled):hover,
77
77
  border-radius: 50%;
78
78
  animation: spinner-rotate 0.8s linear infinite;
79
79
  }
80
+ @media (prefers-reduced-motion: reduce) {
81
+ .loading::after { animation: none; }
82
+ }
80
83
 
81
84
  /* Error state.
82
85
  No !important: the only rule in the kit that also resets a matching element's
@@ -208,6 +211,16 @@ input[type="checkbox"], input[type="radio"] {
208
211
  .list-item.drag-after::after { animation: pulse-line 0.6s ease-in-out infinite; }
209
212
  }
210
213
 
214
+ /* ------------------------------------------------------------
215
+ Focus-visible for controls with no other component sheet
216
+ -------------------------------------------------------------- */
217
+
218
+ .ds-session-new:focus-visible,
219
+ .agentchat-cwd-btn:focus-visible {
220
+ outline: none;
221
+ box-shadow: var(--focus-ring-inset);
222
+ }
223
+
211
224
  /* ------------------------------------------------------------
212
225
  Context Menu Styles
213
226
  -------------------------------------------------------------- */
@@ -775,6 +788,9 @@ input[type="password"]:not(:placeholder-shown) + .input-clear {
775
788
  animation: skeleton-load 1.5s infinite;
776
789
  border-radius: var(--r-2);
777
790
  }
791
+ @media (prefers-reduced-motion: reduce) {
792
+ .skeleton { animation: none; }
793
+ }
778
794
 
779
795
  @keyframes skeleton-load {
780
796
  0% { background-position: 200% 0; }
@@ -72,8 +72,19 @@ body.canvas-host { background: transparent !important; }
72
72
  -webkit-backdrop-filter: none; padding: 0; min-height: 0;
73
73
  flex: 1 1 auto;
74
74
  }
75
- .app-chrome > .app-crumb { order: 1; flex: 1 1 auto; }
76
- .app-chrome > .app-topbar { order: 2; flex: 0 0 auto; }
75
+ .app-chrome > .app-crumb { order: 1; flex: 1 1 auto; min-width: 0; }
76
+ /* flex:1 1 auto + min-width:0 (not the previous 0 0 auto): a merged
77
+ * chrome's topbar carries the search input, which has its own shrink
78
+ * basis (.ds-topbar-search, kits-appended.css) meant to yield space before
79
+ * the row wraps -- but a `0 0 auto` topbar box never shrinks itself, so the
80
+ * search input's flexibility was inert and the whole topbar instead
81
+ * dropped to a second line as one unit once combined crumb+topbar content
82
+ * exceeded the row (witnessed live on the search kit at 1280px: crumb
83
+ * 564px + topbar 596px overflowed a 1184px content box, wrapping the
84
+ * topbar under the crumb and reading as a broken sticky header). Letting
85
+ * the topbar itself shrink means the search input actually compresses
86
+ * first, keeping both bars on one line at realistic content widths. */
87
+ .app-chrome > .app-topbar { order: 2; flex: 1 1 auto; min-width: 0; justify-content: flex-end; }
77
88
  .app-chrome > .app-topbar > .brand { display: none; }
78
89
  .app-chrome > .app-topbar > nav { margin-left: 0; }
79
90
  .app-chrome > .app-crumb > .crumb-right { margin-left: auto; }
@@ -369,6 +380,7 @@ body.canvas-host { background: transparent !important; }
369
380
  /* Full-height route children fill the region instead of scrolling the page. */
370
381
  .app-main > .chat,
371
382
  .app-main > .chat-area,
383
+ .app-main > .chat-kit-page,
372
384
  .app-main > .ds-file-stage,
373
385
  .app-main > .ds-files-stack,
374
386
  .app-main > .grow { flex: 1 1 auto; min-height: 0; }
@@ -361,6 +361,7 @@ html, body {
361
361
  * independent — replacing the `!important` wall that previously did the same
362
362
  * job by force. Do not un-scope these; the scope IS the override mechanism. */
363
363
  .ds-247420 .wm-win {
364
+ position: absolute;
364
365
  background: var(--os-bg-1);
365
366
  border: 1px solid color-mix(in oklab, var(--os-fg) 14%, transparent);
366
367
  border-radius: var(--radius-lg, var(--r-3));