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
@@ -67,11 +67,16 @@ export function ThemeToggle({ compact = false, onChange } = {}) {
67
67
  el.setAttribute('title', titleFor(now));
68
68
  const lab = el.querySelector('.ds-theme-toggle-label');
69
69
  if (lab) lab.textContent = labelFor(now);
70
+ const disc = el.querySelector('.ds-theme-disc');
71
+ if (disc) disc.setAttribute('data-mode', now);
70
72
  })
71
73
  },
72
- // CSS-drawn half-disc so the control still reads as the theme switch
73
- // when the label is hidden (icon-only rail strip).
74
- h('span', { class: 'ds-theme-disc', 'aria-hidden': 'true' }),
74
+ // CSS-drawn disc so the control still reads as the theme switch when
75
+ // the label is hidden (icon-only rail strip). data-mode selects a
76
+ // per-mode fill (solid light / solid dark / half-and-half for auto)
77
+ // so the icon itself — not just the title tooltip — shows current
78
+ // state at a glance.
79
+ h('span', { class: 'ds-theme-disc', 'data-mode': current, 'aria-hidden': 'true' }),
75
80
  h('span', { class: 'ds-theme-toggle-label' }, labelFor(current)));
76
81
  }
77
82
 
@@ -0,0 +1,103 @@
1
+ // copy-code.js -- vanilla-JS "copy" button injector for <pre><code> blocks.
2
+ //
3
+ // Standalone module, no dependencies, no framework assumptions: include it
4
+ // with a plain <script src="…/copy-code.js"></script> (or type="module") on
5
+ // any page rendering doc/code blocks and it wires itself up on
6
+ // DOMContentLoaded, finding every `pre > code` element and injecting a small
7
+ // button that copies the block's text to the clipboard.
8
+ //
9
+ // Self-invoking so a page can include it more than once without double-
10
+ // registering handlers or double-injecting buttons (guarded per-element via
11
+ // a data attribute).
12
+ (function () {
13
+ 'use strict';
14
+
15
+ const COPY_LABEL = 'copy';
16
+ const COPIED_LABEL = 'copied';
17
+ const FAILED_LABEL = 'copy failed';
18
+ const RESET_MS = 1500;
19
+ const MARK_ATTR = 'data-copy-code-wired';
20
+
21
+ function copyText(text) {
22
+ if (navigator.clipboard && navigator.clipboard.writeText) {
23
+ return navigator.clipboard.writeText(text);
24
+ }
25
+ // Fallback for non-secure contexts / browsers without navigator.clipboard.
26
+ return new Promise((resolve, reject) => {
27
+ try {
28
+ const ta = document.createElement('textarea');
29
+ ta.value = text;
30
+ ta.style.position = 'fixed';
31
+ ta.style.opacity = '0';
32
+ document.body.appendChild(ta);
33
+ ta.focus();
34
+ ta.select();
35
+ const ok = document.execCommand('copy');
36
+ document.body.removeChild(ta);
37
+ ok ? resolve() : reject(new Error('execCommand copy failed'));
38
+ } catch (err) {
39
+ reject(err);
40
+ }
41
+ });
42
+ }
43
+
44
+ function makeButton(codeEl) {
45
+ const btn = document.createElement('button');
46
+ btn.type = 'button';
47
+ btn.className = 'copy-code-btn';
48
+ btn.textContent = COPY_LABEL;
49
+ btn.setAttribute('aria-label', 'copy code to clipboard');
50
+
51
+ let resetTimer = null;
52
+ btn.addEventListener('click', () => {
53
+ copyText(codeEl.textContent || '').then(
54
+ () => setLabel(true),
55
+ () => setLabel(false)
56
+ );
57
+ });
58
+
59
+ function setLabel(ok) {
60
+ btn.textContent = ok ? COPIED_LABEL : FAILED_LABEL;
61
+ clearTimeout(resetTimer);
62
+ resetTimer = setTimeout(() => { btn.textContent = COPY_LABEL; }, RESET_MS);
63
+ }
64
+
65
+ return btn;
66
+ }
67
+
68
+ function wireBlock(codeEl) {
69
+ const pre = codeEl.parentElement;
70
+ if (!pre || pre.tagName !== 'PRE') return;
71
+ if (pre.hasAttribute(MARK_ATTR)) return;
72
+ pre.setAttribute(MARK_ATTR, '1');
73
+ // Position context for an absolutely-positioned button; harmless no-op
74
+ // if the page's own CSS already sets a position on <pre>.
75
+ if (!pre.style.position) pre.style.position = 'relative';
76
+ pre.appendChild(makeButton(codeEl));
77
+ }
78
+
79
+ function wireAll(root) {
80
+ (root || document).querySelectorAll('pre > code').forEach(wireBlock);
81
+ }
82
+
83
+ if (document.readyState === 'loading') {
84
+ document.addEventListener('DOMContentLoaded', () => wireAll(document));
85
+ } else {
86
+ wireAll(document);
87
+ }
88
+
89
+ // Minimal default styling, injected once, so the button is usable even on
90
+ // pages with no bespoke .copy-code-btn rule of their own. A page that wants
91
+ // its own look can simply define .copy-code-btn in its own stylesheet --
92
+ // this only runs if that class isn't already styled to be visible.
93
+ if (!document.getElementById('copy-code-btn-style')) {
94
+ const style = document.createElement('style');
95
+ style.id = 'copy-code-btn-style';
96
+ style.textContent =
97
+ '.copy-code-btn{position:absolute;top:.5em;right:.5em;font:inherit;' +
98
+ 'font-size:.75em;line-height:1;padding:.35em .6em;border:1px solid currentColor;' +
99
+ 'border-radius:4px;background:transparent;color:inherit;cursor:pointer;opacity:.6}' +
100
+ '.copy-code-btn:hover{opacity:1}';
101
+ document.head.appendChild(style);
102
+ }
103
+ })();
@@ -75,6 +75,24 @@ body {
75
75
  scrollbar-width: thin;
76
76
  scrollbar-color: var(--bg-3) transparent;
77
77
  }
78
+ /* Firefox gets themed scrollbars via scrollbar-width/-color above; Chrome,
79
+ Safari, and Edge need the ::-webkit-scrollbar family too, or every
80
+ scrollable node in the app shell falls back to the raw OS scrollbar and
81
+ clashes with the dark UI. Same selector scope, same --bg-3 token. */
82
+ .app::-webkit-scrollbar, .ds-stage::-webkit-scrollbar,
83
+ .app *::-webkit-scrollbar, .ds-stage *::-webkit-scrollbar {
84
+ width: 8px;
85
+ height: 8px;
86
+ }
87
+ .app::-webkit-scrollbar-track, .ds-stage::-webkit-scrollbar-track,
88
+ .app *::-webkit-scrollbar-track, .ds-stage *::-webkit-scrollbar-track {
89
+ background: transparent;
90
+ }
91
+ .app::-webkit-scrollbar-thumb, .ds-stage::-webkit-scrollbar-thumb,
92
+ .app *::-webkit-scrollbar-thumb, .ds-stage *::-webkit-scrollbar-thumb {
93
+ background: var(--bg-3);
94
+ border-radius: var(--r-pill, 999px);
95
+ }
78
96
 
79
97
  h1, .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; }
80
98
  h2, .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; }
@@ -41,6 +41,12 @@
41
41
  .ds-demo-label { font-size: var(--fs-xs); font-weight: 500; margin: 0 0 var(--space-1); }
42
42
  .ds-demo-desc { font-size: var(--fs-tiny); color: var(--fg-3); margin: 0 0 var(--space-2); }
43
43
 
44
+ /* Per-preview backlink to the full token map/doc — names which token
45
+ families the specimen above it demonstrates, then links out to the
46
+ generated theme-map for the resolved values. */
47
+ .tokens-backlink { font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--fg-3); margin-top: 2px; }
48
+ .tokens-backlink a { color: inherit; }
49
+
44
50
  @media (max-width: 768px) {
45
51
  .ds-catalog { flex-direction: column; }
46
52
  .ds-catalog-side { width: 100%; height: auto; position: static; border-right: 0; border-bottom: var(--bw-hair) solid var(--rule); }
@@ -137,11 +143,23 @@
137
143
  font-family: var(--ff-mono); font-size: var(--fs-xs);
138
144
  display: inline-flex; align-items: center; gap: 6px;
139
145
  }
140
- /* CSS-drawn half-disc mark on the compact theme toggle: keeps the control
141
- legible when its text label is hidden (icon-only rail strip). */
146
+ /* CSS-drawn disc mark on the compact theme toggle: keeps the control
147
+ legible when its text label is hidden (icon-only rail strip). Per-mode
148
+ fill gives the icon itself a distinct look for auto/light/dark, so state
149
+ reads without relying on the title tooltip alone. */
142
150
  .ds-theme-disc {
143
151
  flex: 0 0 auto; width: 14px; height: 14px; border-radius: 50%;
144
152
  border: var(--bw-hair) solid var(--fg-3);
153
+ /* auto: half-and-half, matches default (no data-mode) for safety */
154
+ background: linear-gradient(90deg, var(--fg-3) 0 50%, transparent 50% 100%);
155
+ }
156
+ .ds-theme-disc[data-mode="auto"] {
145
157
  background: linear-gradient(90deg, var(--fg-3) 0 50%, transparent 50% 100%);
146
158
  }
159
+ .ds-theme-disc[data-mode="paper"] {
160
+ background: var(--bg); border-color: var(--fg-3);
161
+ }
162
+ .ds-theme-disc[data-mode="ink"] {
163
+ background: var(--fg-3); border-color: var(--fg-3);
164
+ }
147
165
 
@@ -69,6 +69,18 @@
69
69
  background: color-mix(in oklab, var(--mascot) 26%, var(--bg-2));
70
70
  color: var(--mascot-deep);
71
71
  }
72
+ /* Plain assistant turns ('them', no aicat persona) previously fell through to
73
+ the bare .chat-avatar gray disc with no role identity at all — 'you' and
74
+ 'aicat' both got a tinted background + ring, the default assistant got
75
+ nothing. Give it the same tinted-background treatment via --color-success
76
+ (the conventional assistant/bot association, distinct from the accent hue
77
+ 'you' owns and the mascot hue 'aicat' owns) plus a thin identity ring so
78
+ role is readable from the avatar alone, not just bubble position/color. */
79
+ .chat-msg.them:not(.aicat) .chat-avatar {
80
+ background: color-mix(in oklab, var(--color-success) 20%, var(--bg-3));
81
+ color: color-mix(in oklab, var(--color-success) 65%, var(--fg));
82
+ box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--color-success) 35%, transparent);
83
+ }
72
84
 
73
85
  .chat-stack {
74
86
  display: flex; flex-direction: column; gap: var(--space-1);
@@ -79,6 +79,11 @@
79
79
  border-radius: var(--r-pill);
80
80
  background: var(--bg-2);
81
81
  }
82
+ /* .ds-collab-chip-avatar (24px identity circle) and .ds-collab-chip-status
83
+ (8px status dot) below are both circular via border-radius: 50% but are
84
+ intentionally distinct: different sizes, different content (initial vs.
85
+ plain fill), different positioning role (layout box vs. absolute badge).
86
+ Not extracting a shared class -- there's nothing substantive to share. */
82
87
  .ds-collab-chip-avatar {
83
88
  position: relative;
84
89
  width: 24px;
@@ -14,6 +14,10 @@
14
14
 
15
15
  /* PhaseWalk */
16
16
  .ds-phasewalk { display: inline-flex; gap: var(--space-hair); vertical-align: middle; }
17
+ /* NOTE: a second toggle-switch-shaped implementation exists in
18
+ plugins-config.css:91 (.ds-plugins-toggle) at a different scale (40x22 vs
19
+ this 18x10) — consider consolidating into a shared .ds-switch primitive
20
+ with a size modifier. */
17
21
  .ds-phasewalk-seg {
18
22
  width: 18px; height: 10px; border-radius: var(--r-hair);
19
23
  background: var(--bg-3); position: relative;
@@ -263,6 +263,12 @@
263
263
  display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
264
264
  gap: var(--space-3); align-items: start;
265
265
  }
266
+ /* Mobile fallback: auto-fill can still pack 2 cramped columns down to ~320px
267
+ viewports; force a single column below the shared mobile breakpoint
268
+ (docs/responsive.md) so thumbnails and file rails always read as a list. */
269
+ @media (max-width: 480px) {
270
+ .ds-file-grid-thumb { grid-template-columns: 1fr; }
271
+ }
266
272
  .ds-file-cell {
267
273
  position: relative;
268
274
  background: var(--bg); border: var(--bw-hair) solid var(--rule); border-radius: var(--r-2);
@@ -729,7 +735,7 @@
729
735
  .ds-preview-code::-webkit-scrollbar,
730
736
  .ds-preview-text::-webkit-scrollbar,
731
737
  .ds-preview-body::-webkit-scrollbar,
732
- .ds-modal-body::-webkit-scrollbar { width: 10px; height: 10px; }
738
+ .ds-modal-body::-webkit-scrollbar { width: 8px; height: 8px; }
733
739
  .ds-preview-code::-webkit-scrollbar-thumb,
734
740
  .ds-preview-text::-webkit-scrollbar-thumb {
735
741
  background: color-mix(in oklab, var(--paper) 28%, transparent);
@@ -88,6 +88,18 @@
88
88
  display: flex; gap: var(--space-2, 10px); flex-wrap: wrap;
89
89
  justify-content: flex-start; margin-top: var(--space-hair, 4px);
90
90
  }
91
+ /* Hero CTA labels are content-driven (e.g. "GitHub", proper nouns from
92
+ home.yaml) and the site's voice is deliberately all-lowercase everywhere
93
+ else. Force the rendered case here rather than hand-lowercasing every
94
+ proper-noun label at the content layer, so a future CTA label doesn't
95
+ silently reintroduce Title Case. */
96
+ .ds-hero-actions .btn { text-transform: lowercase; }
97
+ /* Filter input above the homepage's "ui kits" panel — reuses the same
98
+ .input token the search kit's own topbar query box uses, just placed
99
+ inline above the panel instead of in a topbar slot. */
100
+ .ds-kits-panel-wrap { display: flex; flex-direction: column; gap: var(--space-2, 10px); }
101
+ .ds-kits-filter { display: flex; }
102
+ .ds-kits-filter-input { width: 100%; max-width: 32ch; }
91
103
  /* Below this width the aside card's own internal rhythm (stat rows still
92
104
  stacked vertically at full width) is already correct for a narrow column —
93
105
  no separate breakpoint override needed now that .ds-hero is always a
@@ -167,6 +179,7 @@
167
179
  white-space: nowrap; will-change: transform;
168
180
  animation: ds-marquee-run 28s linear infinite;
169
181
  }
182
+ .ds-marquee-run { display: inline-flex; align-items: center; gap: var(--space-5); }
170
183
  .ds-marquee-item { display: inline-flex; align-items: center; gap: var(--space-5); }
171
184
  .ds-marquee-sep { color: var(--accent-ink); }
172
185
  .ds-marquee:hover .ds-marquee-track,
@@ -487,7 +500,7 @@ table tr.clickable:focus-visible td { background: var(--bg-2); }
487
500
  Form
488
501
  ============================================================ */
489
502
  .row-form {
490
- display: grid; gap: 12px;
503
+ display: grid; gap: var(--space-2-75);
491
504
  padding: var(--space-5);
492
505
  background: var(--bg-2);
493
506
  border-radius: var(--r-3);
@@ -451,6 +451,11 @@
451
451
  .ds-btn-row-tight { gap: var(--space-1-75, 6px); }
452
452
  .ds-key-input { flex: 1 1 160px; min-width: 0; font-family: var(--ff-mono); }
453
453
  .ds-toggle-btn { min-width: 78px; }
454
+ /* Side-by-side checked/unchecked reference pair (settings kit theme panel) --
455
+ documents the visual difference between states in one static, non-live
456
+ spot rather than requiring a viewer to toggle a real control back and
457
+ forth to compare. */
458
+ .ds-toggle-state-sample { display: flex; align-items: center; gap: var(--space-1-5, 6px); }
454
459
  .ds-toggle-label { margin-left: var(--space-2, 8px); color: inherit; opacity: 0.7; }
455
460
  .ds-btn-warn { color: var(--warn); }
456
461
  /* Raw --mascot text measures 3.11:1 on paper (fails 4.5:1); --mascot-deep is
@@ -593,6 +598,14 @@
593
598
  display: flex; flex-direction: column; gap: 0;
594
599
  background: var(--ink); border-radius: var(--r-1);
595
600
  font-family: var(--ff-mono); font-size: var(--fs-micro); line-height: 1.65;
601
+ /* Responsive strategy for long logs: cap the scrollback height and scroll
602
+ vertically rather than growing the page indefinitely; long command/output
603
+ lines wrap (see .cli .cmd's overflow-wrap: anywhere below) instead of
604
+ scrolling horizontally, so wrapping is the chosen default over
605
+ horizontal scroll for this kit. */
606
+ max-height: 60vh;
607
+ overflow-y: auto;
608
+ overflow-wrap: anywhere;
596
609
  }
597
610
  /* .cli is authored in hero-content.css as a STANDALONE hero code block --
598
611
  20px/24px padding, --fs-lg type, --r-3 radius and its own --ink background
@@ -2,15 +2,16 @@
2
2
  /* ============================================================
3
3
  Spinner — animated loading indicator
4
4
  ============================================================ */
5
+ /* Dots spinner — use for small inline/compact loading states. */
5
6
  .ds-spinner {
6
7
  display: inline-flex;
7
- gap: 4px;
8
+ gap: var(--space-1);
8
9
  align-items: center;
9
10
  height: 16px;
10
11
  }
11
12
  .ds-spinner span {
12
- width: 4px;
13
- height: 4px;
13
+ width: var(--space-1);
14
+ height: var(--space-1);
14
15
  border-radius: 50%;
15
16
  background: var(--accent);
16
17
  }
@@ -21,17 +22,17 @@
21
22
  .ds-spinner span:nth-child(3) { animation-delay: 0.36s; }
22
23
  }
23
24
 
24
- .ds-spinner-lg { height: 24px; gap: 6px; }
25
- .ds-spinner-lg span { width: 6px; height: 6px; }
25
+ .ds-spinner-lg { height: 24px; gap: var(--space-1-75); }
26
+ .ds-spinner-lg span { width: var(--space-1-75); height: var(--space-1-75); }
26
27
 
27
- .ds-spinner-sm { height: 12px; gap: 2px; }
28
- .ds-spinner-sm span { width: 2px; height: 2px; }
28
+ .ds-spinner-sm { height: 12px; gap: var(--space-hair); }
29
+ .ds-spinner-sm span { width: var(--space-hair); height: var(--space-hair); }
29
30
 
30
31
  .ds-spinner-xs { height: 8px; gap: 1px; }
31
32
  .ds-spinner-xs span { width: 1.5px; height: 1.5px; }
32
33
 
33
- .ds-spinner-xl { height: 32px; gap: 8px; }
34
- .ds-spinner-xl span { width: 8px; height: 8px; }
34
+ .ds-spinner-xl { height: 32px; gap: var(--space-2); }
35
+ .ds-spinner-xl span { width: var(--space-2); height: var(--space-2); }
35
36
 
36
37
  @keyframes ds-bounce {
37
38
  0%, 80%, 100% { transform: translateY(0); }
@@ -81,7 +82,7 @@
81
82
  flex-shrink: 0;
82
83
  color: var(--alert-tone);
83
84
  font-weight: 600;
84
- min-width: 20px;
85
+ min-width: var(--space-3-5);
85
86
  text-align: center;
86
87
  }
87
88
 
@@ -110,11 +111,12 @@
110
111
  cursor: pointer;
111
112
  color: var(--fg-3);
112
113
  padding: 0;
113
- font-size: 14px;
114
+ font-size: var(--fs-xs);
114
115
  line-height: 1;
115
- min-width: 20px;
116
+ min-width: var(--space-3-5);
116
117
  text-align: center;
117
118
  transition: color var(--dur-snap) var(--ease);
118
119
  }
119
120
  .ds-alert-dismiss:hover { color: var(--fg); }
121
+ .ds-alert-dismiss:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
120
122
 
@@ -17,11 +17,11 @@
17
17
  .ds-otp-box:focus-visible {
18
18
  outline: none;
19
19
  border-color: var(--accent);
20
- box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 40%, transparent);
20
+ box-shadow: var(--focus-ring-inset);
21
21
  }
22
22
  .ds-otp-box-filled { border-color: var(--rule-strong); background: var(--bg-3); }
23
23
  .ds-otp-box-error { border-color: var(--red); }
24
- .ds-otp-box-error:focus-visible { box-shadow: 0 0 0 3px color-mix(in oklab, var(--red) 40%, transparent); }
24
+ .ds-otp-box-error:focus-visible { box-shadow: var(--focus-ring-inset); }
25
25
  .ds-otp-box:disabled { opacity: 0.5; cursor: default; }
26
26
 
27
27
  @media (prefers-reduced-motion: reduce) {
@@ -73,6 +73,13 @@
73
73
  margin: 0; font: inherit; font-size: inherit; font-weight: inherit;
74
74
  letter-spacing: inherit; text-transform: inherit; color: inherit;
75
75
  }
76
+ /* Category glyph beside a panel title (kits / previews / decks / docs on the
77
+ homepage) — same inline-svg vocabulary as icons.js, sized to sit on the
78
+ text baseline rather than dominating it. */
79
+ .ds-panel-title-glyph {
80
+ display: inline-flex; align-items: center; gap: var(--space-1-5, 6px);
81
+ }
82
+ .ds-panel-title-glyph .ds-icon { flex-shrink: 0; color: var(--fg-3); }
76
83
  .panel-body { padding: var(--space-2) var(--space-3) var(--space-3); }
77
84
  .panel-body > * { margin: 0; }
78
85
  /* One-line explanatory caption under a panel's title — e.g. distinguishing
@@ -113,6 +120,14 @@
113
120
  transition: background var(--dur-snap) var(--ease), box-shadow var(--dur-base) var(--ease-spring);
114
121
  }
115
122
  .row + .row { margin-top: 2px; }
123
+ /* Mobile tap-target floor: at compact/comfortable density on a touch
124
+ viewport the padding-only row height can fall under the 44px effective
125
+ tap target (WCAG 2.5.5), especially at [data-density="compact"] (0.75x).
126
+ Force a min-height on narrow/coarse-pointer viewports only, so the dense
127
+ desktop mono/list layout (file-row, index-row) is untouched above 640px. */
128
+ @media (max-width: 640px), (pointer: coarse) {
129
+ .row, a.row, button.row, .row[role="button"] { min-height: 44px; }
130
+ }
116
131
  /* Interactive rows (anchor/button wrappers) get a hairline lift on hover,
117
132
  matching the btn/panel elevation language — non-interactive rows (plain
118
133
  divs used as static list items) are unaffected since the selector needs a
@@ -87,6 +87,9 @@
87
87
  }
88
88
  .ds-plugins-version { font-size: var(--fs-micro); color: var(--fg-3); }
89
89
 
90
+ /* NOTE: a second toggle-switch implementation exists in data-density.css:18
91
+ (.ds-phasewalk-seg) at a different scale (18x10 vs this 40x22) — consider
92
+ consolidating into a shared .ds-switch primitive with a size modifier. */
90
93
  .ds-plugins-toggle {
91
94
  flex: 0 0 auto; width: 40px; height: 22px; border-radius: 11px; border: none; padding: 0;
92
95
  background: var(--rule); position: relative; cursor: pointer;
@@ -22,7 +22,7 @@
22
22
  }
23
23
  .btn-primary:hover { background: var(--fg); color: var(--green); transform: translateY(-1px); box-shadow: var(--shadow-2); }
24
24
  .btn-ghost { background: transparent; color: var(--fg); box-shadow: inset 0 0 0 2px var(--fg); }
25
- .btn-ghost:hover { background: var(--fg); color: var(--bg); transform: translateY(-1px); }
25
+ .btn-ghost:hover { background: color-mix(in oklab, var(--fg) 12%, transparent); transform: translateY(-1px); }
26
26
  .btn:active, .btn-primary:active, .btn-ghost:active { transform: translateY(1px); box-shadow: none; }
27
27
  .btn:focus-visible, .btn-primary:focus-visible, .btn-ghost:focus-visible, .ds-icon-btn:focus-visible {
28
28
  outline: var(--focus-w) solid var(--focus-color);
@@ -54,7 +54,9 @@
54
54
  border-radius: var(--r-1, 6px);
55
55
  background: transparent; color: var(--fg);
56
56
  width: 32px; height: 32px;
57
+ transition: background var(--dur-snap) var(--ease), color var(--dur-snap) var(--ease);
57
58
  }
59
+ .ds-icon-btn:hover { background: var(--bg-2); }
58
60
  .ds-icon-btn-xs { width: 22px; height: 22px; }
59
61
  .ds-icon-btn-sm { width: 26px; height: 26px; }
60
62
  .ds-icon-btn-base { width: 32px; height: 32px; }
@@ -64,7 +66,10 @@
64
66
  .ds-icon-btn-ghost:hover { background: var(--fg); color: var(--bg); }
65
67
  .ds-icon-btn-primary { background: var(--accent); color: var(--accent-fg); }
66
68
  .ds-icon-btn-primary:hover { background: var(--fg); color: var(--accent-ink); }
67
- .ds-icon-btn-danger { background: var(--warn); color: var(--on-color); }
69
+ /* --ink, not --on-color: --on-color is a fixed #fff, which measures 3.07:1 on
70
+ --warn (#FF5A52) -- below the 4.5:1 AA floor. --ink measures 6.27:1. Same
71
+ fix as .btn-primary.danger in app-shell/files.css. */
72
+ .ds-icon-btn-danger { background: var(--warn); color: var(--ink); }
68
73
  .ds-icon-btn-danger:hover { filter: brightness(0.92); }
69
74
  .ds-icon-btn:active { transform: translateY(1px); }
70
75
  .ds-icon-btn:disabled, .ds-icon-btn.is-disabled {
@@ -78,7 +83,7 @@
78
83
  border-radius: var(--r-pill);
79
84
  background: var(--bg-3); color: var(--fg-2);
80
85
  }
81
- .ds-badge.ds-badge--sm { min-width: 14px; height: 14px; padding: 0 var(--space-1); font-size: 10px; }
86
+ .ds-badge.ds-badge--sm { min-width: 14px; height: 14px; padding: 0 var(--space-1); font-size: var(--fs-pico); }
82
87
  .ds-badge.ds-badge--lg { min-width: 22px; height: 22px; padding: 0 var(--space-2); font-size: var(--fs-tiny); }
83
88
  .ds-badge.tone-green, .ds-badge.tone-live, .ds-badge.tone-success {
84
89
  background: var(--green-tint); color: var(--green-deep);
@@ -95,7 +100,7 @@
95
100
  background: var(--sun); color: var(--ink);
96
101
  }
97
102
  .ds-badge.tone-blue { background: var(--accent-tint); color: var(--accent-ink); }
98
- .ds-badge.tone-orange { background: color-mix(in oklab, var(--flame) 30%, var(--sun) 70%); color: var(--ink); }
103
+ .ds-badge.tone-orange { background: color-mix(in oklab, var(--flame) 22%, var(--bg)); color: color-mix(in oklab, var(--flame) 65%, black); }
99
104
 
100
105
  .chip {
101
106
  display: inline-flex; align-items: center; gap: var(--space-1-75);
@@ -163,6 +168,10 @@
163
168
  .ds-pill.tone-accent { background: var(--accent-tint); color: var(--accent-ink); }
164
169
  .ds-pill.tone-muted { background: transparent; color: var(--fg-3); box-shadow: inset 0 0 0 1px var(--rule); }
165
170
 
171
+ /* .glyph-* sizes (14/18/22px) are for small inline text-adjacent glyphs
172
+ (single characters/icons sitting in a text flow), a different scale and
173
+ purpose than the .ds-icon-btn-* tap-target scale (22/26/32/40/48px) below --
174
+ the -sm/-lg naming overlap is coincidental, not a shared system. */
166
175
  .glyph {
167
176
  display: inline-flex; align-items: center; justify-content: center;
168
177
  width: 18px; height: 18px;
@@ -3,6 +3,19 @@
3
3
  Responsive Design — Mobile (480px), Tablet (1024px), Desktop (1440px+)
4
4
  ============================================================ */
5
5
 
6
+ /* Breakpoint reference (CSS custom properties can't be used in @media queries,
7
+ so these are documented here for consistency — always use these exact values):
8
+ - Mobile/tablet boundary: 768px (max-width) — used for compact nav/sidebar
9
+ collapse (catalog-theme.css, sidebar-misc.css). Note: kits-appended.css
10
+ uses 760px for the same kind of collapse in a few places — a near-miss
11
+ of this value, worth aligning to 768px when touched.
12
+ - Tablet/desktop boundary: 900px (max-width) / 901px (min-width) — used for
13
+ main layout reflow (hero-content.css, responsive2-workspace.css)
14
+ - Legacy tablet band: 481px–1024px (min-width 481px / max-width 1025px) —
15
+ used in this file (responsive.css) for a specific tablet treatment,
16
+ consider migrating to the 768/900 pair
17
+ */
18
+
6
19
  /* --------------------------------------------------------------------
7
20
  Mobile Portrait Breakpoint (480px and below)
8
21
  ---------------------------------------------------------------------- */
@@ -281,7 +281,9 @@
281
281
  .ws-rail-item:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: -2px; }
282
282
  .ws-rail-item.active { background: var(--accent-tint); color: var(--fg); box-shadow: inset 2px 0 0 var(--accent); }
283
283
  .ws-rail-item-label { flex: 1; overflow: hidden; text-overflow: ellipsis; }
284
- .ws-rail-item-count { font-family: var(--ff-mono); font-size: var(--fs-tiny); color: var(--fg-3); }
284
+ /* Shape/color come from the shared .ds-badge component (see app-shell/primitives.css);
285
+ this class is now just the rail-item layout hook (spacing, collapsed-state targeting). */
286
+ .ws-rail-item-count { flex: none; margin-inline-start: var(--space-1); }
285
287
  .ws-rail-item-flag { width: 6px; height: 6px; border-radius: 50%; flex: none; }
286
288
  .ws-rail-item-flag.tone-flame { background: var(--flame); box-shadow: 0 0 0 2px color-mix(in oklab, var(--flame) 38%, transparent); }
287
289
  .ws-rail-foot { margin-top: auto; padding-top: var(--space-2); border-top: var(--bw-hair) solid var(--bg-3); }
@@ -324,6 +326,7 @@
324
326
  because the chat thread carries its own --measure gutter. */
325
327
  .ws-main { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; padding: var(--space-4) var(--space-5); }
326
328
  .ws-main.ws-main--flush { padding: 0; }
329
+ /* Non-interactive scroll region (content column), not a focusable control — outline intentionally suppressed. */
327
330
  .ws-main:focus { outline: none; }
328
331
 
329
332
  /* Right context pane */
@@ -71,5 +71,39 @@
71
71
  .ds-live-log { height: auto; max-height: none; overflow: visible; }
72
72
  .ds-stat, .kpi-card { border: 1px solid var(--rule); }
73
73
  .ds-session-row { break-inside: avoid; }
74
+
75
+ /* Receipt table (Receipt() in src/components/content/panel.js, `.kv`) — the
76
+ project_page kit's "receipt" section, and any other kv-table consumer.
77
+ On screen it inherits panel/token colors that can render as near-white
78
+ text on a printer's assumed-white page background if the browser's
79
+ "background graphics" print option is off (the common default); force
80
+ the LIGHT-theme ink/paper tokens (real print-page colors, not a raw
81
+ literal — --ink/--paper are the same "true black text on true white
82
+ page" values the light theme already defines) and visible cell borders
83
+ so it stays legible without relying on background-graphics printing at
84
+ all. break-inside:avoid keeps a receipt from splitting a row across a
85
+ page boundary. */
86
+ .kv { border-collapse: collapse; width: 100%; color: var(--ink); background: var(--paper); }
87
+ .kv tr { break-inside: avoid; }
88
+ .kv td { border: 1px solid var(--paper-3); padding: 4px 8px; color: var(--ink); }
89
+
90
+ /* Any button/interactive-only affordance still present inside otherwise-
91
+ printable content (copy buttons, expand/collapse toggles, filter pills,
92
+ the receipt/doc section's own action row) is chrome, not content. */
93
+ .btn, .btn-primary, .btn-ghost, .row-act, .ds-filter-pills,
94
+ .app-search, input[type="search"] { display: none; }
95
+
96
+ /* Force real ink-on-paper for body copy: panel/fg tokens are tuned for
97
+ screen surfaces (including dark theme, where --panel-text can resolve
98
+ near-white) and a printer defaults to a white page regardless of
99
+ data-theme, so an unconverted dark-theme page would print near-invisible
100
+ text without this override. Same --ink/--paper light-theme pair as the
101
+ receipt table above, not a second raw literal. */
102
+ /* No !important: source order already wins here (this rule is the last
103
+ word inside its own @media print block, same as every other override in
104
+ this file), and an unconditional !important on `body` would beat even a
105
+ future print-specific consumer override with higher specificity. */
106
+ html body, .app-main, .page-body, .ds-lede { color: var(--ink); background: var(--paper); }
107
+ a { color: var(--ink); text-decoration: underline; }
74
108
  }
75
109