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.
@@ -17,11 +17,115 @@
17
17
  /* == appended: ui-kits == */
18
18
  /* ============================================================
19
19
  ui_kit responsive helpers (kits-grids sweep) */
20
- .ds-section-pad { padding: clamp(6px, 1.2vw, 16px); }
21
- .ds-panel-gap { margin: var(--space-2) 0; }
22
- .ds-panel-duo { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-2); margin: var(--space-2) 0; }
20
+ /* App-surface root. .ds-section carries a --space-8 (96px) editorial rhythm
21
+ that is correct between landing-page sections and wrong as the ROOT wrapper
22
+ of an Operate surface, where .app-main has already padded the region used
23
+ there it just pushes the page heading a screen-inch down and adds dead space
24
+ below the last panel. This is the app-mode counterpart: no outer margin, and
25
+ it owns the heading -> lede -> first-panel cadence so each kit stops
26
+ re-deriving it. Pair with .ds-section-pad for the shadow inset. */
27
+ .ds-app-surface { margin: 0; display: flow-root; }
28
+ /* Operate surfaces run the compact heading ceiling. --fs-h1 tops out at 64px,
29
+ a display size that makes the page TITLE outweigh the data it labels; the
30
+ app scale (--fs-h1-app, 22-30px) is what these tokens exist for. Applied
31
+ here rather than as a data-attribute on each kit shell so any surface using
32
+ this root gets the app voice without a per-kit opt-in that kits forget. */
33
+ .ds-app-surface > h1, .ds-app-surface > .t-h1 { font-size: var(--fs-h1-app); line-height: 1.15; }
34
+ .ds-app-surface > h2, .ds-app-surface > .t-h2 { font-size: var(--fs-h2-app); }
35
+ .ds-app-surface > h3, .ds-app-surface > .t-h3 { font-size: var(--fs-h3-app); }
36
+ /* The lede is supporting chrome under an app title, not a marketing stand-first:
37
+ it drops to body size and keeps a readable measure instead of --fs-xl. */
38
+ .ds-app-surface > .lede, .ds-app-surface > .t-lede { font-size: var(--fs-body); max-width: 60ch; }
39
+ .ds-app-surface > h1,
40
+ .ds-app-surface > h2 { margin-bottom: var(--space-2); }
41
+ /* The lede belongs to the heading above it, so it sits tight to that heading
42
+ and separates generously from the content it introduces. */
43
+ .ds-app-surface > .lede,
44
+ .ds-app-surface > .t-lede { margin-bottom: calc(var(--space-6) * var(--density)); }
45
+ /* No leading margin on the first block or trailing margin on the last — the
46
+ shell owns the region's outer edges. */
47
+ .ds-app-surface > :first-child { margin-top: 0; }
48
+ .ds-app-surface > :last-child { margin-bottom: 0; }
49
+
50
+ /* Section padding is the shell's job (.app-main owns --pad-x). This only adds
51
+ the small inset a kit root needs so panel shadows are not clipped by the
52
+ scroll container, and it scales with density like every other app metric —
53
+ the old clamp(6px,1.2vw,16px) sized against the VIEWPORT inside a
54
+ container-query layout that measures everything else in cqi. */
55
+ .ds-section-pad { padding: calc(var(--space-1) * var(--density)); }
56
+
57
+ /* Separation between sibling panels. This is the OUTER rhythm: it must exceed
58
+ .panel-body's 16px inner sibling gap, otherwise proximity says two unrelated
59
+ panels belong together more tightly than two rows inside one panel. The old
60
+ value was --space-2 (8px), which inverted exactly that relationship and made
61
+ the class named "gap" the only rule in the system that removed separation. */
62
+ .ds-panel-gap { margin: calc(var(--space-5) * var(--density)) 0; }
63
+ /* Density-scaled sibling rhythm for any run of stacked panels, so the cadence
64
+ holds without each kit re-declaring a margin per panel. */
65
+ .ds-panel-gap + .ds-panel-gap { margin-top: calc(var(--space-5) * var(--density)); }
66
+
67
+ /* A panel inside a gap-owning parent (grid/flex row). .panel ships a 24px
68
+ bottom margin for the stacked case; inside a row that margin fights the
69
+ parent's gap and makes the last row of a wrapping grid sit on a different
70
+ rhythm than the columns. The container owns separation, so the panel drops
71
+ its own. Also stretches to the row's height so a short panel next to a tall
72
+ one does not leave a ragged bottom edge. */
73
+ /* Drops only the panel's own bottom margin — the row container owns separation.
74
+ Height is deliberately NOT forced here: whether panels in a row should match
75
+ heights depends on whether they are a SET (trio: equal peers, matching edge
76
+ reads as one band) or a PAIR of different things (duo: a 4-bar chart beside a
77
+ 5-row table, where forcing equality just relocates the dead space inside the
78
+ shorter panel). Each container decides via align-items below. */
79
+ .ds-panel-flush { margin-bottom: 0; }
80
+
81
+ /* Paired panels. Container-relative (the shell is the CQ container), so the
82
+ split collapses when the PANELS are cramped rather than when the viewport
83
+ crosses an arbitrary width — with a sidebar open those are far apart.
84
+ minmax(0,1fr) lets a scrolling table shrink instead of forcing overflow. */
85
+ .ds-panel-duo {
86
+ display: grid;
87
+ grid-template-columns: repeat(2, minmax(0, 1fr));
88
+ gap: calc(var(--space-3) * var(--density));
89
+ margin: calc(var(--space-5) * var(--density)) 0;
90
+ /* A duo pairs two DIFFERENT things (a 4-bar chart beside a 5-row table).
91
+ Stretching them to a common height does not create equality, it just moves
92
+ the shorter panel's dead space inside its own body. Each takes its natural
93
+ height and they align at the top, where the eye actually starts. */
94
+ align-items: start;
95
+ }
96
+ @container (max-width: 900px) {
97
+ .ds-panel-duo { grid-template-columns: minmax(0, 1fr); }
98
+ }
99
+ /* Fallback for kit roots that are not themselves a container (older kit shells
100
+ mount outside .app/.ds-stage); harmless where the CQ rule already applies. */
101
+ @media (max-width: 760px) {
102
+ .ds-panel-duo { grid-template-columns: minmax(0, 1fr); }
103
+ }
104
+
105
+ /* Three equal reference panels. Real grid tracks, NOT percentage flex-basis:
106
+ three 33.33% bases plus two gaps exceed 100% and wrap the last panel onto
107
+ its own row. Steps 3 -> 2 -> 1 on container width so the panels reflow when
108
+ they are actually cramped rather than at a viewport number that ignores how
109
+ much chrome the shell is holding. */
110
+ .ds-panel-trio {
111
+ display: grid;
112
+ grid-template-columns: repeat(3, minmax(0, 1fr));
113
+ gap: calc(var(--space-3) * var(--density));
114
+ margin: calc(var(--space-5) * var(--density)) 0;
115
+ /* A trio IS a set of equal peers, so a shared bottom edge reads as one band
116
+ rather than three ragged cards — the opposite call from .ds-panel-duo
117
+ above, and the reason these are two classes and not one. */
118
+ align-items: stretch;
119
+ }
120
+ .ds-panel-trio > .panel { height: 100%; }
121
+ @container (max-width: 1100px) {
122
+ .ds-panel-trio { grid-template-columns: repeat(2, minmax(0, 1fr)); }
123
+ }
124
+ @container (max-width: 720px) {
125
+ .ds-panel-trio { grid-template-columns: minmax(0, 1fr); }
126
+ }
23
127
  @media (max-width: 760px) {
24
- .ds-panel-duo { grid-template-columns: 1fr; }
128
+ .ds-panel-trio { grid-template-columns: minmax(0, 1fr); }
25
129
  }
26
130
  /* wide dense rows/tables scroll inside their panel, never the page */
27
131
  .ds-scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%; }
@@ -56,9 +160,28 @@
56
160
  @media (pointer: coarse) {
57
161
  .ds-deck-controls .btn { min-height: 44px; min-width: 44px; }
58
162
  }
163
+ /* Kit small-label voice — one local composition of the house `.t-label` recipe
164
+ (base.css) for every eyebrow/label in the appended kit blocks below. These
165
+ previously each restated the four declarations with drifted tracking
166
+ (0.1em / 0.06em / --tr-label) and drifted size (11px / --fs-micro); they now
167
+ share --fs-nano + --tr-label. Members declare only their real differences.
168
+ --fs-nano because these are dense kit chrome, never body or UI text. */
169
+ .ds-slide-eyebrow,
170
+ .ds-field-eyebrow,
171
+ .ds-auth-field-label,
172
+ .ds-lightbox-tag {
173
+ font-family: var(--ff-mono);
174
+ font-size: var(--fs-nano);
175
+ letter-spacing: var(--tr-label);
176
+ text-transform: uppercase;
177
+ color: var(--panel-text-3);
178
+ }
179
+
180
+ /* Deliberate overrides: the deck slide eyebrow sits on the app --fg ramp (it is
181
+ full-bleed slide chrome, not panel chrome) and owns its own bottom gutter. */
59
182
  .ds-slide-eyebrow {
60
- font-family: var(--ff-mono); font-size: 11px; letter-spacing: 0.1em;
61
- text-transform: uppercase; color: var(--fg-3); margin-bottom: clamp(12px, 2.5cqw, 24px);
183
+ color: var(--fg-3);
184
+ margin-bottom: clamp(12px, 2.5cqw, 24px);
62
185
  }
63
186
  .ds-slide-col { display: flex; flex-direction: column; justify-content: center; gap: clamp(12px, 2.5cqw, 24px); min-width: 0; }
64
187
  .ds-slide-col--start { align-items: flex-start; }
@@ -95,10 +218,24 @@
95
218
  .ds-slide-split { grid-template-columns: 1fr; }
96
219
  }
97
220
  .ds-slide-split-cell {
98
- padding: 18px; background: var(--bg-2); border-radius: var(--r-2);
221
+ padding: var(--space-3-5); background: var(--bg-2); border-radius: var(--r-2);
99
222
  color: var(--fg-2); font-size: var(--fs-lg); line-height: var(--lh-base); min-width: 0;
100
223
  }
101
- .ds-slide-split-cell--accent { border-left: 3px solid var(--slide-accent, var(--accent)); }
224
+ /* DELIBERATE accent spine this modifier's only job is to mark the emphasized
225
+ half of a split slide (see ui_kits/slide_deck/app.js), so the rule is the
226
+ whole component, not decoration on a card. Routed through the house
227
+ .panel-spine idiom (::before rail at --bw-chunk, squared leading corners)
228
+ instead of an off-scale 3px border-left. */
229
+ .ds-slide-split-cell--accent {
230
+ position: relative;
231
+ padding-left: calc(var(--space-3-5) + var(--bw-chunk));
232
+ border-radius: 0 var(--r-2) var(--r-2) 0;
233
+ }
234
+ .ds-slide-split-cell--accent::before {
235
+ content: ''; position: absolute; left: 0; top: 0; bottom: 0;
236
+ width: var(--bw-chunk); background: var(--slide-accent, var(--accent));
237
+ border-radius: var(--bw-chunk) 0 0 var(--bw-chunk);
238
+ }
102
239
 
103
240
  /* --- error 404 --------------------------------------------- */
104
241
  .ds-err-hero {
@@ -144,7 +281,7 @@
144
281
  display: flex; align-items: center; justify-content: center;
145
282
  padding: clamp(12px, 4vw, 32px);
146
283
  padding-bottom: max(clamp(12px, 4vw, 32px), env(safe-area-inset-bottom));
147
- z-index: 50;
284
+ z-index: var(--z-modal);
148
285
  }
149
286
  .ds-lightbox-card {
150
287
  background: var(--panel-0); border-radius: var(--r-2); padding: clamp(16px, 4vw, 28px);
@@ -258,15 +395,29 @@
258
395
 
259
396
  /* == appended: settings kit cleanup == */
260
397
  /* == appended: settings kit == */
261
- .ds-settings-main { padding: var(--space-2, 8px); }
262
- .ds-settings-body { padding: 14px 18px; }
263
- .ds-settings-body-stack { display: flex; flex-direction: column; gap: var(--space-2-5, 10px); }
264
- .ds-field-block { margin: var(--space-2-5, 10px) 0; }
265
- .ds-field-eyebrow {
266
- font-family: var(--ff-mono); font-size: 11px; letter-spacing: 0.06em;
267
- text-transform: uppercase; color: var(--panel-text-3);
268
- }
269
- .ds-hint-sm { font-size: 12px; color: var(--panel-text-2); }
398
+ /* .app-main already owns this region's padding; this only supplies the inset
399
+ that keeps panel shadows off the scroll edge (see .ds-section-pad). */
400
+ .ds-settings-main { padding: calc(var(--space-1) * var(--density)); }
401
+ /* .panel-body already pads the panel interior — this sat inside it and
402
+ double-padded every field, on an off-scale 14/18 pair. The body is now a
403
+ flow container that owns only the RHYTHM between fields, not a second inset. */
404
+ .ds-settings-body { padding: 0; }
405
+ .ds-settings-body-stack { display: flex; flex-direction: column; gap: calc(var(--space-2-5) * var(--density)); }
406
+ /* Field rhythm: the label/control/hint cluster is tight internally, so fields
407
+ need a clearly larger interval between them than within them, or the whole
408
+ form reads as one undifferentiated column. Margin-collapse-free (flow root
409
+ siblings) — first/last are zeroed so the panel body sets the outer edge. */
410
+ .ds-field-block { display: flex; flex-direction: column; gap: var(--space-1-75); margin: calc(var(--space-4) * var(--density)) 0 0; }
411
+ .ds-settings-body > .ds-field-block:first-child { margin-top: 0; }
412
+ /* Field width is a hint about expected input length. A name or handle stretched
413
+ across a 1000px desktop column tells the user to type a paragraph and leaves
414
+ the eye no left-to-right anchor; capping the control (not the row) keeps the
415
+ label/hint full-width for wrapping while the input reads as the right size.
416
+ Textareas keep a wider cap since prose genuinely uses the room. */
417
+ .ds-field-block > .input { max-width: 44ch; width: 100%; }
418
+ .ds-field-block > textarea.input { max-width: 72ch; }
419
+ /* .ds-field-eyebrow joins the kit small-label voice above with no overrides. */
420
+ .ds-hint-sm { font-size: var(--fs-xs); color: var(--panel-text-2); }
270
421
  .ds-note-quiet { margin: 0; color: var(--panel-text-2); }
271
422
  .ds-btn-row { display: flex; gap: var(--space-2, 8px); flex-wrap: wrap; }
272
423
  .ds-btn-row-tight { gap: var(--space-1-75, 6px); }
@@ -277,21 +428,31 @@
277
428
  /* Raw --mascot text measures 3.11:1 on paper (fails 4.5:1); --mascot-deep is
278
429
  the text-safe tone, same accent/accent-ink split used everywhere else. */
279
430
  .ds-btn-mascot { color: var(--mascot-deep); }
431
+ /* Sticky commit bar. It floats OVER the form it commits, so it needs real
432
+ elevation and a larger top margin than the form's own field rhythm —
433
+ otherwise it reads as one more field rather than as the action for all of
434
+ them. Spacing is density-scaled like the rest of the surface. */
280
435
  .ds-savebar {
281
- position: sticky; bottom: 8px; display: flex; align-items: center;
282
- justify-content: flex-end; gap: 8px; flex-wrap: wrap; padding: 10px;
283
- background: var(--panel-2); border-radius: var(--r-1); margin: 8px 0;
436
+ position: sticky; bottom: var(--space-2); z-index: var(--z-sticky);
437
+ display: flex; align-items: center;
438
+ justify-content: flex-end; gap: var(--space-2); flex-wrap: wrap;
439
+ padding: calc(var(--space-2-75) * var(--density)) calc(var(--space-3) * var(--density));
440
+ background: var(--panel-2); border-radius: var(--r-1);
441
+ margin: calc(var(--space-5) * var(--density)) 0 var(--space-2);
442
+ box-shadow: var(--shadow-2, var(--shadow-1));
284
443
  }
285
444
  .ds-savebar-note { flex: 1 1 160px; min-width: 0; color: var(--panel-text-2); }
286
- .ds-settings-modal { min-width: min(320px, calc(100vw - 32px)); max-width: 480px; }
287
- .ds-modal-body-form { padding: 16px; display: flex; flex-direction: column; gap: 12px; }
288
- .ds-modal-note { margin: 0 0 8px; color: var(--panel-text-2); font-size: 14px; }
445
+ .ds-settings-modal { min-width: min(320px, calc(100vw - var(--space-5))); max-width: 480px; }
446
+ .ds-modal-body-form { padding: var(--space-3); display: flex; flex-direction: column; gap: var(--space-2-75); }
447
+ .ds-modal-note { margin: 0; color: var(--panel-text-2); font-size: var(--fs-xs); }
289
448
  .ds-draft-preview {
290
- background: var(--panel-1); padding: 12px; border-radius: var(--r-0);
291
- font-size: 13px; color: var(--panel-text-2); max-height: 120px;
449
+ background: var(--panel-1); padding: var(--space-2-75); border-radius: var(--r-0);
450
+ font-size: var(--fs-micro); color: var(--panel-text-2); max-height: 120px;
292
451
  overflow-y: auto; font-family: var(--ff-mono); overflow-wrap: anywhere;
293
452
  }
294
- .ds-modal-actions { display: flex; gap: 8px; margin-top: 12px; justify-content: space-between; flex-wrap: wrap; }
453
+ /* Destructive-choice actions get a clearly larger interval than the body's own
454
+ gap — the decision must not read as the next line of the explanation. */
455
+ .ds-modal-actions { display: flex; gap: var(--space-2); margin-top: var(--space-3); justify-content: space-between; flex-wrap: wrap; }
295
456
 
296
457
  /* == appended: signin kit cleanup == */
297
458
  /* -- signin kit (ui_kits/signin) -- */
@@ -303,7 +464,7 @@
303
464
  .ds-auth-sent-title { margin: 0; font-weight: 600; }
304
465
  .ds-auth-sent-sub { margin: 0; color: var(--panel-text-2); overflow-wrap: anywhere; }
305
466
  .ds-auth-field { display: flex; flex-direction: column; gap: 4px; }
306
- .ds-auth-field-label { font-family: var(--ff-mono); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--panel-text-3); }
467
+ /* .ds-auth-field-label joins the kit small-label voice above, no overrides. */
307
468
  .ds-auth-row-between { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; }
308
469
  .ds-auth-remember { display: flex; align-items: center; gap: 6px; cursor: pointer; }
309
470
  .ds-auth-remember-text { color: var(--panel-text-2); font-size: 13px; }
@@ -335,7 +496,7 @@
335
496
  .ds-gal-swatch-name { color: var(--panel-text); }
336
497
  .ds-gal-swatch-hint { color: var(--panel-text-3); overflow-wrap: anywhere; }
337
498
  .ds-lightbox-head { display: flex; justify-content: space-between; align-items: center; gap: 8px; flex-wrap: wrap; }
338
- .ds-lightbox-tag { font-family: var(--ff-mono); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--panel-text-3); }
499
+ /* .ds-lightbox-tag joins the kit small-label voice above, no overrides. */
339
500
 
340
501
  /* --- error_404 kit --- */
341
502
  .ds-err-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; margin-top: 6px; }
@@ -351,10 +512,18 @@
351
512
  .ds-cli-warn .prompt, .ds-cli-warn .cmd { color: var(--mascot-deep); }
352
513
  .ds-cli-log .prompt { color: var(--fg-3); }
353
514
  .ds-cli-log .cmd { color: var(--fg-2); font-family: var(--ff-mono); }
354
- .ds-term-body { padding: 14px 18px; display: flex; flex-direction: column; gap: 4px; background: var(--bg-2); border-radius: var(--r-1); }
515
+ .ds-term-body { padding: var(--space-2-75) var(--space-3-5); display: flex; flex-direction: column; gap: var(--space-1); background: var(--bg-2); border-radius: var(--r-1); }
355
516
  .ds-term-body--tall { min-height: 280px; }
356
- .ds-term-input-row { margin-top: 6px; }
357
- .ds-term-input { flex: 1; min-width: 0; background: transparent; border: 0; outline: 0; font-family: var(--ff-mono); font-size: 13px; color: var(--fg); }
517
+ .ds-term-input-row { margin-top: var(--space-1-75); }
518
+ .ds-term-input { flex: 1; min-width: 0; background: transparent; border: 0; outline: 0; font-family: var(--ff-mono); font-size: var(--fs-micro); color: var(--fg); }
519
+ /* The input itself is a prompt line, not a form control, so it is NOT floored
520
+ to 44px (that would break the terminal's line rhythm). Instead the whole
521
+ prompt ROW becomes the tap target, and the font goes to 16px so focusing it
522
+ does not trigger iOS zoom — a sub-16px input is what causes that. */
523
+ @media (pointer: coarse) {
524
+ .ds-term-input-row { min-height: 44px; align-items: center; }
525
+ .ds-term-input { font-size: max(16px, var(--fs-micro)); }
526
+ }
358
527
  /* full-viewport render root (community-app) */
359
528
  .ds-root-viewport { height: 100vh; height: 100dvh; }
360
529
 
@@ -418,7 +587,7 @@
418
587
  }
419
588
  .ds-sheet-preview-table thead th {
420
589
  position: sticky; top: 0; background: var(--bg-2); color: var(--fg-3);
421
- font-weight: 600; z-index: 1;
590
+ font-weight: 600; z-index: var(--z-sticky);
422
591
  }
423
592
  .ds-sheet-preview-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
424
593
  .ds-sheet-preview-empty { padding: var(--space-3, 16px); color: var(--fg-3); }
@@ -429,3 +598,188 @@
429
598
  }
430
599
  .ds-sheet-preview-error-action:hover { background: var(--bg-2); }
431
600
  .ds-sheet-preview-error-action:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: 2px; }
601
+
602
+ /* == appended: community kit message list == */
603
+ /* ui_kits/community mounts CommunityShell (the .cm-* family) but renders its
604
+ OWN message list in ds-* markup, and its index.html links app-shell.css
605
+ WITHOUT community.css. So these live here, not in community.css: the .cm-*
606
+ sheet never reaches this page, and .ds-community-* is ds-family markup by
607
+ the lint-classes taxonomy anyway. Every rule below deliberately mirrors the
608
+ .chat-msg / .chat-avatar / .chat-stack primitives in chat-polish.css so the
609
+ community thread and the chat kit read as one message language. */
610
+
611
+ /* Page root — owns the full viewport column so CommunityShell's own 100dvh
612
+ flex shell has a height to resolve against, with the topbar/status chrome
613
+ stacked around it. */
614
+ .ds-community-page {
615
+ display: flex;
616
+ flex-direction: column;
617
+ min-height: 100vh;
618
+ min-height: 100dvh;
619
+ min-width: 0;
620
+ }
621
+ /* Main column inside the shell — .cm-main's ds-family counterpart: header
622
+ pinned, message list taking the remaining height and doing the scrolling. */
623
+ .ds-community-main {
624
+ display: flex;
625
+ flex-direction: column;
626
+ flex: 1;
627
+ min-width: 0;
628
+ min-height: 0;
629
+ }
630
+
631
+ /* Thread scroller. Same rhythm as .chat-thread (column + --space-3 gap +
632
+ own overflow), so messages breathe on the same interval as the chat kit. */
633
+ .ds-community-messages {
634
+ display: flex;
635
+ flex-direction: column;
636
+ gap: var(--space-3);
637
+ flex: 1;
638
+ min-height: 0;
639
+ overflow-y: auto;
640
+ padding: var(--space-3);
641
+ scrollbar-width: thin;
642
+ scrollbar-color: var(--bg-3) transparent;
643
+ }
644
+ .ds-community-messages::-webkit-scrollbar { width: 8px; }
645
+ .ds-community-messages::-webkit-scrollbar-thumb { background: var(--bg-3); border-radius: var(--r-0); }
646
+
647
+ /* One message — avatar rail + body stack, matching .chat-msg's flex/gap and
648
+ its quiet tonal hover (no border, per the zero-border aesthetic). */
649
+ .ds-community-msg {
650
+ display: flex;
651
+ align-items: flex-start;
652
+ gap: var(--space-2-75);
653
+ padding: var(--space-1-75) var(--space-2);
654
+ margin: 0;
655
+ border-radius: var(--r-1);
656
+ min-width: 0;
657
+ transition: background var(--dur-base) var(--ease);
658
+ }
659
+ .ds-community-msg:hover { background: color-mix(in oklab, var(--fg) 4%, transparent); }
660
+
661
+ /* Identity disc. The inline `background:` from app.js supplies the per-author
662
+ tone; this owns the SHAPE — a circle, which is the whole reason the avatar
663
+ was rendering as a coloured bar with no rule. Footprint + type match
664
+ .chat-avatar exactly. */
665
+ .ds-community-avatar {
666
+ flex: 0 0 36px;
667
+ width: 36px;
668
+ height: 36px;
669
+ border-radius: 50%;
670
+ display: inline-flex;
671
+ align-items: center;
672
+ justify-content: center;
673
+ overflow: hidden;
674
+ color: var(--ink);
675
+ font-family: var(--ff-mono);
676
+ font-size: var(--fs-micro);
677
+ font-weight: 600;
678
+ line-height: 1;
679
+ user-select: none;
680
+ }
681
+
682
+ /* Body stack — name + time on one baseline, text beneath. .chat-stack's
683
+ column/gap idiom, without the bubble max-width (this is a flat list row,
684
+ not a two-sided bubble thread). */
685
+ .ds-community-msg-body {
686
+ display: flex;
687
+ flex-wrap: wrap;
688
+ align-items: baseline;
689
+ gap: var(--space-1) var(--space-2);
690
+ flex: 1;
691
+ min-width: 0;
692
+ }
693
+ /* Author. The inline `color:` from app.js carries the per-author tone; weight
694
+ and size are what make it read as the row's title. */
695
+ .ds-community-msg-name {
696
+ font-weight: 600;
697
+ font-size: var(--fs-sm);
698
+ line-height: 1.3;
699
+ }
700
+ /* Timestamp — the quiet mono meta voice used by .cm-user-tag / row .meta. */
701
+ .ds-community-msg-time {
702
+ font-family: var(--ff-mono);
703
+ font-size: var(--fs-micro);
704
+ color: var(--fg-3);
705
+ line-height: 1.3;
706
+ }
707
+ /* Message body claims the full second line of the wrapping body row. */
708
+ .ds-community-msg-text {
709
+ flex: 1 0 100%;
710
+ margin: 0;
711
+ min-width: 0;
712
+ color: var(--fg-2);
713
+ font-size: var(--fs-sm);
714
+ line-height: var(--lh-base);
715
+ overflow-wrap: anywhere;
716
+ }
717
+
718
+ @media (prefers-reduced-motion: reduce) {
719
+ .ds-community-msg { transition: none; }
720
+ }
721
+
722
+ /* == appended: cross-kit note / swatch / chat-page / caret == */
723
+
724
+ /* "about this kit" explanatory block — 8 kits render a run of <p> lines here
725
+ (often carrying inline Chip()/<code>) inside a .panel body. This is
726
+ SECONDARY copy annotating the demo above it, so it takes the quiet voice:
727
+ one step down from body, on the --fg-2 ramp, with a comfortable reading
728
+ line-height. It owns only the RHYTHM between its lines; the panel body still
729
+ owns the inset, so this must not re-pad. */
730
+ .ds-pattern-notes {
731
+ display: flex;
732
+ flex-direction: column;
733
+ gap: var(--space-2);
734
+ color: var(--fg-2);
735
+ font-size: var(--fs-sm);
736
+ line-height: var(--lh-base);
737
+ max-width: var(--measure);
738
+ min-width: 0;
739
+ }
740
+ .ds-pattern-notes p { margin: 0; overflow-wrap: anywhere; }
741
+ /* Inline literals inside a note read as quoted machinery, not as body copy —
742
+ same tonal chip treatment .chat-bubble code uses. */
743
+ .ds-pattern-notes code {
744
+ font-family: var(--ff-mono);
745
+ font-size: 0.92em;
746
+ background: color-mix(in oklab, var(--fg) 8%, transparent);
747
+ padding: 0 var(--space-1);
748
+ border-radius: var(--r-hair);
749
+ }
750
+ .ds-pattern-notes strong { color: var(--fg); font-weight: 600; }
751
+
752
+ /* Swatch cell wrapper (system_primer pairs it with .ds-swatch-col, which owns
753
+ the column/gap). This adds only what the wrapper itself needs: a minimum-zero
754
+ track so long token names ellipsize inside the grid instead of widening it. */
755
+ .ds-swatch { min-width: 0; }
756
+ .ds-swatch .ds-swatch-name { overflow-wrap: anywhere; }
757
+
758
+ /* Chat kit page root — the thread must own the leftover column height rather
759
+ than growing the page, so the composer stays on screen. Pairs with
760
+ .ds-chat-layout (flex column, flex:1) which is already defined above. */
761
+ .chat-kit-page {
762
+ display: flex;
763
+ flex-direction: column;
764
+ flex: 1;
765
+ min-height: 0;
766
+ min-width: 0;
767
+ }
768
+
769
+ /* Terminal caret — a solid block that sits on the last demo line. Sized in ch
770
+ so it tracks the mono column it follows. The blink is motion, so it is
771
+ declared ONLY inside prefers-reduced-motion: no-preference (the same
772
+ opt-in shape .cm-ch-spinner / .cm-vs-label::before use); with reduced
773
+ motion the caret is a steady block, still marking the cursor position. */
774
+ .cursor-blink {
775
+ display: inline-block;
776
+ width: 1ch;
777
+ height: 1em;
778
+ margin-left: var(--space-1);
779
+ background: var(--accent);
780
+ vertical-align: text-bottom;
781
+ }
782
+ @media (prefers-reduced-motion: no-preference) {
783
+ .cursor-blink { animation: ds-cursor-blink 1s step-end infinite; }
784
+ @keyframes ds-cursor-blink { 50% { opacity: 0; } }
785
+ }
@@ -55,7 +55,12 @@
55
55
  }
56
56
  .panel-body { padding: var(--space-2) var(--space-3) var(--space-3); }
57
57
  .panel-body > * { margin: 0; }
58
- .panel-body > * + * { margin-top: var(--space-3); }
58
+ /* Inner sibling rhythm scales with density like the outer panel rhythm, so a
59
+ compact/spacious surface stays proportional instead of only its outer
60
+ intervals moving while the panel interiors hold still. Kept strictly below
61
+ the outer .ds-panel-gap interval at every density — that ordering is what
62
+ makes proximity report grouping truthfully. */
63
+ .panel-body > * + * { margin-top: calc(var(--space-3) * var(--density)); }
59
64
  .panel.panel-wide .panel-body { padding: 0; }
60
65
 
61
66
  /* ============================================================
@@ -3,7 +3,7 @@
3
3
  ============================================================ */
4
4
 
5
5
  .ds-plugins-overlay {
6
- position: fixed; inset: 0; z-index: var(--z-modal, 1000);
6
+ position: fixed; inset: 0; z-index: var(--z-modal);
7
7
  background: color-mix(in oklab, var(--ink) 35%, transparent);
8
8
  display: flex; align-items: center; justify-content: center;
9
9
  padding: var(--space-3);
@@ -22,8 +22,9 @@
22
22
  width: 280px; max-width: 80%;
23
23
  transform: translateX(-100%);
24
24
  transition: transform var(--dur-base, 240ms) var(--ease, ease);
25
- /* Above the sticky chrome (z-header): scrim > chrome > content budget. */
26
- z-index: calc(var(--z-header, 100) + 2);
25
+ /* Drawer rung, above the sticky chrome it slides over. +1 puts the panel
26
+ itself over its own scrim (--z-drawer), which is the layer below it. */
27
+ z-index: calc(var(--z-drawer) + 1);
27
28
  background: var(--bg);
28
29
  border-right: 1px solid color-mix(in oklab, var(--fg) 8%, transparent);
29
30
  overflow-y: auto;
@@ -64,8 +65,9 @@
64
65
  display: none;
65
66
  position: absolute; inset: 0;
66
67
  background: color-mix(in oklab, var(--bg) 60%, transparent);
67
- /* Above the sticky chrome so nav behind an open drawer is not clickable. */
68
- z-index: calc(var(--z-header, 100) + 1);
68
+ /* Above the sticky chrome so nav behind an open drawer is not clickable.
69
+ Base of the drawer rung — the panel itself sits one above this. */
70
+ z-index: var(--z-drawer);
69
71
  }
70
72
  @container (max-width: 900px) {
71
73
  .app-body.side-open .app-side-scrim { display: block; }
@@ -77,9 +79,10 @@
77
79
  .app-side-toggle {
78
80
  display: none;
79
81
  position: absolute; top: calc((var(--app-topbar-h) - 44px) / 2); left: 10px;
80
- /* Above the sticky topbar/chrome (z-header) and the drawer/scrim so the
81
- translucent blurred chrome never paints over or intercepts the toggle. */
82
- z-index: calc(var(--z-header, 100) + 3);
82
+ /* Above the sticky topbar/chrome and the drawer/scrim so the translucent
83
+ blurred chrome never paints over or intercepts the toggle. Top of the
84
+ drawer rung: scrim < panel < toggle. */
85
+ z-index: calc(var(--z-drawer) + 2);
83
86
  /* 44x44 minimum hit area (WCAG 2.5.5 / Apple HIG) - 36px is below the
84
87
  reliable-tap threshold on touch. */
85
88
  width: 44px; height: 44px;
@@ -181,7 +184,7 @@
181
184
  /* Column resize handles: a thin keyboard-accessible separator pinned to the
182
185
  right edge of each chrome track; drag (or ArrowLeft/Right) writes a clamped
183
186
  inline --ws-*-w. */
184
- .ws-resizer { grid-row: 1 / -1; align-self: stretch; justify-self: end; width: 8px; margin-right: -4px; z-index: 5; cursor: col-resize; background: transparent; border: none; padding: 0; touch-action: none; }
187
+ .ws-resizer { grid-row: 1 / -1; align-self: stretch; justify-self: end; width: 8px; margin-right: -4px; z-index: var(--z-raised); cursor: col-resize; background: transparent; border: none; padding: 0; touch-action: none; }
185
188
  .ws-resizer::after { content: ""; display: block; width: 2px; height: 100%; margin: 0 auto; background: transparent; transition: background var(--dur-snap) var(--ease); }
186
189
  .ws-resizer:hover::after, .ws-resizer:focus-visible::after { background: var(--accent); }
187
190
  .ws-resizer:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: -2px; }
@@ -370,7 +373,7 @@
370
373
  .ws-pane {
371
374
  position: fixed; inset: 0 0 0 auto;
372
375
  width: min(340px, 85vw);
373
- z-index: 42; transform: translateX(110%);
376
+ z-index: calc(var(--z-drawer) + 1); transform: translateX(110%);
374
377
  transition: transform var(--dur-base) var(--ease);
375
378
  }
376
379
  .ws-shell.ws-pane-open .ws-pane { transform: translateX(0); width: min(340px, 85vw); }
@@ -379,7 +382,7 @@
379
382
  .ws-pane-drawer-toggle { display: inline-flex; }
380
383
  /* Scrim is always present from this stage down so it FADES with the drawer
381
384
  instead of hard-appearing; pointer-events gate keeps it inert while closed. */
382
- .ws-scrim { display: block; opacity: 0; pointer-events: none; position: fixed; inset: 0; z-index: 41; background: color-mix(in srgb, var(--fg) 38%, transparent); transition: opacity var(--dur-base) var(--ease); }
385
+ .ws-scrim { display: block; opacity: 0; pointer-events: none; position: fixed; inset: 0; z-index: var(--z-drawer); background: color-mix(in srgb, var(--fg) 38%, transparent); transition: opacity var(--dur-base) var(--ease); }
383
386
  .ws-shell.ws-sessions-open .ws-scrim, .ws-shell.ws-pane-open .ws-scrim { opacity: 1; pointer-events: auto; }
384
387
  }
385
388
 
@@ -401,7 +404,7 @@
401
404
  /* Immune to the desktop collapse var (--ws-sessions-w:0px persisted from
402
405
  a desktop session would open an invisible 0-width drawer). */
403
406
  width: min(max(var(--ws-sessions-w), 280px), 80vw);
404
- z-index: 42; transform: translateX(-110%);
407
+ z-index: calc(var(--z-drawer) + 1); transform: translateX(-110%);
405
408
  transition: transform var(--dur-base) var(--ease);
406
409
  border-right: var(--bw-hair) solid var(--bg-3);
407
410
  overscroll-behavior: contain;
@@ -428,7 +431,9 @@
428
431
  grid-template-columns: minmax(0, 1fr);
429
432
  grid-template-areas: "content";
430
433
  }
431
- .ws-rail { position: fixed; inset: 0 auto 0 0; width: var(--ws-rail-w-collapsed); z-index: 40; }
434
+ /* Persistent chrome, not a drawer: it stays put while the session/pane
435
+ drawers slide over it, so it sits on the header rung below them. */
436
+ .ws-rail { position: fixed; inset: 0 auto 0 0; width: var(--ws-rail-w-collapsed); z-index: var(--z-header); }
432
437
  .ws-rail .ws-rail-brand,
433
438
  .ws-rail .ws-rail-action-label,
434
439
  .ws-rail .ws-rail-item-label,
@@ -147,7 +147,12 @@ input[type="checkbox"], input[type="radio"] {
147
147
  min-height: 120px;
148
148
  display: flex; align-items: center; justify-content: center;
149
149
  text-align: center; color: var(--fg-3);
150
- transition: all var(--dur-snap) var(--ease);
150
+ /* Explicit list, never `all` — the only things `.drop-zone.active` changes
151
+ are the dashed border tone, the tinted fill and the label colour. `all`
152
+ also animated padding/min-height/border-width, so a drop target visibly
153
+ resized mid-drag instead of snapping. */
154
+ transition: background var(--dur-snap) var(--ease), border-color var(--dur-snap) var(--ease),
155
+ color var(--dur-snap) var(--ease);
151
156
  }
152
157
  .drop-zone.active {
153
158
  border-color: var(--accent);
@@ -187,7 +192,7 @@ input[type="checkbox"], input[type="radio"] {
187
192
  border-radius: var(--r-2);
188
193
  box-shadow: var(--shadow-overlay);
189
194
  min-width: 160px;
190
- z-index: calc(var(--z-overlay, 1000) + 1);
195
+ z-index: var(--z-dropdown);
191
196
  padding: 4px 0;
192
197
  }
193
198
 
@@ -566,7 +571,7 @@ input[type="password"]:not(:placeholder-shown) + .input-clear {
566
571
  position: absolute; top: -40px; left: 0;
567
572
  background: var(--fg); color: var(--bg);
568
573
  padding: 8px 16px; border-radius: var(--r-1);
569
- text-decoration: none; z-index: var(--z-overlay, 1000);
574
+ text-decoration: none; z-index: var(--z-top);
570
575
  }
571
576
  .skip-to-main:focus { top: 10px; }
572
577
 
@@ -689,7 +694,7 @@ input[type="password"]:not(:placeholder-shown) + .input-clear {
689
694
  padding: var(--space-3) var(--space-4);
690
695
  max-width: 320px;
691
696
  box-shadow: var(--shadow-overlay);
692
- z-index: calc(var(--z-overlay, 1000) + 10);
697
+ z-index: var(--z-toast);
693
698
  }
694
699
 
695
700
  @keyframes toast-slide-in {
@@ -15,7 +15,7 @@
15
15
  padding: var(--space-2, 8px) 0;
16
16
  padding-left: env(safe-area-inset-left, 0px);
17
17
  gap: var(--space-2, 8px);
18
- z-index: 10000;
18
+ z-index: var(--z-dock);
19
19
  pointer-events: auto;
20
20
  font: 11px var(--os-mono);
21
21
  color: var(--os-fg);