anentrypoint-design 0.0.486 → 0.0.488

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
@@ -204,7 +204,7 @@
204
204
  --fs-lg: 18px;
205
205
  --fs-xl: 21px;
206
206
  --fs-h4: clamp(24px, 2cqi, 28px);
207
- --fs-h3: clamp(25px, 2.6cqi, 34px);
207
+ --fs-h3: clamp(26px, 2.6cqi, 34px);
208
208
  --fs-h2: clamp(28px, 3.6cqi, 44px);
209
209
  --fs-h1: clamp(34px, 5cqi, 64px);
210
210
  --fs-hero: clamp(42px, 7cqi, 96px);
@@ -548,6 +548,27 @@
548
548
  --code-num: var(--sun);
549
549
  }
550
550
 
551
+ /* [data-accent="X"] overrides (below) hardcode LIGHT-tuned fills (--acid,
552
+ --purple, --green) with no dark-theme companion, so data-theme=dark +
553
+ data-accent=acid/purple/green produced a near-invisible fill against the
554
+ dark page bg (measured: acid 1.15:1, purple 1.04:1, green 2.98:1 on --ink
555
+ -- all under the 3:1 non-text UI-fill floor). Same convention as the base
556
+ --accent retune above: restate the fill as its dark-tuned equivalent so
557
+ the swatch stays visible, using tones already verified elsewhere in this
558
+ file (green retune 7.48:1 on --ink, purple-2 retune 6.11:1 on --ink). */
559
+ .ds-247420[data-theme="ink"][data-accent="acid"],
560
+ .ds-247420[data-theme="dark"][data-accent="acid"] {
561
+ --accent: #BFBFBF; --accent-bright: #BFBFBF; --accent-fg: var(--ink); --accent-ink: var(--paper); --panel-accent: #BFBFBF;
562
+ }
563
+ .ds-247420[data-theme="ink"][data-accent="purple"],
564
+ .ds-247420[data-theme="dark"][data-accent="purple"] {
565
+ --accent: #C277FF; --accent-bright: #C277FF; --accent-fg: var(--ink); --accent-ink: #C277FF; --panel-accent: #C277FF;
566
+ }
567
+ .ds-247420[data-theme="ink"][data-accent="green"],
568
+ .ds-247420[data-theme="dark"][data-accent="green"] {
569
+ --accent: #5CBF52; --accent-bright: #5CBF52; --accent-fg: var(--ink); --accent-ink: #5CBF52; --panel-accent: #5CBF52;
570
+ }
571
+
551
572
  /* Restores the pre-existing (weaker) dark-theme selection tint after the
552
573
  light-theme-only bump in app-shell.css's base ::selection rule. */
553
574
  .ds-247420[data-theme="ink"] ::selection,
@@ -850,6 +871,21 @@
850
871
  .ds-247420, .ds-247420 body { margin: 0; padding: 0; }
851
872
  .ds-247420 button, .ds-247420 input, .ds-247420 select, .ds-247420 textarea { font: inherit; }
852
873
 
874
+ /* Global reduced-motion safety net: catches any component transition/animation
875
+ that lacks its own per-file prefers-reduced-motion override (most components
876
+ already opt out locally; this is the backstop for the rest, e.g.
877
+ plugins-config.css, catalog-theme.css, sidebar-misc.css, row-print.css,
878
+ topbar.css, primitives.css, git-status.css, panel-row.css). Scroll-snap
879
+ carousels handle scroll-behavior themselves and are unaffected. */
880
+ @media (prefers-reduced-motion: reduce) {
881
+ .ds-247420 *, .ds-247420 *::before, .ds-247420 *::after {
882
+ animation-duration: 0.001ms !important;
883
+ animation-iteration-count: 1 !important;
884
+ transition-duration: 0.001ms !important;
885
+ scroll-behavior: auto !important;
886
+ }
887
+ }
888
+
853
889
  .ds-247420 .skip-link {
854
890
  --skip-link-preflight-translate-clear: -100%;
855
891
  position: absolute;
@@ -1034,6 +1070,13 @@
1034
1070
  text-underline-offset: 4px;
1035
1071
  text-decoration-thickness: 2px;
1036
1072
  }
1073
+ /* Visited state was entirely absent -- a returning user had no way to tell an
1074
+ already-read link from an unread one, the one link state browsers don't
1075
+ fake for you. --purple-2 keeps it distinct from both the unvisited accent
1076
+ and any hover/danger tone already in the palette. */
1077
+ .ds-247420 a.ds-link-accent:visited, .ds-247420 .ds-prose a:visited {
1078
+ color: var(--purple-2);
1079
+ }
1037
1080
  .ds-247420 a.ds-link-accent:hover, .ds-247420 .ds-prose a:hover {
1038
1081
  background: var(--accent); color: var(--accent-fg); text-decoration-color: transparent;
1039
1082
  }
@@ -1117,7 +1160,7 @@
1117
1160
  display: flex; flex-wrap: wrap;
1118
1161
  align-items: center; gap: var(--space-4);
1119
1162
  min-height: var(--app-topbar-h);
1120
- padding: 10px var(--pad-x);
1163
+ padding: var(--space-2-5) var(--pad-x);
1121
1164
  background: color-mix(in oklab, var(--bg) 97%, transparent);
1122
1165
  }
1123
1166
 
@@ -1130,7 +1173,7 @@
1130
1173
  position: sticky; top: 0; z-index: var(--z-header);
1131
1174
  display: flex; flex-wrap: wrap; align-items: center;
1132
1175
  gap: var(--space-2) var(--space-4);
1133
- padding: 8px var(--pad-x);
1176
+ padding: var(--space-2) var(--pad-x);
1134
1177
  background: color-mix(in oklab, var(--bg) 97%, transparent);
1135
1178
  }
1136
1179
  .ds-247420 .app-chrome > .app-topbar,
@@ -1148,7 +1191,7 @@
1148
1191
  .ds-247420 .app-topbar > .app-search { flex: 1 1 auto; }
1149
1192
  .ds-247420 .app-topbar > nav { margin-left: auto; }
1150
1193
  .ds-247420 .app-topbar > .app-topbar-theme { flex: 0 0 auto; }
1151
- .ds-247420 .app-topbar nav { display: flex; gap: 4px; font-size: var(--fs-sm); flex-wrap: wrap; }
1194
+ .ds-247420 .app-topbar nav { display: flex; gap: var(--space-1); font-size: var(--fs-sm); flex-wrap: wrap; }
1152
1195
  .ds-247420 .app-topbar nav a { flex: 0 0 auto; }
1153
1196
  .ds-247420 .app-topbar nav a {
1154
1197
  color: var(--fg-2);
@@ -1201,6 +1244,12 @@
1201
1244
  border-radius: var(--r-pill);
1202
1245
  font-size: var(--fs-sm); color: var(--fg-3);
1203
1246
  max-width: 360px;
1247
+ /* Match the system-wide input border language (.ds-field / .ds-search-input:
1248
+ tonal fill + printed baseline rule) instead of a borderless pill, so the
1249
+ topbar search reads as the same control family as every other input. */
1250
+ box-shadow: inset 0 0 0 var(--bw-hair, 1px) var(--rule),
1251
+ inset 0 calc(-1 * var(--bw-rule)) 0 var(--rule-strong);
1252
+ transition: box-shadow var(--dur-snap) var(--ease), background var(--dur-snap) var(--ease);
1204
1253
  }
1205
1254
  .ds-247420 .app-search .icon { opacity: 0.6; }
1206
1255
  .ds-247420 .app-search input {
@@ -1208,20 +1257,32 @@
1208
1257
  font-family: inherit; font-size: inherit; color: var(--fg);
1209
1258
  width: 100%;
1210
1259
  }
1260
+ /* Same focus language as .ds-field/.ds-search-input: no outline ring, the
1261
+ baseline rule thickens to the accent instead — matching every other form
1262
+ input in the system rather than a one-off outline ring. */
1211
1263
  .ds-247420 .app-search:focus-within {
1212
- outline: var(--focus-w) solid var(--focus-color);
1213
- outline-offset: var(--focus-offset);
1264
+ background: var(--bg);
1265
+ box-shadow: inset 0 0 0 var(--bw-hair, 1px) var(--rule),
1266
+ inset 0 calc(-1 * var(--bw-chunk)) 0 var(--accent);
1214
1267
  }
1215
1268
 
1216
1269
  .ds-247420 .app-crumb {
1217
1270
  min-height: var(--app-crumb-h);
1218
- padding: 6px var(--pad-x);
1219
- display: flex; align-items: center; gap: 8px;
1271
+ padding: var(--space-1-75) var(--pad-x);
1272
+ display: flex; align-items: center; gap: var(--space-2);
1220
1273
  font-size: var(--fs-sm); color: var(--fg-3);
1274
+ min-width: 0;
1221
1275
  }
1222
- .ds-247420 .app-crumb .sep { opacity: 0.5; }
1223
- .ds-247420 .app-crumb .leaf { color: var(--fg); font-weight: 600; }
1224
- .ds-247420 .app-crumb .crumb-right { margin-left: auto; color: var(--fg-3); }
1276
+ .ds-247420 .app-crumb .sep { opacity: 0.5; flex: 0 0 auto; }
1277
+ /* Trail segments and the current/leaf segment truncate independently instead
1278
+ of the whole crumb bar overflowing a long segment (deep file path, long
1279
+ title) must not push the sibling segments or the right slot off-screen. */
1280
+ .ds-247420 .app-crumb > span:not(.sep):not(.crumb-right) {
1281
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
1282
+ min-width: 0; flex: 0 1 auto;
1283
+ }
1284
+ .ds-247420 .app-crumb .leaf { color: var(--fg); font-weight: 600; flex: 0 1 auto; }
1285
+ .ds-247420 .app-crumb .crumb-right { margin-left: auto; color: var(--fg-3); flex: 0 0 auto; }
1225
1286
 
1226
1287
  /* The shell is the grid cell (.app-body's fixed sidebar column); it must own
1227
1288
  its own scroll, not just size to content, because .app-body's row track is
@@ -1264,7 +1325,7 @@
1264
1325
  .ds-247420 .app-side .group {
1265
1326
  font-size: var(--fs-xs); font-weight: 600;
1266
1327
  color: var(--fg-3); padding: 0 var(--space-3);
1267
- margin-bottom: 6px;
1328
+ margin-bottom: var(--space-1-75);
1268
1329
  }
1269
1330
  .ds-247420 .app-side a {
1270
1331
  display: grid; grid-template-columns: 18px 1fr auto;
@@ -1279,7 +1340,7 @@
1279
1340
  min-height: 44px;
1280
1341
  border-radius: var(--r-1);
1281
1342
  color: var(--fg-2);
1282
- margin-bottom: 2px;
1343
+ margin-bottom: var(--space-hair);
1283
1344
  }
1284
1345
  @media (pointer: coarse) {
1285
1346
  .ds-247420 .app-side a { min-height: 44px; padding: var(--space-2-75) var(--space-3); }
@@ -1375,6 +1436,14 @@
1375
1436
  width:100% restores fill-then-cap for every .narrow page, not just wide
1376
1437
  viewports (the shrink-to-fit bug undersized even the base 760px case). */
1377
1438
  .ds-247420 .app-main.narrow { width: 100%; max-width: var(--measure-narrow); margin: 0 auto; }
1439
+ /* .narrow means "prose reading measure", not "shrink the whole shell on wide
1440
+ screens" -- the fixed 760px floor read as narrow on any viewport above
1441
+ ~1300px since the clamp below only widens the no-side case. Sidebar-present
1442
+ narrow pages (docs, papers) get the same wide-viewport widening treatment,
1443
+ just without the no-side variant's extra sidebar-width allowance. */
1444
+ .ds-247420 .app-body:not(.no-side) .app-main.narrow {
1445
+ max-width: clamp(var(--measure-narrow), 46vw, 1000px);
1446
+ }
1378
1447
  /* A shell with no sidebar has no competing column, so the prose clamp there was
1379
1448
  leaving the whole sidebar's worth of width empty -- 760px of content inside a
1380
1449
  1413px shell on a real page. Widen the clamp by the space the absent sidebar
@@ -1439,8 +1508,16 @@
1439
1508
  color: var(--fg-3);
1440
1509
  border-top: 1px solid var(--rule);
1441
1510
  }
1442
- .ds-247420 .app-status .item { color: inherit; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
1511
+ .ds-247420 .app-status .item { color: inherit; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; display: flex; align-items: center; gap: var(--space-3); }
1443
1512
  .ds-247420 .app-status .item:first-of-type { color: var(--accent-ink); }
1513
+ /* Subtle divider between adjacent status segments — a thin inset rule, not a
1514
+ full-height border, so it reads as a quiet separator rather than boxing
1515
+ each item. */
1516
+ .ds-247420 .app-status .item:not(:first-of-type)::before {
1517
+ content: ''; flex: 0 0 auto;
1518
+ width: 1px; height: 10px;
1519
+ background: var(--rule-strong);
1520
+ }
1444
1521
  .ds-247420 .app-status .spread { flex: 1; }
1445
1522
  /* The status bar NEVER wraps: at narrow widths the trailing items (hints,
1446
1523
  agent target) drop in priority order instead of breaking mid-word onto a
@@ -1541,6 +1618,10 @@
1541
1618
  .ds-247420 .ds-icon-btn-base { width: 32px; height: 32px; }
1542
1619
  .ds-247420 .ds-icon-btn-lg { width: 40px; height: 40px; }
1543
1620
  .ds-247420 .ds-icon-btn-xl { width: 48px; height: 48px; }
1621
+ @media (pointer: coarse) {
1622
+ .ds-247420 .ds-icon-btn-xs { width: 32px; height: 32px; }
1623
+ .ds-247420 .ds-icon-btn-sm { width: 32px; height: 32px; }
1624
+ }
1544
1625
  .ds-247420 .ds-icon-btn-ghost { background: transparent; }
1545
1626
  .ds-247420 .ds-icon-btn-ghost:hover { background: var(--fg); color: var(--bg); }
1546
1627
  .ds-247420 .ds-icon-btn-primary { background: var(--accent); color: var(--accent-fg); }
@@ -1562,7 +1643,11 @@
1562
1643
  border-radius: var(--r-pill);
1563
1644
  background: var(--bg-3); color: var(--fg-2);
1564
1645
  }
1565
- .ds-247420 .ds-badge.ds-badge--sm { min-width: var(--icon-xs); height: var(--icon-xs); padding: 0 var(--space-1); font-size: var(--fs-pico); }
1646
+ /* --fs-nano (11px), not --fs-pico (10px): --fs-pico sits below the 11px
1647
+ legibility floor other small-scale labels in this system respect (see
1648
+ .ds-stat-lbl, .ds-session-row-id), and this size is used live for the
1649
+ workspace rail item-count badge (atoms.js Badge size="sm"). */
1650
+ .ds-247420 .ds-badge.ds-badge--sm { min-width: var(--icon-xs); height: var(--icon-xs); padding: 0 var(--space-1); font-size: var(--fs-nano); }
1566
1651
  .ds-247420 .ds-badge.ds-badge--lg { min-width: 22px; height: 22px; padding: 0 var(--space-2); font-size: var(--fs-tiny); }
1567
1652
  .ds-247420 .ds-badge.tone-green, .ds-247420 .ds-badge.tone-live, .ds-247420 .ds-badge.tone-success {
1568
1653
  background: var(--green-tint); color: var(--green-deep);
@@ -1619,7 +1704,6 @@
1619
1704
  .ds-247420 .chip.tone-disabled { background: var(--bg-3); color: var(--fg-3); }
1620
1705
  .ds-247420 .chip.tone-blue { background: var(--accent-tint); color: var(--accent-ink); }
1621
1706
  .ds-247420 .chip.tone-orange { background: color-mix(in oklab, var(--flame) 30%, var(--sun) 70%); color: var(--ink); }
1622
- .ds-247420 .chip.tone-yellow { background: var(--sun); color: var(--ink); }
1623
1707
 
1624
1708
  .ds-247420 .chip.ds-chip-removable { padding-right: var(--space-1); }
1625
1709
  .ds-247420 .ds-chip-remove-btn {
@@ -1659,6 +1743,7 @@
1659
1743
  }
1660
1744
  .ds-247420 .glyph { --glyph-size-sm: var(--fs-nano); --glyph-size-base: var(--fs-tiny); --glyph-size-lg: var(--fs-body); }
1661
1745
  .ds-247420 .glyph-sm { width: var(--icon-xs); height: var(--icon-xs); }
1746
+ .ds-247420 .glyph-base { width: 18px; height: 18px; }
1662
1747
  .ds-247420 .glyph-lg { width: 22px; height: 22px; }
1663
1748
 
1664
1749
  /* Shared empty-state base -- hero-content.css's .empty and community.css's
@@ -1677,6 +1762,44 @@
1677
1762
  .ds-247420 .ds-empty-state--panel { background: var(--bg-2); border-radius: var(--r-1); }
1678
1763
  .ds-247420 .ds-empty-state--compact { padding: var(--space-5) var(--space-3); }
1679
1764
 
1765
+ /* .ds-tooltip — shared floating bubble (overlay-primitives/tooltip.js appends
1766
+ one instance to <body> and repositions it via useFloating). Fade timing
1767
+ matches the component's own 350ms hover-intent delay. */
1768
+ .ds-247420 .ds-tooltip {
1769
+ position: fixed;
1770
+ z-index: var(--z-top);
1771
+ max-width: 240px;
1772
+ padding: var(--space-1) var(--space-2);
1773
+ border-radius: var(--r-1);
1774
+ background: var(--fg);
1775
+ color: var(--bg);
1776
+ font-size: var(--fs-xs);
1777
+ line-height: 1.35;
1778
+ box-shadow: var(--shadow-2);
1779
+ pointer-events: none;
1780
+ opacity: 0;
1781
+ transform: scale(0.96);
1782
+ transition: opacity var(--dur-snap) var(--ease), transform var(--dur-snap) var(--ease);
1783
+ }
1784
+ .ds-247420 .ds-tooltip:not([hidden]) { opacity: 1; transform: scale(1); }
1785
+ .ds-247420 .ds-tooltip::after {
1786
+ content: '';
1787
+ position: absolute;
1788
+ width: 6px;
1789
+ height: 6px;
1790
+ background: var(--fg);
1791
+ transform: rotate(45deg);
1792
+ }
1793
+ .ds-247420 .ds-tooltip[data-placement^="top"]::after { bottom: -3px; left: 50%; margin-left: -3px; }
1794
+ .ds-247420 .ds-tooltip[data-placement^="bottom"]::after { top: -3px; left: 50%; margin-left: -3px; }
1795
+ .ds-247420 .ds-tooltip[data-placement^="left"]::after { right: -3px; top: 50%; margin-top: -3px; }
1796
+ .ds-247420 .ds-tooltip[data-placement^="right"]::after { left: -3px; top: 50%; margin-top: -3px; }
1797
+ .ds-247420 .ds-tooltip.kind-danger { background: var(--warn); color: var(--ink); }
1798
+ .ds-247420 .ds-tooltip.kind-danger::after { background: var(--warn); }
1799
+ @media (prefers-reduced-motion: reduce) {
1800
+ .ds-247420 .ds-tooltip { transition: none; }
1801
+ }
1802
+
1680
1803
  /* ============================================================
1681
1804
  Panel — soft tonal container, no border decoration
1682
1805
  ============================================================ */
@@ -1738,9 +1861,19 @@
1738
1861
  font-size: var(--fs-tiny); font-weight: 600;
1739
1862
  color: var(--fg-3);
1740
1863
  letter-spacing: var(--tr-caps); text-transform: uppercase;
1864
+ /* A hairline divider is the header's only structural separator from the
1865
+ body — without it, an uppercase caps label at fs-tiny/600 reads as just
1866
+ another dim meta line once the eye drops past the top of the panel. */
1867
+ border-bottom: var(--bw-hair) solid var(--rule);
1868
+ margin-bottom: var(--space-1);
1741
1869
  }
1742
1870
  .ds-247420 .panel.panel-wide .panel-head { padding-left: 0; padding-right: 0; }
1743
- .ds-247420 .panel-head > :last-child {
1871
+ /* A borderless .panel-wide (no card edge to lean on) would otherwise show a
1872
+ divider floating with nothing to visually anchor it — drop it there and
1873
+ rely on spacing alone, matching panel-wide's already-transparent chrome. */
1874
+ .ds-247420 .panel.panel-wide .panel-head { border-bottom: none; margin-bottom: 0; }
1875
+ .ds-247420 .panel.panel--dense .panel-head { border-bottom: var(--bw-hair) solid var(--rule); margin-bottom: 0; }
1876
+ .ds-247420 .panel-head > :last-child:not(.ds-badge) {
1744
1877
  font-weight: 500; color: var(--fg-3);
1745
1878
  font-family: var(--ff-mono); letter-spacing: 0;
1746
1879
  }
@@ -2041,6 +2174,12 @@
2041
2174
  padding-bottom: var(--space-2); border-bottom: var(--bw-hair) solid var(--bg-3);
2042
2175
  }
2043
2176
  .ds-247420 .ds-hero-stat:last-child { border-bottom: 0; padding-bottom: 0; }
2177
+ /* HeroFromPageData's richer badge shape ({label, desc}) renders a strong
2178
+ figure plus a muted descriptor inside the same .ds-hero-stat row — give
2179
+ the pair a baseline-aligned inline layout instead of falling back to
2180
+ unstyled default inline flow. */
2181
+ .ds-247420 .ds-hero-stat-n { font-weight: 700; color: var(--fg); margin-right: var(--space-2); }
2182
+ .ds-247420 .ds-hero-stat-l { color: var(--fg-3); font-size: var(--fs-xs); }
2044
2183
  .ds-247420 .ds-hero-title {
2045
2184
  font-family: var(--ff-display); font-weight: 700;
2046
2185
  /* Bespoke display clamp, deliberately off the --fs-* ladder: --fs-hero is
@@ -2153,6 +2292,8 @@
2153
2292
  padding: var(--space-2) 0;
2154
2293
  font-family: var(--ff-display); font-weight: 600;
2155
2294
  text-transform: uppercase; letter-spacing: var(--tr-caps);
2295
+ mask-image: linear-gradient(90deg, transparent 0, var(--fg) 28px, var(--fg) calc(100% - 28px), transparent 100%);
2296
+ -webkit-mask-image: linear-gradient(90deg, transparent 0, var(--fg) 28px, var(--fg) calc(100% - 28px), transparent 100%);
2156
2297
  }
2157
2298
  .ds-247420 .ds-marquee-track {
2158
2299
  display: inline-flex; gap: var(--space-5);
@@ -2185,7 +2326,7 @@
2185
2326
  .ds-247420 .cli {
2186
2327
  display: flex; align-items: center; gap: 14px;
2187
2328
  overflow-x: auto;
2188
- padding: 20px 24px;
2329
+ padding: var(--space-3-5) var(--space-4);
2189
2330
  background: var(--ink); color: var(--paper);
2190
2331
  border-radius: var(--r-3);
2191
2332
  font-family: var(--ff-mono); font-size: var(--fs-lg);
@@ -2193,12 +2334,13 @@
2193
2334
  .ds-247420 .cli .prompt { color: var(--green-2); }
2194
2335
  .ds-247420 .cli .cmd { flex: 1; min-width: 0; overflow-x: auto; white-space: nowrap; color: var(--paper); }
2195
2336
  .ds-247420 .cli .copy {
2196
- padding: 8px 16px; background: transparent; color: var(--paper);
2337
+ padding: var(--space-2) var(--space-3); background: transparent; color: var(--paper);
2197
2338
  font-size: var(--fs-tiny); text-transform: uppercase; letter-spacing: var(--tr-caps);
2198
2339
  cursor: pointer; box-shadow: inset 0 0 0 1px var(--rule-strong);
2199
2340
  border-radius: var(--r-pill);
2200
2341
  }
2201
2342
  .ds-247420 .cli .copy:hover { background: var(--fg-3); }
2343
+ .ds-247420 .cli .copy:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
2202
2344
 
2203
2345
  /* Multi-line CLI block: when .cli holds .ds-cli-row / .ds-cli-comment children
2204
2346
  (quickstart scripts, multi-command snippets — see CliBlock in
@@ -2234,7 +2376,7 @@
2234
2376
  .ds-247420 .ds-quickstart-cli { flex-direction: column; align-items: stretch; gap: var(--space-2); }
2235
2377
  .ds-247420 .ds-quickstart-copy {
2236
2378
  align-self: flex-end;
2237
- padding: 8px 16px; background: transparent; color: var(--paper);
2379
+ padding: var(--space-2) var(--space-3); background: transparent; color: var(--paper);
2238
2380
  font-family: var(--ff-mono); font-size: var(--fs-tiny); text-transform: uppercase; letter-spacing: var(--tr-caps);
2239
2381
  cursor: pointer; box-shadow: inset 0 0 0 1px var(--rule-strong);
2240
2382
  border-radius: var(--r-pill); border: none;
@@ -2430,7 +2572,7 @@
2430
2572
  display: inline-flex; align-items: center; gap: var(--space-hair);
2431
2573
  font-size: var(--fs-xs); font-weight: 600;
2432
2574
  }
2433
- .ds-247420 .kpi-delta-up { color: var(--green); }
2575
+ .ds-247420 .kpi-delta-up { color: var(--success); }
2434
2576
  .ds-247420 .kpi-delta-down { color: var(--danger); }
2435
2577
  .ds-247420 .ds-sparkline path { stroke: currentColor; }
2436
2578
  .ds-247420 .ds-sparkline-up { color: var(--success); }
@@ -3284,6 +3426,13 @@
3284
3426
  border-radius: var(--r-3); background: var(--bg);
3285
3427
  transition: border-color var(--dur-base) var(--ease), background var(--dur-base) var(--ease);
3286
3428
  }
3429
+ /* Idle hover: a quiet pre-drag affordance (border firms up, faint tint) so a
3430
+ pointer resting over the zone reads as "droppable" before a drag even
3431
+ starts — distinct from and weaker than the .dragover treatment below. */
3432
+ .ds-247420 .ds-dropzone:hover {
3433
+ border-color: var(--fg-3);
3434
+ background: var(--bg-2);
3435
+ }
3287
3436
  .ds-247420 .ds-dropzone-inner {
3288
3437
  display: flex; align-items: center; justify-content: center;
3289
3438
  gap: var(--space-2); flex-wrap: wrap;
@@ -3292,6 +3441,9 @@
3292
3441
  /* Icon size, not type — see .ds-file-cell-media .ds-file-icon above. */
3293
3442
  .ds-247420 .ds-dropzone-glyph { font-size: var(--icon-lg, 24px); color: var(--fg-3); }
3294
3443
  .ds-247420 .ds-dropzone-label { font-size: var(--fs-sm); color: var(--fg-3); }
3444
+ .ds-247420 .ds-dropzone:not(.dragover):not(.rejected):hover {
3445
+ border-color: var(--fg-3);
3446
+ }
3295
3447
  .ds-247420 .ds-dropzone.dragover {
3296
3448
  border-color: var(--accent);
3297
3449
  background: var(--accent-tint);
@@ -3305,6 +3457,26 @@
3305
3457
  0%, 100% { border-color: var(--accent); }
3306
3458
  50% { border-color: color-mix(in oklab, var(--accent) 45%, transparent); }
3307
3459
  }
3460
+ /* Rejected — an in-flight drag carrying a disallowed payload (wrong type,
3461
+ over the size cap). Distinct from dragover: warn tone, not accent, so the
3462
+ two "something is happening" states never read as the same affordance. */
3463
+ .ds-247420 .ds-dropzone.rejected {
3464
+ border-color: var(--warn);
3465
+ background: color-mix(in oklab, var(--warn) 10%, var(--bg));
3466
+ }
3467
+ @media (prefers-reduced-motion: no-preference) {
3468
+ .ds-247420 .ds-dropzone.rejected { animation: ds-dropzone-shake 0.4s var(--ease); }
3469
+ }
3470
+ .ds-247420 .ds-dropzone.rejected .ds-dropzone-glyph,
3471
+ .ds-247420 .ds-dropzone.rejected .ds-dropzone-label { color: var(--warn); }
3472
+ @keyframes ds-dropzone-shake {
3473
+ 0%, 100% { transform: translateX(0); }
3474
+ 25% { transform: translateX(-3px); }
3475
+ 75% { transform: translateX(3px); }
3476
+ }
3477
+ .ds-247420 .ds-dropzone--wrap.rejected > .ds-dropzone-inner {
3478
+ outline-color: var(--warn);
3479
+ }
3308
3480
  /* Wrapper form (has children): no permanent chrome - the content renders as if
3309
3481
  the zone were not there, and the dashed affordance overlays it ONLY while a
3310
3482
  drag is in flight. */
@@ -3412,6 +3584,8 @@
3412
3584
  max(var(--space-3), env(safe-area-inset-bottom))
3413
3585
  max(var(--space-3), env(safe-area-inset-left));
3414
3586
  background: color-mix(in oklab, var(--ink) 55%, transparent);
3587
+ backdrop-filter: blur(2px);
3588
+ -webkit-backdrop-filter: blur(2px);
3415
3589
  }
3416
3590
  @media (prefers-reduced-motion: no-preference) {
3417
3591
  .ds-247420 .ds-modal-backdrop { animation: ds-modal-fade var(--dur-base) var(--ease); }
@@ -3486,10 +3660,17 @@
3486
3660
  floor. --ink on the same fill measures 6.27:1. Same principle as the
3487
3661
  --accent/--accent-ink split the token layer documents: a light saturated fill
3488
3662
  takes the dark text tone. Measured in a live browser. */
3663
+ /* Severity is never color-alone (gd-14, color-blindness/grayscale-print safe):
3664
+ the bold weight + widened letter-spacing is a shape cue that survives a
3665
+ colorless render, matching how .btn-primary.danger differs from every
3666
+ other .btn-primary at a glance even with color stripped out. */
3489
3667
  .ds-247420 .btn-primary.danger {
3490
3668
  background: var(--warn);
3491
3669
  border-color: var(--warn);
3492
3670
  color: var(--ink);
3671
+ box-shadow: inset 0 0 0 var(--bw-hair, 1px) color-mix(in oklab, var(--ink) 25%, transparent);
3672
+ font-weight: 700;
3673
+ letter-spacing: 0.01em;
3493
3674
  }
3494
3675
  .ds-247420 .btn-primary.danger:hover { filter: brightness(0.92); border-color: var(--warn); }
3495
3676
 
@@ -3826,6 +4007,7 @@
3826
4007
  text-transform: lowercase; letter-spacing: 0.02em; user-select: none;
3827
4008
  transition: transform 0.12s ease, box-shadow 0.12s ease;
3828
4009
  position: relative;
4010
+ box-shadow: inset 0 0 0 1px var(--rule);
3829
4011
  }
3830
4012
  .ds-247420 .chat-msg:hover .chat-avatar { transform: scale(1.03); box-shadow: 0 1px 4px color-mix(in oklab, var(--fg) 6%, transparent); }
3831
4013
  .ds-247420 .chat-msg.you .chat-avatar {
@@ -3867,6 +4049,13 @@
3867
4049
  min-width: 0;
3868
4050
  transition: transform 0.12s ease, box-shadow 0.12s ease;
3869
4051
  }
4052
+ /* Tail corner: the bubble edge nearest its own avatar (bottom-left for
4053
+ 'them', bottom-right for 'you') squares off slightly, matching bubble
4054
+ position to the sender's avatar the way a speech-bubble tail would — a
4055
+ consistent radius on all four corners reads as a generic card, not a
4056
+ turn in a conversation. */
4057
+ .ds-247420 .chat-msg:not(.you) .chat-bubble { border-bottom-left-radius: var(--r-0); }
4058
+ .ds-247420 .chat-msg.you .chat-bubble { border-bottom-right-radius: var(--r-0); }
3870
4059
  .ds-247420 .chat-msg:hover .chat-bubble { transform: translateY(-1px); box-shadow: 0 2px 8px color-mix(in oklab, var(--fg) 6%, transparent); transition: transform var(--dur-base) var(--ease), box-shadow var(--dur-base) var(--ease); }
3871
4060
  /* Own bubble — calm tinted lead, not a full electric fill (chat stays readable). */
3872
4061
  .ds-247420 .chat-msg.you .chat-bubble { background: var(--accent-tint); color: var(--fg); }
@@ -4304,6 +4493,163 @@
4304
4493
  fell back to the paper tone at 3.13:1 on ink. */
4305
4494
  .ds-247420 .aicat-meta .name { color: var(--mascot-deep, var(--mascot)); font-weight: 600; }
4306
4495
 
4496
+ /* ============================================================
4497
+ Composer attachment-detected badge, context line, hint, toolbar,
4498
+ note strip, elapsed counter, dragover state — all referenced from
4499
+ composer.js / composer-affordances.js but previously had no CSS at
4500
+ all (unstyled DOM nodes / plain text falling through to block
4501
+ defaults).
4502
+ ============================================================ */
4503
+ .ds-247420 .chat-composer-context {
4504
+ display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-1);
4505
+ width: 100%; order: -1;
4506
+ padding: var(--space-1) var(--space-1) 0;
4507
+ font-family: var(--ff-mono); font-size: var(--fs-tiny); color: var(--fg-3);
4508
+ letter-spacing: 0.01em;
4509
+ }
4510
+ .ds-247420 button.chat-composer-context {
4511
+ background: transparent; border: 0; cursor: pointer; text-align: left;
4512
+ border-radius: var(--r-0);
4513
+ }
4514
+ .ds-247420 button.chat-composer-context:hover { color: var(--fg-2); }
4515
+ .ds-247420 button.chat-composer-context:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
4516
+ .ds-247420 .chat-composer-context-sep { color: var(--fg-3); opacity: 0.6; }
4517
+ .ds-247420 .chat-composer-context-bit {
4518
+ background: transparent; border: 0; padding: 0; margin: 0; cursor: pointer;
4519
+ font: inherit; color: var(--fg-3); border-radius: var(--r-0);
4520
+ transition: color var(--dur-snap, .15s) var(--ease, ease);
4521
+ }
4522
+ .ds-247420 .chat-composer-context-bit:hover { color: var(--accent-ink); text-decoration: underline; text-underline-offset: 2px; }
4523
+ .ds-247420 .chat-composer-context-bit:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
4524
+ .ds-247420 .chat-composer-context-text { color: var(--fg-3); }
4525
+
4526
+ .ds-247420 .chat-composer-detected-badge {
4527
+ display: flex; align-items: center; gap: var(--space-2);
4528
+ width: 100%; order: -1;
4529
+ padding: var(--space-1) var(--space-2-5);
4530
+ margin: 0 0 var(--space-1);
4531
+ background: color-mix(in oklab, var(--accent) 12%, var(--bg-2));
4532
+ border: 1px solid color-mix(in oklab, var(--accent) 28%, transparent);
4533
+ border-radius: var(--r-1);
4534
+ font-size: var(--fs-xs); color: var(--fg);
4535
+ }
4536
+ .ds-247420 .chat-composer-detected-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
4537
+ .ds-247420 .chat-composer-detected-dismiss {
4538
+ flex-shrink: 0; width: 20px; height: 20px;
4539
+ display: inline-flex; align-items: center; justify-content: center;
4540
+ background: transparent; border: 0; cursor: pointer; border-radius: 50%;
4541
+ color: var(--fg-3); font-family: var(--ff-mono); font-size: var(--fs-tiny); line-height: 1;
4542
+ transition: background var(--dur-snap, .15s) var(--ease, ease), color var(--dur-snap, .15s) var(--ease, ease);
4543
+ }
4544
+ .ds-247420 .chat-composer-detected-dismiss:hover { background: color-mix(in oklab, var(--fg) 10%, transparent); color: var(--fg); }
4545
+ .ds-247420 .chat-composer-detected-dismiss:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
4546
+
4547
+ .ds-247420 .chat-composer-hint {
4548
+ width: 100%; order: 2;
4549
+ font-family: var(--ff-mono); font-size: var(--fs-tiny); color: var(--fg-3);
4550
+ padding: 0 var(--space-1);
4551
+ }
4552
+
4553
+ .ds-247420 .chat-composer-toolbar {
4554
+ display: flex; align-items: center; gap: var(--space-1);
4555
+ flex-shrink: 0;
4556
+ }
4557
+ .ds-247420 .chat-composer-toolbar .composer-btn {
4558
+ width: 32px; height: 32px; border-radius: var(--r-1);
4559
+ }
4560
+ @media (pointer: coarse) {
4561
+ .ds-247420 .chat-composer-toolbar .composer-btn { width: 44px; height: 44px; }
4562
+ }
4563
+
4564
+ .ds-247420 .chat-composer-note {
4565
+ width: 100%; order: 3;
4566
+ font-family: var(--ff-mono); font-size: var(--fs-tiny); color: var(--fg-3);
4567
+ padding: var(--space-1) var(--space-1) 0;
4568
+ }
4569
+
4570
+ .ds-247420 .chat-composer-elapsed {
4571
+ font-family: var(--ff-mono); font-size: var(--fs-tiny); color: var(--fg-3);
4572
+ flex-shrink: 0; padding: 0 var(--space-1);
4573
+ }
4574
+
4575
+ .ds-247420 .chat-composer.dragover {
4576
+ border-color: var(--accent);
4577
+ box-shadow: 0 0 0 2px color-mix(in oklab, var(--accent) 18%, transparent);
4578
+ background: color-mix(in oklab, var(--accent) 4%, var(--bg));
4579
+ }
4580
+
4581
+ /* ============================================================
4582
+ EmojiPicker (ov-emoji-*) — an anchored popover with search input,
4583
+ category tabs, and an emoji grid; previously entirely unstyled.
4584
+ Search input mirrors .ds-search-input's tonal-fill treatment so it
4585
+ reads consistently with the rest of the system's search fields.
4586
+ ============================================================ */
4587
+ .ds-247420 .ov-emoji-root {
4588
+ position: fixed;
4589
+ display: flex; flex-direction: column; gap: var(--space-2);
4590
+ width: 280px; max-height: 320px;
4591
+ padding: var(--space-2-5);
4592
+ background: var(--bg);
4593
+ border: 1px solid color-mix(in oklab, var(--fg) 12%, transparent);
4594
+ border-radius: var(--r-2);
4595
+ box-shadow: var(--shadow-overlay, var(--shadow-3));
4596
+ z-index: var(--z-dropdown, var(--z-raised));
4597
+ }
4598
+ .ds-247420 .ov-emoji-search {
4599
+ width: 100%; box-sizing: border-box;
4600
+ font: inherit; font-size: var(--fs-sm); color: var(--fg);
4601
+ background: var(--bg-2); border: 0;
4602
+ box-shadow: inset 0 0 0 var(--bw-hair, 1px) var(--rule);
4603
+ border-radius: var(--r-1);
4604
+ height: var(--ctl-sm, 32px);
4605
+ padding: 0 var(--space-2-5);
4606
+ transition: box-shadow var(--dur-snap, .15s) var(--ease, ease);
4607
+ }
4608
+ .ds-247420 .ov-emoji-search::placeholder { color: var(--fg-3); }
4609
+ .ds-247420 .ov-emoji-search:focus-visible,
4610
+ .ds-247420 .ov-emoji-search:focus {
4611
+ outline: none;
4612
+ box-shadow: inset 0 0 0 var(--bw-hair, 1px) var(--accent), 0 0 0 2px color-mix(in oklab, var(--accent) 18%, transparent);
4613
+ }
4614
+ .ds-247420 .ov-emoji-tabs {
4615
+ display: flex; gap: var(--space-1);
4616
+ border-bottom: 1px solid color-mix(in oklab, var(--fg) 10%, transparent);
4617
+ padding-bottom: var(--space-1);
4618
+ }
4619
+ .ds-247420 .ov-emoji-tab {
4620
+ flex: 1;
4621
+ display: inline-flex; align-items: center; justify-content: center;
4622
+ height: 30px; font-size: var(--fs-sm);
4623
+ background: transparent; border: 0; border-radius: var(--r-1);
4624
+ cursor: pointer;
4625
+ transition: background var(--dur-snap, .15s) var(--ease, ease);
4626
+ }
4627
+ .ds-247420 .ov-emoji-tab:hover { background: color-mix(in oklab, var(--fg) 8%, transparent); }
4628
+ .ds-247420 .ov-emoji-tab[aria-selected="true"] { background: color-mix(in oklab, var(--accent) 16%, transparent); }
4629
+ .ds-247420 .ov-emoji-tab:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
4630
+ .ds-247420 .ov-emoji-grid { overflow-y: auto; flex: 1; min-height: 0; scrollbar-width: thin; scrollbar-color: var(--bg-3) transparent; }
4631
+ .ds-247420 .ov-emoji-grid-inner {
4632
+ display: grid; grid-template-columns: repeat(6, 1fr); gap: var(--space-1);
4633
+ }
4634
+ .ds-247420 .ov-emoji-cell {
4635
+ display: inline-flex; align-items: center; justify-content: center;
4636
+ aspect-ratio: 1; font-size: var(--fs-lg); line-height: 1;
4637
+ background: transparent; border: 0; border-radius: var(--r-1);
4638
+ cursor: pointer;
4639
+ transition: background var(--dur-snap, .15s) var(--ease, ease), transform 0.12s ease;
4640
+ }
4641
+ .ds-247420 .ov-emoji-cell:hover { background: color-mix(in oklab, var(--fg) 10%, transparent); transform: scale(1.12); }
4642
+ .ds-247420 .ov-emoji-cell:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: 1px; background: color-mix(in oklab, var(--fg) 10%, transparent); }
4643
+ .ds-247420 .ov-emoji-empty {
4644
+ grid-column: 1 / -1;
4645
+ text-align: center; color: var(--fg-3); font-size: var(--fs-xs);
4646
+ padding: var(--space-3) 0;
4647
+ }
4648
+ @media (prefers-reduced-motion: reduce) {
4649
+ .ds-247420 .ov-emoji-cell { transition: background var(--dur-snap, .15s) var(--ease, ease); }
4650
+ .ds-247420 .ov-emoji-cell:hover { transform: none; }
4651
+ }
4652
+
4307
4653
  /* ============================================================
4308
4654
  Sidebar polish: hide zero-count badges; tonal active state;
4309
4655
  monospace count chips.
@@ -4631,6 +4977,7 @@
4631
4977
  }
4632
4978
  .ds-247420 .ds-context-menu-item.danger {
4633
4979
  color: var(--warn);
4980
+ box-shadow: inset 2px 0 0 var(--warn);
4634
4981
  }
4635
4982
  .ds-247420 .ds-context-menu-item.danger:hover {
4636
4983
  background: color-mix(in oklab, var(--warn) 12%, var(--bg));
@@ -4733,6 +5080,7 @@
4733
5080
  .ds-247420 .prose p { margin: 0 0 var(--space-4) 0; }
4734
5081
  .ds-247420 .prose p:last-child { margin-bottom: 0; }
4735
5082
  .ds-247420 .prose a { color: var(--accent-ink); text-decoration-color: var(--rule-strong); text-underline-offset: 0.15em; }
5083
+ .ds-247420 .prose a:visited { color: color-mix(in oklab, var(--accent-ink) 70%, var(--purple-2, var(--fg-3))); }
4736
5084
  .ds-247420 .prose a:hover { text-decoration-color: currentColor; }
4737
5085
 
4738
5086
  /* Rubber-stamp motif — a bordered, uppercase, letter-spaced badge with a
@@ -4848,38 +5196,13 @@
4848
5196
  }
4849
5197
 
4850
5198
  /* Switch primitive (form-primitives.js Toggle) -- track + sliding knob.
4851
- Previously unstyled anywhere in this repo (a bare button with no visible
4852
- affordance), so every real consumer resorted to a local literal-bracket-
4853
- text reimplementation ('[x] on'/'[ ] off') instead. Sized off --space
4854
- tokens: a 40x22 track (2x11 knob diameter + 2px inset each side) is the
4855
- standard iOS/Material switch proportion, not an arbitrary literal. */
4856
- .ds-247420 .ds-toggle {
4857
- appearance: none; -webkit-appearance: none;
4858
- display: inline-flex; align-items: center; flex-shrink: 0;
4859
- width: 40px; height: 22px; padding: 2px;
4860
- border: 1px solid var(--rule-strong); border-radius: var(--r-pill, 999px);
4861
- background: var(--bg-3); box-shadow: none; cursor: pointer;
4862
- transition: background-color var(--dur-snap) var(--ease), border-color var(--dur-snap) var(--ease);
4863
- }
4864
- .ds-247420 .ds-toggle-knob {
4865
- width: 16px; height: 16px; border-radius: 50%;
4866
- background: var(--fg-3);
4867
- transition: transform var(--dur-snap) var(--ease), background-color var(--dur-snap) var(--ease);
4868
- }
4869
- .ds-247420 .ds-toggle[aria-checked="true"] {
4870
- background: var(--accent-ink); border-color: var(--accent-ink);
4871
- }
4872
- .ds-247420 .ds-toggle[aria-checked="true"] .ds-toggle-knob {
4873
- background: var(--panel-0, var(--bg));
4874
- transform: translateX(18px);
4875
- }
4876
- .ds-247420 .ds-toggle:disabled { opacity: 0.5; cursor: not-allowed; }
4877
- .ds-247420 .ds-toggle:focus-visible {
4878
- outline: var(--focus-w) solid var(--focus-color);
4879
- outline-offset: var(--focus-offset);
4880
- }
4881
- .ds-247420 .ds-toggle-row { display: inline-flex; align-items: center; gap: var(--space-2); cursor: pointer; }
4882
- .ds-247420 .ds-toggle-row .ds-toggle-label { color: inherit; }
5199
+ Canonical definition lives in editor-primitives.css alongside the rest of
5200
+ the .ds-check/.ds-radio/.ds-toggle form-primitive family (that file loads
5201
+ AFTER this one in the build.mjs concat order, so a second .ds-toggle block
5202
+ here was dead weight that silently lost the cascade while still disagreeing
5203
+ on size/colors/knob-offset with the surviving rule a duplicate-source-of-
5204
+ -truth foot-gun, not an intentional override). Removed; see
5205
+ editor-primitives.css's ".ds-toggle" block for track/knob/disabled/focus. */
4883
5206
 
4884
5207
  /* -- Field controls: themed base for TextField / Select / SearchInput --
4885
5208
  Root fix: previously only `transition` was set, so themed apps got
@@ -4968,6 +5291,15 @@
4968
5291
  button) - transparent to any flex/grid layout the bare input previously
4969
5292
  sat in directly. */
4970
5293
  .ds-247420 .ds-search-input-wrap { display: contents; }
5294
+ /* Leading-icon slot, matching .app-search's icon placement in Topbar so the
5295
+ two search surfaces read as one control family instead of icon-vs-no-icon. */
5296
+ .ds-247420 .ds-search-field { position: relative; display: inline-flex; align-items: center; min-width: 0; }
5297
+ .ds-247420 .ds-search-field .ds-search-icon {
5298
+ position: absolute; left: var(--space-3);
5299
+ display: inline-flex; pointer-events: none;
5300
+ color: var(--fg-3); opacity: 0.6;
5301
+ }
5302
+ .ds-247420 .ds-search-field .ds-search-input { padding-left: calc(var(--space-3) + 15px + var(--space-2)); }
4971
5303
 
4972
5304
  /* Visible clear (X) button for SearchInput - mirrors .ds-alert-dismiss's
4973
5305
  ghost-icon treatment but sized to the kit's 44px touch-target floor
@@ -4989,7 +5321,7 @@
4989
5321
  transition: background var(--dur-snap) var(--ease), color var(--dur-snap) var(--ease);
4990
5322
  }
4991
5323
  .ds-247420 .ds-search-clear:hover { background: var(--bg-2); color: var(--fg); }
4992
- .ds-247420 .ds-search-clear:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 2px; }
5324
+ .ds-247420 .ds-search-clear:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
4993
5325
 
4994
5326
  .ds-247420 .ds-field input:not(.input):focus-visible,
4995
5327
  .ds-247420 .ds-field textarea:not(.input):focus-visible,
@@ -5009,7 +5341,7 @@
5009
5341
  }
5010
5342
  .ds-247420 .ds-field input:not(.input)[aria-invalid="true"],
5011
5343
  .ds-247420 .ds-field textarea:not(.input)[aria-invalid="true"] {
5012
- box-shadow: inset 0 0 0 var(--bw-hair, 1px) var(--flame);
5344
+ box-shadow: inset 0 0 0 var(--bw-hair, 1px) var(--danger);
5013
5345
  }
5014
5346
 
5015
5347
  /* Clear button for text inputs */
@@ -5314,9 +5646,11 @@
5314
5646
  .ds-247420 .ds-skeleton {
5315
5647
  background: linear-gradient(90deg, var(--bg-2) 25%, var(--bg-3) 50%, var(--bg-2) 75%);
5316
5648
  background-size: 200% 100%;
5317
- animation: ds-shimmer 1.5s infinite;
5318
5649
  border-radius: var(--r-1);
5319
5650
  }
5651
+ @media (prefers-reduced-motion: no-preference) {
5652
+ .ds-247420 .ds-skeleton { animation: ds-shimmer 1.5s infinite; }
5653
+ }
5320
5654
 
5321
5655
  @keyframes ds-shimmer {
5322
5656
  0% { background-position: 200% 0; }
@@ -5586,10 +5920,26 @@
5586
5920
  /* Column resize handles: a thin keyboard-accessible separator pinned to the
5587
5921
  right edge of each chrome track; drag (or ArrowLeft/Right) writes a clamped
5588
5922
  inline --ws-*-w. */
5589
- .ds-247420 .ws-resizer { grid-row: 1 / -1; align-self: stretch; justify-self: end; width: 8px; margin-right: -4px; z-index: var(--z-raised); cursor: col-resize; background: transparent; border: none; padding: 0; touch-action: none; }
5590
- .ds-247420 .ws-resizer::after { content: ""; display: block; width: 2px; height: 100%; margin: 0 auto; background: transparent; transition: background var(--dur-snap) var(--ease); }
5591
- .ds-247420 .ws-resizer:hover::after, .ds-247420 .ws-resizer:focus-visible::after { background: var(--accent); }
5923
+ .ds-247420 .ws-resizer { position: relative; grid-row: 1 / -1; align-self: stretch; justify-self: end; width: 8px; margin-right: -4px; z-index: var(--z-raised); cursor: col-resize; background: transparent; border: none; padding: 0; touch-action: none; display: flex; align-items: center; justify-content: center; }
5924
+ .ds-247420 .ws-resizer::after { content: ""; display: block; width: 2px; height: 100%; margin: 0 auto; background: var(--rule); opacity: 0.4; transition: background var(--dur-snap) var(--ease), opacity var(--dur-snap) var(--ease); }
5925
+ .ds-247420 .ws-resizer:hover::after, .ds-247420 .ws-resizer:focus-visible::after { background: var(--accent); opacity: 1; }
5926
+ /* Grip: a small pill of dots centered on the handle, visible at rest (low
5927
+ contrast) so the resizer reads as a draggable affordance rather than an
5928
+ invisible hit zone, then brightens to accent on hover/drag like the line. */
5929
+ .ds-247420 .ws-resizer::before {
5930
+ content: ""; position: absolute; width: 4px; height: 20px; border-radius: var(--r-hair);
5931
+ background-image: radial-gradient(circle, var(--fg-3) 1px, transparent 1px);
5932
+ background-size: 4px 5px; background-repeat: repeat-y; background-position: center;
5933
+ opacity: 0.5; transition: opacity var(--dur-snap) var(--ease), background-image var(--dur-snap) var(--ease);
5934
+ pointer-events: none;
5935
+ }
5936
+ .ds-247420 .ws-resizer:hover::before, .ds-247420 .ws-resizer:focus-visible::before {
5937
+ opacity: 1; background-image: radial-gradient(circle, var(--accent) 1px, transparent 1px);
5938
+ }
5592
5939
  .ds-247420 .ws-resizer:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: -2px; }
5940
+ /* Active drag state: wider + fully opaque accent grip so the handle a
5941
+ fast-moving pointer has left :hover on still visibly reads as "gripped". */
5942
+ .ds-247420 .ws-resizer.ws-resizer-active::after { background: var(--accent); width: 3px; }
5593
5943
  .ds-247420 .ws-resizer.ws-resizer-rail { grid-column: 1; }
5594
5944
  .ds-247420 .ws-resizer.ws-resizer-sessions { grid-column: 2; }
5595
5945
  .ds-247420 .ws-resizer.ws-resizer-pane { grid-column: 3; justify-self: end; }
@@ -5644,7 +5994,10 @@
5644
5994
  .ds-247420 .ws-rail-toggle:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
5645
5995
  .ds-247420 .ws-rail-inner { display: flex; flex-direction: column; min-height: 0; flex: 1; padding: var(--space-3) var(--space-2); gap: var(--space-2); }
5646
5996
  .ds-247420 .ws-rail-head { display: flex; align-items: center; min-height: 36px; padding: 0 var(--space-2); }
5647
- .ds-247420 .ws-rail-brand { font-family: var(--ff-body); font-weight: 600; font-size: var(--fs-body); color: var(--fg); white-space: nowrap; overflow: hidden; }
5997
+ /* Wordmark treatment matches .brand (topbar.css) same display family/weight/
5998
+ tracking — so the rail brand reads as the SAME logotype as every other kit's
5999
+ topbar wordmark instead of a lighter, differently-set imitation. */
6000
+ .ds-247420 .ws-rail-brand { font-family: var(--ff-display); font-weight: 700; font-size: var(--fs-body); letter-spacing: var(--tr-tight); color: var(--fg); white-space: nowrap; overflow: hidden; }
5648
6001
  .ds-247420 .ws-rail-action {
5649
6002
  display: flex; align-items: center; gap: var(--space-2);
5650
6003
  padding: var(--space-2) var(--space-3); margin: var(--space-1) 0;
@@ -5876,7 +6229,7 @@
5876
6229
 
5877
6230
  @media (prefers-reduced-motion: reduce) {
5878
6231
  .ds-247420 .ws-shell, .ds-247420 .ws-rail, .ds-247420 .ws-pane, .ds-247420 .ws-sessions, .ds-247420 .ws-scrim { transition: none; }
5879
- .ds-247420 .ws-resizer::after { transition: none; }
6232
+ .ds-247420 .ws-resizer::after, .ds-247420 .ws-resizer::before { transition: none; }
5880
6233
  }
5881
6234
 
5882
6235
  /* ============================================================
@@ -6342,7 +6695,17 @@
6342
6695
  aspect-ratio: auto;
6343
6696
  }
6344
6697
  }
6345
- .ds-247420 .ds-deck-slide { flex: 1; display: flex; min-width: 0; }
6698
+ .ds-247420 .ds-deck-slide {
6699
+ flex: 1; display: flex; min-width: 0;
6700
+ animation: ds-deck-slide-in var(--dur-base) var(--ease);
6701
+ }
6702
+ @media (prefers-reduced-motion: reduce) {
6703
+ .ds-247420 .ds-deck-slide { animation: none; }
6704
+ }
6705
+ @keyframes ds-deck-slide-in {
6706
+ from { opacity: 0; transform: translateY(var(--space-1-75)); }
6707
+ to { opacity: 1; transform: none; }
6708
+ }
6346
6709
  .ds-247420 .ds-deck-controls {
6347
6710
  /* 14px sits between --space-2-75 (12) and --space-3 (16) with no tier; it is
6348
6711
  the deck control spacing tuned against the mono glyph run. */
@@ -6353,6 +6716,19 @@
6353
6716
  @media (pointer: coarse) {
6354
6717
  .ds-247420 .ds-deck-controls .btn { min-height: 44px; min-width: 44px; }
6355
6718
  }
6719
+ /* Dot progress indicator: one dot per slide, current slide filled with
6720
+ --accent (same fill token .ds-dot-on uses elsewhere in the system), rest
6721
+ dim at --fg-3 so the row reads as a track, not a second counter. */
6722
+ .ds-247420 .ds-deck-progress {
6723
+ display: flex; align-items: center; justify-content: center;
6724
+ gap: var(--space-1-5); padding-bottom: var(--space-2);
6725
+ }
6726
+ .ds-247420 .ds-deck-dot {
6727
+ width: var(--space-1-5); height: var(--space-1-5); border-radius: 50%;
6728
+ background: var(--fg-3); flex: 0 0 auto;
6729
+ transition: background var(--dur-base) var(--ease), transform var(--dur-base) var(--ease);
6730
+ }
6731
+ .ds-247420 .ds-deck-dot--active { background: var(--accent); transform: scale(1.3); }
6356
6732
  /* Kit small-label voice — one local composition of the house `.t-label` recipe
6357
6733
  (base.css) for every eyebrow/label in the appended kit blocks below. These
6358
6734
  previously each restated the four declarations with drifted tracking
@@ -6463,7 +6839,12 @@
6463
6839
  }
6464
6840
  .ds-247420 .ds-gallery-tile {
6465
6841
  cursor: pointer; display: flex; flex-direction: column; gap: var(--space-1-75);
6466
- padding: var(--space-2-75); border-radius: var(--r-1); min-height: clamp(120px, 18vw, 160px);
6842
+ padding: var(--space-2-75); border-radius: var(--r-1);
6843
+ /* Fixed aspect-ratio (not just a min-height floor) so every tile in the grid
6844
+ occupies the same shape regardless of caption length — the ascii specimen
6845
+ is centered inside via .ds-tile-cap's flex alignment, the equivalent of
6846
+ object-fit: cover for a text tile with no bitmap to crop. */
6847
+ aspect-ratio: 4 / 3;
6467
6848
  border: 0; text-align: left; font: inherit; color: inherit;
6468
6849
  background: var(--tile-tone, var(--panel-1));
6469
6850
  transition: transform var(--dur-base) var(--ease), box-shadow var(--dur-base) var(--ease);
@@ -6473,7 +6854,7 @@
6473
6854
  translateY + shadow idiom .chat-msg:hover .chat-bubble already uses. */
6474
6855
  .ds-247420 .ds-gallery-tile:hover { transform: translateY(-2px); box-shadow: 0 2px 8px color-mix(in oklab, var(--fg) 10%, transparent); }
6475
6856
  .ds-247420 .ds-gallery-tile:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
6476
- .ds-247420 .ds-gallery-tile--tight { min-height: clamp(96px, 14vw, 120px); }
6857
+ .ds-247420 .ds-gallery-tile--tight { aspect-ratio: 3 / 2; }
6477
6858
  /* The lightbox scrim/card chrome itself is now Dialog's (.ds-ep-dialog /
6478
6859
  .ds-ep-dialog-backdrop in editor-primitives.css) — Dialog already ships
6479
6860
  role="dialog", aria-modal, Tab-trap and focus-restore-on-close, which the
@@ -6497,13 +6878,21 @@
6497
6878
  @media (max-width: 480px) {
6498
6879
  .ds-247420 .ds-topbar-search { flex-basis: 100%; }
6499
6880
  }
6500
- .ds-247420 .ds-empty-state { padding: clamp(var(--space-3), 5vw, var(--space-4)); text-align: center; color: var(--panel-text-3); }
6881
+ /* Namespaced .ds-kit-empty-state (not .ds-empty-state) -- .ds-empty-state is
6882
+ primitives.css's shared base (padding var(--space-6), color var(--fg-3));
6883
+ this sheet loads after it in app-shell.css's import order, so reusing the
6884
+ same class name silently overrode that base's padding/color for every
6885
+ consumer, defeating the "shared base, one place" contract primitives.css
6886
+ documents. This block is unused by any component today, but kept
6887
+ namespaced so a future consumer gets this sheet's fluid clamp() treatment
6888
+ deliberately rather than by cascade accident. */
6889
+ .ds-247420 .ds-kit-empty-state { padding: clamp(var(--space-3), 5vw, var(--space-4)); text-align: center; color: var(--panel-text-3); }
6501
6890
  /* Fluid ICON size (empty-state mark), not a type tier: it scales with the
6502
6891
  viewport between 24 and 32px. Every fluid --fs-* tier is a heading clamp
6503
6892
  tuned to cqi + heading weight, so none of them substitutes here. */
6504
- .ds-247420 .ds-empty-state-glyph { font-size: clamp(24px, 6vw, 32px); }
6505
- .ds-247420 .ds-empty-state-msg { margin: var(--space-1-75) 0; }
6506
- .ds-247420 .ds-empty-state-hint { margin: 0; font-size: var(--fs-sm); }
6893
+ .ds-247420 .ds-kit-empty-state-glyph { font-size: clamp(24px, 6vw, 32px); }
6894
+ .ds-247420 .ds-kit-empty-state-msg { margin: var(--space-1-75) 0; }
6895
+ .ds-247420 .ds-kit-empty-state-hint { margin: 0; font-size: var(--fs-sm); }
6507
6896
 
6508
6897
  /* --- system primer ------------------------------------------ */
6509
6898
  /* The three primer panels below share one fluid inset,
@@ -6892,6 +7281,7 @@
6892
7281
  border-radius: 50%; background: var(--bg-3); color: var(--fg);
6893
7282
  font-family: var(--ff-mono); font-weight: 600; text-transform: lowercase;
6894
7283
  letter-spacing: 0.02em; user-select: none; flex-shrink: 0; object-fit: cover;
7284
+ box-shadow: inset 0 0 0 1px var(--rule);
6895
7285
  }
6896
7286
  .ds-247420 .ds-avatar-sm { width: 24px; height: 24px; font-size: var(--fs-pico); }
6897
7287
  .ds-247420 .ds-avatar-md { width: 36px; height: 36px; font-size: var(--fs-micro); }
@@ -7019,6 +7409,7 @@
7019
7409
  font-weight: 600;
7020
7410
  line-height: 1;
7021
7411
  user-select: none;
7412
+ box-shadow: inset 0 0 0 1px var(--rule);
7022
7413
  }
7023
7414
 
7024
7415
  /* Body stack — name + time on one baseline, text beneath. .chat-stack's
@@ -9454,6 +9845,9 @@
9454
9845
  font-size: var(--fs-sm);
9455
9846
  }
9456
9847
  .ds-247420 .vx-toggle { accent-color: var(--accent); width: 18px; height: 18px; cursor: pointer; }
9848
+ @media (pointer: coarse) {
9849
+ .ds-247420 .vx-toggle { width: 24px; height: 24px; }
9850
+ }
9457
9851
 
9458
9852
  .ds-247420 .vx-queue {
9459
9853
  display: flex; align-items: center; gap: var(--space-2);
@@ -10205,7 +10599,7 @@
10205
10599
  .ds-247420 .ds-session-agent { font-size: var(--fs-tiny); color: var(--fg-3); }
10206
10600
  /* New-activity cue is a hollow accent RING so it stays shape-distinct from the
10207
10601
  running live disc (a solid pulsing accent dot) for colour-blind / reduced-motion users. */
10208
- .ds-247420 .ds-session-unread { width: 8px; height: 8px; border-radius: 50%; background: var(--fg-2); display: inline-block; }
10602
+ .ds-247420 .ds-session-unread { width: 8px; height: 8px; border-radius: 50%; background: transparent; box-shadow: inset 0 0 0 1.5px var(--accent); display: inline-block; flex: none; }
10209
10603
  .ds-247420 .ds-session-state { padding: var(--space-5) var(--space-3); text-align: center; color: var(--fg-3); font-size: var(--fs-sm); }
10210
10604
  .ds-247420 .ds-session-state-error { color: var(--flame); }
10211
10605
 
@@ -12197,7 +12591,7 @@
12197
12591
  outline: var(--focus-w) solid var(--focus-color);
12198
12592
  outline-offset: var(--focus-offset);
12199
12593
  }
12200
- .ds-247420 .ds-toggle:disabled { cursor: not-allowed; }
12594
+ .ds-247420 .ds-toggle:disabled { cursor: not-allowed; opacity: 0.5; }
12201
12595
 
12202
12596
  @media (prefers-reduced-motion: no-preference) {
12203
12597
  .ds-247420 .ds-toggle { transition: background var(--dur-base, 160ms) var(--ease), border-color var(--dur-base, 160ms) var(--ease); }
@@ -12555,6 +12949,7 @@
12555
12949
  }
12556
12950
  .ds-247420 .ov-emoji-tab:hover { background: var(--bg-2); }
12557
12951
  .ds-247420 .ov-emoji-tab[aria-selected="true"] { border-bottom-color: var(--accent); background: var(--bg-2); }
12952
+ .ds-247420 .ov-emoji-tab:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: -2px; }
12558
12953
  .ds-247420 .ov-emoji-grid { overflow: auto; max-height: 220px; padding: var(--space-2, 8px); }
12559
12954
  .ds-247420 .ov-emoji-grid-inner { display: grid; grid-template-columns: repeat(8, 1fr); gap: var(--space-1, 4px); }
12560
12955
  .ds-247420 .ov-emoji-cell {
@@ -12563,8 +12958,12 @@
12563
12958
  border-radius: var(--r-1, 8px); cursor: pointer;
12564
12959
  display: inline-flex; align-items: center; justify-content: center;
12565
12960
  }
12566
- .ds-247420 .ov-emoji-cell:hover, .ds-247420 .ov-emoji-cell:focus-visible {
12567
- background: color-mix(in oklab, var(--accent) 18%, transparent); outline: none;
12961
+ .ds-247420 .ov-emoji-cell:hover {
12962
+ background: color-mix(in oklab, var(--accent) 18%, transparent);
12963
+ }
12964
+ .ds-247420 .ov-emoji-cell:focus-visible {
12965
+ background: color-mix(in oklab, var(--accent) 18%, transparent);
12966
+ outline: var(--focus-w) solid var(--focus-color); outline-offset: 1px;
12568
12967
  }
12569
12968
 
12570
12969
  /* ---------------------------------------------------------------
@@ -12656,6 +13055,9 @@
12656
13055
  .ds-247420 .ov-set-control:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: 1px; border-color: var(--accent-ink); }
12657
13056
  .ds-247420 input.ov-set-control[type="range"] { padding: 0; accent-color: var(--accent); }
12658
13057
  .ds-247420 .ov-set-toggle { width: 18px; height: 18px; accent-color: var(--accent); flex-shrink: 0; cursor: pointer; }
13058
+ @media (pointer: coarse) {
13059
+ .ds-247420 .ov-set-toggle { width: 24px; height: 24px; }
13060
+ }
12659
13061
  .ds-247420 .ov-set-btn {
12660
13062
  width: 100%; appearance: none; cursor: pointer;
12661
13063
  background: var(--accent); color: var(--accent-fg);