anentrypoint-design 0.0.312 → 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
 
@@ -2655,6 +2755,54 @@
2655
2755
  .ds-247420 .ds-preview-meta { display: none; }
2656
2756
  }
2657
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
+
2658
2806
  /* -- File browser UX affordances ---------------------------- */
2659
2807
  /* Toolbar filter input — compact search box. */
2660
2808
  .ds-247420 .ds-filter-input {
@@ -3514,19 +3662,61 @@
3514
3662
  Enhanced Form Inputs
3515
3663
  -------------------------------------------------------------- */
3516
3664
 
3517
- .ds-247420 input[type="text"],
3518
- .ds-247420 input[type="email"],
3519
- .ds-247420 input[type="password"],
3520
- .ds-247420 input[type="number"],
3521
- .ds-247420 input[type="search"],
3522
- .ds-247420 input[type="url"],
3523
- .ds-247420 input[type="tel"],
3524
- .ds-247420 textarea,
3525
- .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);
3526
3689
  transition: background var(--dur-snap) var(--ease),
3527
3690
  border-color var(--dur-snap) var(--ease),
3528
3691
  box-shadow var(--dur-snap) var(--ease);
3529
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
+ }
3530
3720
 
3531
3721
  .ds-247420 input[type="text"]::placeholder,
3532
3722
  .ds-247420 input[type="email"]::placeholder,
@@ -3659,11 +3849,29 @@
3659
3849
  the electric lead on focus, instead of a uniform hairline box. */
3660
3850
  box-shadow: inset 0 0 0 var(--bw-hair, 1px) var(--rule),
3661
3851
  inset 0 calc(-1 * var(--bw-rule)) 0 var(--rule-strong);
3662
- border-radius: var(--r-2, 10px);
3663
- 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);
3664
3858
  transition: box-shadow var(--dur-snap) var(--ease), background var(--dur-snap) var(--ease);
3665
3859
  }
3666
- .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); }
3667
3875
  .ds-247420 .ds-search-input::placeholder { color: var(--fg-3); }
3668
3876
  /* Only present when SearchInput is passed resultCount - transparent to any
3669
3877
  flex/grid layout the bare input previously sat in directly. */
@@ -5361,7 +5569,7 @@
5361
5569
  background: color-mix(in oklab, var(--fg) 8%, transparent);
5362
5570
  }
5363
5571
  .ds-247420 .cm-cat-add:focus-visible {
5364
- outline: 2px solid var(--accent-ink);
5572
+ outline: var(--focus-w) solid var(--focus-color);
5365
5573
  outline-offset: 2px;
5366
5574
  }
5367
5575
 
@@ -5544,7 +5752,7 @@
5544
5752
  background: color-mix(in oklab, var(--fg) 10%, transparent);
5545
5753
  }
5546
5754
  .ds-247420 .cm-ch-action-btn:focus-visible {
5547
- outline: 2px solid var(--accent-ink);
5755
+ outline: var(--focus-w) solid var(--focus-color);
5548
5756
  outline-offset: 2px;
5549
5757
  }
5550
5758
 
@@ -6127,44 +6335,44 @@
6127
6335
  .ds-247420 .cm-server-icon:focus-visible,
6128
6336
  .ds-247420 .cm-server-back:focus-visible,
6129
6337
  .ds-247420 .cm-server-add:focus-visible {
6130
- outline: 2px solid var(--accent-ink);
6338
+ outline: var(--focus-w) solid var(--focus-color);
6131
6339
  outline-offset: 2px;
6132
6340
  border-radius: var(--r-2);
6133
6341
  }
6134
6342
 
6135
6343
  .ds-247420 .cm-channel-item:focus-visible {
6136
- outline: 2px solid var(--accent-ink);
6344
+ outline: var(--focus-w) solid var(--focus-color);
6137
6345
  outline-offset: -2px;
6138
6346
  border-radius: var(--r-1);
6139
6347
  }
6140
6348
 
6141
6349
  .ds-247420 .cm-server-header:focus-visible,
6142
6350
  .ds-247420 .cm-category-header:focus-visible {
6143
- outline: 2px solid var(--accent-ink);
6351
+ outline: var(--focus-w) solid var(--focus-color);
6144
6352
  outline-offset: -2px;
6145
6353
  }
6146
6354
 
6147
6355
  .ds-247420 .cm-member-item:focus-visible {
6148
- outline: 2px solid var(--accent-ink);
6356
+ outline: var(--focus-w) solid var(--focus-color);
6149
6357
  outline-offset: -2px;
6150
6358
  border-radius: var(--r-1);
6151
6359
  }
6152
6360
 
6153
6361
  .ds-247420 .cm-vs-btn:focus-visible,
6154
6362
  .ds-247420 .cm-user-btn:focus-visible {
6155
- outline: 2px solid var(--accent-ink);
6363
+ outline: var(--focus-w) solid var(--focus-color);
6156
6364
  outline-offset: 2px;
6157
6365
  border-radius: var(--r-1);
6158
6366
  }
6159
6367
 
6160
6368
  /* ThreadPanel / Forum / Page interactive elements */
6161
6369
  .ds-247420 .cm-tp-item:focus-visible,
6162
- .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); }
6163
6371
  .ds-247420 .cm-tp-new:focus-visible,
6164
6372
  .ds-247420 .cm-tp-close:focus-visible,
6165
6373
  .ds-247420 .cm-forum-new:focus-visible,
6166
6374
  .ds-247420 .cm-forum-sort:focus-visible,
6167
- .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); }
6168
6376
  .ds-247420 .cm-forum-search:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--accent-ink); }
6169
6377
 
6170
6378
  /* ---------- mobile header ---------- */
@@ -6193,7 +6401,7 @@
6193
6401
  border-radius: var(--r-1);
6194
6402
  }
6195
6403
  .ds-247420 .cm-mh-btn:hover { background: var(--panel-3); }
6196
- .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; }
6197
6405
  .ds-247420 .cm-mh-title {
6198
6406
  flex: 1 1 auto;
6199
6407
  min-width: 0;
@@ -6238,7 +6446,7 @@
6238
6446
  border-radius: var(--r-1);
6239
6447
  }
6240
6448
  .ds-247420 .cm-rb-cancel:hover { background: var(--panel-3); color: var(--fg); }
6241
- .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; }
6242
6450
 
6243
6451
  /* ---------- banner ---------- */
6244
6452
  .ds-247420 .cm-banner {
@@ -7893,12 +8101,36 @@
7893
8101
  }
7894
8102
 
7895
8103
  /* Tabs */
7896
- .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; }
7897
8105
  .ds-247420 .ds-ep-tabs-head {
7898
8106
  display: flex; flex-shrink: 0; flex-wrap: nowrap;
7899
8107
  overflow-x: auto; scroll-behavior: smooth;
7900
8108
  background: var(--panel-1);
7901
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; }
7902
8134
  }
7903
8135
  .ds-247420 .ds-ep-tab {
7904
8136
  flex: 1; appearance: none; background: transparent; border: 0;
@@ -7913,6 +8145,9 @@
7913
8145
  color: var(--accent);
7914
8146
  border-bottom-color: var(--accent);
7915
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; }
7916
8151
  /* Flex column so a full-height docked child (e.g. an inspector panel) fills
7917
8152
  the tab body instead of collapsing under a block container. */
7918
8153
  .ds-247420 .ds-ep-tabs-body { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; }
@@ -8118,7 +8353,8 @@
8118
8353
  .ds-247420 .ds-ep-actionbar > * { flex: 1 1 0; }
8119
8354
  .ds-247420 .ds-ep-actionbar button {
8120
8355
  width: 100%;
8121
- 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);
8122
8358
  border-radius: var(--r-1, 4px);
8123
8359
  font: var(--fs-tiny, 12px)/1 var(--ff-mono, monospace);
8124
8360
  }
@@ -8488,15 +8724,19 @@
8488
8724
  display: flex; flex-direction: column;
8489
8725
  outline: none;
8490
8726
  }
8491
- .ds-247420 .ds-ep-dialog-head { padding: 14px 16px 6px; border-bottom: 1px solid var(--rule); }
8492
- .ds-247420 .ds-ep-dialog-title { margin: 0; font-size: 16px; font-weight: 600; }
8493
- .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); }
8494
8732
  .ds-247420 .ds-ep-dialog-actions {
8495
- display: flex; gap: 8px; justify-content: flex-end;
8496
- 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);
8497
8735
  }
8498
8736
  .ds-247420 .ds-ep-dialog-btn {
8499
- 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;
8500
8740
  border: 1px solid var(--rule);
8501
8741
  border-radius: var(--r-1, 6px);
8502
8742
  background: var(--panel-2, transparent);
@@ -8530,7 +8770,7 @@
8530
8770
  border: 1px solid var(--rule);
8531
8771
  border-left: 3px solid var(--accent);
8532
8772
  border-radius: var(--r-1, 6px);
8533
- padding: 10px 14px;
8773
+ padding: var(--space-2) var(--space-2-75);
8534
8774
  box-shadow: var(--shadow-3);
8535
8775
  min-width: 220px; max-width: 360px;
8536
8776
  opacity: 1;
@@ -8623,7 +8863,10 @@
8623
8863
  .ds-247420 .ds-radio-row,
8624
8864
  .ds-247420 .ds-toggle-row {
8625
8865
  display: inline-flex; align-items: center; gap: var(--space-2, 8px);
8626
- 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;
8627
8870
  padding: var(--space-1, 4px) var(--space-2, 8px);
8628
8871
  cursor: pointer;
8629
8872
  color: var(--fg);
@@ -8632,6 +8875,10 @@
8632
8875
  .ds-247420 .ds-check-row:has(input:disabled),
8633
8876
  .ds-247420 .ds-radio-row:has(input:disabled),
8634
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
+ }
8635
8882
 
8636
8883
  .ds-247420 .ds-check, .ds-247420 .ds-radio {
8637
8884
  width: 18px; height: 18px;
@@ -8818,9 +9065,12 @@
8818
9065
  color: inherit; font: inherit;
8819
9066
  border: 1px solid var(--rule);
8820
9067
  border-radius: var(--r-1, 6px);
8821
- padding: 6px 12px;
9068
+ padding: 0 var(--space-2-75);
8822
9069
  cursor: pointer;
8823
- 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;
8824
9074
  }
8825
9075
  @media (pointer: coarse) {
8826
9076
  .ds-247420 .ds-dropdown-trigger { min-height: 44px; }