anentrypoint-design 0.0.379 → 0.0.381

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": "0.0.379",
3
+ "version": "0.0.381",
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",
@@ -538,14 +538,23 @@ export function ProcessRegistryTable({ processes = [], emptyText = 'no live proc
538
538
 
539
539
  export function HomeView({ state = {}, onNav, onToggleWork, works = [], posts = [], manifesto = [], currentlyShipping } = {}) {
540
540
  return [
541
+ // The page's one deliberate kicker. 'an entrypoint' is the collective's
542
+ // name-as-tagline — it says something the <h1> does not, and it is the
543
+ // masthead position where a print eyebrow actually belongs. The sections
544
+ // below intentionally carry NO eyebrow: each has a heading that already
545
+ // names it, so a kicker there would only restate the <h3> one word
546
+ // shorter. Do not add eyebrows to the sibling sections to "match" this.
541
547
  Hero({
542
548
  eyebrow: 'an entrypoint',
543
549
  title: 'Small, weird, useful tools — built in public.',
544
550
  body: '247420 is a creative collective of eight, scattered across three timezones. We have been shipping open-source tools for the web since 2018.',
545
551
  accent: 'Some become the future. Most don\'t. That\'s the deal.'
546
552
  }),
553
+ // Titleless section: promoted its former eyebrow to the actual heading
554
+ // rather than leaving a kicker hovering over an unnamed panel. The label
555
+ // was carrying the section's only name, so it became the <h3>.
547
556
  currentlyShipping ? Section({
548
- eyebrow: 'currently shipping',
557
+ title: 'currently shipping',
549
558
  children: Panel({
550
559
  kind: 'wide',
551
560
  children: currentlyShipping.map((row, i) => {
@@ -560,15 +569,15 @@ export function HomeView({ state = {}, onNav, onToggleWork, works = [], posts =
560
569
  })
561
570
  }) : null,
562
571
  works.length ? Section({
563
- eyebrow: 'works', title: 'Everything else.',
572
+ title: 'Everything else.',
564
573
  children: WorksList({ works, openedIndex: state.opened ?? -1, onToggle: onToggleWork })
565
574
  }) : null,
566
575
  posts.length ? Section({
567
- eyebrow: 'writing', title: 'When we have something to say.',
576
+ title: 'When we have something to say.',
568
577
  children: WritingList({ posts })
569
578
  }) : null,
570
579
  manifesto.length ? Section({
571
- eyebrow: 'who\'s here', title: 'Eight people, three timezones, one ongoing conversation.',
580
+ title: 'Eight people, three timezones, one ongoing conversation.',
572
581
  children: Manifesto({ paragraphs: manifesto })
573
582
  }) : null
574
583
  ].filter(Boolean);
@@ -10,6 +10,13 @@
10
10
  import * as webjsx from '../../../vendor/webjsx/index.js';
11
11
  const h = webjsx.createElement;
12
12
 
13
+ // The ONE eyebrow on the freddie surface, and it earns it by being the only
14
+ // label that varies: `freddie · <id>` names the specific unimplemented route,
15
+ // which is the whole point of a stub — the reader needs to know WHICH page
16
+ // failed to resolve, and the <h2> below only shows a human title. The real
17
+ // freddie pages (src/components/freddie.js) deliberately carry NO eyebrow:
18
+ // there, the kicker was the constant string 'freddie' on all 22 headers, which
19
+ // named nothing the topbar/crumb did not already say. Do not add siblings.
13
20
  export function renderPageStub({ id, title }) {
14
21
  return h('div', { class: 'ds-freddie-stub' },
15
22
  h('span', { class: 'eyebrow' }, 'freddie · ' + id),
@@ -80,7 +80,7 @@ export const home = makePage((ctx) => {
80
80
  const tools = ctx.host?.pi?.tools?.size ?? s.toolsCount ?? '—';
81
81
  const skills = ctx.host?.pi?.skills?.size ?? s.skillsCount ?? '—';
82
82
  return [
83
- PageHeader({ eyebrow: 'freddie', title: 'dashboard', lede: 'agent harness · live overview' }),
83
+ PageHeader({ title: 'dashboard', lede: 'agent harness · live overview' }),
84
84
  Kpi({ items: [
85
85
  [tools, 'tools'],
86
86
  [skills, 'skills'],
@@ -315,7 +315,7 @@ export const voice = makePage((ctx) => {
315
315
  const v = s.voice;
316
316
  const enabled = v && (v.enabled || v.transcription || v.tts);
317
317
  return [
318
- PageHeader({ eyebrow: 'freddie', title: 'voice', lede: 'voice surfaces', right: enabled ? Chip({ tone: 'ok', children: 'enabled' }) : Chip({ tone: 'neutral', children: 'not configured' }) }),
318
+ PageHeader({ title: 'voice', lede: 'voice surfaces', right: enabled ? Chip({ tone: 'ok', children: 'enabled' }) : Chip({ tone: 'neutral', children: 'not configured' }) }),
319
319
  enabled
320
320
  ? section('backends', Table({ headers: ['capability', 'status'], rows: [['transcription', v.transcription ? Chip({ tone: 'ok', children: 'on' }) : Chip({ tone: 'neutral', children: 'off' })], ['tts', v.tts ? Chip({ tone: 'ok', children: 'on' }) : Chip({ tone: 'neutral', children: 'off' })]] }))
321
321
  : section('status', emptyState('no voice backend wired in this build. configure a transcription/tts plugin to enable.')),
@@ -349,7 +349,7 @@ export const sessions = makePage((ctx) => {
349
349
  if (s.error && !s.list) return errorState(s.error, load);
350
350
  const list = Array.isArray(s.list) ? s.list : [];
351
351
  return [
352
- PageHeader({ eyebrow: 'freddie', title: 'sessions', lede: list.length + ' sessions', right: refreshBtn(refresh, s.refreshing) }),
352
+ PageHeader({ title: 'sessions', lede: list.length + ' sessions', right: refreshBtn(refresh, s.refreshing) }),
353
353
  s.error && s.list ? refreshError(s.error) : null,
354
354
  SearchInput({ value: s.q, label: 'search sessions', placeholder: 'search messages…', onInput: (v) => { s.q = v; }, onSubmit: (v) => search(v) }),
355
355
  section('sessions',
@@ -392,7 +392,7 @@ export const projects = makePage((ctx) => {
392
392
  const d = s.data || {}; const list = d.projects || [];
393
393
  const activeName = (d.active && d.active.name) || d.active || 'default';
394
394
  return [
395
- PageHeader({ eyebrow: 'freddie', title: 'projects', lede: 'isolated workspaces · active: ' + activeName }),
395
+ PageHeader({ title: 'projects', lede: 'isolated workspaces · active: ' + activeName }),
396
396
  noteAlert(s.note),
397
397
  section('projects',
398
398
  list.length ? list.map((p, i) => Row({
@@ -421,7 +421,7 @@ export const agents = makePage((ctx) => {
421
421
  if (s.error && !s.data) return errorState(s.error, load);
422
422
  const d = s.data || {};
423
423
  return [
424
- PageHeader({ eyebrow: 'freddie', title: 'agents', lede: 'live agent activity' }),
424
+ PageHeader({ title: 'agents', lede: 'live agent activity' }),
425
425
  s.error && s.data ? refreshError(s.error) : null,
426
426
  Kpi({ items: [[d.count ?? 0, 'active'], [d.turns ?? 0, 'total turns'], [d.last_activity ? fmtAgo(d.last_activity) : '—', 'last activity']] }),
427
427
  section('detail', Table({ headers: ['field', 'value'], rows: Object.entries(d).map(([k, v]) => [k, String(v)]) })),
@@ -450,7 +450,7 @@ export const analytics = makePage((ctx) => {
450
450
  const ok = samp.filter(x => x && x.available !== false).length;
451
451
  const sum = s.avail?.summary || {};
452
452
  return [
453
- PageHeader({ eyebrow: 'freddie', title: 'analytics', lede: 'provider availability & sampler health' }),
453
+ PageHeader({ title: 'analytics', lede: 'provider availability & sampler health' }),
454
454
  s.error && (s.sampler || s.avail) ? refreshError(s.error) : null,
455
455
  Kpi({ items: [[ok + '/' + samp.length, 'providers up'], [sum.total_models ?? '—', 'models'], [sum.usable_in_any_mode ?? '—', 'usable']] }),
456
456
  section('sampler', samp.length ? Table({ headers: ['provider', 'available', 'fails'], rows: Object.entries(s.sampler.status).map(([k, v]) => [k, v.available === false ? 'no' : 'yes', String(v.failCount ?? 0)]) }) : emptyState('no sampler data')),
@@ -482,7 +482,7 @@ export const models = makePage((ctx) => {
482
482
  return () => {
483
483
  const s = ctx.state;
484
484
  return [
485
- PageHeader({ eyebrow: 'freddie', title: 'models', lede: s.data ? (s.data.summary?.total_models ?? 0) + ' models across ' + (s.data.summary?.total_providers ?? 0) + ' providers' : 'model availability matrix' }),
485
+ PageHeader({ title: 'models', lede: s.data ? (s.data.summary?.total_models ?? 0) + ' models across ' + (s.data.summary?.total_providers ?? 0) + ' providers' : 'model availability matrix' }),
486
486
  ModelsConfig({
487
487
  data: s.data, loading: s.loading, error: s.error,
488
488
  selectedProviderId: s.selectedProviderId, onSelectProvider: (id) => ctx.set({ selectedProviderId: id, selectedModel: null }),
@@ -514,7 +514,7 @@ export const cron = makePage((ctx) => {
514
514
  if (s.error && !s.list) return errorState(s.error, load);
515
515
  const list = Array.isArray(s.list) ? s.list : [];
516
516
  return [
517
- PageHeader({ eyebrow: 'freddie', title: 'cron', lede: list.length + ' scheduled jobs' }),
517
+ PageHeader({ title: 'cron', lede: list.length + ' scheduled jobs' }),
518
518
  noteAlert(s.note),
519
519
  section('jobs', list.length ? list.map((j, i) => Row({
520
520
  key: i, code: j.enabled ? Icon('play') : Icon('pause'), title: j.cron, sub: trunc(j.prompt, TRUNC_SUB).text,
@@ -553,7 +553,7 @@ export const skills = makePage((ctx) => {
553
553
  enabled: skillState[sk.name] !== false,
554
554
  }));
555
555
  return [
556
- PageHeader({ eyebrow: 'freddie', title: 'skills', lede: mapped.length + ' skills' }),
556
+ PageHeader({ title: 'skills', lede: mapped.length + ' skills' }),
557
557
  SkillsConfig({
558
558
  skills: mapped, selected: s.selected, loading: s.loading, error: s.error,
559
559
  busyName: s.busyName, query: s.query, onQuery: (q) => ctx.set({ query: q }),
@@ -577,7 +577,7 @@ export const plugins = makePage((ctx) => {
577
577
  const s = ctx.state;
578
578
  const list = Array.isArray(s.list) ? s.list : (s.list?.plugins || []);
579
579
  return [
580
- PageHeader({ eyebrow: 'freddie', title: 'plugins', lede: list.length + ' plugins loaded' }),
580
+ PageHeader({ title: 'plugins', lede: list.length + ' plugins loaded' }),
581
581
  PluginsConfig({
582
582
  plugins: list, selected: s.selected, loading: s.loading, error: s.error,
583
583
  onSelect: (name) => ctx.set({ selected: s.selected === name ? null : name }),
@@ -620,7 +620,7 @@ export const config = makePage((ctx) => {
620
620
  const skinList = Array.isArray(s.skins) ? s.skins : (s.skins?.skins || s.skins?.available || []);
621
621
  const activeSkin = cfg.skin || s.skins?.active || '';
622
622
  return [
623
- PageHeader({ eyebrow: 'freddie', title: 'config', lede: 'runtime configuration' }),
623
+ PageHeader({ title: 'config', lede: 'runtime configuration' }),
624
624
  noteAlert(s.note),
625
625
  liveRegion(s.busy ? 'saving configuration' : ''),
626
626
  nested.length ? h('div', { class: 'ds-alert ds-alert-info', role: 'note' },
@@ -676,7 +676,7 @@ export const env = makePage((ctx) => {
676
676
  const providerEnvs = new Set(auth.map(a => a.env));
677
677
  const otherRows = vars.filter(v => !providerEnvs.has(v.key)).map(v => [v.key, v.set ? Chip({ tone: 'ok', children: v.source || 'set' }) : Chip({ tone: 'neutral', children: 'unset' })]);
678
678
  return [
679
- PageHeader({ eyebrow: 'freddie', title: 'keys', lede: 'provider api keys · stored locally, never displayed' }),
679
+ PageHeader({ title: 'keys', lede: 'provider api keys · stored locally, never displayed' }),
680
680
  noteAlert(s.note),
681
681
  section('provider keys',
682
682
  auth.length ? auth.map((a, i) => Row({
@@ -707,7 +707,7 @@ export const tools = makePage((ctx) => {
707
707
  const groups = {};
708
708
  for (const t of list) { const g = t.toolset || 'core'; (groups[g] = groups[g] || []).push(t); }
709
709
  return [
710
- PageHeader({ eyebrow: 'freddie', title: 'tools', lede: list.length + ' tools' }),
710
+ PageHeader({ title: 'tools', lede: list.length + ' tools' }),
711
711
  SearchInput({ value: s.q, label: 'filter tools', placeholder: 'filter tools…', onInput: (v) => ctx.set({ q: v }) }),
712
712
  ...Object.entries(groups).map(([g, ts]) => section(g + ' · ' + ts.length, ts.map((t, i) => h('div', { key: i },
713
713
  Row({ title: t.name, sub: trunc(t.schema?.description || t.description, TRUNC_DESC).text, onClick: () => ctx.set({ open: ctx.state.open === t.name ? null : t.name }), active: ctx.state.open === t.name }),
@@ -733,7 +733,7 @@ export const batch = makePage((ctx) => {
733
733
  return () => {
734
734
  const s = ctx.state;
735
735
  return [
736
- PageHeader({ eyebrow: 'freddie', title: 'batch', lede: 'parallel prompt runner' }),
736
+ PageHeader({ title: 'batch', lede: 'parallel prompt runner' }),
737
737
  noteAlert(s.note),
738
738
  section('prompts',
739
739
  TextField({ label: 'prompts (one per line)', value: s.prompts, multiline: true, rows: 6, onInput: (v) => { s.prompts = v; } }),
@@ -767,7 +767,7 @@ export const gateway = makePage((ctx) => {
767
767
  const platforms = d.platforms || d;
768
768
  const rows = Object.entries(platforms).map(([k, v]) => [k, typeof v === 'object' ? (v.running || v.up ? Chip({ tone: 'ok', children: 'up' }) : Chip({ tone: 'miss', children: 'down' })) : String(v)]);
769
769
  return [
770
- PageHeader({ eyebrow: 'freddie', title: 'gateway', lede: 'messaging platform status' }),
770
+ PageHeader({ title: 'gateway', lede: 'messaging platform status' }),
771
771
  s.error && s.data ? refreshError(s.error) : null,
772
772
  section('platforms', rows.length ? Table({ headers: ['platform', 'status'], rows }) : emptyState('no platforms configured')),
773
773
  ].filter(Boolean);
@@ -806,7 +806,7 @@ export const chains = makePage((ctx) => {
806
806
  const chainsList = s.list?.chains || s.list || [];
807
807
  const up = s.health && (s.health.ok || s.health.status === 'ok' || s.health.healthy);
808
808
  return [
809
- PageHeader({ eyebrow: 'freddie', title: 'chains', lede: 'acptoapi fallback chains', right: up ? Chip({ tone: 'ok', children: 'acptoapi up' }) : Chip({ tone: 'miss', children: 'acptoapi down' }) }),
809
+ PageHeader({ title: 'chains', lede: 'acptoapi fallback chains', right: up ? Chip({ tone: 'ok', children: 'acptoapi up' }) : Chip({ tone: 'miss', children: 'acptoapi down' }) }),
810
810
  noteAlert(s.note),
811
811
  section('chains', Array.isArray(chainsList) && chainsList.length ? chainsList.map((c, i) => Row({
812
812
  key: i, title: c.name || c, sub: Array.isArray(c.links) ? c.links.join(' -> ') : '',
@@ -833,7 +833,7 @@ export const machines = makePage((ctx) => {
833
833
  const d = s.data || {};
834
834
  const list = Array.isArray(d) ? d : (d.machines || Object.entries(d).map(([kind, v]) => ({ kind, ...(typeof v === 'object' ? v : { value: v }) })));
835
835
  return [
836
- PageHeader({ eyebrow: 'freddie', title: 'machines', lede: 'persisted xstate machine census' }),
836
+ PageHeader({ title: 'machines', lede: 'persisted xstate machine census' }),
837
837
  s.error && s.data ? refreshError(s.error) : null,
838
838
  section('machines', list.length ? Table({
839
839
  headers: ['kind', 'key', 'state'],
@@ -863,7 +863,7 @@ export const health = makePage((ctx) => {
863
863
  const hd = s.health || {};
864
864
  const provs = Array.isArray(s.providers) ? s.providers : (s.providers?.providers || []);
865
865
  return [
866
- PageHeader({ eyebrow: 'freddie', title: 'health', lede: 'system & provider health', right: hd.ok ? Chip({ tone: 'ok', children: 'healthy' }) : Chip({ tone: 'miss', children: 'degraded' }) }),
866
+ PageHeader({ title: 'health', lede: 'system & provider health', right: hd.ok ? Chip({ tone: 'ok', children: 'healthy' }) : Chip({ tone: 'miss', children: 'degraded' }) }),
867
867
  s.error && (s.health || s.providers) ? refreshError(s.error) : null,
868
868
  section('checks', Object.keys(hd).length ? Table({ headers: ['check', 'status'], rows: Object.entries(hd).map(([k, v]) => [k, typeof v === 'object' ? truncJson(v) : (v === true ? Chip({ tone: 'ok', children: 'ok' }) : v === false ? Chip({ tone: 'miss', children: 'no' }) : String(v))]) }) : emptyState('no health data')),
869
869
  provs.length ? section('providers', Table({ headers: ['provider', 'status'], rows: provs.map(p => { const n = typeof p === 'string' ? p : p.name || p.id; const ok = typeof p === 'object' ? (p.ok ?? p.available) : null; return [n, ok == null ? '—' : (ok ? Chip({ tone: 'ok', children: 'up' }) : Chip({ tone: 'miss', children: 'down' }))]; }) })) : null,
@@ -931,7 +931,7 @@ export const logs = makePage((ctx) => {
931
931
  const severities = ['error', 'warning', 'info', 'debug'];
932
932
  return [
933
933
  PageHeader({
934
- eyebrow: 'freddie', title: 'logs', lede: 'live JSONL log tail — /api/logs/stream',
934
+ title: 'logs', lede: 'live JSONL log tail — /api/logs/stream',
935
935
  right: s.connected ? Chip({ tone: 'ok', children: 'live' }) : Chip({ tone: 'miss', children: 'reconnecting…' }),
936
936
  }),
937
937
  s.wsError ? refreshError(s.wsError) : null,
@@ -981,7 +981,7 @@ export const debug = makePage((ctx) => {
981
981
  const d = s.data || {};
982
982
  const subsystems = d.subsystems || Object.keys(d);
983
983
  return [
984
- PageHeader({ eyebrow: 'freddie', title: 'debug', lede: 'subsystem snapshots & logs' }),
984
+ PageHeader({ title: 'debug', lede: 'subsystem snapshots & logs' }),
985
985
  section('subsystems', subsystems.length ? subsystems.map((name, i) => Row({
986
986
  key: i, title: name, onClick: () => loadLogs(name), active: s.sub === name,
987
987
  })) : emptyState('no debug subsystems')),
@@ -1041,7 +1041,7 @@ export const git = makePage((ctx) => {
1041
1041
  const worktrees = wtFailed ? [] : (s.worktrees && s.worktrees.worktrees) || s.worktrees || [];
1042
1042
  const current = Array.isArray(worktrees) ? (worktrees.find(w => w.path === s.cwd) || {}).path : undefined;
1043
1043
  return [
1044
- PageHeader({ eyebrow: 'freddie', title: 'git', lede: s.cwd || 'active project' }),
1044
+ PageHeader({ title: 'git', lede: s.cwd || 'active project' }),
1045
1045
  noteAlert(s.note),
1046
1046
  statusFailed ? refreshError(statusFailed) : null,
1047
1047
  section('worktrees',
@@ -23,7 +23,10 @@ button, input, select, textarea { font: inherit; }
23
23
  color: var(--accent-fg);
24
24
  padding: 8px 16px;
25
25
  text-decoration: none;
26
- z-index: 100;
26
+ /* Must outrank every layer of chrome once focused, or the first Tab press
27
+ lands on a control hidden behind the sticky header (was z-index:100,
28
+ which the header alone now matches). */
29
+ z-index: var(--z-top);
27
30
  border-radius: var(--r-pill);
28
31
  font-weight: 600;
29
32
  font-size: var(--fs-sm);
@@ -78,6 +81,13 @@ h4, .t-h4 { font-family: var(--ff-display); font-size: var(--fs-h4); line-height
78
81
  .t-mono { font-family: var(--ff-mono); }
79
82
  .t-body { font-family: var(--ff-body); }
80
83
 
84
+ /* House small-label voice, in two sizes. Every declaration here is sourced
85
+ from a token, so a sheet that cannot join these selector lists (community.css
86
+ and gm-prose.css ship as separate <link>-able exports and must not depend on
87
+ app-shell.css being present) still composes the same voice by referencing the
88
+ same four tokens — --ff-mono / --fs-* / uppercase / --tr-label. That token
89
+ set, not this rule, is the single source of truth; a selector that restates a
90
+ literal instead of the token is the drift this pairing exists to prevent. */
81
91
  .t-label { font-family: var(--ff-mono); font-size: var(--fs-tiny); text-transform: uppercase; letter-spacing: var(--tr-label); font-weight: 500; }
82
92
  .t-micro { font-family: var(--ff-mono); font-size: var(--fs-micro); text-transform: uppercase; letter-spacing: var(--tr-label); color: var(--fg-3); }
83
93
  .t-meta { font-family: var(--ff-body); font-size: var(--fs-sm); color: var(--fg-3); }
@@ -121,6 +121,13 @@
121
121
  transition: color var(--dur-base) var(--ease), background var(--dur-base) var(--ease);
122
122
  }
123
123
  .ds-segmented .ds-seg-btn:hover { color: var(--fg); }
124
+ /* Touch floor — a segmented control is a primary mode switch but renders ~30px
125
+ tall from its 4px padding, below the 44px minimum the rest of the system
126
+ holds. Grows the hit area only; the pill's visual size is unchanged at
127
+ fine-pointer sizes. */
128
+ @media (pointer: coarse) {
129
+ .ds-segmented .ds-seg-btn { min-height: 44px; padding-inline: var(--space-3); }
130
+ }
124
131
  .ds-segmented .ds-seg-btn:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
125
132
  .ds-segmented .ds-seg-btn.is-on {
126
133
  background: var(--bg); color: var(--fg);
@@ -126,7 +126,12 @@
126
126
  .chat-bubble.chat-md li { margin: var(--space-1) 0; }
127
127
  .chat-bubble.chat-md blockquote {
128
128
  margin: var(--space-2) 0; padding: 4px 12px;
129
- border-left: 3px solid color-mix(in oklab, currentColor 30%, transparent);
129
+ /* DELIBERATE, but slimmed to the --bw-rule tier. This is a real markdown
130
+ <blockquote> inside a chat bubble: the left rule is the standard
131
+ typographic quotation mark, not a colored alert stripe (the tone is a
132
+ currentColor mix that inherits the bubble's own ink — never an accent).
133
+ A 3px off-scale literal read as the generic tell; 2px reads as type. */
134
+ border-left: var(--bw-rule) solid color-mix(in oklab, currentColor 30%, transparent);
130
135
  color: color-mix(in oklab, currentColor 75%, transparent); font-style: italic;
131
136
  }
132
137
  .chat-bubble.chat-md pre {
@@ -102,10 +102,28 @@
102
102
  .ds-sub-btn:hover { border-color: var(--accent); color: var(--accent-ink); }
103
103
  .ds-sub-btn span { display: block; font-size: 18px; font-weight: 700; color: var(--accent-ink); }
104
104
 
105
- /* SessionRow — base .ds-session-row (layout, hover/focus/active states,
106
- rail-tone indicators) is canonically defined in chat.css alongside the
107
- rest of the chat session-list component; this file only carries the
108
- dev/admin-row sub-parts (id/counts/devcnt/span) that decorate it. */
105
+ /* SessionRow — the RICH treatment (hover/focus/active, rail tones, nesting,
106
+ inline rename/delete) lives in chat.css with the rest of the chat session
107
+ list. But SessionRow is part of this data-density family and is used by kits
108
+ that never link chat.css (gm_inspector links only colors_and_type +
109
+ app-shell + editor-primitives), where the component rendered with NO layout
110
+ at all: gap resolved to `normal`, so id/counts/devcnt/phasewalk/span all
111
+ butted together at 0px. The layout floor therefore belongs here, next to the
112
+ sub-parts it positions, so the component is never shipped shapeless.
113
+ chat.css still layers its interactive states on top; these declarations are
114
+ identical to the ones there, so a kit loading both gets the same result. */
115
+ .ds-session-row {
116
+ position: relative; display: flex; align-items: center; gap: var(--space-2);
117
+ width: 100%; padding: var(--space-2); min-height: 52px; margin-bottom: 2px;
118
+ background: transparent; border: none; border-radius: var(--r-1);
119
+ text-align: left; color: var(--fg);
120
+ }
121
+ /* Only an interactive row gets the pointer affordance (chat.css sets cursor
122
+ unconditionally because every row there is clickable; here rows are often
123
+ static readouts). */
124
+ .ds-session-row[role="button"], a.ds-session-row { cursor: pointer; }
125
+
126
+ /* Sub-parts that decorate the row above. */
109
127
  .ds-session-row-id {
110
128
  font-family: var(--ff-mono); font-size: var(--fs-micro); color: var(--accent-ink);
111
129
  flex: 0 1 160px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
@@ -301,7 +301,7 @@
301
301
  }
302
302
  .ds-file-cell-meta { font-family: var(--ff-mono); font-size: var(--fs-micro); color: var(--fg-3); text-align: left; }
303
303
  .ds-file-cell-check {
304
- position: absolute; top: 4px; left: 4px; z-index: 1;
304
+ position: absolute; top: 4px; left: 4px; z-index: var(--z-raised);
305
305
  background: color-mix(in oklab, var(--bg) 78%, transparent);
306
306
  }
307
307
 
@@ -447,7 +447,7 @@
447
447
  .ds-dropzone--wrap { position: relative; border: none; border-radius: 0; background: transparent; }
448
448
  .ds-dropzone--wrap > .ds-dropzone-inner { display: none; }
449
449
  .ds-dropzone--wrap.dragover > .ds-dropzone-inner {
450
- display: flex; position: absolute; inset: 0; z-index: 5;
450
+ display: flex; position: absolute; inset: 0; z-index: var(--z-raised);
451
451
  margin: 0; padding: var(--space-3);
452
452
  align-items: center; justify-content: center;
453
453
  background: color-mix(in srgb, var(--bg) 84%, transparent);
@@ -539,7 +539,7 @@
539
539
 
540
540
  /* -- Modals ------------------------------------------------- */
541
541
  .ds-modal-backdrop {
542
- position: fixed; inset: 0; z-index: var(--z-modal, 800);
542
+ position: fixed; inset: 0; z-index: var(--z-modal);
543
543
  display: flex; align-items: center; justify-content: center;
544
544
  /* Notch/home-indicator safe: near-fullscreen modals must not slide under. */
545
545
  padding: max(var(--space-3), env(safe-area-inset-top))
@@ -100,7 +100,7 @@
100
100
  ============================================================ */
101
101
  .ds-grain { position: relative; isolation: isolate; }
102
102
  .ds-grain::after {
103
- content: ''; position: absolute; inset: 0; z-index: -1;
103
+ content: ''; position: absolute; inset: 0; z-index: var(--z-below);
104
104
  pointer-events: none;
105
105
  background-image: var(--grain);
106
106
  background-size: 160px 160px;