anentrypoint-design 0.0.477 → 0.0.480

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.
Files changed (53) hide show
  1. package/README.md +101 -0
  2. package/chat.css +4 -4
  3. package/colors_and_type.css +33 -3
  4. package/community.css +22 -8
  5. package/dist/247420.css +324 -49
  6. package/dist/247420.js +123 -55
  7. package/dist/index.html +109 -32
  8. package/package.json +1 -1
  9. package/src/community-app.js +4 -2
  10. package/src/components/community/navigation.js +2 -2
  11. package/src/components/community/presence.js +10 -4
  12. package/src/components/community/views.js +8 -2
  13. package/src/components/content/avatar.js +30 -0
  14. package/src/components/content/hero.js +9 -2
  15. package/src/components/content.js +2 -2
  16. package/src/components/freddie/pages-missing.js +1 -1
  17. package/src/components/overlay-primitives/emoji-picker.js +15 -3
  18. package/src/components/shell/app-shell.js +15 -4
  19. package/src/components/shell/workspace-shell.js +1 -1
  20. package/src/components/theme-toggle.js +8 -3
  21. package/src/copy-code.js +103 -0
  22. package/src/css/app-shell/base.css +18 -0
  23. package/src/css/app-shell/catalog-theme.css +20 -2
  24. package/src/css/app-shell/chat-polish.css +12 -0
  25. package/src/css/app-shell/collab.css +5 -0
  26. package/src/css/app-shell/data-density.css +4 -0
  27. package/src/css/app-shell/files.css +7 -1
  28. package/src/css/app-shell/hero-content.css +14 -1
  29. package/src/css/app-shell/kits-appended.css +13 -0
  30. package/src/css/app-shell/loading-alerts.css +14 -12
  31. package/src/css/app-shell/otp-input.css +2 -2
  32. package/src/css/app-shell/panel-row.css +15 -0
  33. package/src/css/app-shell/plugins-config.css +3 -0
  34. package/src/css/app-shell/primitives.css +13 -4
  35. package/src/css/app-shell/responsive.css +13 -0
  36. package/src/css/app-shell/responsive2-workspace.css +4 -1
  37. package/src/css/app-shell/row-print.css +34 -0
  38. package/src/css/app-shell/states-interactions.css +62 -4
  39. package/src/css/app-shell/topbar.css +1 -0
  40. package/src/kits/os/app-panes.css +3 -2
  41. package/src/kits/os/icons.js +16 -0
  42. package/src/kits/os/shell-geometry.js +2 -2
  43. package/src/kits/os/shell.js +24 -3
  44. package/src/kits/os/theme.css +272 -78
  45. package/src/kits/os/wm.css +19 -6
  46. package/src/kits/os/wm.js +38 -1
  47. package/src/kits/spoint/host-join-lobby.css +3 -3
  48. package/src/page-html/client-script.js +96 -28
  49. package/src/page-html.js +3 -2
  50. package/src/theme.js +6 -2
  51. package/src/web-components/ds-chat.js +9 -0
  52. package/src/web-components/freddie-chat.js +2 -2
  53. package/types/components.d.ts +6 -0
package/dist/247420.css CHANGED
@@ -203,12 +203,24 @@
203
203
  --fs-body: 16px;
204
204
  --fs-lg: 18px;
205
205
  --fs-xl: 21px;
206
- --fs-h4: clamp(22px, 2cqi, 26px);
206
+ --fs-h4: clamp(24px, 2cqi, 28px);
207
207
  --fs-h3: clamp(25px, 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);
211
211
  --fs-mega: clamp(56px, 11cqi, 168px);
212
+ /* Bridges the fixed --fs-* ladder (tops out at --fs-xl, 21px) and the fluid
213
+ heading clamps below it — use for fixed (non-clamping) UI text that needs
214
+ to sit between --fs-xl and a heading tier, e.g. empty-state icon glyphs. */
215
+ --fs-2xl: 28px;
216
+
217
+ /* Icon size scale — canonical sizes for glyphs/icons across the kit going
218
+ forward. Existing ad-hoc pixel values (6/7/8/10/12/14/16/18px etc.)
219
+ don't need to be migrated in this pass; new/updated icon usage should. */
220
+ --icon-xs: 12px;
221
+ --icon-sm: 16px;
222
+ --icon-md: 20px;
223
+ --icon-lg: 24px;
212
224
 
213
225
  /* App/chrome typescale: the default heading scale above is tuned for
214
226
  marketing/hero layouts (h1 up to 64px). Application chrome — page titles,
@@ -357,6 +369,9 @@
357
369
  --z-below: -1;
358
370
  --z-base: 0;
359
371
  --z-raised: 100;
372
+ --z-raised-1: calc(var(--z-raised) + 1);
373
+ --z-raised-2: calc(var(--z-raised) + 2);
374
+ --z-raised-3: calc(var(--z-raised) + 3);
360
375
  --z-sticky: 200;
361
376
  --z-header: 300;
362
377
  --z-drawer: 400;
@@ -417,6 +432,9 @@
417
432
  --focus-w: 2px;
418
433
  --focus-offset: 2px;
419
434
  --focus-ring-inset: inset 0 0 0 var(--focus-w) var(--focus-color);
435
+
436
+ /* letterbox background for video/media containers */
437
+ --media-letterbox: #000;
420
438
  }
421
439
 
422
440
  /* ============================================================
@@ -718,8 +736,20 @@
718
736
 
719
737
  /* Type-scale modifier — opt-in body/lg/xl bump for surfaces that want denser
720
738
  or larger reading sizes without a custom override. */
721
- .ds-247420[data-typescale="sm"] { --fs-body: 15px; --fs-lg: 17px; --fs-xl: 20px; }
722
- .ds-247420[data-typescale="lg"] { --fs-body: 17px; --fs-lg: 20px; --fs-xl: 24px; }
739
+ .ds-247420[data-typescale="sm"] {
740
+ --fs-body: 15px; --fs-lg: 17px; --fs-xl: 20px;
741
+ --fs-h4: clamp(22px, 1.9cqi, 24px);
742
+ --fs-h3: clamp(23px, 2.4cqi, 32px);
743
+ --fs-h2: clamp(26px, 3.4cqi, 41px);
744
+ --fs-h1: clamp(32px, 4.7cqi, 60px);
745
+ }
746
+ .ds-247420[data-typescale="lg"] {
747
+ --fs-body: 17px; --fs-lg: 20px; --fs-xl: 24px;
748
+ --fs-h4: clamp(27px, 2.2cqi, 31px);
749
+ --fs-h3: clamp(28px, 2.9cqi, 38px);
750
+ --fs-h2: clamp(31px, 4cqi, 49px);
751
+ --fs-h1: clamp(38px, 5.6cqi, 71px);
752
+ }
723
753
 
724
754
  /* Debug grid overlay — 8px mascot grid. Toggle via class on any container. */
725
755
  .ds-247420 .with-grid-overlay {
@@ -882,6 +912,21 @@
882
912
  scrollbar-width: thin;
883
913
  scrollbar-color: var(--bg-3) transparent;
884
914
  }
915
+ /* Firefox gets themed scrollbars via scrollbar-width/-color above; Chrome,
916
+ Safari, and Edge need the ::-webkit-scrollbar family too, or every
917
+ scrollable node in the app shell falls back to the raw OS scrollbar and
918
+ clashes with the dark UI. Same selector scope, same --bg-3 token. */
919
+ .ds-247420 .app::-webkit-scrollbar, .ds-247420 .ds-stage::-webkit-scrollbar, .ds-247420 .app *::-webkit-scrollbar, .ds-247420 .ds-stage *::-webkit-scrollbar {
920
+ width: 8px;
921
+ height: 8px;
922
+ }
923
+ .ds-247420 .app::-webkit-scrollbar-track, .ds-247420 .ds-stage::-webkit-scrollbar-track, .ds-247420 .app *::-webkit-scrollbar-track, .ds-247420 .ds-stage *::-webkit-scrollbar-track {
924
+ background: transparent;
925
+ }
926
+ .ds-247420 .app::-webkit-scrollbar-thumb, .ds-247420 .ds-stage::-webkit-scrollbar-thumb, .ds-247420 .app *::-webkit-scrollbar-thumb, .ds-247420 .ds-stage *::-webkit-scrollbar-thumb {
927
+ background: var(--bg-3);
928
+ border-radius: var(--r-pill, 999px);
929
+ }
885
930
 
886
931
  .ds-247420 h1, .ds-247420 .t-h1 { font-family: var(--ff-display); font-size: var(--fs-h1); line-height: var(--lh-snug); letter-spacing: var(--tr-tight); font-weight: 600; margin: 0; }
887
932
  .ds-247420 h2, .ds-247420 .t-h2 { font-family: var(--ff-display); font-size: var(--fs-h2); line-height: var(--lh-snug); letter-spacing: var(--tr-tight); font-weight: 600; margin: 0; }
@@ -1091,6 +1136,7 @@
1091
1136
  .ds-247420 .app-topbar > .brand { flex: 0 0 auto; }
1092
1137
  .ds-247420 .app-topbar > .app-search { flex: 1 1 auto; }
1093
1138
  .ds-247420 .app-topbar > nav { margin-left: auto; }
1139
+ .ds-247420 .app-topbar > .app-topbar-theme { flex: 0 0 auto; }
1094
1140
  .ds-247420 .app-topbar nav { display: flex; gap: 4px; font-size: var(--fs-sm); flex-wrap: wrap; }
1095
1141
  .ds-247420 .app-topbar nav a { flex: 0 0 auto; }
1096
1142
  .ds-247420 .app-topbar nav a {
@@ -1428,7 +1474,7 @@
1428
1474
  }
1429
1475
  .ds-247420 .btn-primary:hover { background: var(--fg); color: var(--green); transform: translateY(-1px); box-shadow: var(--shadow-2); }
1430
1476
  .ds-247420 .btn-ghost { background: transparent; color: var(--fg); box-shadow: inset 0 0 0 2px var(--fg); }
1431
- .ds-247420 .btn-ghost:hover { background: var(--fg); color: var(--bg); transform: translateY(-1px); }
1477
+ .ds-247420 .btn-ghost:hover { background: color-mix(in oklab, var(--fg) 12%, transparent); transform: translateY(-1px); }
1432
1478
  .ds-247420 .btn:active, .ds-247420 .btn-primary:active, .ds-247420 .btn-ghost:active { transform: translateY(1px); box-shadow: none; }
1433
1479
  .ds-247420 .btn:focus-visible, .ds-247420 .btn-primary:focus-visible, .ds-247420 .btn-ghost:focus-visible, .ds-247420 .ds-icon-btn:focus-visible {
1434
1480
  outline: var(--focus-w) solid var(--focus-color);
@@ -1460,7 +1506,9 @@
1460
1506
  border-radius: var(--r-1, 6px);
1461
1507
  background: transparent; color: var(--fg);
1462
1508
  width: 32px; height: 32px;
1509
+ transition: background var(--dur-snap) var(--ease), color var(--dur-snap) var(--ease);
1463
1510
  }
1511
+ .ds-247420 .ds-icon-btn:hover { background: var(--bg-2); }
1464
1512
  .ds-247420 .ds-icon-btn-xs { width: 22px; height: 22px; }
1465
1513
  .ds-247420 .ds-icon-btn-sm { width: 26px; height: 26px; }
1466
1514
  .ds-247420 .ds-icon-btn-base { width: 32px; height: 32px; }
@@ -1470,7 +1518,10 @@
1470
1518
  .ds-247420 .ds-icon-btn-ghost:hover { background: var(--fg); color: var(--bg); }
1471
1519
  .ds-247420 .ds-icon-btn-primary { background: var(--accent); color: var(--accent-fg); }
1472
1520
  .ds-247420 .ds-icon-btn-primary:hover { background: var(--fg); color: var(--accent-ink); }
1473
- .ds-247420 .ds-icon-btn-danger { background: var(--warn); color: var(--on-color); }
1521
+ /* --ink, not --on-color: --on-color is a fixed #fff, which measures 3.07:1 on
1522
+ --warn (#FF5A52) -- below the 4.5:1 AA floor. --ink measures 6.27:1. Same
1523
+ fix as .btn-primary.danger in app-shell/files.css. */
1524
+ .ds-247420 .ds-icon-btn-danger { background: var(--warn); color: var(--ink); }
1474
1525
  .ds-247420 .ds-icon-btn-danger:hover { filter: brightness(0.92); }
1475
1526
  .ds-247420 .ds-icon-btn:active { transform: translateY(1px); }
1476
1527
  .ds-247420 .ds-icon-btn:disabled, .ds-247420 .ds-icon-btn.is-disabled {
@@ -1484,7 +1535,7 @@
1484
1535
  border-radius: var(--r-pill);
1485
1536
  background: var(--bg-3); color: var(--fg-2);
1486
1537
  }
1487
- .ds-247420 .ds-badge.ds-badge--sm { min-width: 14px; height: 14px; padding: 0 var(--space-1); font-size: 10px; }
1538
+ .ds-247420 .ds-badge.ds-badge--sm { min-width: 14px; height: 14px; padding: 0 var(--space-1); font-size: var(--fs-pico); }
1488
1539
  .ds-247420 .ds-badge.ds-badge--lg { min-width: 22px; height: 22px; padding: 0 var(--space-2); font-size: var(--fs-tiny); }
1489
1540
  .ds-247420 .ds-badge.tone-green, .ds-247420 .ds-badge.tone-live, .ds-247420 .ds-badge.tone-success {
1490
1541
  background: var(--green-tint); color: var(--green-deep);
@@ -1501,7 +1552,7 @@
1501
1552
  background: var(--sun); color: var(--ink);
1502
1553
  }
1503
1554
  .ds-247420 .ds-badge.tone-blue { background: var(--accent-tint); color: var(--accent-ink); }
1504
- .ds-247420 .ds-badge.tone-orange { background: color-mix(in oklab, var(--flame) 30%, var(--sun) 70%); color: var(--ink); }
1555
+ .ds-247420 .ds-badge.tone-orange { background: color-mix(in oklab, var(--flame) 22%, var(--bg)); color: color-mix(in oklab, var(--flame) 65%, black); }
1505
1556
 
1506
1557
  .ds-247420 .chip {
1507
1558
  display: inline-flex; align-items: center; gap: var(--space-1-75);
@@ -1569,6 +1620,10 @@
1569
1620
  .ds-247420 .ds-pill.tone-accent { background: var(--accent-tint); color: var(--accent-ink); }
1570
1621
  .ds-247420 .ds-pill.tone-muted { background: transparent; color: var(--fg-3); box-shadow: inset 0 0 0 1px var(--rule); }
1571
1622
 
1623
+ /* .glyph-* sizes (14/18/22px) are for small inline text-adjacent glyphs
1624
+ (single characters/icons sitting in a text flow), a different scale and
1625
+ purpose than the .ds-icon-btn-* tap-target scale (22/26/32/40/48px) below --
1626
+ the -sm/-lg naming overlap is coincidental, not a shared system. */
1572
1627
  .ds-247420 .glyph {
1573
1628
  display: inline-flex; align-items: center; justify-content: center;
1574
1629
  width: 18px; height: 18px;
@@ -1654,6 +1709,13 @@
1654
1709
  margin: 0; font: inherit; font-size: inherit; font-weight: inherit;
1655
1710
  letter-spacing: inherit; text-transform: inherit; color: inherit;
1656
1711
  }
1712
+ /* Category glyph beside a panel title (kits / previews / decks / docs on the
1713
+ homepage) — same inline-svg vocabulary as icons.js, sized to sit on the
1714
+ text baseline rather than dominating it. */
1715
+ .ds-247420 .ds-panel-title-glyph {
1716
+ display: inline-flex; align-items: center; gap: var(--space-1-5, 6px);
1717
+ }
1718
+ .ds-247420 .ds-panel-title-glyph .ds-icon { flex-shrink: 0; color: var(--fg-3); }
1657
1719
  .ds-247420 .panel-body { padding: var(--space-2) var(--space-3) var(--space-3); }
1658
1720
  .ds-247420 .panel-body > * { margin: 0; }
1659
1721
  /* One-line explanatory caption under a panel's title — e.g. distinguishing
@@ -1694,6 +1756,14 @@
1694
1756
  transition: background var(--dur-snap) var(--ease), box-shadow var(--dur-base) var(--ease-spring);
1695
1757
  }
1696
1758
  .ds-247420 .row + .row { margin-top: 2px; }
1759
+ /* Mobile tap-target floor: at compact/comfortable density on a touch
1760
+ viewport the padding-only row height can fall under the 44px effective
1761
+ tap target (WCAG 2.5.5), especially at [data-density="compact"] (0.75x).
1762
+ Force a min-height on narrow/coarse-pointer viewports only, so the dense
1763
+ desktop mono/list layout (file-row, index-row) is untouched above 640px. */
1764
+ @media (max-width: 640px), (pointer: coarse) {
1765
+ .ds-247420 .row, .ds-247420 a.row, .ds-247420 button.row, .ds-247420 .row[role="button"] { min-height: 44px; }
1766
+ }
1697
1767
  /* Interactive rows (anchor/button wrappers) get a hairline lift on hover,
1698
1768
  matching the btn/panel elevation language — non-interactive rows (plain
1699
1769
  divs used as static list items) are unaffected since the selector needs a
@@ -1955,6 +2025,18 @@
1955
2025
  display: flex; gap: var(--space-2, 10px); flex-wrap: wrap;
1956
2026
  justify-content: flex-start; margin-top: var(--space-hair, 4px);
1957
2027
  }
2028
+ /* Hero CTA labels are content-driven (e.g. "GitHub", proper nouns from
2029
+ home.yaml) and the site's voice is deliberately all-lowercase everywhere
2030
+ else. Force the rendered case here rather than hand-lowercasing every
2031
+ proper-noun label at the content layer, so a future CTA label doesn't
2032
+ silently reintroduce Title Case. */
2033
+ .ds-247420 .ds-hero-actions .btn { text-transform: lowercase; }
2034
+ /* Filter input above the homepage's "ui kits" panel — reuses the same
2035
+ .input token the search kit's own topbar query box uses, just placed
2036
+ inline above the panel instead of in a topbar slot. */
2037
+ .ds-247420 .ds-kits-panel-wrap { display: flex; flex-direction: column; gap: var(--space-2, 10px); }
2038
+ .ds-247420 .ds-kits-filter { display: flex; }
2039
+ .ds-247420 .ds-kits-filter-input { width: 100%; max-width: 32ch; }
1958
2040
  /* Below this width the aside card's own internal rhythm (stat rows still
1959
2041
  stacked vertically at full width) is already correct for a narrow column —
1960
2042
  no separate breakpoint override needed now that .ds-hero is always a
@@ -2034,6 +2116,7 @@
2034
2116
  white-space: nowrap; will-change: transform;
2035
2117
  animation: ds-marquee-run 28s linear infinite;
2036
2118
  }
2119
+ .ds-247420 .ds-marquee-run { display: inline-flex; align-items: center; gap: var(--space-5); }
2037
2120
  .ds-247420 .ds-marquee-item { display: inline-flex; align-items: center; gap: var(--space-5); }
2038
2121
  .ds-247420 .ds-marquee-sep { color: var(--accent-ink); }
2039
2122
  .ds-247420 .ds-marquee:hover .ds-marquee-track,
@@ -2354,7 +2437,7 @@
2354
2437
  Form
2355
2438
  ============================================================ */
2356
2439
  .ds-247420 .row-form {
2357
- display: grid; gap: 12px;
2440
+ display: grid; gap: var(--space-2-75);
2358
2441
  padding: var(--space-5);
2359
2442
  background: var(--bg-2);
2360
2443
  border-radius: var(--r-3);
@@ -2387,6 +2470,19 @@
2387
2470
  Responsive Design — Mobile (480px), Tablet (1024px), Desktop (1440px+)
2388
2471
  ============================================================ */
2389
2472
 
2473
+ /* Breakpoint reference (CSS custom properties can't be used in @media queries,
2474
+ so these are documented here for consistency — always use these exact values):
2475
+ - Mobile/tablet boundary: 768px (max-width) — used for compact nav/sidebar
2476
+ collapse (catalog-theme.css, sidebar-misc.css). Note: kits-appended.css
2477
+ uses 760px for the same kind of collapse in a few places — a near-miss
2478
+ of this value, worth aligning to 768px when touched.
2479
+ - Tablet/desktop boundary: 900px (max-width) / 901px (min-width) — used for
2480
+ main layout reflow (hero-content.css, responsive2-workspace.css)
2481
+ - Legacy tablet band: 481px–1024px (min-width 481px / max-width 1025px) —
2482
+ used in this file (responsive.css) for a specific tablet treatment,
2483
+ consider migrating to the 768/900 pair
2484
+ */
2485
+
2390
2486
  /* --------------------------------------------------------------------
2391
2487
  Mobile Portrait Breakpoint (480px and below)
2392
2488
  ---------------------------------------------------------------------- */
@@ -2960,6 +3056,12 @@
2960
3056
  display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
2961
3057
  gap: var(--space-3); align-items: start;
2962
3058
  }
3059
+ /* Mobile fallback: auto-fill can still pack 2 cramped columns down to ~320px
3060
+ viewports; force a single column below the shared mobile breakpoint
3061
+ (docs/responsive.md) so thumbnails and file rails always read as a list. */
3062
+ @media (max-width: 480px) {
3063
+ .ds-247420 .ds-file-grid-thumb { grid-template-columns: 1fr; }
3064
+ }
2963
3065
  .ds-247420 .ds-file-cell {
2964
3066
  position: relative;
2965
3067
  background: var(--bg); border: var(--bw-hair) solid var(--rule); border-radius: var(--r-2);
@@ -3426,7 +3528,7 @@
3426
3528
  .ds-247420 .ds-preview-code::-webkit-scrollbar,
3427
3529
  .ds-247420 .ds-preview-text::-webkit-scrollbar,
3428
3530
  .ds-247420 .ds-preview-body::-webkit-scrollbar,
3429
- .ds-247420 .ds-modal-body::-webkit-scrollbar { width: 10px; height: 10px; }
3531
+ .ds-247420 .ds-modal-body::-webkit-scrollbar { width: 8px; height: 8px; }
3430
3532
  .ds-247420 .ds-preview-code::-webkit-scrollbar-thumb,
3431
3533
  .ds-247420 .ds-preview-text::-webkit-scrollbar-thumb {
3432
3534
  background: color-mix(in oklab, var(--paper) 28%, transparent);
@@ -3499,6 +3601,12 @@
3499
3601
  .ds-247420 .ds-demo-label { font-size: var(--fs-xs); font-weight: 500; margin: 0 0 var(--space-1); }
3500
3602
  .ds-247420 .ds-demo-desc { font-size: var(--fs-tiny); color: var(--fg-3); margin: 0 0 var(--space-2); }
3501
3603
 
3604
+ /* Per-preview backlink to the full token map/doc — names which token
3605
+ families the specimen above it demonstrates, then links out to the
3606
+ generated theme-map for the resolved values. */
3607
+ .ds-247420 .tokens-backlink { font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--fg-3); margin-top: 2px; }
3608
+ .ds-247420 .tokens-backlink a { color: inherit; }
3609
+
3502
3610
  @media (max-width: 768px) {
3503
3611
  .ds-247420 .ds-catalog { flex-direction: column; }
3504
3612
  .ds-247420 .ds-catalog-side { width: 100%; height: auto; position: static; border-right: 0; border-bottom: var(--bw-hair) solid var(--rule); }
@@ -3595,13 +3703,25 @@
3595
3703
  font-family: var(--ff-mono); font-size: var(--fs-xs);
3596
3704
  display: inline-flex; align-items: center; gap: 6px;
3597
3705
  }
3598
- /* CSS-drawn half-disc mark on the compact theme toggle: keeps the control
3599
- legible when its text label is hidden (icon-only rail strip). */
3706
+ /* CSS-drawn disc mark on the compact theme toggle: keeps the control
3707
+ legible when its text label is hidden (icon-only rail strip). Per-mode
3708
+ fill gives the icon itself a distinct look for auto/light/dark, so state
3709
+ reads without relying on the title tooltip alone. */
3600
3710
  .ds-247420 .ds-theme-disc {
3601
3711
  flex: 0 0 auto; width: 14px; height: 14px; border-radius: 50%;
3602
3712
  border: var(--bw-hair) solid var(--fg-3);
3713
+ /* auto: half-and-half, matches default (no data-mode) for safety */
3603
3714
  background: linear-gradient(90deg, var(--fg-3) 0 50%, transparent 50% 100%);
3604
3715
  }
3716
+ .ds-247420 .ds-theme-disc[data-mode="auto"] {
3717
+ background: linear-gradient(90deg, var(--fg-3) 0 50%, transparent 50% 100%);
3718
+ }
3719
+ .ds-247420 .ds-theme-disc[data-mode="paper"] {
3720
+ background: var(--bg); border-color: var(--fg-3);
3721
+ }
3722
+ .ds-247420 .ds-theme-disc[data-mode="ink"] {
3723
+ background: var(--fg-3); border-color: var(--fg-3);
3724
+ }
3605
3725
 
3606
3726
  /* ============================================================
3607
3727
  Chat kit polish — styles every class emitted by Chat /
@@ -3674,6 +3794,18 @@
3674
3794
  background: color-mix(in oklab, var(--mascot) 26%, var(--bg-2));
3675
3795
  color: var(--mascot-deep);
3676
3796
  }
3797
+ /* Plain assistant turns ('them', no aicat persona) previously fell through to
3798
+ the bare .chat-avatar gray disc with no role identity at all — 'you' and
3799
+ 'aicat' both got a tinted background + ring, the default assistant got
3800
+ nothing. Give it the same tinted-background treatment via --color-success
3801
+ (the conventional assistant/bot association, distinct from the accent hue
3802
+ 'you' owns and the mascot hue 'aicat' owns) plus a thin identity ring so
3803
+ role is readable from the avatar alone, not just bubble position/color. */
3804
+ .ds-247420 .chat-msg.them:not(.aicat) .chat-avatar {
3805
+ background: color-mix(in oklab, var(--color-success) 20%, var(--bg-3));
3806
+ color: color-mix(in oklab, var(--color-success) 65%, var(--fg));
3807
+ box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--color-success) 35%, transparent);
3808
+ }
3677
3809
 
3678
3810
  .ds-247420 .chat-stack {
3679
3811
  display: flex; flex-direction: column; gap: var(--space-1);
@@ -4243,12 +4375,26 @@
4243
4375
  more "still looks clickable-green") and the steeper opacity drop reads as
4244
4376
  unmistakably inert at a glance, not just slightly dimmed. */
4245
4377
  .ds-247420 .btn:disabled, .ds-247420 .btn-primary:disabled, .ds-247420 .btn-ghost:disabled, .ds-247420 .btn.is-disabled, .ds-247420 .btn-primary.is-disabled, .ds-247420 .btn-ghost.is-disabled, .ds-247420 button:disabled, .ds-247420[disabled] {
4246
- opacity: 0.45;
4378
+ opacity: 0.5;
4247
4379
  filter: grayscale(60%);
4248
4380
  cursor: not-allowed;
4249
4381
  pointer-events: none;
4250
4382
  }
4251
4383
 
4384
+ /* Systemic hover fallback — button-like elements with no design-system class
4385
+ (a bare <button>, [role="button"], or a generic .row/.chip usage) get a
4386
+ `transition` declared above (line ~106) but nothing to transition to, so
4387
+ they look inert until clicked. This is a low-specificity (0,0,1)/(0,1,1)
4388
+ catch-all: any element that also carries `.btn`/`.btn-primary`/`.btn-ghost`/
4389
+ `.ds-icon-btn`/etc. keeps its own component `:hover` (those selectors are
4390
+ equal-or-higher specificity and, for the shared ones, declared later in
4391
+ the cascade), so this never overrides a deliberate per-component hover —
4392
+ it only fills the gap for elements that don't have one. */
4393
+ .ds-247420 button:not(:disabled):hover,
4394
+ .ds-247420[role="button"]:not([aria-disabled="true"]):hover {
4395
+ background-color: var(--panel-hover, var(--bg-3));
4396
+ }
4397
+
4252
4398
  /* Loading state — spinning indicator */
4253
4399
  @keyframes spinner-rotate {
4254
4400
  from { transform: rotate(0deg); }
@@ -4256,10 +4402,12 @@
4256
4402
  }
4257
4403
  /* Reusable spin utility: rotates the element (e.g. a refresh Icon used as a
4258
4404
  busy indicator) via the shared spinner-rotate keyframe. Respects reduced
4259
- motion. Use instead of a decorative rotation glyph. */
4405
+ motion. Use instead of a decorative rotation glyph.
4406
+ Ring spinner — use for larger blocking/full-region loading states. */
4260
4407
  .ds-247420 .ds-spin { animation: spinner-rotate 0.8s linear infinite; transform-origin: center; }
4261
4408
  @media (prefers-reduced-motion: reduce) { .ds-247420 .ds-spin { animation: none; } }
4262
4409
 
4410
+ /* Ring spinner — use for larger blocking/full-region loading states. */
4263
4411
  .ds-247420 .loading { position: relative; }
4264
4412
  .ds-247420 .loading::after {
4265
4413
  content: '';
@@ -4680,7 +4828,7 @@
4680
4828
  background: var(--accent-ink); border-color: var(--accent-ink);
4681
4829
  }
4682
4830
  .ds-247420 .ds-toggle[aria-checked="true"] .ds-toggle-knob {
4683
- background: var(--panel-0, #fff);
4831
+ background: var(--panel-0, var(--bg));
4684
4832
  transform: translateX(18px);
4685
4833
  }
4686
4834
  .ds-247420 .ds-toggle:disabled { opacity: 0.5; cursor: not-allowed; }
@@ -4814,7 +4962,7 @@
4814
4962
  .ds-247420 .ds-field input:not(.input):disabled,
4815
4963
  .ds-247420 .ds-field textarea:not(.input):disabled,
4816
4964
  .ds-247420 .ds-field .ds-select:disabled {
4817
- opacity: .55;
4965
+ opacity: 0.5;
4818
4966
  cursor: not-allowed;
4819
4967
  }
4820
4968
  .ds-247420 .ds-field input:not(.input)[aria-invalid="true"],
@@ -5026,18 +5174,61 @@
5026
5174
  }
5027
5175
  }
5028
5176
 
5177
+ /* Stacking host for the imperative toast()/<Toast> component
5178
+ (src/components/editor-primitives/toast.js). One fixed-position column;
5179
+ individual toasts are appended/removed as siblings inside it rather than
5180
+ each toast fixed-positioning itself, so more than one toast in flight
5181
+ stacks instead of overlapping at the same bottom-right point. */
5182
+ .ds-247420 .ds-ep-toast-host {
5183
+ position: fixed; z-index: var(--z-toast);
5184
+ bottom: 24px; right: 24px;
5185
+ display: flex; flex-direction: column-reverse; gap: var(--space-2);
5186
+ pointer-events: none;
5187
+ }
5188
+ .ds-247420 .ds-ep-toast {
5189
+ pointer-events: auto;
5190
+ display: flex; align-items: center; gap: var(--space-3);
5191
+ background: var(--bg-2); color: var(--fg);
5192
+ border: 1px solid var(--rule);
5193
+ border-radius: var(--r-2);
5194
+ padding: var(--space-3) var(--space-4);
5195
+ max-width: 320px;
5196
+ box-shadow: var(--shadow-overlay);
5197
+ font-size: var(--fs-sm);
5198
+ }
5199
+ @media (prefers-reduced-motion: no-preference) {
5200
+ .ds-247420 .ds-ep-toast { animation: toast-slide-in 0.3s ease-out; }
5201
+ .ds-247420 .ds-ep-toast.leaving { animation: toast-slide-in 0.2s ease-in reverse; }
5202
+ }
5203
+ .ds-247420 .ds-ep-toast.kind-error { border-color: var(--danger, var(--warn)); }
5204
+ .ds-247420 .ds-ep-toast.kind-success { border-color: var(--green-2); }
5205
+ .ds-247420 .ds-ep-toast.kind-warning { border-color: var(--amber); }
5206
+ .ds-247420 .ds-ep-toast-msg { flex: 1; }
5207
+ .ds-247420 .ds-ep-toast-action {
5208
+ flex-shrink: 0;
5209
+ background: transparent; border: 0; color: var(--accent-ink);
5210
+ font: inherit; font-weight: 600; cursor: pointer; padding: 0;
5211
+ }
5212
+ .ds-247420 .ds-ep-toast-action:hover { text-decoration: underline; }
5213
+
5214
+ @media (max-width: 480px) {
5215
+ .ds-247420 .ds-ep-toast-host { bottom: 16px; right: 16px; left: 16px; }
5216
+ .ds-247420 .ds-ep-toast { max-width: none; }
5217
+ }
5218
+
5029
5219
  /* ============================================================
5030
5220
  Spinner — animated loading indicator
5031
5221
  ============================================================ */
5222
+ /* Dots spinner — use for small inline/compact loading states. */
5032
5223
  .ds-247420 .ds-spinner {
5033
5224
  display: inline-flex;
5034
- gap: 4px;
5225
+ gap: var(--space-1);
5035
5226
  align-items: center;
5036
5227
  height: 16px;
5037
5228
  }
5038
5229
  .ds-247420 .ds-spinner span {
5039
- width: 4px;
5040
- height: 4px;
5230
+ width: var(--space-1);
5231
+ height: var(--space-1);
5041
5232
  border-radius: 50%;
5042
5233
  background: var(--accent);
5043
5234
  }
@@ -5048,17 +5239,17 @@
5048
5239
  .ds-247420 .ds-spinner span:nth-child(3) { animation-delay: 0.36s; }
5049
5240
  }
5050
5241
 
5051
- .ds-247420 .ds-spinner-lg { height: 24px; gap: 6px; }
5052
- .ds-247420 .ds-spinner-lg span { width: 6px; height: 6px; }
5242
+ .ds-247420 .ds-spinner-lg { height: 24px; gap: var(--space-1-75); }
5243
+ .ds-247420 .ds-spinner-lg span { width: var(--space-1-75); height: var(--space-1-75); }
5053
5244
 
5054
- .ds-247420 .ds-spinner-sm { height: 12px; gap: 2px; }
5055
- .ds-247420 .ds-spinner-sm span { width: 2px; height: 2px; }
5245
+ .ds-247420 .ds-spinner-sm { height: 12px; gap: var(--space-hair); }
5246
+ .ds-247420 .ds-spinner-sm span { width: var(--space-hair); height: var(--space-hair); }
5056
5247
 
5057
5248
  .ds-247420 .ds-spinner-xs { height: 8px; gap: 1px; }
5058
5249
  .ds-247420 .ds-spinner-xs span { width: 1.5px; height: 1.5px; }
5059
5250
 
5060
- .ds-247420 .ds-spinner-xl { height: 32px; gap: 8px; }
5061
- .ds-247420 .ds-spinner-xl span { width: 8px; height: 8px; }
5251
+ .ds-247420 .ds-spinner-xl { height: 32px; gap: var(--space-2); }
5252
+ .ds-247420 .ds-spinner-xl span { width: var(--space-2); height: var(--space-2); }
5062
5253
 
5063
5254
  @keyframes ds-bounce {
5064
5255
  0%, 80%, 100% { transform: translateY(0); }
@@ -5108,7 +5299,7 @@
5108
5299
  flex-shrink: 0;
5109
5300
  color: var(--alert-tone);
5110
5301
  font-weight: 600;
5111
- min-width: 20px;
5302
+ min-width: var(--space-3-5);
5112
5303
  text-align: center;
5113
5304
  }
5114
5305
 
@@ -5137,13 +5328,14 @@
5137
5328
  cursor: pointer;
5138
5329
  color: var(--fg-3);
5139
5330
  padding: 0;
5140
- font-size: 14px;
5331
+ font-size: var(--fs-xs);
5141
5332
  line-height: 1;
5142
- min-width: 20px;
5333
+ min-width: var(--space-3-5);
5143
5334
  text-align: center;
5144
5335
  transition: color var(--dur-snap) var(--ease);
5145
5336
  }
5146
5337
  .ds-247420 .ds-alert-dismiss:hover { color: var(--fg); }
5338
+ .ds-247420 .ds-alert-dismiss:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
5147
5339
 
5148
5340
  /* ============================================================
5149
5341
  Responsive Perfection — Drawer rail, ultrawide cap, mobile compact
@@ -5428,7 +5620,9 @@
5428
5620
  .ds-247420 .ws-rail-item:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: -2px; }
5429
5621
  .ds-247420 .ws-rail-item.active { background: var(--accent-tint); color: var(--fg); box-shadow: inset 2px 0 0 var(--accent); }
5430
5622
  .ds-247420 .ws-rail-item-label { flex: 1; overflow: hidden; text-overflow: ellipsis; }
5431
- .ds-247420 .ws-rail-item-count { font-family: var(--ff-mono); font-size: var(--fs-tiny); color: var(--fg-3); }
5623
+ /* Shape/color come from the shared .ds-badge component (see app-shell/primitives.css);
5624
+ this class is now just the rail-item layout hook (spacing, collapsed-state targeting). */
5625
+ .ds-247420 .ws-rail-item-count { flex: none; margin-inline-start: var(--space-1); }
5432
5626
  .ds-247420 .ws-rail-item-flag { width: 6px; height: 6px; border-radius: 50%; flex: none; }
5433
5627
  .ds-247420 .ws-rail-item-flag.tone-flame { background: var(--flame); box-shadow: 0 0 0 2px color-mix(in oklab, var(--flame) 38%, transparent); }
5434
5628
  .ds-247420 .ws-rail-foot { margin-top: auto; padding-top: var(--space-2); border-top: var(--bw-hair) solid var(--bg-3); }
@@ -5471,6 +5665,7 @@
5471
5665
  because the chat thread carries its own --measure gutter. */
5472
5666
  .ds-247420 .ws-main { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; padding: var(--space-4) var(--space-5); }
5473
5667
  .ds-247420 .ws-main.ws-main--flush { padding: 0; }
5668
+ /* Non-interactive scroll region (content column), not a focusable control — outline intentionally suppressed. */
5474
5669
  .ds-247420 .ws-main:focus { outline: none; }
5475
5670
 
5476
5671
  /* Right context pane */
@@ -5703,6 +5898,39 @@
5703
5898
  .ds-247420 .ds-live-log { height: auto; max-height: none; overflow: visible; }
5704
5899
  .ds-247420 .ds-stat, .ds-247420 .kpi-card { border: 1px solid var(--rule); }
5705
5900
  .ds-247420 .ds-session-row { break-inside: avoid; }
5901
+
5902
+ /* Receipt table (Receipt() in src/components/content/panel.js, `.kv`) — the
5903
+ project_page kit's "receipt" section, and any other kv-table consumer.
5904
+ On screen it inherits panel/token colors that can render as near-white
5905
+ text on a printer's assumed-white page background if the browser's
5906
+ "background graphics" print option is off (the common default); force
5907
+ the LIGHT-theme ink/paper tokens (real print-page colors, not a raw
5908
+ literal — --ink/--paper are the same "true black text on true white
5909
+ page" values the light theme already defines) and visible cell borders
5910
+ so it stays legible without relying on background-graphics printing at
5911
+ all. break-inside:avoid keeps a receipt from splitting a row across a
5912
+ page boundary. */
5913
+ .ds-247420 .kv { border-collapse: collapse; width: 100%; color: var(--ink); background: var(--paper); }
5914
+ .ds-247420 .kv tr { break-inside: avoid; }
5915
+ .ds-247420 .kv td { border: 1px solid var(--paper-3); padding: 4px 8px; color: var(--ink); }
5916
+
5917
+ /* Any button/interactive-only affordance still present inside otherwise-
5918
+ printable content (copy buttons, expand/collapse toggles, filter pills,
5919
+ the receipt/doc section's own action row) is chrome, not content. */
5920
+ .ds-247420 .btn, .ds-247420 .btn-primary, .ds-247420 .btn-ghost, .ds-247420 .row-act, .ds-247420 .ds-filter-pills, .ds-247420 .app-search, .ds-247420 input[type="search"] { display: none; }
5921
+
5922
+ /* Force real ink-on-paper for body copy: panel/fg tokens are tuned for
5923
+ screen surfaces (including dark theme, where --panel-text can resolve
5924
+ near-white) and a printer defaults to a white page regardless of
5925
+ data-theme, so an unconverted dark-theme page would print near-invisible
5926
+ text without this override. Same --ink/--paper light-theme pair as the
5927
+ receipt table above, not a second raw literal. */
5928
+ /* No !important: source order already wins here (this rule is the last
5929
+ word inside its own @media print block, same as every other override in
5930
+ this file), and an unconditional !important on `body` would beat even a
5931
+ future print-specific consumer override with higher specificity. */
5932
+ .ds-247420 body, .ds-247420 .app-main, .ds-247420 .page-body, .ds-247420 .ds-lede { color: var(--ink); background: var(--paper); }
5933
+ .ds-247420 a { color: var(--ink); text-decoration: underline; }
5706
5934
  }
5707
5935
 
5708
5936
  /* ============================================================
@@ -5721,6 +5949,10 @@
5721
5949
 
5722
5950
  /* PhaseWalk */
5723
5951
  .ds-247420 .ds-phasewalk { display: inline-flex; gap: var(--space-hair); vertical-align: middle; }
5952
+ /* NOTE: a second toggle-switch-shaped implementation exists in
5953
+ plugins-config.css:91 (.ds-plugins-toggle) at a different scale (40x22 vs
5954
+ this 18x10) — consider consolidating into a shared .ds-switch primitive
5955
+ with a size modifier. */
5724
5956
  .ds-247420 .ds-phasewalk-seg {
5725
5957
  width: 18px; height: 10px; border-radius: var(--r-hair);
5726
5958
  background: var(--bg-3); position: relative;
@@ -6355,6 +6587,11 @@
6355
6587
  .ds-247420 .ds-btn-row-tight { gap: var(--space-1-75, 6px); }
6356
6588
  .ds-247420 .ds-key-input { flex: 1 1 160px; min-width: 0; font-family: var(--ff-mono); }
6357
6589
  .ds-247420 .ds-toggle-btn { min-width: 78px; }
6590
+ /* Side-by-side checked/unchecked reference pair (settings kit theme panel) --
6591
+ documents the visual difference between states in one static, non-live
6592
+ spot rather than requiring a viewer to toggle a real control back and
6593
+ forth to compare. */
6594
+ .ds-247420 .ds-toggle-state-sample { display: flex; align-items: center; gap: var(--space-1-5, 6px); }
6358
6595
  .ds-247420 .ds-toggle-label { margin-left: var(--space-2, 8px); color: inherit; opacity: 0.7; }
6359
6596
  .ds-247420 .ds-btn-warn { color: var(--warn); }
6360
6597
  /* Raw --mascot text measures 3.11:1 on paper (fails 4.5:1); --mascot-deep is
@@ -6497,6 +6734,14 @@
6497
6734
  display: flex; flex-direction: column; gap: 0;
6498
6735
  background: var(--ink); border-radius: var(--r-1);
6499
6736
  font-family: var(--ff-mono); font-size: var(--fs-micro); line-height: 1.65;
6737
+ /* Responsive strategy for long logs: cap the scrollback height and scroll
6738
+ vertically rather than growing the page indefinitely; long command/output
6739
+ lines wrap (see .cli .cmd's overflow-wrap: anywhere below) instead of
6740
+ scrolling horizontally, so wrapping is the chosen default over
6741
+ horizontal scroll for this kit. */
6742
+ max-height: 60vh;
6743
+ overflow-y: auto;
6744
+ overflow-wrap: anywhere;
6500
6745
  }
6501
6746
  /* .cli is authored in hero-content.css as a STANDALONE hero code block --
6502
6747
  20px/24px padding, --fs-lg type, --r-3 radius and its own --ink background
@@ -7116,6 +7361,9 @@
7116
7361
  }
7117
7362
  .ds-247420 .ds-plugins-version { font-size: var(--fs-micro); color: var(--fg-3); }
7118
7363
 
7364
+ /* NOTE: a second toggle-switch implementation exists in data-density.css:18
7365
+ (.ds-phasewalk-seg) at a different scale (18x10 vs this 40x22) — consider
7366
+ consolidating into a shared .ds-switch primitive with a size modifier. */
7119
7367
  .ds-247420 .ds-plugins-toggle {
7120
7368
  flex: 0 0 auto; width: 40px; height: 22px; border-radius: 11px; border: none; padding: 0;
7121
7369
  background: var(--rule); position: relative; cursor: pointer;
@@ -7358,11 +7606,11 @@
7358
7606
  .ds-247420 .ds-otp-box:focus-visible {
7359
7607
  outline: none;
7360
7608
  border-color: var(--accent);
7361
- box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 40%, transparent);
7609
+ box-shadow: var(--focus-ring-inset);
7362
7610
  }
7363
7611
  .ds-247420 .ds-otp-box-filled { border-color: var(--rule-strong); background: var(--bg-3); }
7364
7612
  .ds-247420 .ds-otp-box-error { border-color: var(--red); }
7365
- .ds-247420 .ds-otp-box-error:focus-visible { box-shadow: 0 0 0 3px color-mix(in oklab, var(--red) 40%, transparent); }
7613
+ .ds-247420 .ds-otp-box-error:focus-visible { box-shadow: var(--focus-ring-inset); }
7366
7614
  .ds-247420 .ds-otp-box:disabled { opacity: 0.5; cursor: default; }
7367
7615
 
7368
7616
  @media (prefers-reduced-motion: reduce) {
@@ -7626,6 +7874,11 @@
7626
7874
  border-radius: var(--r-pill);
7627
7875
  background: var(--bg-2);
7628
7876
  }
7877
+ /* .ds-collab-chip-avatar (24px identity circle) and .ds-collab-chip-status
7878
+ (8px status dot) below are both circular via border-radius: 50% but are
7879
+ intentionally distinct: different sizes, different content (initial vs.
7880
+ plain fill), different positioning role (layout box vs. absolute badge).
7881
+ Not extracting a shared class -- there's nothing substantive to share. */
7629
7882
  .ds-247420 .ds-collab-chip-avatar {
7630
7883
  position: relative;
7631
7884
  width: 24px;
@@ -7790,6 +8043,9 @@
7790
8043
  min-width: 18px;
7791
8044
  height: 18px;
7792
8045
  padding: 0 var(--space-1-5);
8046
+ /* TODO(a11y): --paper is a fixed near-white on --warn (#FF5A52) -- likely
8047
+ ~3:1, below the 4.5:1 AA floor (same defect fixed via --ink elsewhere
8048
+ for --warn text). Small numeric badge; needs contrast audit. */
7793
8049
  background: var(--warn);
7794
8050
  color: var(--paper);
7795
8051
  font-family: var(--ff-mono);
@@ -8122,6 +8378,9 @@
8122
8378
  min-width: 16px;
8123
8379
  height: 16px;
8124
8380
  padding: 0 var(--space-1-5);
8381
+ /* TODO(a11y): --paper is a fixed near-white on --warn (#FF5A52) -- likely
8382
+ ~3:1, below the 4.5:1 AA floor (same defect fixed via --ink elsewhere
8383
+ for --warn text). Small numeric badge; needs contrast audit. */
8125
8384
  background: var(--warn);
8126
8385
  color: var(--paper);
8127
8386
  font-family: var(--ff-mono);
@@ -8205,7 +8464,7 @@
8205
8464
  height: 20px;
8206
8465
  border-radius: 50%;
8207
8466
  background: var(--avatar-bg, var(--bg-3));
8208
- color: var(--fg-2);
8467
+ color: var(--avatar-fg, var(--fg-2));
8209
8468
  display: inline-flex;
8210
8469
  align-items: center;
8211
8470
  justify-content: center;
@@ -8311,8 +8570,11 @@
8311
8570
  background: color-mix(in oklab, var(--fg) 18%, transparent);
8312
8571
  }
8313
8572
  .ds-247420 .cm-vs-btn.danger {
8573
+ /* --ink, not --paper: --paper is a fixed near-white, which measures ~3:1 on
8574
+ --warn (#FF5A52) -- below the 4.5:1 AA floor. Same fix pattern as
8575
+ .btn-primary.danger in app-shell/files.css. */
8314
8576
  background: var(--warn);
8315
- color: var(--paper);
8577
+ color: var(--ink);
8316
8578
  }
8317
8579
  .ds-247420 .cm-vs-btn.danger:hover {
8318
8580
  background: color-mix(in oklab, var(--warn) 80%, var(--ink));
@@ -8337,7 +8599,7 @@
8337
8599
  height: 32px;
8338
8600
  border-radius: 50%;
8339
8601
  background: var(--avatar-bg, var(--bg-3));
8340
- color: var(--fg-2);
8602
+ color: var(--avatar-fg, var(--fg-2));
8341
8603
  display: inline-flex;
8342
8604
  align-items: center;
8343
8605
  justify-content: center;
@@ -8548,7 +8810,7 @@
8548
8810
  height: 28px;
8549
8811
  border-radius: 50%;
8550
8812
  background: var(--avatar-bg, var(--bg-3));
8551
- color: var(--fg-2);
8813
+ color: var(--avatar-fg, var(--fg-2));
8552
8814
  display: inline-flex;
8553
8815
  align-items: center;
8554
8816
  justify-content: center;
@@ -8609,7 +8871,7 @@
8609
8871
  height: 56px;
8610
8872
  border-radius: 50%;
8611
8873
  background: var(--avatar-bg, var(--bg-3));
8612
- color: var(--fg-2);
8874
+ color: var(--avatar-fg, var(--fg-2));
8613
8875
  display: inline-flex;
8614
8876
  align-items: center;
8615
8877
  justify-content: center;
@@ -9320,10 +9582,14 @@
9320
9582
  /* PageView — cm-page / cm-page-* */
9321
9583
  .ds-247420 .cm-page { display: flex; flex-direction: column; height: 100%; min-height: 0; overflow-y: auto; background: var(--bg-1, var(--bg-2)); color: var(--fg); }
9322
9584
  .ds-247420 .cm-page-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); padding: var(--space-3); border-bottom: var(--bw-hair) solid var(--rule); }
9585
+ .ds-247420 .cm-page-head-title { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
9323
9586
  .ds-247420 .cm-page-title { margin: 0; font-size: var(--fs-xl); font-weight: 700; }
9587
+ .ds-247420 .cm-page-meta { display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-xs); color: var(--fg-2, var(--fg)); }
9588
+ .ds-247420 .cm-page-author { font-weight: 500; }
9589
+ .ds-247420 .cm-page-time { opacity: .75; }
9324
9590
  .ds-247420 .cm-page-edit { padding: var(--space-1) var(--space-2); border: var(--bw-hair) solid var(--rule-strong); border-radius: var(--r-1); background: var(--bg-3); color: var(--fg); cursor: pointer; }
9325
9591
  .ds-247420 .cm-page-body { padding: var(--space-3); line-height: 1.6; }
9326
- .ds-247420 .cm-page-empty { color: var(--fg-2, var(--fg)); opacity: .7; }
9592
+ .ds-247420 .cm-page-empty { color: var(--fg-2, var(--fg)); opacity: .82; }
9327
9593
 
9328
9594
  /* ============================================================
9329
9595
  freddie pages — layout for FREDDIE_PAGES renderers.
@@ -9336,8 +9602,9 @@
9336
9602
  .ds-247420 .fd-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-2, 10px); padding: var(--space-5, 32px) var(--space-3, 16px); color: var(--fg-2, var(--fg)); text-align: center; }
9337
9603
  /* 28px is an ICON size (empty-state mark), not a type tier — the --fs-* ladder
9338
9604
  tops out at --fs-xl (21px) before every remaining rung becomes a fluid
9339
- heading clamp, so neither substitutes without visibly resizing the mark. */
9340
- .ds-247420 .fd-empty-glyph { font-size: 28px; opacity: .55; line-height: 1; }
9605
+ heading clamp, so neither substitutes without visibly resizing the mark.
9606
+ --fs-2xl bridges that gap as a fixed 28px token. */
9607
+ .ds-247420 .fd-empty-glyph { font-size: var(--fs-2xl); opacity: .55; line-height: 1; }
9341
9608
  .ds-247420 .fd-pre { margin: 0; padding: var(--space-2, 10px); background: var(--bg-3, var(--panel-bg-2)); border: var(--bw-hair, 1px) solid var(--rule, var(--panel-accent)); border-radius: var(--r-1, 6px); overflow: auto; max-height: 460px; white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word; font-family: var(--font-mono, ui-monospace, monospace); font-size: var(--fs-micro); line-height: 1.5; }
9342
9609
  .ds-247420 .fd-skill-body { max-height: 320px; }
9343
9610
  .ds-247420 .fd-row-actions { display: inline-flex; gap: var(--space-1, 6px); align-items: center; }
@@ -9923,7 +10190,7 @@
9923
10190
  background: var(--bg); border: var(--border-w) solid var(--accent); border-radius: var(--r-1);
9924
10191
  padding: var(--space-1) var(--space-2);
9925
10192
  }
9926
- .ds-247420 .ds-session-rename-input:focus { outline: none; }
10193
+ .ds-247420 .ds-session-rename-input:focus { outline: none; box-shadow: var(--focus-ring-inset); }
9927
10194
 
9928
10195
  /* Inline delete confirm — same row height, two flat buttons, no modal;
9929
10196
  mirrors SessionDashboard's arm-then-confirm bulk-stop control. */
@@ -10800,7 +11067,7 @@
10800
11067
  /* Minimap internal order: centerline < viewport < dot < tooltip. All four
10801
11068
  live inside the one raised widget, so they share its rung and differ
10802
11069
  only by a local offset. */
10803
- z-index: calc(var(--z-raised) + 1);
11070
+ z-index: var(--z-raised-1);
10804
11071
  }
10805
11072
  .ds-247420 .chat-minimap-centerline {
10806
11073
  position: absolute;
@@ -10820,7 +11087,7 @@
10820
11087
  border-radius: var(--r-pill, 999px);
10821
11088
  transform: translate(-50%, -50%) scale(1);
10822
11089
  transition: transform 0.1s;
10823
- z-index: calc(var(--z-raised) + 2);
11090
+ z-index: var(--z-raised-2);
10824
11091
  }
10825
11092
  .ds-247420 .chat-minimap-dot.is-user {
10826
11093
  background: color-mix(in oklab, var(--accent) 18%, transparent);
@@ -10845,7 +11112,7 @@
10845
11112
  /* Top of the minimap's internal order. Deliberately NOT --z-tooltip: it is
10846
11113
  clipped to the minimap rail rather than floating over the app, so it must
10847
11114
  not outrank real overlays that may cover the rail. */
10848
- z-index: calc(var(--z-raised) + 3);
11115
+ z-index: var(--z-raised-3);
10849
11116
  pointer-events: none;
10850
11117
  opacity: 0.45;
10851
11118
  font-size: var(--fs-tiny);
@@ -12216,6 +12483,14 @@
12216
12483
  box-shadow: var(--shadow-3);
12217
12484
  outline: none; overflow: hidden;
12218
12485
  }
12486
+ .ds-247420 .ov-emoji-search {
12487
+ flex-shrink: 0; margin: var(--space-2, 8px); margin-bottom: 0;
12488
+ padding: var(--space-1-5, 6px) var(--space-2, 8px);
12489
+ border: 1px solid var(--rule); border-radius: var(--r-1, 10px);
12490
+ background: var(--bg-1, var(--bg-3)); color: var(--fg);
12491
+ font: inherit; font-size: var(--fs-sm, 13px);
12492
+ }
12493
+ .ds-247420 .ov-emoji-search:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: 1px; }
12219
12494
  .ds-247420 .ov-emoji-tabs {
12220
12495
  display: flex; flex-shrink: 0;
12221
12496
  border-bottom: 1px solid var(--rule);
@@ -12374,7 +12649,7 @@
12374
12649
  .ds-247420 .ov-lightbox-x { position: absolute; top: 16px; right: 16px; width: 40px; height: 40px; border: none; border-radius: 50%; background: color-mix(in oklab, var(--on-color) 12%, transparent); color: var(--on-color); font-size: 24px; line-height: 1; cursor: pointer; }
12375
12650
  .ds-247420 .ov-lightbox-x:hover { background: color-mix(in oklab, var(--on-color) 24%, transparent); }
12376
12651
  .ds-247420 .ov-lightbox-stage { display: flex; flex-direction: column; align-items: center; gap: var(--space-2); max-width: 92vw; max-height: 90vh; }
12377
- .ds-247420 .ov-lightbox-video { max-width: 92vw; max-height: 82vh; border-radius: var(--r-1); background: #000; /* true black media letterbox, intentional */ }
12652
+ .ds-247420 .ov-lightbox-video { max-width: 92vw; max-height: 82vh; border-radius: var(--r-1); background: var(--media-letterbox); /* true black media letterbox, intentional */ }
12378
12653
  .ds-247420 .ov-lightbox-label { color: var(--on-color); font-size: var(--fs-tiny); opacity: .85; }
12379
12654
 
12380
12655
  /* ---------------------------------------------------------------
@@ -13408,14 +13683,14 @@
13408
13683
  * prefixer leaves it untouched (consumers mount on <html class="ds-247420">).
13409
13684
  * All values read kit tokens — no hardcoded hex, no inline design in the app. */
13410
13685
 
13411
- .ds-247420 .site-panel { margin: var(--space-2); }
13686
+ .ds-247420 .site-panel { margin: var(--space-2); max-width: 1100px; padding: var(--space-3, 16px); }
13412
13687
 
13413
13688
  /* Hero block */
13414
13689
  .ds-247420 .site-hero { padding: var(--space-4); }
13415
13690
  .ds-247420 .site-hero-h { margin: 0 0 var(--space-2); }
13416
13691
  .ds-247420 .site-hero-body { margin: var(--space-2) 0 var(--space-3); color: var(--fg-2); max-width: 64ch; }
13417
- .ds-247420 .site-chip-row { display: flex; gap: 6px; flex-wrap: wrap; margin: 0 0 var(--space-3); }
13418
- .ds-247420 .site-cta-row { display: flex; gap: var(--space-2); flex-wrap: wrap; }
13692
+ .ds-247420 .site-chip-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: 0 0 var(--space-3); }
13693
+ .ds-247420 .site-cta-row { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
13419
13694
 
13420
13695
  /* Quickstart CLI block — these .cli/.prompt/.cmd nodes were previously unstyled. */
13421
13696
  .ds-247420 .site-cli { padding: var(--space-3); background: var(--bg); border: var(--bw-hair, 1px) solid var(--rule); border-radius: var(--r-1); }
@@ -13630,12 +13905,12 @@
13630
13905
  background: var(--bg);
13631
13906
  color: var(--fg);
13632
13907
  border: 1px solid var(--bw-rule, var(--rule));
13633
- border-radius: var(--r-1, 6px);
13634
- padding: 8px 10px;
13908
+ border-radius: var(--r-1, 8px);
13909
+ padding: var(--space-2) var(--space-2-5);
13635
13910
  font: var(--fs-body) var(--ff-mono);
13636
13911
  outline: none;
13637
13912
  }
13638
- .ds-247420 .sp-lobby-input:focus { border-color: var(--accent); }
13913
+ .ds-247420 .sp-lobby-input:focus-visible { border-color: var(--accent); box-shadow: var(--focus-ring-inset); }
13639
13914
  .ds-247420 .sp-lobby-link { font-size: var(--fs-small, 11px); }
13640
13915
  .ds-247420 .sp-lobby-code {
13641
13916
  font-family: var(--ff-display);