anentrypoint-design 1.0.19 → 1.0.21

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anentrypoint-design",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "description": "247420 design system SDK — webjsx + modified ripple-ui, single-file ESM bundle for reproducible use of the AnEntrypoint design.",
5
5
  "type": "module",
6
6
  "main": "./dist/247420.js",
@@ -39,9 +39,14 @@ export function AgentEmptyState({ name, selectedAgent, suggestions, onSuggestion
39
39
  'aria-label': 'copy install command for ' + c.agent, title: 'copy command',
40
40
  onclick: (e) => {
41
41
  const btn = e.currentTarget;
42
- navigator.clipboard && navigator.clipboard.writeText(c.command);
43
- btn.textContent = 'copied';
44
- setTimeout(() => { btn.textContent = 'copy'; }, 1200);
42
+ const done = () => { btn.textContent = 'copied'; setTimeout(() => { btn.textContent = 'copy'; }, 1200); };
43
+ // Falls back whenever the async Clipboard API is
44
+ // absent OR its promise rejects (permission denied,
45
+ // an unfocused document) instead of optimistically
46
+ // claiming "copied" before the write is confirmed.
47
+ const fallback = () => { try { const t = document.createElement('textarea'); t.value = c.command; document.body.appendChild(t); t.select(); document.execCommand('copy'); document.body.removeChild(t); done(); } catch { /* swallow: no copy mechanism available */ } };
48
+ if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(c.command).then(done, fallback);
49
+ else fallback();
45
50
  },
46
51
  }, 'copy'))))
47
52
  : null,
@@ -56,14 +56,27 @@ export function fileIconName(name) {
56
56
  // this extraction. Same behavior in every caller: try the async Clipboard
57
57
  // API, fall back to a hidden textarea + execCommand('copy') when unavailable,
58
58
  // flip the trigger button's own label/class to "copied" for ~1.6s either way.
59
+ function execCommandCopy(text) {
60
+ const t = document.createElement('textarea');
61
+ t.value = text; document.body.appendChild(t); t.select();
62
+ document.execCommand('copy'); document.body.removeChild(t);
63
+ }
59
64
  export function copyToClipboardWithFeedback(text, btn) {
60
65
  const done = () => {
61
66
  btn.textContent = 'copied';
62
67
  btn.classList.add('is-copied');
63
68
  setTimeout(() => { btn.textContent = 'copy'; btn.classList.remove('is-copied'); }, 1600);
64
69
  };
65
- if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(text).then(done).catch(() => {});
66
- else { try { const t = document.createElement('textarea'); t.value = text; document.body.appendChild(t); t.select(); document.execCommand('copy'); document.body.removeChild(t); done(); } catch { /* swallow: legacy execCommand copy fallback unsupported, nothing more to try */ } }
70
+ // Falls back to execCommand whenever the async Clipboard API is either
71
+ // absent OR its promise rejects (permission denied, an unfocused
72
+ // document -- a real failure mode, not just an old-browser one). The
73
+ // prior version only fell back when navigator.clipboard didn't exist at
74
+ // all, so a live rejection silently did nothing -- no feedback, no copy.
75
+ if (navigator.clipboard && navigator.clipboard.writeText) {
76
+ navigator.clipboard.writeText(text).then(done, () => { try { execCommandCopy(text); done(); } catch { /* swallow: no copy mechanism available */ } });
77
+ } else {
78
+ try { execCommandCopy(text); done(); } catch { /* swallow: no copy mechanism available */ }
79
+ }
67
80
  }
68
81
 
69
82
  // Inject a per-block copy button into every <pre> inside a rendered-markdown
@@ -31,7 +31,7 @@ export function WorksList({ works = [], openedIndex = -1, onToggle }) {
31
31
  h('p', { class: 'ds-work-body' }, w.body)
32
32
  ),
33
33
  h('div', { class: 'ds-work-actions' },
34
- Btn({ variant: 'primary', href: w.href || '#', children: 'open ->' }),
34
+ Btn({ variant: 'primary', href: w.href || '#', 'aria-label': 'open ' + (w.title || 'project'), children: 'open ->' }),
35
35
  Btn({ href: w.source || '#', children: 'source' })
36
36
  )
37
37
  ) : null
@@ -90,11 +90,12 @@ function jsonCopyButton(text) {
90
90
  btn.classList.toggle('copied', ok);
91
91
  setTimeout(() => { btn.textContent = 'copy'; btn.classList.remove('copied'); }, 1200);
92
92
  };
93
- try {
94
- navigator.clipboard.writeText(text).then(() => show('copied', true), () => show('failed', false));
95
- } catch {
96
- show('failed', false);
97
- }
93
+ const fallback = () => {
94
+ try { const t = document.createElement('textarea'); t.value = text; document.body.appendChild(t); t.select(); document.execCommand('copy'); document.body.removeChild(t); show('copied', true); }
95
+ catch { show('failed', false); }
96
+ };
97
+ if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(text).then(() => show('copied', true), fallback);
98
+ else fallback();
98
99
  },
99
100
  }, 'copy');
100
101
  }
@@ -61,8 +61,12 @@ export function FilePreviewCode({ content = '', lang, filename, wrap, onWrapTogg
61
61
  const onCopy = (e) => {
62
62
  const btn = e.currentTarget;
63
63
  const done = () => { btn.textContent = 'copied'; btn.classList.add('is-copied'); setTimeout(() => { btn.textContent = 'copy'; btn.classList.remove('is-copied'); }, 1600); };
64
- if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(content).then(done).catch(() => {});
65
- else { try { const t = document.createElement('textarea'); t.value = content; document.body.appendChild(t); t.select(); document.execCommand('copy'); document.body.removeChild(t); done(); } catch { /* swallow: legacy execCommand copy fallback unsupported, nothing more to try */ } }
64
+ const fallback = () => { try { const t = document.createElement('textarea'); t.value = content; document.body.appendChild(t); t.select(); document.execCommand('copy'); document.body.removeChild(t); done(); } catch { /* swallow: no copy mechanism available */ } };
65
+ // Falls back whenever the async Clipboard API is absent OR its
66
+ // promise rejects (permission denied, an unfocused document) --
67
+ // was previously only reached when navigator.clipboard didn't exist.
68
+ if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(content).then(done, fallback);
69
+ else fallback();
66
70
  };
67
71
  const hasPreview = previewHtml != null && onModeChange;
68
72
  const activeMode = hasPreview ? (mode || 'source') : 'source';
@@ -43,6 +43,12 @@ export function Topbar({ brand = '247420', leaf = '', items = [], active = '', o
43
43
  href,
44
44
  class: active === cleanLabel ? 'active' : '',
45
45
  'aria-current': active === cleanLabel ? 'page' : null,
46
+ // The visible label keeps its ' ->' ASCII arrow (sanctioned
47
+ // per AGENTS.md's icon policy), but a screen reader spelling
48
+ // that out literally ("source dash greater-than") is noise --
49
+ // aria-label gives the same cleaned text the active-state
50
+ // comparison already computes, read once, without the arrow.
51
+ 'aria-label': cleanLabel !== String(label) ? cleanLabel : null,
46
52
  onclick: (e) => {
47
53
  if (!String(href).startsWith('http') && onNav) {
48
54
  e.preventDefault();
@@ -65,7 +65,7 @@ export function Btn({ href, variant = 'default', size = 'md', children, onClick,
65
65
  // modifier that snaps height/padding/font to the --ctl-* ladder. Unknown
66
66
  // sizes fall back to md so a typo never drops the button's base styling.
67
67
  const sizeCls = size === 'sm' ? ' btn-sm' : (size === 'lg' ? ' btn-lg' : '');
68
- const cls = (resolvedVariant === 'primary' ? 'btn-primary' : (resolvedVariant === 'ghost' ? 'btn-ghost' : (resolvedVariant === 'danger' ? 'btn-primary danger' : 'btn')))
68
+ const cls = (resolvedVariant === 'primary' ? 'btn-primary' : (resolvedVariant === 'ghost' ? 'btn-ghost' : (resolvedVariant === 'danger' ? 'btn-primary danger' : (resolvedVariant === 'link' ? 'btn-link' : 'btn'))))
69
69
  + sizeCls
70
70
  + (disabled ? ' is-disabled' : '')
71
71
  + (className ? ' ' + className : '');
@@ -109,14 +109,14 @@ body {
109
109
  border-radius: var(--r-pill, 999px);
110
110
  }
111
111
 
112
- 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; }
112
+ 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: var(--fw-semibold); margin: 0; }
113
113
  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; }
114
114
  h3, .t-h3 { font-family: var(--ff-display); font-size: var(--fs-h3); line-height: var(--lh-snug); letter-spacing: -0.01em; font-weight: 600; margin: 0; }
115
115
  h4, .t-h4 { font-family: var(--ff-display); font-size: var(--fs-h4); line-height: var(--lh-snug); font-weight: 600; margin: 0; }
116
116
 
117
- [data-typescale="app"] h1, [data-typescale="app"] .t-h1 { font-size: var(--fs-h1-app); line-height: 1.15; }
118
- [data-typescale="app"] h2, [data-typescale="app"] .t-h2 { font-size: var(--fs-h2-app); }
119
- [data-typescale="app"] h3, [data-typescale="app"] .t-h3 { font-size: var(--fs-h3-app); }
117
+ [data-typescale="app"] h1, [data-typescale="app"] .t-h1 { font-size: var(--fs-h1-app); line-height: 1.15; font-weight: var(--fw-medium); }
118
+ [data-typescale="app"] h2, [data-typescale="app"] .t-h2 { font-size: var(--fs-h2-app); font-weight: var(--fw-medium); }
119
+ [data-typescale="app"] h3, [data-typescale="app"] .t-h3 { font-size: var(--fs-h3-app); font-weight: var(--fw-medium); }
120
120
  [data-typescale="app"] h4, [data-typescale="app"] .t-h4 { font-size: var(--fs-h4-app); }
121
121
 
122
122
  .t-hero { font-family: var(--ff-display); font-size: var(--fs-hero); line-height: var(--lh-tight); letter-spacing: var(--tr-tighter); font-weight: 700; margin: 0; }
@@ -126,8 +126,8 @@ h4, .t-h4 { font-family: var(--ff-display); font-size: var(--fs-h4); line-height
126
126
  .t-mono { font-family: var(--ff-mono); font-weight: 400; }
127
127
  .t-body { font-family: var(--ff-body); }
128
128
 
129
- .t-label { font-family: var(--ff-mono); font-size: var(--fs-tiny); text-transform: uppercase; letter-spacing: var(--tr-label); font-weight: 500; }
130
- .t-micro { font-family: var(--ff-mono); font-size: var(--fs-micro); text-transform: uppercase; letter-spacing: var(--tr-label); color: var(--fg-3); }
129
+ .t-label { font-family: var(--ff-body); font-size: var(--fs-tiny); letter-spacing: 0; font-weight: 600; }
130
+ .t-micro { font-family: var(--ff-body); font-size: var(--fs-micro); letter-spacing: 0; color: var(--fg-3); font-weight: 600; }
131
131
  .t-meta { font-family: var(--ff-body); font-size: var(--fs-sm); color: var(--fg-3); }
132
132
 
133
133
  .lede, .t-lede {
@@ -50,8 +50,7 @@
50
50
  .ds-cal-weekday-label {
51
51
  font-size: var(--fs-micro);
52
52
  font-weight: 600;
53
- letter-spacing: var(--tr-caps);
54
- text-transform: uppercase;
53
+ letter-spacing: 0;
55
54
  color: var(--fg-3);
56
55
  text-align: center;
57
56
  padding-block: var(--space-1);
@@ -16,7 +16,7 @@
16
16
  .ds-catalog-side h1 { font-size: var(--fs-lg); margin: 0 0 var(--space-1); }
17
17
  .ds-catalog-side .subtitle { font-size: var(--fs-micro); color: var(--fg-3); margin: 0 0 var(--space-4); }
18
18
  .ds-catalog-side nav h4 {
19
- font-size: var(--fs-micro); text-transform: uppercase; letter-spacing: var(--tr-label);
19
+ font-size: var(--fs-micro); letter-spacing: var(--tr-tight); font-weight: 600;
20
20
  color: var(--fg-3); margin: var(--space-3) 0 var(--space-1);
21
21
  }
22
22
  .ds-catalog-side nav a {
@@ -55,7 +55,7 @@
55
55
  width: 36px; height: 36px; flex-shrink: 0;
56
56
  border-radius: 50%; background: var(--bg-3); color: var(--fg);
57
57
  display: inline-flex; align-items: center; justify-content: center;
58
- font-family: var(--ff-mono); font-size: var(--fs-micro); font-weight: 600;
58
+ font-family: var(--ff-body); font-size: var(--fs-micro); font-weight: 600;
59
59
  text-transform: lowercase; letter-spacing: 0.02em; user-select: none;
60
60
  transition: transform 0.12s ease, box-shadow 0.12s ease;
61
61
  position: relative;
@@ -355,7 +355,7 @@
355
355
  .chat-link .thumb,
356
356
  .chat-link img.thumb { width: 80px; height: 80px; border-radius: var(--r-0); object-fit: cover; background: var(--bg-3) center/cover no-repeat; }
357
357
  .chat-link .meta { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
358
- .chat-link .host { font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--fg-3); text-transform: lowercase; }
358
+ .chat-link .host { font-family: var(--ff-body); font-size: var(--fs-xs); color: var(--fg-3); text-transform: lowercase; }
359
359
  .chat-link .title { font-weight: 600; font-size: var(--fs-sm); line-height: 1.3; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
360
360
  .chat-link .desc { font-size: var(--fs-xs); color: var(--fg-2); line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
361
361
 
@@ -367,7 +367,7 @@
367
367
  .chat-meta .who { font-weight: 600; color: var(--fg-2); }
368
368
  .chat-meta .who::after { content: ' ·'; color: var(--fg-3); font-weight: 400; }
369
369
  .chat-meta .t { color: var(--fg-3); }
370
- .chat-meta .tick { color: var(--fg-3); margin-left: var(--space-hair); letter-spacing: -2px; font-family: var(--ff-mono); }
370
+ .chat-meta .tick { color: var(--fg-3); margin-left: var(--space-hair); letter-spacing: -2px; font-family: var(--ff-body); }
371
371
  .chat-meta .tick.read { color: var(--accent-ink); }
372
372
 
373
373
  .chat-reactions {
@@ -690,8 +690,8 @@
690
690
  .btn-primary.danger {
691
691
  background: var(--warn);
692
692
  border-color: var(--warn);
693
- color: var(--ink);
694
- box-shadow: inset 0 0 0 var(--bw-hair, 1px) color-mix(in oklab, var(--ink) 25%, transparent);
693
+ color: var(--warn-fg);
694
+ box-shadow: inset 0 0 0 var(--bw-hair, 1px) color-mix(in oklab, var(--warn-fg) 25%, transparent);
695
695
  font-weight: 700;
696
696
  letter-spacing: 0.01em;
697
697
  }
@@ -20,7 +20,7 @@
20
20
  .ds-git-status-chip {
21
21
  display: inline-flex; align-items: center; justify-content: center;
22
22
  flex: 0 0 auto; width: 18px; height: 18px;
23
- font-family: var(--ff-mono); font-size: var(--fs-micro); font-weight: 700;
23
+ font-family: var(--ff-body); font-size: var(--fs-micro); font-weight: 700;
24
24
  border-radius: var(--r-1);
25
25
  }
26
26
  .ds-git-status-chip.tone-add { color: var(--green-2); background: color-mix(in oklab, var(--green-2) 16%, transparent); }
@@ -38,7 +38,7 @@
38
38
  flex: 0 0 auto; font-size: var(--fs-micro); color: var(--fg-3);
39
39
  border: var(--bw-hair) solid var(--rule); border-radius: var(--r-1); padding: 1px 6px;
40
40
  }
41
- .ds-git-row-stats { flex: 0 0 auto; display: inline-flex; gap: 6px; font-family: var(--ff-mono); font-size: var(--fs-micro); }
41
+ .ds-git-row-stats { flex: 0 0 auto; display: inline-flex; gap: 6px; font-family: var(--ff-body); font-size: var(--fs-micro); }
42
42
  .ds-git-stat-add { color: var(--green-2); }
43
43
  .ds-git-stat-del { color: var(--flame); }
44
44
 
@@ -218,11 +218,15 @@
218
218
  border-radius: var(--r-3);
219
219
  font-family: var(--ff-mono); font-size: var(--fs-lg);
220
220
  }
221
- .cli .prompt { color: var(--green-2); }
221
+ /* user-select:none (matching .ds-cli-row .prompt below) so selecting/copying
222
+ a command line grabs only the command text, not the prompt mark itself --
223
+ otherwise every copy-pasted line carries a leading '$ ' that has to be
224
+ stripped by hand before it can be re-run. */
225
+ .cli .prompt { color: var(--green-2); user-select: none; }
222
226
  .cli .cmd { flex: 1; min-width: 0; overflow-x: auto; white-space: nowrap; color: var(--paper); }
223
227
  .cli .copy {
224
228
  padding: var(--space-2) var(--space-3); background: transparent; color: var(--paper);
225
- font-size: var(--fs-tiny); text-transform: uppercase; letter-spacing: var(--tr-caps);
229
+ font-size: var(--fs-tiny); letter-spacing: var(--tr-tight); font-weight: 500;
226
230
  cursor: pointer; box-shadow: inset 0 0 0 1px var(--rule-strong);
227
231
  border-radius: var(--r-pill);
228
232
  }
@@ -264,7 +268,7 @@
264
268
  .ds-quickstart-copy {
265
269
  align-self: flex-end;
266
270
  padding: var(--space-2) var(--space-3); background: transparent; color: var(--paper);
267
- font-family: var(--ff-mono); font-size: var(--fs-tiny); text-transform: uppercase; letter-spacing: var(--tr-caps);
271
+ font-family: var(--ff-body); font-size: var(--fs-tiny); letter-spacing: var(--tr-tight); font-weight: 500;
268
272
  cursor: pointer; box-shadow: inset 0 0 0 1px var(--rule-strong);
269
273
  border-radius: var(--r-pill); border: none;
270
274
  }
@@ -431,7 +435,11 @@ table td.is-num, table th.is-num { text-align: right; font-variant-numeric: tabu
431
435
  ============================================================ */
432
436
  .ds-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: currentColor; flex-shrink: 0; vertical-align: middle; }
433
437
  .ds-dot-on { color: var(--green-2); }
434
- .ds-dot-off { color: var(--fg-3); }
438
+ /* Off/idle reads as a HOLLOW ring (transparent fill, currentColor outline)
439
+ instead of a solid dot in a dimmer color -- filled-vs-outline is a shape
440
+ cue that survives grayscale/colorblind simulation, where a solid green dot
441
+ and a solid gray dot at the same size can look identical in luminance. */
442
+ .ds-dot-off { color: var(--fg-3); background: transparent; box-shadow: inset 0 0 0 1.5px currentColor; }
435
443
  /* live is a distinct hue from a plain "on" dot — see .chip.tone-live's
436
444
  comment for the success-vs-live rationale. */
437
445
  .ds-dot-live { color: var(--sky); }
@@ -496,7 +504,7 @@ table td.is-num, table th.is-num { text-align: right; font-variant-numeric: tabu
496
504
  .kpi-card .lbl {
497
505
  font-size: var(--fs-xs); color: var(--fg-3);
498
506
  font-weight: 600; margin-top: 8px;
499
- letter-spacing: var(--tr-caps); text-transform: uppercase;
507
+ letter-spacing: var(--tr-tight);
500
508
  }
501
509
  .kpi-foot {
502
510
  display: flex; align-items: center; justify-content: space-between;
@@ -487,8 +487,13 @@
487
487
  /* .ds-field-eyebrow joins the kit small-label voice above with no overrides. */
488
488
  .ds-hint-sm { font-size: var(--fs-xs); color: var(--panel-text-2); }
489
489
  .ds-note-quiet { margin: 0; color: var(--panel-text-2); }
490
- .ds-btn-row { display: flex; gap: var(--space-2, 8px); flex-wrap: wrap; }
490
+ .ds-btn-row { display: flex; gap: var(--space-2, 8px); flex-wrap: wrap; align-items: center; }
491
491
  .ds-btn-row-tight { gap: var(--space-1-75, 6px); }
492
+ /* Size-ladder demo group: each size's caption+button pair stacks and the
493
+ group aligns at the BOTTOM so buttons of different heights share one
494
+ visual baseline instead of centering on their own mismatched midpoints. */
495
+ .ds-btn-size-row { display: flex; flex-wrap: wrap; gap: var(--space-4); align-items: flex-end; }
496
+ .ds-btn-size-col { display: flex; flex-direction: column; gap: var(--space-1-75); align-items: flex-start; }
492
497
  .ds-key-input { flex: 1 1 160px; min-width: 0; font-family: var(--ff-mono); }
493
498
  .ds-toggle-btn { min-width: 78px; }
494
499
  /* Side-by-side checked/unchecked reference pair (settings kit theme panel) --
@@ -799,7 +804,7 @@
799
804
  .ds-avatar {
800
805
  display: inline-flex; align-items: center; justify-content: center;
801
806
  border-radius: 50%; background: var(--bg-3); color: var(--fg);
802
- font-family: var(--ff-mono); font-weight: 600; text-transform: lowercase;
807
+ font-family: var(--ff-body); font-weight: 600; text-transform: lowercase;
803
808
  letter-spacing: 0.02em; user-select: none; flex-shrink: 0; object-fit: cover;
804
809
  box-shadow: inset 0 0 0 1px var(--rule);
805
810
  }
@@ -1024,8 +1029,7 @@
1024
1029
  color: var(--fg-3);
1025
1030
  font-family: var(--ff-mono);
1026
1031
  font-size: var(--fs-xs);
1027
- letter-spacing: 0.02em;
1028
- text-transform: uppercase;
1032
+ letter-spacing: 0;
1029
1033
  }
1030
1034
  .panel.panel-docs .panel-title::before {
1031
1035
  content: 'docs · ';
@@ -52,7 +52,7 @@
52
52
  display: flex; align-items: center; gap: var(--space-2);
53
53
  margin-bottom: var(--space-2);
54
54
  }
55
- .ds-mc-detail-title { font-family: var(--ff-mono); font-size: var(--fs-base); font-weight: 700; }
55
+ .ds-mc-detail-title { font-family: var(--ff-body); font-size: var(--fs-base); font-weight: 700; }
56
56
 
57
57
  .ds-mc-discovery-error {
58
58
  display: flex; align-items: center; gap: var(--space-1-75);
@@ -2,18 +2,40 @@
2
2
  Panel — soft tonal container, no border decoration
3
3
  ============================================================ */
4
4
  .panel {
5
- background: var(--panel-1, var(--bg-2));
6
- border: var(--bw-hair) solid var(--rule);
5
+ /* PilotDeck-parity: the reference canvas has NO visible container edge
6
+ anywhere except a single hairline (sidebar only) page bg and panel
7
+ fill read as the same tone. Stacking a tonal fill + a border + a shadow
8
+ (the previous three-layer treatment) is what critics independently
9
+ flagged as "every card reads as a boxed gray tile" / "grid of gray
10
+ cards" — the single most-repeated, highest-impact gap. Panels are now
11
+ flat (transparent, no border): content groups are separated by
12
+ whitespace and an optional --panel-shadow-1 whisper only, never a fill.
13
+ Use .panel-tonal to opt back into the filled/bordered look for surfaces
14
+ that genuinely need a floating card (e.g. dropdowns, dialogs already
15
+ have their own elevation scale and are unaffected). */
16
+ background: transparent;
17
+ border: none;
7
18
  border-radius: var(--r-2);
8
- margin: 0 0 var(--space-4);
9
- padding: var(--space-3);
19
+ /* AGENTS.md "outer 2:1 inner": outer separation is --space-6, inner
20
+ sibling gap --space-4 (48:24 = 2:1), matching .ds-panel-trio/
21
+ .ds-panel-duo. Bumped up one rung from --space-5/--space-3 for more
22
+ PilotDeck-style airy whitespace while preserving the 2:1 ratio. */
23
+ margin: 0 0 var(--space-6);
24
+ padding: var(--space-4);
10
25
  position: relative;
11
- /* Crisp, restrained elevation — a hairline border does the edge definition;
12
- the shadow only adds a whisper of lift, not a soft glow-card float. */
13
- box-shadow: var(--shadow-1);
14
26
  transition: box-shadow var(--dur-base) var(--ease-spring), border-color var(--dur-snap) var(--ease),
15
27
  transform var(--dur-base) var(--ease-spring);
16
28
  }
29
+ /* Opt-in tonal/bordered card — the pre-restyle .panel look, kept for
30
+ surfaces that need to read as a discrete floating card rather than a
31
+ text grouping on flat canvas (e.g. a composer input, which PilotDeck
32
+ itself DOES give a faint 1px border+no fill — see .panel-inline below
33
+ for that exact treatment). */
34
+ .panel.panel-tonal {
35
+ background: var(--panel-1, var(--bg-2));
36
+ border: var(--bw-hair) solid var(--rule);
37
+ box-shadow: var(--panel-shadow-1);
38
+ }
17
39
  /* Editorial accent spine — an opt-in printed-ink rule down the left edge that
18
40
  reads as a magazine pull-quote bar, not a glow card. */
19
41
  .panel.panel-spine {
@@ -25,7 +47,9 @@
25
47
  width: var(--bw-chunk); background: var(--accent);
26
48
  border-radius: var(--bw-chunk) 0 0 var(--bw-chunk);
27
49
  }
28
- .panel.panel-inline { background: var(--bg-2); padding: var(--space-3) var(--space-3); }
50
+ /* PilotDeck's composer is the ONE element with a visible edge on an
51
+ otherwise flat canvas — a faint 1px border, no tonal fill. */
52
+ .panel.panel-inline { background: transparent; border: var(--bw-hair) solid var(--rule); padding: var(--space-3) var(--space-3); }
29
53
  .panel.panel-wide { background: transparent; box-shadow: none; border: none; }
30
54
  /* Changelog (and any other panel-wide content) needs its OWN container-query
31
55
  context: the existing `@container (max-width: 760px) .ds-changelog-row`
@@ -56,11 +80,11 @@
56
80
  rows sit on ONE left axis (the old 24/32 padding was authored for the
57
81
  pre-padding .panel-wide era and indented the label 16px past its content). */
58
82
  padding: var(--space-2) var(--space-3);
59
- font-size: var(--fs-tiny); font-weight: 600;
60
- color: var(--fg-3);
61
- letter-spacing: var(--tr-caps); text-transform: uppercase;
83
+ font-size: var(--fs-sm); font-weight: 600;
84
+ color: var(--fg-2);
85
+ letter-spacing: 0;
62
86
  /* A hairline divider is the header's only structural separator from the
63
- body — without it, an uppercase caps label at fs-tiny/600 reads as just
87
+ body — without it, a sentence-case label at fs-sm/600 reads as just
64
88
  another dim meta line once the eye drops past the top of the panel. */
65
89
  border-bottom: var(--bw-hair) solid var(--rule);
66
90
  margin-bottom: var(--space-1);
@@ -53,7 +53,7 @@
53
53
 
54
54
  .ds-plugins-row-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
55
55
  .ds-plugins-row-name {
56
- font-family: var(--ff-mono); font-size: var(--fs-sm); color: var(--fg);
56
+ font-family: var(--ff-body); font-size: var(--fs-sm); color: var(--fg);
57
57
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
58
58
  }
59
59
  .ds-plugins-row-meta {
@@ -82,7 +82,7 @@
82
82
  }
83
83
  .ds-plugins-detail-title { display: flex; align-items: center; gap: 8px; min-width: 0; }
84
84
  .ds-plugins-detail-title .name {
85
- font-family: var(--ff-mono); font-size: var(--fs-md); font-weight: 600; color: var(--fg);
85
+ font-family: var(--ff-body); font-size: var(--fs-md); font-weight: 600; color: var(--fg);
86
86
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
87
87
  }
88
88
  .ds-plugins-version { font-size: var(--fs-micro); color: var(--fg-3); }
@@ -118,7 +118,7 @@
118
118
 
119
119
  .ds-plugins-group-label {
120
120
  font-size: var(--fs-micro); font-weight: 600; color: var(--fg-3);
121
- text-transform: uppercase; letter-spacing: 0.02em; margin-bottom: 6px;
121
+ letter-spacing: 0; margin-bottom: 6px;
122
122
  }
123
123
  .ds-plugins-requires-list { display: flex; flex-wrap: wrap; gap: 6px; }
124
124
  .ds-plugins-chip {
@@ -21,9 +21,18 @@
21
21
  font-weight: 700;
22
22
  }
23
23
  .btn-primary:hover { background: var(--fg); color: var(--bg); transform: translateY(-1px); box-shadow: var(--shadow-2); }
24
- .btn-ghost { background: transparent; color: var(--fg); box-shadow: inset 0 0 0 2px var(--fg); }
24
+ .btn-ghost { background: transparent; color: var(--fg); box-shadow: inset 0 0 0 var(--bw-hair) var(--fg); }
25
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
+ /* Disabled state: --fg-3 (the tuned tertiary-text token, AA-clear against
28
+ every panel tier) instead of a blind opacity halving -- opacity multiplies
29
+ the SURROUNDING contrast too, so a light-on-dark button can drop under 3:1
30
+ even though "disabled" only needs to stay legible, not full-strength. */
31
+ .btn:disabled, .btn-primary:disabled, .btn-ghost:disabled,
32
+ .btn.is-disabled, .btn-primary.is-disabled, .btn-ghost.is-disabled {
33
+ background: var(--bg-2); color: var(--fg-3); border-color: var(--rule);
34
+ box-shadow: none; cursor: not-allowed; pointer-events: none;
35
+ }
27
36
  .btn:focus-visible, .btn-primary:focus-visible, .btn-ghost:focus-visible, .ds-icon-btn:focus-visible {
28
37
  outline: var(--focus-w) solid var(--focus-color);
29
38
  outline-offset: var(--focus-offset);
@@ -33,6 +42,23 @@
33
42
  height: var(--ctl-sm); min-height: var(--ctl-sm);
34
43
  padding: 0 var(--space-2); font-size: var(--fs-tiny); gap: var(--space-1);
35
44
  }
45
+
46
+ /* Link-styled button: an underline (not just color) is the affordance that
47
+ separates "this navigates" from "this triggers an action" -- .btn-ghost's
48
+ outline reads as a secondary ACTION button, not a link, at a glance. */
49
+ .btn-link {
50
+ display: inline-flex; align-items: center; gap: var(--space-1);
51
+ background: none; border: none; box-shadow: none; padding: 0;
52
+ height: auto; min-height: 0;
53
+ font-family: var(--ff-body); font-weight: 600; font-size: var(--fs-sm);
54
+ color: var(--accent-ink); text-decoration: underline; text-underline-offset: 3px;
55
+ cursor: pointer;
56
+ }
57
+ .btn-link:hover { color: var(--fg); }
58
+ .btn-link:focus-visible {
59
+ outline: var(--focus-w) solid var(--focus-color);
60
+ outline-offset: var(--focus-offset);
61
+ }
36
62
  .btn-lg, .btn-lg.btn-primary, .btn-lg.btn-ghost {
37
63
  height: var(--ctl-lg); min-height: var(--ctl-lg);
38
64
  padding: 0 var(--space-4); font-size: var(--fs-body); gap: var(--space-2);
@@ -73,10 +99,13 @@
73
99
  .ds-icon-btn-ghost:hover { background: var(--fg); color: var(--bg); }
74
100
  .ds-icon-btn-primary { background: var(--accent); color: var(--accent-fg); }
75
101
  .ds-icon-btn-primary:hover { background: var(--fg); color: var(--accent-ink); }
76
- /* --ink, not --on-color: --on-color is a fixed #fff, which measures 3.07:1 on
77
- --warn (#FF5A52) -- below the 4.5:1 AA floor. --ink measures 6.27:1. Same
78
- fix as .btn-primary.danger in app-shell/files.css. */
79
- .ds-icon-btn-danger { background: var(--warn); color: var(--ink); }
102
+ /* --warn-fg (not a bare --ink/--on-color literal): --ink only clears AA
103
+ against dark theme's lighter --warn (#FF5A52, 6.27:1); light theme's
104
+ --warn is a darker red (#C41C0C) where --ink measures just 2.92:1, well
105
+ under the 4.5:1 floor. --warn-fg is the theme-paired token (light: paper
106
+ text 5.97:1, dark: ink text 6.27:1) -- same fix as .btn-primary.danger in
107
+ app-shell/files.css, which had this exact light-theme regression live. */
108
+ .ds-icon-btn-danger { background: var(--warn); color: var(--warn-fg); }
80
109
  .ds-icon-btn-danger:hover { filter: brightness(0.92); }
81
110
  .ds-icon-btn:active { transform: translateY(1px); }
82
111
  .ds-icon-btn:disabled, .ds-icon-btn.is-disabled {
@@ -282,7 +311,7 @@
282
311
  .ds-tooltip[data-placement^="bottom"]::after { top: -3px; left: 50%; margin-left: -3px; }
283
312
  .ds-tooltip[data-placement^="left"]::after { right: -3px; top: 50%; margin-top: -3px; }
284
313
  .ds-tooltip[data-placement^="right"]::after { left: -3px; top: 50%; margin-top: -3px; }
285
- .ds-tooltip.kind-danger { background: var(--warn); color: var(--ink); }
314
+ .ds-tooltip.kind-danger { background: var(--warn); color: var(--warn-fg); }
286
315
  .ds-tooltip.kind-danger::after { background: var(--warn); }
287
316
  @media (prefers-reduced-motion: reduce) {
288
317
  .ds-tooltip { transition: none; }
@@ -245,11 +245,14 @@
245
245
  @media (prefers-reduced-motion: no-preference) {
246
246
  .ws-shell { transition: grid-template-columns var(--dur-base) var(--ease); }
247
247
  }
248
- /* Ultrawide: cap the shell so the 1fr content track doesn't stretch to
249
- unreadable measures while the fixed side columns pin to their clamps. */
250
- @media (min-width: 1920px) {
251
- .ws-shell { max-width: 1920px; margin-inline: auto; }
252
- }
248
+ /* Ultrawide: .ws-shell itself stays full-width (fills the real screen edges
249
+ at any viewport, matching .app's own unbounded-width rule in topbar.css) --
250
+ only the 1fr content track's OWN inner measure is capped, so rail/sessions/
251
+ pane keep their fixed/clamped widths and only the middle content column's
252
+ text gets a readability ceiling, instead of the whole grid (and its side
253
+ columns) shrinking together and leaving dead margin on either side of the
254
+ shell. */
255
+ .ws-content { max-width: var(--measure-wide, 1200px); margin-inline: auto; width: 100%; }
253
256
 
254
257
  /* Rail (left nav) */
255
258
  .ws-rail {
@@ -19,12 +19,12 @@
19
19
 
20
20
  .app-side .count {
21
21
  padding: 1px 8px; font-size: var(--fs-micro); font-weight: 700;
22
- font-family: var(--ff-mono); letter-spacing: 0.04em;
22
+ font-family: var(--ff-body); letter-spacing: 0.04em;
23
23
  min-width: 18px; text-align: center;
24
24
  }
25
25
  .app-side a:has(> .count:empty) > .count,
26
26
  .app-side .count:empty { display: none; }
27
- .app-side a .glyph { font-family: var(--ff-mono); font-size: 13px; font-weight: 600; color: var(--fg-3); text-align: center; }
27
+ .app-side a .glyph { font-family: var(--ff-body); font-size: 13px; font-weight: 600; color: var(--fg-3); text-align: center; }
28
28
  .app-side a.active .glyph { color: var(--accent-ink); }
29
29
 
30
30
  /* -- select primitive --------------------------------------------------- */
@@ -14,7 +14,7 @@
14
14
  .ds-skills-group-label {
15
15
  padding: var(--space-2) var(--space-2) 3px;
16
16
  font-size: var(--fs-micro); font-weight: 600; color: var(--fg-3);
17
- text-transform: uppercase; letter-spacing: 0.06em;
17
+ letter-spacing: 0;
18
18
  }
19
19
 
20
20
  .ds-skills-description { font-size: var(--fs-sm); color: var(--fg-2); line-height: 1.5; }