anentrypoint-design 0.0.208 → 0.0.209

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/app-shell.css CHANGED
@@ -143,8 +143,8 @@ pre {
143
143
  margin: 0;
144
144
  border-radius: var(--r-3);
145
145
  }
146
- pre .k { color: var(--mascot); }
147
- pre .s { color: var(--sun); }
146
+ pre .k { color: var(--code-str-alt, var(--mascot)); }
147
+ pre .s { color: var(--code-string, var(--green)); }
148
148
  pre .c { color: var(--fg-3); }
149
149
  pre .n { color: var(--green-2); }
150
150
 
@@ -386,13 +386,21 @@ body.canvas-host { background: transparent !important; }
386
386
  width: 100%;
387
387
  min-height: var(--app-status-h);
388
388
  padding: 10px var(--pad-x);
389
- font-family: var(--ff-mono); font-size: var(--fs-xs);
389
+ /* Status chrome is prose, not code - mono is reserved for code/paths. */
390
+ font-family: var(--ff-body); font-size: var(--fs-xs);
390
391
  color: var(--fg-3);
391
392
  border-top: 1px solid var(--rule);
392
393
  }
393
- .app-status .item { color: inherit; }
394
+ .app-status .item { color: inherit; white-space: nowrap; }
394
395
  .app-status .item:first-of-type { color: var(--accent); }
395
396
  .app-status .spread { flex: 1; }
397
+ /* The status bar NEVER wraps: at narrow widths the trailing items (hints,
398
+ agent target) drop in priority order instead of breaking mid-word onto a
399
+ second line. */
400
+ .app-status { flex-wrap: nowrap; overflow: hidden; }
401
+ @media (max-width: 1100px) {
402
+ .app-status .item:last-of-type:not(:only-of-type) { display: none; }
403
+ }
396
404
 
397
405
  /* ============================================================
398
406
  Primitives
@@ -439,7 +447,8 @@ body.canvas-host { background: transparent !important; }
439
447
  .ds-icon-btn-ghost:hover { background: var(--bg-2); }
440
448
  .ds-icon-btn-primary { background: var(--accent); color: var(--accent-fg); }
441
449
  .ds-icon-btn-primary:hover { background: var(--fg); color: var(--bg); }
442
- .ds-icon-btn-danger { background: var(--flame); color: var(--paper); }
450
+ /* warn = destructive ACTION tone; flame stays exclusively error STATUS. */
451
+ .ds-icon-btn-danger { background: var(--warn); color: var(--paper); }
443
452
  .ds-icon-btn-danger:hover { filter: brightness(1.1); }
444
453
  .ds-icon-btn:active { transform: translateY(1px); }
445
454
  .ds-icon-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@@ -513,11 +522,15 @@ body.canvas-host { background: transparent !important; }
513
522
  .panel.panel-wide { background: transparent; }
514
523
  .panel-head {
515
524
  display: flex; align-items: baseline; justify-content: space-between;
516
- padding: var(--space-4) var(--space-5) var(--space-3);
525
+ /* Shares the .panel-body 16px side padding so the caps label and the body
526
+ rows sit on ONE left axis (the old 24/32 padding was authored for the
527
+ pre-padding .panel-wide era and indented the label 16px past its content). */
528
+ padding: var(--space-2) var(--space-3);
517
529
  font-size: var(--fs-xs); font-weight: 600;
518
530
  color: var(--fg-3);
519
531
  letter-spacing: var(--tr-caps); text-transform: uppercase;
520
532
  }
533
+ .panel.panel-wide .panel-head { padding-left: 0; padding-right: 0; }
521
534
  .panel-head > :last-child {
522
535
  font-weight: 500; color: var(--fg-3);
523
536
  font-family: var(--ff-mono); letter-spacing: 0;
@@ -585,6 +598,12 @@ body.canvas-host { background: transparent !important; }
585
598
  .row .code { font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--fg-3); font-variant-numeric: tabular-nums; letter-spacing: 0.01em; }
586
599
  .row .title { font-family: var(--ff-body); font-weight: 600; font-size: var(--fs-lg); line-height: var(--lh-snug, 1.3); display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; min-width: 0; }
587
600
  .row .title .sub { font-family: var(--ff-body); font-weight: 400; font-size: var(--fs-sm); color: var(--fg-3); }
601
+ /* App typescale: list rows are quiet working chrome (the 18px/600 default is a
602
+ marketing-surface voice). One size for ALL app list rows - matches
603
+ .ds-file-row/.ds-session-title at fs-sm/500; values mirror the kit's own
604
+ mobile block below. Marketing pages without data-typescale keep the default. */
605
+ [data-typescale="app"] .row { padding: 12px 16px; }
606
+ [data-typescale="app"] .row .title { font-size: var(--fs-sm); font-weight: 500; }
588
607
  .row .sub { font-family: var(--ff-body); font-weight: 400; font-size: var(--fs-sm); color: var(--fg-3); }
589
608
  .row .meta { font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--fg-3); text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; align-self: center; }
590
609
  /* WorksList meta pairs a label with a disclosure chevron, inline-aligned. */
@@ -1030,8 +1049,8 @@ table tr.clickable:focus-visible td { background: var(--bg-2); }
1030
1049
  padding: 12px 18px; font-size: var(--fs-sm);
1031
1050
  }
1032
1051
 
1033
- /* Panel */
1034
- .panel-head { padding: var(--space-4) var(--space-4); }
1052
+ /* Panel - keep the head on the body's left axis at laptop widths too. */
1053
+ .panel-head { padding: var(--space-2) var(--space-3); }
1035
1054
  .panel-body { padding: var(--space-2) var(--space-3); }
1036
1055
 
1037
1056
  /* Hero — fluid base font-size; only width unconstrained on tablet. */
@@ -1103,13 +1122,15 @@ table tr.clickable:focus-visible td { background: var(--bg-2); }
1103
1122
 
1104
1123
  /* Listing wrapper holds the optional filter + sort header above the grid. */
1105
1124
  .ds-file-listing { display: flex; flex-direction: column; gap: var(--space-2); min-height: 0; }
1106
- .ds-file-filter { padding: 0 0 var(--space-1); }
1125
+ /* The quick filter is a compact control, not a full-width form row - a giant
1126
+ input above the grid reads as a search page and costs fold height. */
1127
+ .ds-file-filter { padding: 0 0 var(--space-1); display: flex; justify-content: flex-end; }
1107
1128
  .ds-file-filter-input {
1108
- width: 100%; padding: 8px 12px; font-size: var(--fs-sm);
1129
+ width: min(280px, 100%); padding: 6px 12px; font-size: var(--fs-sm);
1109
1130
  background: var(--bg); color: var(--fg);
1110
- border: var(--bw-hair) solid var(--rule); border-radius: var(--r-2);
1131
+ border: var(--bw-hair) solid var(--rule); border-radius: var(--r-1);
1111
1132
  }
1112
- .ds-file-filter-input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
1133
+ .ds-file-filter-input:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--accent); }
1113
1134
  .ds-file-sort { display: flex; gap: var(--space-2); padding: 0 var(--space-2) var(--space-1); }
1114
1135
  .ds-file-sort-btn {
1115
1136
  font-size: var(--fs-tiny); font-family: var(--ff-mono); color: var(--fg-3);
@@ -1121,6 +1142,16 @@ table tr.clickable:focus-visible td { background: var(--bg-2); }
1121
1142
 
1122
1143
  /* Skeleton shimmer rows shown while a directory loads. */
1123
1144
  .ds-file-row-skeleton { cursor: default; pointer-events: none; }
1145
+ /* In-place refresh: dim the populated grid instead of swapping it for shimmer
1146
+ rows (the rows stay; the round-trip reads as a settle, not a reload). */
1147
+ .ds-file-grid.is-refreshing { opacity: .6; }
1148
+ @media (prefers-reduced-motion: no-preference) {
1149
+ .ds-file-grid { transition: opacity .15s var(--ease); }
1150
+ }
1151
+ /* EventList loading skeleton (shares the .ds-skel shimmer base above). */
1152
+ .ds-event-row-skeleton { display: flex; align-items: center; gap: var(--space-3); padding: 12px 16px; pointer-events: none; }
1153
+ .ds-skel-rank { height: 12px; width: 2.5ch; }
1154
+ .ds-event-state { padding: var(--space-2) 16px; }
1124
1155
  .ds-skel { display: inline-block; border-radius: var(--r-1); background: var(--bg-2); position: relative; overflow: hidden; }
1125
1156
  .ds-skel::after {
1126
1157
  content: ''; position: absolute; inset: 0;
@@ -1169,7 +1200,7 @@ table tr.clickable:focus-visible td { background: var(--bg-2); }
1169
1200
  .ds-file-row[data-file-type="code"] { border-left-width: 3px; border-left-color: var(--green-2); }
1170
1201
  .ds-file-row[data-file-type="text"] { border-left-width: 3px; border-left-color: var(--ink-3); }
1171
1202
  .ds-file-row[data-file-type="archive"] { border-left-width: 3px; border-left-color: var(--flame); }
1172
- .ds-file-row[data-file-type="document"] { border-left-width: 3px; border-left-color: var(--sun); }
1203
+ .ds-file-row[data-file-type="document"] { border-left-width: 3px; border-left-color: var(--amber, var(--sun)); }
1173
1204
  .ds-file-row[data-file-type="symlink"] { border-left-width: 3px; border-left-color: var(--purple); }
1174
1205
  .ds-file-row[data-file-type="other"] { border-left-width: 3px; border-left-color: var(--fg-3); }
1175
1206
 
@@ -1200,7 +1231,7 @@ table tr.clickable:focus-visible td { background: var(--bg-2); }
1200
1231
  .ds-file-row[data-file-type="audio"] .ds-file-icon { color: var(--sky); }
1201
1232
  .ds-file-row[data-file-type="code"] .ds-file-icon { color: var(--green-2); }
1202
1233
  .ds-file-row[data-file-type="archive"] .ds-file-icon { color: var(--flame); }
1203
- .ds-file-row[data-file-type="document"] .ds-file-icon { color: var(--sun); }
1234
+ .ds-file-row[data-file-type="document"] .ds-file-icon { color: var(--amber, var(--sun)); }
1204
1235
 
1205
1236
  /* Row actions — hidden until hover/focus, revealed without layout shift. */
1206
1237
  .ds-file-actions {
@@ -1250,6 +1281,19 @@ table tr.clickable:focus-visible td { background: var(--bg-2); }
1250
1281
  background: var(--bg-2); border: var(--bw-hair) solid var(--rule); border-radius: var(--r-2);
1251
1282
  }
1252
1283
  .ds-bulkbar-count { font-size: var(--fs-tiny); color: var(--fg-2); }
1284
+ /* BulkBar is a toolbar (it even declares role=toolbar): compact rect buttons,
1285
+ quiet destructive outline - the app arm-confirms destructive bulk actions
1286
+ via ConfirmDialog, so the strip never needs a loud CTA. */
1287
+ .ds-bulkbar .btn, .ds-bulkbar .btn-primary {
1288
+ padding: 5px 12px; min-height: 32px; border-radius: var(--r-1); font-weight: 500;
1289
+ }
1290
+ .ds-bulkbar .btn { background: transparent; border: var(--bw-hair) solid var(--rule); color: var(--fg-2); }
1291
+ .ds-bulkbar .btn:hover { background: var(--bg-2); color: var(--fg); }
1292
+ .ds-bulkbar .btn-primary.danger { background: transparent; color: var(--warn); border: var(--bw-hair) solid var(--warn); }
1293
+ .ds-bulkbar .btn-primary.danger:hover { background: color-mix(in oklab, var(--warn) 12%, transparent); color: var(--warn); }
1294
+ @media (pointer: coarse) {
1295
+ .ds-bulkbar .btn, .ds-bulkbar .btn-primary { min-height: 44px; }
1296
+ }
1253
1297
 
1254
1298
  /* Density picker — list / compact / thumbnails. */
1255
1299
  .ds-density {
@@ -1336,6 +1380,11 @@ table tr.clickable:focus-visible td { background: var(--bg-2); }
1336
1380
  .app-main > div > .ds-file-stage,
1337
1381
  .app-main .ds-file-stage { padding-top: 0; }
1338
1382
 
1383
+ /* Full-width files stack: the stage's space-3 vertical beat without its
1384
+ 920px cap/auto-margins, so the roots / toolbar / bulkbar / uploads / grid
1385
+ bands keep one consistent rhythm instead of touching edge-to-edge. */
1386
+ .ds-files-stack { display: flex; flex-direction: column; gap: var(--space-3); min-height: 0; }
1387
+
1339
1388
  /* Breadcrumb path */
1340
1389
  .ds-crumb-path {
1341
1390
  display: flex; align-items: center; flex-wrap: wrap; gap: 2px;
@@ -1356,10 +1405,38 @@ table tr.clickable:focus-visible td { background: var(--bg-2); }
1356
1405
  display: flex; align-items: center; justify-content: space-between;
1357
1406
  gap: var(--space-2); flex-wrap: wrap;
1358
1407
  }
1408
+ /* Dense page header: one row, small heading, single-line muted lede. The
1409
+ working surfaces (files, live, history, settings) lead with content, not a
1410
+ display H1 + paragraph that costs the first 150px of every fold. */
1411
+ .ds-page-header-dense { margin: 0 0 var(--space-3); }
1412
+ .ds-page-header-dense-row { display: flex; align-items: baseline; gap: var(--space-3); min-width: 0; }
1413
+ .ds-page-header-dense-row h1 {
1414
+ margin: 0; font-size: var(--fs-lg); line-height: 1.3; font-weight: 600;
1415
+ letter-spacing: normal; flex: 0 0 auto;
1416
+ }
1417
+ .ds-page-header-dense-lede {
1418
+ flex: 1 1 auto; min-width: 0;
1419
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
1420
+ font-size: var(--fs-sm); color: var(--fg-3);
1421
+ }
1422
+ .ds-page-header-dense .ds-page-header-right { flex: 0 0 auto; margin-left: auto; }
1359
1423
  .ds-file-toolbar-left,
1360
1424
  .ds-file-toolbar-right {
1361
1425
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
1362
1426
  }
1427
+ /* Toolbar-scoped buttons: quiet, compact, rectangular - a toolbar is chrome,
1428
+ not a CTA row; the stadium pill stays for real page-level actions. */
1429
+ .ds-file-toolbar .btn, .ds-file-toolbar .btn-ghost {
1430
+ padding: 5px 12px; min-height: 32px;
1431
+ border-radius: var(--r-1);
1432
+ font-weight: 500;
1433
+ background: transparent;
1434
+ border: var(--bw-hair) solid var(--rule); color: var(--fg-2);
1435
+ }
1436
+ .ds-file-toolbar .btn:hover, .ds-file-toolbar .btn-ghost:hover { background: var(--bg-2); color: var(--fg); }
1437
+ @media (pointer: coarse) {
1438
+ .ds-file-toolbar .btn, .ds-file-toolbar .btn-ghost { min-height: 44px; }
1439
+ }
1363
1440
  .ds-meta-mono {
1364
1441
  font-family: var(--ff-mono); font-size: var(--fs-xs);
1365
1442
  color: var(--fg-3); letter-spacing: var(--tr-caps);
@@ -1400,6 +1477,20 @@ table tr.clickable:focus-visible td { background: var(--bg-2); }
1400
1477
  0%, 100% { border-color: var(--accent); }
1401
1478
  50% { border-color: color-mix(in oklab, var(--accent) 45%, transparent); }
1402
1479
  }
1480
+ /* Wrapper form (has children): no permanent chrome - the content renders as if
1481
+ the zone were not there, and the dashed affordance overlays it ONLY while a
1482
+ drag is in flight. */
1483
+ .ds-dropzone--wrap { position: relative; border: none; border-radius: 0; background: transparent; }
1484
+ .ds-dropzone--wrap > .ds-dropzone-inner { display: none; }
1485
+ .ds-dropzone--wrap.dragover > .ds-dropzone-inner {
1486
+ display: flex; position: absolute; inset: 0; z-index: 5;
1487
+ margin: 0; padding: var(--space-3);
1488
+ align-items: center; justify-content: center;
1489
+ background: color-mix(in srgb, var(--bg) 84%, transparent);
1490
+ outline: var(--bw-rule) dashed var(--accent); outline-offset: -6px;
1491
+ border-radius: var(--r-3);
1492
+ }
1493
+ .ds-dropzone--wrap.dragover { animation: none; }
1403
1494
 
1404
1495
  /* Upload progress */
1405
1496
  .ds-upload-progress { display: flex; flex-direction: column; gap: 6px; }
@@ -1593,7 +1684,7 @@ table tr.clickable:focus-visible td { background: var(--bg-2); }
1593
1684
  border-radius: var(--r-pill); color: var(--fg);
1594
1685
  font-family: inherit; font-size: var(--fs-xs);
1595
1686
  }
1596
- .ds-filter-input:focus { outline: 2px solid var(--accent); outline-offset: 2px; }
1687
+ .ds-filter-input:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--accent); }
1597
1688
 
1598
1689
  /* Loading skeleton — placeholder rows while a directory loads. */
1599
1690
  .ds-file-grid-loading { display: flex; flex-direction: column; gap: 4px; }
@@ -1620,44 +1711,19 @@ table tr.clickable:focus-visible td { background: var(--bg-2); }
1620
1711
  .ds-file-row[draggable="true"] { cursor: grab; }
1621
1712
  .ds-file-row[draggable="true"]:active { cursor: grabbing; }
1622
1713
 
1623
- /* Multi-select per-row checkbox overlay + selected state + bulk bar. */
1714
+ /* (A stale duplicate multi-select block used to live here: an absolute-overlay
1715
+ .ds-file-check hidden until hover, plus .ds-file-check.on / .ds-file-row.selected
1716
+ / .ds-bulk-bar rules NO component ever emits. Being later in source at equal
1717
+ specificity it clobbered the real in-flow checkbox at ~1243 - the shipped
1718
+ multi-select rendered invisible-until-hover with clipped brackets. The live
1719
+ rules are the earlier block; only the still-load-bearing bits remain below.) */
1624
1720
  .ds-file-row { position: relative; }
1625
- .ds-file-check {
1626
- position: absolute; left: 4px; top: 50%; transform: translateY(-50%);
1627
- width: 18px; height: 18px; padding: 0; z-index: 1;
1628
- display: inline-flex; align-items: center; justify-content: center;
1629
- border: var(--bw-hair) solid var(--rule); border-radius: 5px;
1630
- background: var(--bg); color: var(--accent-fg);
1631
- font-size: var(--fs-micro); line-height: 1; cursor: pointer;
1632
- opacity: 0; transition: opacity var(--dur-base) var(--ease), background var(--dur-snap) var(--ease);
1633
- }
1634
- .ds-file-row:hover .ds-file-check,
1635
- .ds-file-row:focus-within .ds-file-check,
1636
- .ds-file-check.on { opacity: 1; }
1637
- /* Touch / coarse-pointer devices have no hover — keep checkboxes AND the
1638
- per-row action buttons (rename/delete/download) visible so the file manager
1639
- is fully reachable without a pointer (e.g. an iPad in landscape). */
1721
+ /* Touch / coarse-pointer devices have no hover — keep the per-row action
1722
+ buttons (rename/delete/download) visible so the file manager is fully
1723
+ reachable without a pointer (e.g. an iPad in landscape). */
1640
1724
  @media (hover: none), (pointer: coarse) {
1641
- .ds-file-check { opacity: 1; }
1642
1725
  .ds-file-actions { opacity: 1; }
1643
1726
  }
1644
- .ds-file-check.on { background: var(--accent); border-color: var(--accent); }
1645
- .ds-file-row.selected {
1646
- background: var(--accent-tint);
1647
- border-color: var(--accent);
1648
- }
1649
- .ds-file-row.selected:hover { background: color-mix(in oklab, var(--accent) 22%, var(--bg)); }
1650
-
1651
- .ds-bulk-bar {
1652
- display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2);
1653
- padding: 10px 14px; border-radius: var(--r-2);
1654
- background: var(--accent-tint);
1655
- border: var(--bw-hair) solid var(--accent);
1656
- position: sticky; top: var(--space-2); z-index: 2;
1657
- }
1658
- .ds-bulk-count {
1659
- font-weight: 600; font-size: var(--fs-sm); margin-right: auto;
1660
- }
1661
1727
 
1662
1728
  /* Keyboard-shortcuts hint — compact two-column legend. */
1663
1729
  .ds-shortcuts-hint {
@@ -1700,6 +1766,13 @@ table tr.clickable:focus-visible td { background: var(--bg-2); }
1700
1766
  font-family: var(--ff-mono); font-size: var(--fs-xs);
1701
1767
  display: inline-flex; align-items: center; gap: 6px;
1702
1768
  }
1769
+ /* CSS-drawn half-disc mark on the compact theme toggle: keeps the control
1770
+ legible when its text label is hidden (icon-only rail strip). */
1771
+ .ds-theme-disc {
1772
+ flex: 0 0 auto; width: 14px; height: 14px; border-radius: 50%;
1773
+ border: var(--bw-hair) solid var(--fg-3);
1774
+ background: linear-gradient(90deg, var(--fg-3) 0 50%, transparent 50% 100%);
1775
+ }
1703
1776
 
1704
1777
  /* ============================================================
1705
1778
  Chat kit polish — styles every class emitted by Chat /
@@ -1880,10 +1953,10 @@ table tr.clickable:focus-visible td { background: var(--bg-2); }
1880
1953
  .chat-bubble.chat-code .token.atrule,
1881
1954
  .chat-bubble.chat-code .token.selector { color: var(--accent); }
1882
1955
  .chat-bubble.chat-code .token.string,
1883
- .chat-bubble.chat-code .token.attr-value { color: var(--mascot); }
1956
+ .chat-bubble.chat-code .token.attr-value { color: var(--code-str-alt, var(--mascot)); }
1884
1957
  .chat-bubble.chat-code .token.comment { color: var(--fg-3); font-style: italic; }
1885
1958
  .chat-bubble.chat-code .token.number,
1886
- .chat-bubble.chat-code .token.boolean { color: var(--sun); }
1959
+ .chat-bubble.chat-code .token.boolean { color: var(--code-num, var(--sun)); }
1887
1960
  .chat-bubble.chat-code .token.punctuation { color: var(--fg-2); }
1888
1961
  .chat-bubble.chat-code .token.property,
1889
1962
  .chat-bubble.chat-code .token.attr-name { color: var(--sky); }
@@ -2052,7 +2125,9 @@ table tr.clickable:focus-visible td { background: var(--bg-2); }
2052
2125
  display: flex; align-items: flex-end; flex-wrap: wrap; gap: var(--space-2);
2053
2126
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-3);
2054
2127
  background: var(--bg);
2055
- border: var(--bw-hair) solid var(--rule);
2128
+ /* The composer sits bg-on-bg: the 14% --rule hairline is ~1.3:1 on paper,
2129
+ leaving the primary input near-invisible at rest - use the strong rule. */
2130
+ border: var(--bw-hair) solid var(--rule-strong);
2056
2131
  border-radius: var(--r-2);
2057
2132
  flex-shrink: 0;
2058
2133
  transition: border-color var(--dur-snap) var(--ease), box-shadow var(--dur-snap) var(--ease);
@@ -2066,7 +2141,9 @@ table tr.clickable:focus-visible td { background: var(--bg-2); }
2066
2141
  background: none; color: var(--fg);
2067
2142
  border: none; border-radius: 0;
2068
2143
  font-family: inherit; font-size: var(--fs-sm);
2069
- line-height: 1.45; resize: none;
2144
+ /* 1.5 is the value that actually renders (a later polish block used to
2145
+ re-declare it; the sizing block now states the truth). */
2146
+ line-height: 1.5; resize: none;
2070
2147
  min-height: 28px; max-height: 200px;
2071
2148
  box-sizing: border-box; overflow-y: auto;
2072
2149
  scrollbar-width: thin;
@@ -2133,13 +2210,19 @@ table tr.clickable:focus-visible td { background: var(--bg-2); }
2133
2210
  /* ── select primitive ─────────────────────────────────────────────────── */
2134
2211
  .ds-select {
2135
2212
  width: 100%;
2136
- padding: 10px 14px;
2213
+ min-width: 0;
2214
+ padding: 10px 30px 10px 14px; /* right gap clears the chevron */
2137
2215
  background: var(--bg-2);
2138
2216
  border: 0;
2139
2217
  border-radius: var(--r-2);
2140
2218
  font-family: inherit;
2141
2219
  font-size: var(--fs-sm);
2142
2220
  color: var(--fg);
2221
+ /* Clip long option text gracefully - a select that cuts 'Claude Sonnet
2222
+ (latest' mid-parenthesis reads as broken; ellipsis reads as truncated. */
2223
+ text-overflow: ellipsis;
2224
+ white-space: nowrap;
2225
+ overflow: hidden;
2143
2226
  appearance: none;
2144
2227
  background-image: linear-gradient(45deg, transparent 50%, var(--fg-3) 50%),
2145
2228
  linear-gradient(135deg, var(--fg-3) 50%, transparent 50%);
@@ -2148,15 +2231,12 @@ table tr.clickable:focus-visible td { background: var(--bg-2); }
2148
2231
  background-repeat: no-repeat;
2149
2232
  cursor: pointer;
2150
2233
  }
2151
- .ds-select:focus { box-shadow: inset 0 0 0 2px var(--accent); }
2234
+ .ds-select:focus-visible { box-shadow: inset 0 0 0 2px var(--accent); }
2152
2235
  .ds-select:hover { background-color: var(--bg-3); }
2153
2236
 
2154
- /* ── chat composer autogrow polish ────────────────────────────────────── */
2237
+ /* ── chat composer autogrow polish (sizing lives in the primary block) ── */
2155
2238
  .chat-composer textarea {
2156
- resize: none;
2157
- overflow-y: auto;
2158
2239
  transition: height var(--dur-snap) var(--ease);
2159
- line-height: 1.5;
2160
2240
  }
2161
2241
  .chat-composer textarea:disabled {
2162
2242
  opacity: 0.5;
@@ -3059,36 +3139,109 @@ input[type="password"]:not(:placeholder-shown) + .input-clear {
3059
3139
  .ws-pane-toggle:hover { background: var(--bg-3); color: var(--fg); }
3060
3140
  .ws-pane-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
3061
3141
 
3062
- /* Responsive: below 900px collapse to a single content column; rail+sessions+pane
3063
- become overlay drawers the host can toggle, or simply hide on mobile. We keep it
3064
- honest: stack rail (icon row) over content, hide sessions+pane unless toggled. */
3142
+ /* Drawer toggles and the scrim are hidden by default and revealed by the staged
3143
+ media blocks BELOW - these base rules must precede those blocks in source
3144
+ order: at equal specificity the later rule wins regardless of media, so a
3145
+ trailing display:none silently kills every drawer affordance (shipped bug:
3146
+ the mobile drawers were dead because these lines used to sit last). */
3147
+ .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); }
3148
+ .ws-scrim { display: none; }
3149
+
3150
+ /* Responsive: the columns yield to the CONTENT in stages - the main column is
3151
+ the product, so the right pane gives way first, the sessions list second,
3152
+ and only below 900px does the rail itself drop to a fixed icon strip. Yielded
3153
+ columns stay reachable as overlay drawers (.ws-pane-open / .ws-sessions-open,
3154
+ toggled from the crumb bar) over a shared fade scrim. Before this staging the
3155
+ 4-track grid survived down to 901px and crushed the chat thread to ~245px
3156
+ while the 320px context pane kept its full width. */
3157
+
3158
+ /* Stage 1 (<=1480px): drop the pane TRACK; the context pane becomes a right
3159
+ overlay drawer. */
3160
+ @media (max-width: 1480px) {
3161
+ .ws-shell,
3162
+ .ws-shell.ws-no-pane,
3163
+ .ws-shell.ws-pane-collapsed,
3164
+ .ws-shell.ws-no-pane.ws-pane-collapsed:not(.ws-no-sessions) {
3165
+ grid-template-columns: var(--ws-rail-w) var(--ws-sessions-w) minmax(0, 1fr);
3166
+ grid-template-areas: "rail sessions content";
3167
+ }
3168
+ .ws-shell.ws-no-sessions,
3169
+ .ws-shell.ws-no-pane.ws-no-sessions,
3170
+ .ws-shell.ws-no-pane.ws-pane-collapsed.ws-no-sessions {
3171
+ grid-template-columns: var(--ws-rail-w) minmax(0, 1fr);
3172
+ grid-template-areas: "rail content";
3173
+ }
3174
+ /* Drawer width is a literal: --ws-pane-w is 0px while pane-collapsed, and a
3175
+ desktop-collapsed pane must still open as a full drawer. */
3176
+ .ws-pane {
3177
+ position: fixed; inset: 0 0 0 auto;
3178
+ width: min(340px, 85vw);
3179
+ z-index: 42; transform: translateX(110%);
3180
+ transition: transform var(--dur-base) var(--ease);
3181
+ }
3182
+ .ws-shell.ws-pane-open .ws-pane { transform: translateX(0); width: min(340px, 85vw); }
3183
+ .ws-shell.ws-pane-open.ws-pane-collapsed .ws-pane { border-left: var(--bw-hair) solid var(--bg-3); }
3184
+ .ws-shell.ws-pane-open.ws-pane-collapsed .ws-pane > :not(.ws-pane-toggle) { display: revert; }
3185
+ /* The desktop collapse chevron is meaningless in drawer mode. */
3186
+ .ws-pane-toggle { display: none; }
3187
+ .ws-pane-drawer-toggle { display: inline-flex; }
3188
+ /* Scrim is always present from this stage down so it FADES with the drawer
3189
+ instead of hard-appearing; pointer-events gate keeps it inert while closed. */
3190
+ .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); }
3191
+ .ws-shell.ws-sessions-open .ws-scrim, .ws-shell.ws-pane-open .ws-scrim { opacity: 1; pointer-events: auto; }
3192
+ }
3193
+
3194
+ /* Stage 2 (<=1100px): drop the sessions TRACK; the conversation list becomes a
3195
+ left overlay drawer. */
3196
+ @media (max-width: 1100px) {
3197
+ .ws-shell,
3198
+ .ws-shell.ws-no-pane,
3199
+ .ws-shell.ws-pane-collapsed,
3200
+ .ws-shell.ws-no-sessions,
3201
+ .ws-shell.ws-no-pane.ws-no-sessions,
3202
+ .ws-shell.ws-no-pane.ws-pane-collapsed:not(.ws-no-sessions),
3203
+ .ws-shell.ws-no-pane.ws-pane-collapsed.ws-no-sessions {
3204
+ grid-template-columns: var(--ws-rail-w) minmax(0, 1fr);
3205
+ grid-template-areas: "rail content";
3206
+ }
3207
+ .ws-sessions {
3208
+ position: fixed; inset: 0 auto 0 0;
3209
+ width: min(var(--ws-sessions-w), 80vw);
3210
+ z-index: 42; transform: translateX(-110%);
3211
+ transition: transform var(--dur-base) var(--ease);
3212
+ border-right: var(--bw-hair) solid var(--bg-3);
3213
+ }
3214
+ .ws-shell.ws-sessions-open .ws-sessions { transform: translateX(0); }
3215
+ .ws-sessions-drawer-toggle { display: inline-flex; }
3216
+ }
3217
+
3218
+ /* Stage 3 (<=900px): single content column; the rail drops to a fixed icon-only
3219
+ strip. Labels hide UNCONDITIONALLY here - a 60px rail showing clipped label
3220
+ text ('age', 'Ne') is the worst of both worlds. */
3065
3221
  @media (max-width: 900px) {
3066
3222
  .ws-shell,
3067
3223
  .ws-shell.ws-no-sessions,
3068
3224
  .ws-shell.ws-no-pane,
3069
- .ws-shell.ws-no-pane.ws-no-sessions {
3070
- grid-template-columns: 1fr;
3225
+ .ws-shell.ws-pane-collapsed,
3226
+ .ws-shell.ws-no-pane.ws-no-sessions,
3227
+ .ws-shell.ws-no-pane.ws-pane-collapsed:not(.ws-no-sessions),
3228
+ .ws-shell.ws-no-pane.ws-pane-collapsed.ws-no-sessions {
3229
+ grid-template-columns: minmax(0, 1fr);
3071
3230
  grid-template-areas: "content";
3072
3231
  }
3073
3232
  .ws-rail { position: fixed; inset: 0 auto 0 0; width: var(--ws-rail-w-collapsed); z-index: 40; }
3074
- .ws-sessions { position: fixed; inset: 0 auto 0 var(--ws-rail-w-collapsed); width: min(var(--ws-sessions-w), 80vw); z-index: 42; transform: translateX(-110%); transition: transform var(--dur-base) var(--ease); }
3075
- .ws-shell.ws-sessions-open .ws-sessions { transform: translateX(0); }
3076
- /* On mobile the pane is a drawer driven by .ws-pane-open (an explicit open
3077
- intent), NOT by :not(.ws-pane-collapsed) - the desktop collapse default is
3078
- "open" which would otherwise pop the pane over the thread on every load. */
3079
- .ws-pane { position: fixed; inset: 0 0 0 auto; width: min(var(--ws-pane-w), 85vw); z-index: 42; transform: translateX(110%); transition: transform var(--dur-base) var(--ease); }
3080
- .ws-shell.ws-pane-open .ws-pane { transform: translateX(0); }
3233
+ .ws-rail .ws-rail-brand,
3234
+ .ws-rail .ws-rail-action-label,
3235
+ .ws-rail .ws-rail-item-label,
3236
+ .ws-rail .ws-rail-item-count,
3237
+ .ws-rail .ds-theme-toggle-label { display: none; }
3238
+ .ws-rail .ws-rail-item, .ws-rail .ws-rail-action { justify-content: center; gap: 0; }
3239
+ .ws-rail .ws-rail-head { justify-content: center; }
3240
+ /* Expand/collapse is meaningless on the icon strip. */
3241
+ .ws-rail-toggle { display: none; }
3242
+ .ws-rail-foot { display: flex; justify-content: center; }
3243
+ .ws-sessions { inset: 0 auto 0 var(--ws-rail-w-collapsed); }
3081
3244
  .ws-content { grid-area: content; padding-left: var(--ws-rail-w-collapsed); }
3082
- .ws-rail-collapsed .ws-rail-item-label, .ws-rail-collapsed .ws-rail-brand { display: none; }
3083
- /* Tap-scrim behind an open drawer; dismiss on click. Hidden unless a drawer
3084
- is open so it never blocks the content area on desktop. */
3085
- /* Scrim is always present on mobile so it can FADE with the drawer instead of
3086
- hard-appearing via a display toggle; pointer-events gate keeps it inert
3087
- while closed. Reduced-motion users get the global transition kill -> instant. */
3088
- .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); }
3089
- .ws-shell.ws-sessions-open .ws-scrim, .ws-shell.ws-pane-open .ws-scrim { opacity: 1; pointer-events: auto; }
3090
- /* The mobile drawer-toggle affordances live in the crumb bar. */
3091
- .ws-drawer-toggle { display: inline-flex; }
3092
3245
  /* The Crumb's narrow collapse elsewhere keys on the .app @container, which
3093
3246
  does not exist inside WorkspaceShell - mirror it here so the crumb bar
3094
3247
  cannot overflow a narrow viewport (trail hides, leaf ellipsizes, the
@@ -3099,11 +3252,8 @@ input[type="password"]:not(:placeholder-shown) + .input-clear {
3099
3252
  .ws-crumb .app-crumb .leaf { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
3100
3253
  .ws-crumb .app-crumb .crumb-right { margin-left: 0; flex-wrap: wrap; min-width: 0; display: inline-flex; }
3101
3254
  }
3102
- /* Drawer toggles are mobile-only chrome; hidden on the desktop grid. */
3103
- .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); }
3104
3255
  .ws-drawer-toggle:hover { background: var(--bg-2); color: var(--fg); }
3105
3256
  .ws-drawer-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
3106
- .ws-scrim { display: none; }
3107
3257
 
3108
3258
  /* ============================================================
3109
3259
  Row title highlight, expanded-row actions, filter pills.