anentrypoint-design 0.0.311 → 0.0.313

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/dist/247420.css CHANGED
@@ -186,7 +186,9 @@
186
186
  --space-1-75: 6px;
187
187
  --space-2: 8px;
188
188
  --space-2-5: 10px;
189
+ --space-2-75: 12px; /* fills the 10->16 hole (row-form gap, dense cell pad) */
189
190
  --space-3: 16px;
191
+ --space-3-5: 20px; /* fills the 16->24 hole (settings body, mono block pad) */
190
192
  --space-4: 24px;
191
193
  --space-5: 32px;
192
194
  --space-6: 48px;
@@ -260,6 +262,19 @@
260
262
  --size-base: 42px;
261
263
  --size-lg: 56px;
262
264
 
265
+ /* Dense control-height ladder — the actual heights front-door controls
266
+ (buttons, fields, rows) snap to, one rung tighter than the --size-*
267
+ chrome scale so app UI reads compact instead of marketing-scale. Density
268
+ scales them so [data-density=compact/spacious] moves every control at once.
269
+ Mirrors webgeist's 30/38/46 button ladder, retuned for this system. */
270
+ --ctl-sm: calc(28px * var(--density));
271
+ --ctl-md: calc(34px * var(--density));
272
+ --ctl-lg: calc(42px * var(--density));
273
+ /* The canonical single-line input/select/searchfield height (== --ctl-md)
274
+ and the default list-row min-height, both density-aware. */
275
+ --field-height: var(--ctl-md);
276
+ --row-height: calc(40px * var(--density));
277
+
263
278
  /* Unified focus-ring tokens (single source for the design system). Outset
264
279
  rings (the default) tokenize colour+width+offset; bordered text fields use
265
280
  the inset variant. Deliberate negative offsets on edge-flush/clipping
@@ -289,25 +304,26 @@
289
304
  outline-offset: var(--focus-offset);
290
305
  }
291
306
 
292
- /* Link focus-visible */
307
+ /* Link focus-visible — all route through the single --focus-* token set so a
308
+ focus retune (colour/width/offset) happens in one place, never per selector. */
293
309
  .ds-247420 a:focus-visible,
294
310
  .ds-247420[role="link"]:focus-visible {
295
- outline: 2px solid var(--accent-ink);
296
- outline-offset: 2px;
311
+ outline: var(--focus-w) solid var(--focus-color);
312
+ outline-offset: var(--focus-offset);
297
313
  }
298
314
 
299
315
  /* Form inputs with focus-visible */
300
316
  .ds-247420 input:focus-visible,
301
317
  .ds-247420 textarea:focus-visible,
302
318
  .ds-247420 select:focus-visible {
303
- outline: 2px solid var(--accent-ink);
304
- outline-offset: 2px;
319
+ outline: var(--focus-w) solid var(--focus-color);
320
+ outline-offset: var(--focus-offset);
305
321
  }
306
322
 
307
323
  /* Interactive element focus-visible */
308
324
  .ds-247420[tabindex]:focus-visible {
309
- outline: 2px solid var(--accent-ink);
310
- outline-offset: 2px;
325
+ outline: var(--focus-w) solid var(--focus-color);
326
+ outline-offset: var(--focus-offset);
311
327
  }
312
328
 
313
329
  .ds-247420[data-theme="ink"],
@@ -855,13 +871,19 @@
855
871
  }
856
872
  .ds-247420 .app-side a {
857
873
  display: grid; grid-template-columns: 18px 1fr auto;
858
- gap: 10px; align-items: center;
859
- padding: 12px 14px;
860
- min-height: 44px;
861
- border-radius: var(--r-pill);
874
+ gap: var(--space-2-5); align-items: center;
875
+ /* Dense rounded-rect nav row (was a 44px pill with 12px 14px padding — tall
876
+ and consumer-shaped). A --r-1 rounded rect at ~34px reads as compact app
877
+ chrome; the full 44px touch target is restored under pointer:coarse below. */
878
+ padding: var(--space-2) var(--space-2-5);
879
+ min-height: 34px;
880
+ border-radius: var(--r-1);
862
881
  color: var(--fg-2);
863
882
  margin-bottom: 2px;
864
883
  }
884
+ @media (pointer: coarse) {
885
+ .ds-247420 .app-side a { min-height: 44px; padding: var(--space-2-75) var(--space-3); }
886
+ }
865
887
  .ds-247420 .app-side a:hover { background: var(--bg-2); color: var(--fg); }
866
888
  .ds-247420 .app-side a:focus-visible {
867
889
  outline: var(--focus-w) solid var(--focus-color);
@@ -984,10 +1006,16 @@
984
1006
  ============================================================ */
985
1007
  .ds-247420 .btn, .ds-247420 .btn-primary, .ds-247420 .btn-ghost {
986
1008
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
987
- padding: 8px 14px; min-height: 32px;
1009
+ /* Fixed height + horizontal-only padding: the control height is exact and
1010
+ alignable (flex centres the label vertically), the way webgeist snaps every
1011
+ control to a height ladder. Default is the --ctl-md rung; --btn-sm/--btn-lg
1012
+ retune height + h-padding + font per size. Density scales the ladder. */
1013
+ height: var(--ctl-md); min-height: var(--ctl-md);
1014
+ padding: 0 var(--space-2-75);
988
1015
  font-family: var(--ff-body);
989
1016
  font-weight: 600; font-size: var(--fs-sm);
990
1017
  text-decoration: none;
1018
+ white-space: nowrap;
991
1019
  cursor: pointer;
992
1020
  /* Rounded-rect app chrome, not a marketing-page stadium CTA.
993
1021
  Shape vocabulary: r-1/r-2 = actionable buttons, icon-buttons, and
@@ -1020,6 +1048,33 @@
1020
1048
  outline-offset: var(--focus-offset);
1021
1049
  }
1022
1050
 
1051
+ /* Button size ladder — the size prop on Btn() (sm | md | lg). md is the base
1052
+ rule above; sm/lg retune height (off the --ctl-* ladder), horizontal padding,
1053
+ and font-size so a size swap is one class, never inline padding overrides
1054
+ (the old preview faked sizes with style=). Applies to every variant. */
1055
+ .ds-247420 .btn-sm, .ds-247420 .btn-sm.btn-primary, .ds-247420 .btn-sm.btn-ghost {
1056
+ height: var(--ctl-sm); min-height: var(--ctl-sm);
1057
+ padding: 0 var(--space-2); font-size: var(--fs-tiny); gap: var(--space-1);
1058
+ }
1059
+ .ds-247420 .btn-lg, .ds-247420 .btn-lg.btn-primary, .ds-247420 .btn-lg.btn-ghost {
1060
+ height: var(--ctl-lg); min-height: var(--ctl-lg);
1061
+ padding: 0 var(--space-4); font-size: var(--fs-body); gap: var(--space-2);
1062
+ }
1063
+
1064
+ /* App-mode quiet interactions — the marketing hover-lift (translateY + shadow)
1065
+ and the .row .meta slide read as decorative float on a dense working surface.
1066
+ Under [data-typescale="app"], state changes are background/border-only: crisp,
1067
+ no motion, no elevation. Marketing surfaces (no data-typescale) keep the lift. */
1068
+ .ds-247420[data-typescale="app"] .btn:hover,
1069
+ .ds-247420[data-typescale="app"] .btn-primary:hover,
1070
+ .ds-247420[data-typescale="app"] .btn-ghost:hover { transform: none; box-shadow: none; }
1071
+ .ds-247420[data-typescale="app"] .btn:active,
1072
+ .ds-247420[data-typescale="app"] .btn-primary:active,
1073
+ .ds-247420[data-typescale="app"] .btn-ghost:active { transform: none; }
1074
+ .ds-247420[data-typescale="app"] .panel { transition: box-shadow var(--dur-snap) var(--ease), border-color var(--dur-snap) var(--ease); }
1075
+ .ds-247420[data-typescale="app"] a.row:hover .meta,
1076
+ .ds-247420[data-typescale="app"] .row[role="button"]:hover .meta { transform: none; }
1077
+
1023
1078
  /* ============================================================
1024
1079
  IconButton — square icon-only button
1025
1080
  ============================================================ */
@@ -1057,6 +1112,10 @@
1057
1112
  border-radius: var(--r-pill);
1058
1113
  background: var(--bg-3); color: var(--fg-2);
1059
1114
  }
1115
+ /* Badge size ladder — sm for dense inline counts, lg for prominent markers.
1116
+ Base (above) stays the 18px default. */
1117
+ .ds-247420 .ds-badge.ds-badge--sm { min-width: 14px; height: 14px; padding: 0 var(--space-1); font-size: 10px; }
1118
+ .ds-247420 .ds-badge.ds-badge--lg { min-width: 22px; height: 22px; padding: 0 var(--space-2); font-size: var(--fs-tiny); }
1060
1119
  /* Canonical tone names are tone-success / tone-error / tone-neutral. Every
1061
1120
  other tone name grouped into the same rule below (tone-green/tone-live,
1062
1121
  tone-flame, tone-wip, tone-sun/tone-yellow, tone-ok/tone-miss) is a
@@ -1080,7 +1139,7 @@
1080
1139
  .ds-247420 .ds-badge.tone-orange { background: color-mix(in oklab, var(--flame) 30%, var(--sun) 70%); color: var(--ink); }
1081
1140
 
1082
1141
  .ds-247420 .chip {
1083
- display: inline-flex; align-items: center; gap: 6px;
1142
+ display: inline-flex; align-items: center; gap: var(--space-1-75);
1084
1143
  padding: 3px 10px;
1085
1144
  background: var(--bg-2); color: var(--fg-2);
1086
1145
  font-family: var(--ff-body);
@@ -1088,6 +1147,17 @@
1088
1147
  letter-spacing: var(--tr-caps); text-transform: uppercase;
1089
1148
  border-radius: var(--r-pill);
1090
1149
  }
1150
+ /* Chip size ladder — sm for dense toolbars/table cells, lg for prominent
1151
+ status. tone-* colouring (below) is orthogonal to size. */
1152
+ .ds-247420 .chip.chip--sm { padding: var(--space-half) var(--space-2); font-size: var(--fs-micro); gap: var(--space-1); }
1153
+ .ds-247420 .chip.chip--lg { padding: var(--space-1-75) var(--space-2-75); font-size: var(--fs-xs); }
1154
+ /* Tag variant — a rectangular, sentence-case chip for dense data (table cells,
1155
+ inline labels) where the all-caps pill reads too shouty and space-hungry.
1156
+ Keeps the tone-* palette; drops the caps + pill for a tight --r-0 corner. */
1157
+ .ds-247420 .chip.chip--tag {
1158
+ text-transform: none; letter-spacing: 0; font-weight: 500;
1159
+ border-radius: var(--r-0); padding: var(--space-half) var(--space-2);
1160
+ }
1091
1161
  /* Same canonical/legacy-alias grouping as .ds-badge above. */
1092
1162
  .ds-247420 .chip.tone-green, .ds-247420 .chip.tone-live, .ds-247420 .chip.tone-success, .ds-247420 .chip.tone-ok {
1093
1163
  background: var(--green-tint); color: var(--green-deep);
@@ -1157,6 +1227,17 @@
1157
1227
  }
1158
1228
  .ds-247420 .panel.panel-inline { background: var(--bg-2); padding: var(--space-3) var(--space-3); }
1159
1229
  .ds-247420 .panel.panel-wide { background: transparent; box-shadow: none; border: none; }
1230
+ /* Dense panel — a compact container for app surfaces (inspector sections,
1231
+ sidebars) that want the .panel look without the roomy 16px pad + 24px stack
1232
+ gap. Tighter corner + padding + head, so it sits between .panel and the
1233
+ flush editor .ds-ep-panel. */
1234
+ .ds-247420 .panel.panel--dense {
1235
+ padding: var(--space-2); margin-bottom: var(--space-2-75);
1236
+ border-radius: var(--r-1);
1237
+ }
1238
+ .ds-247420 .panel.panel--dense .panel-head { padding: var(--space-1-5) var(--space-2); }
1239
+ .ds-247420 .panel.panel--dense .panel-body { padding: var(--space-1-5) var(--space-2) var(--space-2); }
1240
+ .ds-247420 .panel.panel--dense .panel-body > * + * { margin-top: var(--space-2); }
1160
1241
  .ds-247420 .panel-head {
1161
1242
  display: flex; align-items: baseline; justify-content: space-between;
1162
1243
  /* Shares the .panel-body 16px side padding so the caps label and the body
@@ -1186,9 +1267,13 @@
1186
1267
  grid-template-columns: minmax(80px, 12ch) minmax(0, 1fr) auto;
1187
1268
  gap: var(--space-3);
1188
1269
  align-items: baseline;
1189
- padding: var(--space-3) var(--space-4);
1270
+ /* App density is the BASE: a list row is quiet working chrome, so it defaults
1271
+ to the compact 12px/16px rhythm (density-scaled) rather than the old
1272
+ 16px/24px marketing padding. .row--lede restores the roomier marketing
1273
+ voice for editorial index pages. */
1274
+ padding: calc(var(--space-2-75) * var(--density)) var(--space-3);
1190
1275
  background: var(--bg);
1191
- border-radius: var(--r-2);
1276
+ border-radius: var(--r-1);
1192
1277
  color: var(--fg);
1193
1278
  position: relative;
1194
1279
  /* Only background + the leading rail animate; padding/size never change on
@@ -1266,8 +1351,14 @@
1266
1351
  .ds-247420 .row.row-nocode { grid-template-columns: minmax(0, 1fr) auto; }
1267
1352
 
1268
1353
  .ds-247420 .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; }
1269
- .ds-247420 .row .title { font-family: var(--ff-body); font-weight: 600; font-size: var(--fs-lg); line-height: var(--lh-snug); display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; min-width: 0; }
1354
+ /* Title defaults to the quiet app voice (fs-sm/500) — the 18px/600 marketing
1355
+ voice is now opt-in via .row--lede. */
1356
+ .ds-247420 .row .title { font-family: var(--ff-body); font-weight: 500; font-size: var(--fs-sm); line-height: var(--lh-snug); display: flex; align-items: baseline; gap: var(--space-2-5); flex-wrap: wrap; min-width: 0; }
1270
1357
  .ds-247420 .row .title .sub { font-family: var(--ff-body); font-weight: 400; font-size: var(--fs-sm); color: var(--fg-3); }
1358
+ /* Marketing/editorial index row — restores the roomier padding + larger title
1359
+ the default carried before app-scale became the base. */
1360
+ .ds-247420 .row--lede { padding: var(--space-3) var(--space-4); }
1361
+ .ds-247420 .row--lede .title { font-size: var(--fs-lg); font-weight: 600; }
1271
1362
  /* App typescale: list rows are quiet working chrome (the 18px/600 default is a
1272
1363
  marketing-surface voice). One size for ALL app list rows - matches
1273
1364
  .ds-file-row/.ds-session-title at fs-sm/500; values mirror the kit's own
@@ -1506,16 +1597,19 @@
1506
1597
  .ds-247420 table {
1507
1598
  width: 100%; border-collapse: collapse;
1508
1599
  font-family: var(--ff-body); font-size: var(--fs-sm);
1509
- border-radius: var(--r-3); overflow: hidden;
1600
+ /* A data grid, not a card: a tight --r-0 corner (was --r-3/22px, which read
1601
+ as a rounded card). Cells drop to 8px 12px for a dense, scannable row
1602
+ (was 12/14 x 16 => ~43px rows). Density scales the vertical padding. */
1603
+ border-radius: var(--r-0); overflow: hidden;
1510
1604
  }
1511
1605
  .ds-247420 table th {
1512
1606
  text-align: left;
1513
- padding: 12px 16px;
1607
+ padding: calc(var(--space-2) * var(--density)) var(--space-2-75);
1514
1608
  font-size: var(--fs-tiny); font-weight: 600;
1515
1609
  color: var(--fg-3); text-transform: uppercase; letter-spacing: var(--tr-caps);
1516
1610
  background: var(--bg-2);
1517
1611
  }
1518
- .ds-247420 table td { padding: 14px 16px; border-top: 1px solid var(--rule); }
1612
+ .ds-247420 table td { padding: calc(var(--space-2) * var(--density)) var(--space-2-75); border-top: 1px solid var(--rule); }
1519
1613
  .ds-247420 table tr.clickable { cursor: pointer; }
1520
1614
  .ds-247420 table tr.clickable:hover td { background: var(--bg-2); }
1521
1615
  .ds-247420 table tr.clickable:focus-visible {
@@ -1583,13 +1677,16 @@
1583
1677
  gap: var(--space-2);
1584
1678
  }
1585
1679
  .ds-247420 .kpi-card {
1586
- padding: var(--space-5);
1680
+ /* Was --space-5 (32px) pad + --r-3 (22px) — four cards ate a full fold. A
1681
+ 16px pad on a --r-1 corner keeps them prominent but dense; the number drops
1682
+ from a 48px marketing display to a 32px app-scale figure. */
1683
+ padding: var(--space-3);
1587
1684
  background: var(--bg-2);
1588
- border-radius: var(--r-3);
1685
+ border-radius: var(--r-1);
1589
1686
  }
1590
1687
  .ds-247420 .kpi-card .num {
1591
1688
  font-family: var(--ff-body); font-weight: 600;
1592
- font-size: clamp(30px, 3.6cqi, 48px); line-height: 1;
1689
+ font-size: clamp(22px, 3cqi, 32px); line-height: 1;
1593
1690
  letter-spacing: var(--tr-tight);
1594
1691
  }
1595
1692
  .ds-247420 .kpi-card .lbl {
@@ -1638,11 +1735,14 @@
1638
1735
  Empty state
1639
1736
  ============================================================ */
1640
1737
  .ds-247420 .empty {
1641
- padding: var(--space-8);
1738
+ /* Was --space-8 (96px) — an empty list spent 192px of vertical space on
1739
+ nothing. --space-6 (48px) still centres a message with air, without the
1740
+ empty state dominating the fold. Mobile drops it further (below). */
1741
+ padding: var(--space-6);
1642
1742
  text-align: center;
1643
1743
  color: var(--fg-3);
1644
1744
  background: var(--bg-2);
1645
- border-radius: var(--r-3);
1745
+ border-radius: var(--r-1);
1646
1746
  font-size: var(--fs-sm);
1647
1747
  }
1648
1748
 
@@ -1983,7 +2083,8 @@
1983
2083
  background: var(--bg); color: var(--fg);
1984
2084
  border: var(--bw-hair) solid var(--rule); border-radius: var(--r-1);
1985
2085
  }
1986
- .ds-247420 .ds-file-controls .ds-file-filter-input { flex: 0 1 220px; }
2086
+ .ds-247420 .ds-file-controls .ds-file-filter-wrap { flex: 0 1 220px; display: contents; }
2087
+ .ds-247420 .ds-file-controls .ds-file-filter-wrap .ds-file-filter-input { flex: 0 1 220px; }
1987
2088
  .ds-247420 .ds-file-filter-input:focus-visible { outline: none; box-shadow: var(--focus-ring-inset); }
1988
2089
  .ds-247420 .ds-file-sort { display: flex; gap: var(--space-2); padding: 0 var(--space-2) var(--space-1); }
1989
2090
  .ds-247420 .ds-file-sort-btn {
@@ -2654,6 +2755,54 @@
2654
2755
  .ds-247420 .ds-preview-meta { display: none; }
2655
2756
  }
2656
2757
 
2758
+ /* ============================================================
2759
+ Catalog / demo shell — a shared, compact framing for the preview/*.html
2760
+ component galleries, so each preview stops hand-rolling its own \3c style>
2761
+ (body padding, section margins, demo boxes). Mirrors webgeist's dense
2762
+ catalog: a 240px sticky sidebar + capped main + a hairline-bordered,
2763
+ flex-wrapping .ds-demo cell that packs variants inline and reflows.
2764
+ ============================================================ */
2765
+ .ds-247420 .ds-catalog { display: flex; min-height: 100vh; gap: 0; }
2766
+ .ds-247420 .ds-catalog-side {
2767
+ width: 240px; flex-shrink: 0;
2768
+ border-right: var(--bw-hair) solid var(--rule);
2769
+ padding: var(--space-3);
2770
+ position: sticky; top: 0; height: 100vh; overflow-y: auto;
2771
+ }
2772
+ .ds-247420 .ds-catalog-side h1 { font-size: var(--fs-lg); margin: 0 0 var(--space-1); }
2773
+ .ds-247420 .ds-catalog-side .subtitle { font-size: var(--fs-micro); color: var(--fg-3); margin: 0 0 var(--space-4); }
2774
+ .ds-247420 .ds-catalog-side nav h4 {
2775
+ font-size: var(--fs-micro); text-transform: uppercase; letter-spacing: var(--tr-label);
2776
+ color: var(--fg-3); margin: var(--space-3) 0 var(--space-1);
2777
+ }
2778
+ .ds-247420 .ds-catalog-side nav a {
2779
+ display: block; padding: var(--space-half) 0; font-size: var(--fs-tiny);
2780
+ color: var(--fg-2); text-decoration: none;
2781
+ }
2782
+ .ds-247420 .ds-catalog-side nav a:hover { color: var(--accent-ink); }
2783
+ .ds-247420 .ds-catalog-main { flex: 1; padding: var(--space-5); max-width: var(--measure-wide); min-width: 0; }
2784
+ .ds-247420 .ds-catalog-section { margin-bottom: var(--space-5); padding-top: var(--space-3); border-top: var(--bw-hair) solid var(--rule); }
2785
+ .ds-247420 .ds-catalog-section > h2 { font-size: var(--fs-h3-app); margin: 0 0 var(--space-3); }
2786
+
2787
+ /* The gallery cell: a hairline-bordered, flex-wrapping bin that packs a
2788
+ component's variants inline and reflows — the signature "showcase" frame. */
2789
+ .ds-247420 .ds-demo {
2790
+ border: var(--bw-hair) solid var(--rule);
2791
+ border-radius: var(--r-1);
2792
+ padding: var(--space-3);
2793
+ display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2);
2794
+ background: var(--bg);
2795
+ }
2796
+ .ds-247420 .ds-demo + .ds-demo { margin-top: var(--space-2-75); }
2797
+ .ds-247420 .ds-demo-label { font-size: var(--fs-xs); font-weight: 500; margin: 0 0 var(--space-1); }
2798
+ .ds-247420 .ds-demo-desc { font-size: var(--fs-tiny); color: var(--fg-3); margin: 0 0 var(--space-2); }
2799
+
2800
+ @media (max-width: 768px) {
2801
+ .ds-247420 .ds-catalog { flex-direction: column; }
2802
+ .ds-247420 .ds-catalog-side { width: 100%; height: auto; position: static; border-right: 0; border-bottom: var(--bw-hair) solid var(--rule); }
2803
+ .ds-247420 .ds-catalog-main { padding: var(--space-3); }
2804
+ }
2805
+
2657
2806
  /* -- File browser UX affordances ---------------------------- */
2658
2807
  /* Toolbar filter input — compact search box. */
2659
2808
  .ds-247420 .ds-filter-input {
@@ -3151,10 +3300,13 @@
3151
3300
  re-declare it; the sizing block now states the truth). */
3152
3301
  line-height: 1.5; resize: none;
3153
3302
  min-height: 28px; max-height: 200px;
3154
- box-sizing: border-box; overflow-y: hidden;
3303
+ box-sizing: border-box;
3304
+ /* Always auto, not focus-only: a capped-height composer that loses focus
3305
+ while scrolled must still show its scrollbar - otherwise once the field
3306
+ blurs there is zero cue that content exists below the fold. */
3307
+ overflow-y: auto;
3155
3308
  scrollbar-width: thin;
3156
3309
  }
3157
- .ds-247420 .chat-composer textarea:focus { overflow-y: auto; }
3158
3310
  .ds-247420 .chat-composer textarea::placeholder { color: var(--fg-3); }
3159
3311
  .ds-247420 .chat-composer textarea:focus { background: none; border: none; box-shadow: none; outline: none; }
3160
3312
  .ds-247420 .chat-composer .send {
@@ -3510,19 +3662,61 @@
3510
3662
  Enhanced Form Inputs
3511
3663
  -------------------------------------------------------------- */
3512
3664
 
3513
- .ds-247420 input[type="text"],
3514
- .ds-247420 input[type="email"],
3515
- .ds-247420 input[type="password"],
3516
- .ds-247420 input[type="number"],
3517
- .ds-247420 input[type="search"],
3518
- .ds-247420 input[type="url"],
3519
- .ds-247420 input[type="tel"],
3520
- .ds-247420 textarea,
3521
- .ds-247420 select {
3665
+ /* Base chrome for any BARE native control (outside .ds-field/.row-form/.input):
3666
+ the same editorial tonal-fill + printed-baseline-rule as .ds-field, so a raw
3667
+ <input> is never a browser-default white box in dark mode. The guards are
3668
+ wrapped in :where() so they add ZERO specificity — the whole selector stays
3669
+ at (0,1,1), exactly like `.ds-field input`, so the later .ds-field / .ds-field--sm
3670
+ / .row-form descendant rules win by source order instead of being out-specified
3671
+ (a bare :not(.input) counts as a class and would beat every .ds-field rule,
3672
+ which clobbered the size variants). */
3673
+ .ds-247420 input[type="text"]:where(:not(.input):not(.ds-search-input)),
3674
+ .ds-247420 input[type="email"]:where(:not(.input):not(.ds-search-input)),
3675
+ .ds-247420 input[type="password"]:where(:not(.input):not(.ds-search-input)),
3676
+ .ds-247420 input[type="number"]:where(:not(.input):not(.ds-search-input)),
3677
+ .ds-247420 input[type="search"]:where(:not(.input):not(.ds-search-input)),
3678
+ .ds-247420 input[type="url"]:where(:not(.input):not(.ds-search-input)),
3679
+ .ds-247420 input[type="tel"]:where(:not(.input):not(.ds-search-input)),
3680
+ .ds-247420 textarea:where(:not(.input)),
3681
+ .ds-247420 select:where(:not(.ds-select)) {
3682
+ font: inherit; color: var(--fg);
3683
+ background: var(--bg-2);
3684
+ border: 0;
3685
+ box-shadow: inset 0 0 0 var(--bw-hair, 1px) var(--rule),
3686
+ inset 0 calc(-1 * var(--bw-rule)) 0 var(--rule-strong);
3687
+ border-radius: var(--r-1);
3688
+ padding: 0 var(--space-3);
3522
3689
  transition: background var(--dur-snap) var(--ease),
3523
3690
  border-color var(--dur-snap) var(--ease),
3524
3691
  box-shadow var(--dur-snap) var(--ease);
3525
3692
  }
3693
+ /* Single-line bare controls snap to the field height; textarea keeps auto
3694
+ height with symmetric padding (its content decides the box). */
3695
+ .ds-247420 input[type="text"]:where(:not(.input):not(.ds-search-input)),
3696
+ .ds-247420 input[type="email"]:where(:not(.input):not(.ds-search-input)),
3697
+ .ds-247420 input[type="password"]:where(:not(.input):not(.ds-search-input)),
3698
+ .ds-247420 input[type="number"]:where(:not(.input):not(.ds-search-input)),
3699
+ .ds-247420 input[type="search"]:where(:not(.input):not(.ds-search-input)),
3700
+ .ds-247420 input[type="url"]:where(:not(.input):not(.ds-search-input)),
3701
+ .ds-247420 input[type="tel"]:where(:not(.input):not(.ds-search-input)),
3702
+ .ds-247420 select:where(:not(.ds-select)) {
3703
+ height: var(--field-height);
3704
+ }
3705
+ .ds-247420 textarea:where(:not(.input)) { padding: var(--space-2) var(--space-3); min-height: calc(4 * 1.5em); }
3706
+ .ds-247420 input[type="text"]:where(:not(.input):not(.ds-search-input)):focus-visible,
3707
+ .ds-247420 input[type="email"]:where(:not(.input):not(.ds-search-input)):focus-visible,
3708
+ .ds-247420 input[type="password"]:where(:not(.input):not(.ds-search-input)):focus-visible,
3709
+ .ds-247420 input[type="number"]:where(:not(.input):not(.ds-search-input)):focus-visible,
3710
+ .ds-247420 input[type="search"]:where(:not(.input):not(.ds-search-input)):focus-visible,
3711
+ .ds-247420 input[type="url"]:where(:not(.input):not(.ds-search-input)):focus-visible,
3712
+ .ds-247420 input[type="tel"]:where(:not(.input):not(.ds-search-input)):focus-visible,
3713
+ .ds-247420 textarea:where(:not(.input)):focus-visible,
3714
+ .ds-247420 select:where(:not(.ds-select)):focus-visible {
3715
+ outline: none;
3716
+ background: var(--bg);
3717
+ box-shadow: inset 0 0 0 var(--bw-hair, 1px) var(--rule),
3718
+ inset 0 calc(-1 * var(--bw-chunk)) 0 var(--accent);
3719
+ }
3526
3720
 
3527
3721
  .ds-247420 input[type="text"]::placeholder,
3528
3722
  .ds-247420 input[type="email"]::placeholder,
@@ -3655,12 +3849,33 @@
3655
3849
  the electric lead on focus, instead of a uniform hairline box. */
3656
3850
  box-shadow: inset 0 0 0 var(--bw-hair, 1px) var(--rule),
3657
3851
  inset 0 calc(-1 * var(--bw-rule)) 0 var(--rule-strong);
3658
- border-radius: var(--r-2, 10px);
3659
- padding: 10px 14px;
3852
+ /* Tighter corner than the old --r-2 (14px): a dense pro control, not a soft
3853
+ consumer box. Single-line controls snap to --field-height with h-only
3854
+ padding so they align pixel-exact with buttons on the same --ctl-md rung. */
3855
+ border-radius: var(--r-1);
3856
+ height: var(--field-height);
3857
+ padding: 0 var(--space-3);
3660
3858
  transition: box-shadow var(--dur-snap) var(--ease), background var(--dur-snap) var(--ease);
3661
3859
  }
3662
- .ds-247420 .ds-field textarea { min-height: calc(4 * 1.5em); resize: vertical; }
3860
+ /* textarea is multi-line: height is auto (min-height instead), keep symmetric
3861
+ vertical padding so text doesn't sit flush to the top edge. */
3862
+ .ds-247420 .ds-field textarea { height: auto; min-height: calc(4 * 1.5em); padding: var(--space-2) var(--space-3); resize: vertical; }
3863
+
3864
+ /* Field size ladder (size prop on TextField/Select/SearchInput). md == base.
3865
+ sm/lg retune height + h-padding + font off the --ctl-* ladder, matching the
3866
+ button ladder so a form of mixed controls stays on one rhythm. */
3867
+ .ds-247420 .ds-field--sm input, .ds-247420 .ds-field--sm .ds-select, .ds-247420 .ds-field--sm .ds-search-input {
3868
+ height: var(--ctl-sm); padding: 0 var(--space-2-5); font-size: var(--fs-tiny);
3869
+ }
3870
+ .ds-247420 .ds-field--sm textarea { height: auto; padding: var(--space-1-75) var(--space-2-5); font-size: var(--fs-tiny); }
3871
+ .ds-247420 .ds-field--lg input, .ds-247420 .ds-field--lg .ds-select, .ds-247420 .ds-field--lg .ds-search-input {
3872
+ height: var(--ctl-lg); padding: 0 var(--space-4); font-size: var(--fs-body);
3873
+ }
3874
+ .ds-247420 .ds-field--lg textarea { height: auto; padding: var(--space-2-5) var(--space-4); font-size: var(--fs-body); }
3663
3875
  .ds-247420 .ds-search-input::placeholder { color: var(--fg-3); }
3876
+ /* Only present when SearchInput is passed resultCount - transparent to any
3877
+ flex/grid layout the bare input previously sat in directly. */
3878
+ .ds-247420 .ds-search-input-wrap { display: contents; }
3664
3879
 
3665
3880
  .ds-247420 .ds-field input:focus-visible,
3666
3881
  .ds-247420 .ds-field textarea:focus-visible,
@@ -5354,7 +5569,7 @@
5354
5569
  background: color-mix(in oklab, var(--fg) 8%, transparent);
5355
5570
  }
5356
5571
  .ds-247420 .cm-cat-add:focus-visible {
5357
- outline: 2px solid var(--accent-ink);
5572
+ outline: var(--focus-w) solid var(--focus-color);
5358
5573
  outline-offset: 2px;
5359
5574
  }
5360
5575
 
@@ -5537,7 +5752,7 @@
5537
5752
  background: color-mix(in oklab, var(--fg) 10%, transparent);
5538
5753
  }
5539
5754
  .ds-247420 .cm-ch-action-btn:focus-visible {
5540
- outline: 2px solid var(--accent-ink);
5755
+ outline: var(--focus-w) solid var(--focus-color);
5541
5756
  outline-offset: 2px;
5542
5757
  }
5543
5758
 
@@ -6120,44 +6335,44 @@
6120
6335
  .ds-247420 .cm-server-icon:focus-visible,
6121
6336
  .ds-247420 .cm-server-back:focus-visible,
6122
6337
  .ds-247420 .cm-server-add:focus-visible {
6123
- outline: 2px solid var(--accent-ink);
6338
+ outline: var(--focus-w) solid var(--focus-color);
6124
6339
  outline-offset: 2px;
6125
6340
  border-radius: var(--r-2);
6126
6341
  }
6127
6342
 
6128
6343
  .ds-247420 .cm-channel-item:focus-visible {
6129
- outline: 2px solid var(--accent-ink);
6344
+ outline: var(--focus-w) solid var(--focus-color);
6130
6345
  outline-offset: -2px;
6131
6346
  border-radius: var(--r-1);
6132
6347
  }
6133
6348
 
6134
6349
  .ds-247420 .cm-server-header:focus-visible,
6135
6350
  .ds-247420 .cm-category-header:focus-visible {
6136
- outline: 2px solid var(--accent-ink);
6351
+ outline: var(--focus-w) solid var(--focus-color);
6137
6352
  outline-offset: -2px;
6138
6353
  }
6139
6354
 
6140
6355
  .ds-247420 .cm-member-item:focus-visible {
6141
- outline: 2px solid var(--accent-ink);
6356
+ outline: var(--focus-w) solid var(--focus-color);
6142
6357
  outline-offset: -2px;
6143
6358
  border-radius: var(--r-1);
6144
6359
  }
6145
6360
 
6146
6361
  .ds-247420 .cm-vs-btn:focus-visible,
6147
6362
  .ds-247420 .cm-user-btn:focus-visible {
6148
- outline: 2px solid var(--accent-ink);
6363
+ outline: var(--focus-w) solid var(--focus-color);
6149
6364
  outline-offset: 2px;
6150
6365
  border-radius: var(--r-1);
6151
6366
  }
6152
6367
 
6153
6368
  /* ThreadPanel / Forum / Page interactive elements */
6154
6369
  .ds-247420 .cm-tp-item:focus-visible,
6155
- .ds-247420 .cm-forum-item:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: -2px; border-radius: var(--r-1); }
6370
+ .ds-247420 .cm-forum-item:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: -2px; border-radius: var(--r-1); }
6156
6371
  .ds-247420 .cm-tp-new:focus-visible,
6157
6372
  .ds-247420 .cm-tp-close:focus-visible,
6158
6373
  .ds-247420 .cm-forum-new:focus-visible,
6159
6374
  .ds-247420 .cm-forum-sort:focus-visible,
6160
- .ds-247420 .cm-page-edit:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; border-radius: var(--r-1); }
6375
+ .ds-247420 .cm-page-edit:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: 2px; border-radius: var(--r-1); }
6161
6376
  .ds-247420 .cm-forum-search:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--accent-ink); }
6162
6377
 
6163
6378
  /* ---------- mobile header ---------- */
@@ -6186,7 +6401,7 @@
6186
6401
  border-radius: var(--r-1);
6187
6402
  }
6188
6403
  .ds-247420 .cm-mh-btn:hover { background: var(--panel-3); }
6189
- .ds-247420 .cm-mh-btn:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
6404
+ .ds-247420 .cm-mh-btn:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: 2px; }
6190
6405
  .ds-247420 .cm-mh-title {
6191
6406
  flex: 1 1 auto;
6192
6407
  min-width: 0;
@@ -6231,7 +6446,7 @@
6231
6446
  border-radius: var(--r-1);
6232
6447
  }
6233
6448
  .ds-247420 .cm-rb-cancel:hover { background: var(--panel-3); color: var(--fg); }
6234
- .ds-247420 .cm-rb-cancel:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 1px; }
6449
+ .ds-247420 .cm-rb-cancel:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: 1px; }
6235
6450
 
6236
6451
  /* ---------- banner ---------- */
6237
6452
  .ds-247420 .cm-banner {
@@ -7093,6 +7308,15 @@
7093
7308
  .ds-247420 .ds-session-head .ds-search-input { min-height: 44px; }
7094
7309
  }
7095
7310
  .ds-247420 .ds-session-list, .ds-247420 .ds-session-groups { flex: 1; min-height: 0; overflow-y: auto; padding: var(--space-2); }
7311
+ .ds-247420 .ds-session-loadmore {
7312
+ width: 100%; margin-top: var(--space-1); padding: 8px 12px;
7313
+ font-size: var(--fs-sm); font-family: inherit; color: var(--fg-3);
7314
+ background: none; border: var(--bw-hair) dashed var(--rule); border-radius: var(--r-1);
7315
+ cursor: pointer; text-align: center;
7316
+ }
7317
+ .ds-247420 .ds-session-loadmore:hover { color: var(--fg); border-color: var(--fg-3); }
7318
+ .ds-247420 .ds-session-loadmore:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
7319
+ @media (pointer: coarse) { .ds-247420 .ds-session-loadmore { min-height: 44px; } }
7096
7320
  /* Grouped rows (Today/Yesterday/...) lay out like the flat list; the section
7097
7321
  label sticks to the top of the scroll area for Claude-Desktop-style headers. */
7098
7322
  .ds-247420 .ds-session-group { display: flex; flex-direction: column; }
@@ -7877,12 +8101,36 @@
7877
8101
  }
7878
8102
 
7879
8103
  /* Tabs */
7880
- .ds-247420 .ds-ep-tabs { display: flex; flex-direction: column; min-height: 0; height: 100%; }
8104
+ .ds-247420 .ds-ep-tabs { display: flex; flex-direction: column; min-height: 0; height: 100%; position: relative; }
7881
8105
  .ds-247420 .ds-ep-tabs-head {
7882
8106
  display: flex; flex-shrink: 0; flex-wrap: nowrap;
7883
8107
  overflow-x: auto; scroll-behavior: smooth;
7884
8108
  background: var(--panel-1);
7885
8109
  border-bottom: 1px solid var(--rule);
8110
+ position: relative;
8111
+ }
8112
+ /* Sliding indicator — a single 2px bar under the active tab that animates its
8113
+ left/width between tabs (webgeist's "expensive" underline-slide), instead of
8114
+ each tab toggling its own static border. A REAL child element positioned by
8115
+ the Tabs ref (inline left/width/top). It lives as a child of the OUTER
8116
+ .ds-ep-tabs column (position:relative), NOT the .ds-ep-tabs-head, because an
8117
+ absolutely-positioned child of a horizontal flex container mis-sizes to 0 in
8118
+ Chromium; the outer column is a vertical flex, which sizes it correctly.
8119
+ The ref sets top to the head's height so it sits on the head's bottom edge.
8120
+ .has-slider gates the static per-tab border off (below). */
8121
+ .ds-247420 .ds-ep-tab-indicator {
8122
+ /* Sized by left+right insets (set inline by positionSlider), NOT width — an
8123
+ abspos element's `width` computes to 0 in some flex-sibling layouts in
8124
+ Chromium, whereas insets size reliably. left+right at the same value
8125
+ collapse it to 0 until positioned (no-JS: invisible). */
8126
+ position: absolute; top: 0; left: 0; right: 100%; height: 2px;
8127
+ background: var(--accent);
8128
+ transition: left var(--dur-base, 160ms) var(--ease, ease),
8129
+ right var(--dur-base, 160ms) var(--ease, ease);
8130
+ pointer-events: none;
8131
+ }
8132
+ @media (prefers-reduced-motion: reduce) {
8133
+ .ds-247420 .ds-ep-tab-indicator { transition: none; }
7886
8134
  }
7887
8135
  .ds-247420 .ds-ep-tab {
7888
8136
  flex: 1; appearance: none; background: transparent; border: 0;
@@ -7897,6 +8145,9 @@
7897
8145
  color: var(--accent);
7898
8146
  border-bottom-color: var(--accent);
7899
8147
  }
8148
+ /* When the JS slider is active, the active tab's own static border is
8149
+ redundant (the sliding bar draws it) — hide it so they don't double up. */
8150
+ .ds-247420 .ds-ep-tabs-head.has-slider .ds-ep-tab.active { border-bottom-color: transparent; }
7900
8151
  /* Flex column so a full-height docked child (e.g. an inspector panel) fills
7901
8152
  the tab body instead of collapsing under a block container. */
7902
8153
  .ds-247420 .ds-ep-tabs-body { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; }
@@ -8102,7 +8353,8 @@
8102
8353
  .ds-247420 .ds-ep-actionbar > * { flex: 1 1 0; }
8103
8354
  .ds-247420 .ds-ep-actionbar button {
8104
8355
  width: 100%;
8105
- min-height: 28px; height: 28px; padding: 0 10px;
8356
+ /* Snapped to --ctl-sm (was a lone 28px literal that happened to match). */
8357
+ min-height: var(--ctl-sm); height: var(--ctl-sm); padding: 0 var(--space-2-5);
8106
8358
  border-radius: var(--r-1, 4px);
8107
8359
  font: var(--fs-tiny, 12px)/1 var(--ff-mono, monospace);
8108
8360
  }
@@ -8472,15 +8724,19 @@
8472
8724
  display: flex; flex-direction: column;
8473
8725
  outline: none;
8474
8726
  }
8475
- .ds-247420 .ds-ep-dialog-head { padding: 14px 16px 6px; border-bottom: 1px solid var(--rule); }
8476
- .ds-247420 .ds-ep-dialog-title { margin: 0; font-size: 16px; font-weight: 600; }
8477
- .ds-247420 .ds-ep-dialog-body { padding: 14px 16px; }
8727
+ /* Padding tokenized to the --space-* scale (was raw 14/16/10/6) and shaved to
8728
+ app density — a dialog is working chrome, not a marketing card. */
8729
+ .ds-247420 .ds-ep-dialog-head { padding: var(--space-2-75) var(--space-3) var(--space-1-5); border-bottom: 1px solid var(--rule); }
8730
+ .ds-247420 .ds-ep-dialog-title { margin: 0; font-size: var(--fs-body); font-weight: 600; }
8731
+ .ds-247420 .ds-ep-dialog-body { padding: var(--space-2-75) var(--space-3); }
8478
8732
  .ds-247420 .ds-ep-dialog-actions {
8479
- display: flex; gap: 8px; justify-content: flex-end;
8480
- padding: 10px 14px; border-top: 1px solid var(--rule);
8733
+ display: flex; gap: var(--space-2); justify-content: flex-end;
8734
+ padding: var(--space-2) var(--space-2-75); border-top: 1px solid var(--rule);
8481
8735
  }
8482
8736
  .ds-247420 .ds-ep-dialog-btn {
8483
- padding: 8px 14px;
8737
+ /* Snapped to the --ctl-sm rung + tokenized padding (was 8px 14px). */
8738
+ min-height: var(--ctl-sm); padding: 0 var(--space-3);
8739
+ display: inline-flex; align-items: center; justify-content: center;
8484
8740
  border: 1px solid var(--rule);
8485
8741
  border-radius: var(--r-1, 6px);
8486
8742
  background: var(--panel-2, transparent);
@@ -8514,7 +8770,7 @@
8514
8770
  border: 1px solid var(--rule);
8515
8771
  border-left: 3px solid var(--accent);
8516
8772
  border-radius: var(--r-1, 6px);
8517
- padding: 10px 14px;
8773
+ padding: var(--space-2) var(--space-2-75);
8518
8774
  box-shadow: var(--shadow-3);
8519
8775
  min-width: 220px; max-width: 360px;
8520
8776
  opacity: 1;
@@ -8607,7 +8863,10 @@
8607
8863
  .ds-247420 .ds-radio-row,
8608
8864
  .ds-247420 .ds-toggle-row {
8609
8865
  display: inline-flex; align-items: center; gap: var(--space-2, 8px);
8610
- min-height: 44px;
8866
+ /* Desktop density: ~30px rows, not the 44px touch target (which stacked a
8867
+ lot of dead vertical space in inspector/settings forms). The full 44px
8868
+ target is restored under pointer:coarse below. */
8869
+ min-height: 30px;
8611
8870
  padding: var(--space-1, 4px) var(--space-2, 8px);
8612
8871
  cursor: pointer;
8613
8872
  color: var(--fg);
@@ -8616,6 +8875,10 @@
8616
8875
  .ds-247420 .ds-check-row:has(input:disabled),
8617
8876
  .ds-247420 .ds-radio-row:has(input:disabled),
8618
8877
  .ds-247420 .ds-toggle-row:has(button:disabled) { opacity: 0.5; cursor: not-allowed; }
8878
+ /* Touch devices: restore the 44px minimum tap target. */
8879
+ @media (pointer: coarse) {
8880
+ .ds-247420 .ds-check-row, .ds-247420 .ds-radio-row, .ds-247420 .ds-toggle-row { min-height: 44px; }
8881
+ }
8619
8882
 
8620
8883
  .ds-247420 .ds-check, .ds-247420 .ds-radio {
8621
8884
  width: 18px; height: 18px;
@@ -8802,9 +9065,12 @@
8802
9065
  color: inherit; font: inherit;
8803
9066
  border: 1px solid var(--rule);
8804
9067
  border-radius: var(--r-1, 6px);
8805
- padding: 6px 12px;
9068
+ padding: 0 var(--space-2-75);
8806
9069
  cursor: pointer;
8807
- min-height: 32px;
9070
+ /* Snapped to the --ctl-sm rung so this small control shares the height
9071
+ ladder with buttons/fields instead of a lone 32px literal. */
9072
+ min-height: var(--ctl-sm); height: var(--ctl-sm);
9073
+ display: inline-flex; align-items: center;
8808
9074
  }
8809
9075
  @media (pointer: coarse) {
8810
9076
  .ds-247420 .ds-dropdown-trigger { min-height: 44px; }