@starci/grammar 0.17.1 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +26 -8
  2. package/dist/.build-stamp.json +4 -4
  3. package/dist/common/components-forms.css +8 -8
  4. package/dist/common/components-navigation.css +146 -34
  5. package/dist/common/components-overlays.css +13 -15
  6. package/dist/common/components-spacing.css +97 -0
  7. package/dist/common/index.d.ts +1 -1
  8. package/dist/common/index.d.ts.map +1 -1
  9. package/dist/common/index.js +1 -1
  10. package/dist/common/index.js.map +1 -1
  11. package/dist/common/registry.d.ts +15 -3
  12. package/dist/common/registry.d.ts.map +1 -1
  13. package/dist/common/registry.js +3 -2
  14. package/dist/common/registry.js.map +1 -1
  15. package/dist/common/renderers-navigation.d.ts +4 -4
  16. package/dist/common/renderers-navigation.d.ts.map +1 -1
  17. package/dist/common/renderers-navigation.js +1 -1
  18. package/dist/common/renderers-navigation.js.map +1 -1
  19. package/dist/common/renderers.d.ts +5 -1
  20. package/dist/common/renderers.d.ts.map +1 -1
  21. package/dist/common/renderers.js +4 -0
  22. package/dist/common/renderers.js.map +1 -1
  23. package/dist/common/spacing.d.ts +32 -2
  24. package/dist/common/spacing.d.ts.map +1 -1
  25. package/dist/common/spacing.js +29 -2
  26. package/dist/common/spacing.js.map +1 -1
  27. package/dist/common/styles.css +226 -79
  28. package/dist/core/branch/FlowGraph/index.d.ts +21 -7
  29. package/dist/core/branch/FlowGraph/index.d.ts.map +1 -1
  30. package/dist/core/branch/FlowGraph/index.js +236 -42
  31. package/dist/core/branch/FlowGraph/index.js.map +1 -1
  32. package/dist/core/branch/FlowGraph/layout.d.ts +58 -0
  33. package/dist/core/branch/FlowGraph/layout.d.ts.map +1 -1
  34. package/dist/core/branch/FlowGraph/layout.js +198 -0
  35. package/dist/core/branch/FlowGraph/layout.js.map +1 -1
  36. package/dist/core/branch/FlowGraph/types.d.ts +33 -3
  37. package/dist/core/branch/FlowGraph/types.d.ts.map +1 -1
  38. package/dist/core/branch/Rail/index.d.ts +1 -1
  39. package/dist/core/branch/Rail/index.d.ts.map +1 -1
  40. package/dist/core/branch/Rail/index.js +1 -1
  41. package/dist/core/branch/Rail/index.js.map +1 -1
  42. package/dist/core/branch/Tabs/index.d.ts +4 -4
  43. package/dist/core/branch/Tabs/index.d.ts.map +1 -1
  44. package/dist/core/branch/Tabs/index.js +1 -3
  45. package/dist/core/branch/Tabs/index.js.map +1 -1
  46. package/dist/core/composite/Stepper/index.d.ts +7 -2
  47. package/dist/core/composite/Stepper/index.d.ts.map +1 -1
  48. package/dist/core/composite/Stepper/index.js +1 -1
  49. package/dist/core/composite/Stepper/index.js.map +1 -1
  50. package/dist/core/composition/Cluster/index.d.ts +23 -0
  51. package/dist/core/composition/Cluster/index.d.ts.map +1 -0
  52. package/dist/core/composition/Cluster/index.js +8 -0
  53. package/dist/core/composition/Cluster/index.js.map +1 -0
  54. package/dist/core/composition/Grid/index.d.ts +32 -0
  55. package/dist/core/composition/Grid/index.d.ts.map +1 -0
  56. package/dist/core/composition/Grid/index.js +19 -0
  57. package/dist/core/composition/Grid/index.js.map +1 -0
  58. package/dist/core/composition/Page/index.js +1 -1
  59. package/dist/core/composition/Page/index.js.map +1 -1
  60. package/dist/core/composition/Stack/index.d.ts +30 -0
  61. package/dist/core/composition/Stack/index.d.ts.map +1 -0
  62. package/dist/core/composition/Stack/index.js +23 -0
  63. package/dist/core/composition/Stack/index.js.map +1 -0
  64. package/dist/core/dna.d.ts +7 -30
  65. package/dist/core/dna.d.ts.map +1 -1
  66. package/dist/core/dna.js +3 -17
  67. package/dist/core/dna.js.map +1 -1
  68. package/dist/core/primitive/Button/index.js +7 -2
  69. package/dist/core/primitive/Button/index.js.map +1 -1
  70. package/dist/core/primitive/ConnectionStatus/index.d.ts +15 -0
  71. package/dist/core/primitive/ConnectionStatus/index.d.ts.map +1 -0
  72. package/dist/core/primitive/ConnectionStatus/index.js +15 -0
  73. package/dist/core/primitive/ConnectionStatus/index.js.map +1 -0
  74. package/dist/core/primitive/Progress/index.d.ts +9 -2
  75. package/dist/core/primitive/Progress/index.d.ts.map +1 -1
  76. package/dist/core/primitive/Progress/index.js +3 -3
  77. package/dist/core/primitive/Progress/index.js.map +1 -1
  78. package/dist/core/styles.css +1 -1
  79. package/dist/offset-pop/components-navigation.css +1 -1
  80. package/dist/offset-pop/dna.d.ts +7 -53
  81. package/dist/offset-pop/dna.d.ts.map +1 -1
  82. package/dist/offset-pop/dna.js +3 -17
  83. package/dist/offset-pop/dna.js.map +1 -1
  84. package/package.json +1 -1
package/README.md CHANGED
@@ -214,7 +214,7 @@ Each group below matches a Storybook group. Run `npm run storybook`: every story
214
214
  the family and theme chosen in the toolbar. It shows the default, disabled, invalid, pending and
215
215
  selected/current states where they apply. Overlay stories open by default.
216
216
 
217
- **Primitives (28).** Avatar, Badge, Button, CloseButton, Divider, GrammarRoot, Heading, Icon,
217
+ **Primitives (29).** Avatar, Badge, Button, CloseButton, ConnectionStatus, Divider, GrammarRoot, Heading, Icon,
218
218
  IconButton, IconTile, Image, IncludedMark, Kbd, Label, LeadingNumber, Link, Mascot, Meter, PlainPressable,
219
219
  Progress, ProgressCircle, RankArtwork, RelativeTime, Skeleton, Spinner, StateMark, Text, TextAction.
220
220
 
@@ -409,8 +409,8 @@ The package requires Node 22 or newer, as does the pinned DiceBear engine.
409
409
  MarkdownTableFrame, MediaFrame, Rail, SectionHeader, StaticStateRow, SurfaceAccordionCard,
410
410
  SurfaceCard, SurfaceCopyGroup, SurfaceListCard, VerticalScrollRegion.
411
411
 
412
- **Compositions (12).** BottomNav, Footer, NavigationFeatureNav, Page, PageContainer, PageHeader,
413
- PaneGrid, PaneSection, PrimaryRailLayout, Sidebar, TopBar, WorkspaceShell. Storybook pins settings and data-list content
412
+ **Compositions (15).** BottomNav, Cluster, Footer, Grid, NavigationFeatureNav, Page, PageContainer, PageHeader,
413
+ PaneGrid, PaneSection, PrimaryRailLayout, Sidebar, Stack, TopBar, WorkspaceShell. Storybook pins settings and data-list content
414
414
  recipes to each family; the mobile app recipes reuse the fixed App shell with its left navigation drawer.
415
415
 
416
416
  `PageHeader` heads one page: `breadcrumbs`, `title` (the page's `h1`), `description`, `meta`, `status` chips beside the
@@ -444,9 +444,9 @@ exactly one `PaneGrid` attached flush to the band's separator, reaching both con
444
444
  and arrays of Panes are legal; anything else throws `PANE_GRID_CHILD_NOT_PANE` in development). A `Pane` owns its
445
445
  content inset (`inset="content"`, default, or `"flush"` for a nested PaneGrid or a full-bleed canvas) and takes no
446
446
  `className`: it never pads itself, it publishes the inset to its descendants so dividers and scrollbars stay full-bleed.
447
- The inline inset is `--grammar-pane-inset-inline` (1rem) on both sides at every breakpoint; the block start is
448
- `--grammar-pane-inset-block-start` (0.75rem); the block end is `--grammar-pane-inset-block-end-outer` (1rem) when the
449
- pane touches the grid's bottom edge and `--grammar-pane-inset-block-end-divider` (0.75rem) when it faces a divider.
447
+ Every side takes the container inset `--grammar-inset` (0.75rem) at every breakpoint, through
448
+ `--grammar-pane-inset-inline`, `--grammar-pane-inset-block-start`, `--grammar-pane-inset-block-end-outer` (the grid's
449
+ bottom edge) and `--grammar-pane-inset-block-end-divider` (a divider below).
450
450
  A `collapse="drawer"` pane renders its children in the drawer under the same inset.
451
451
 
452
452
  `PaneSection` is the one band primitive of a pane: every child of a Pane is a `PaneSection` or one nested `PaneGrid`.
@@ -462,8 +462,26 @@ box keeps the vendor's own padding. In a Pane, `ListBox` resolves `inset="panel"
462
462
  padding so the row box and the selected pill start at the edge, and a `Tabs variant="secondary"` track drops its inline
463
463
  padding so the first pill does.
464
464
 
465
- Card padding has one place per axis too: `--grammar-card-padding-inline` and `--grammar-card-padding-block` (1rem each)
466
- pad the content of `SurfaceCard` and its kin.
465
+ **Spacing.** One scale, 0 4 8 12 24 32 (`COMMON_SPACING_SCALE`), and every value is a root token named by its role:
466
+ gaps `--grammar-identity-gap` (4), `--grammar-inline-gap` (8), `--grammar-row-gap` (12), `--grammar-section-gap` (24)
467
+ and `--grammar-region-gap` (32) (`COMMON_GAP_ROLES`); insets `--grammar-inset` (12, every container: card, pane,
468
+ overlay, band), `--grammar-inset-nested` (8, a box inside a card) and `--grammar-inset-open` (24, an empty state or a
469
+ drop target); the page gutter `--grammar-page-inset` (12, 24 from 48rem, 32 from 70rem); and the control inline
470
+ padding `--grammar-control-padding-inline-{sm,md,lg}` (8, 12, 24) with `--grammar-control-padding-glyph-*` one step
471
+ less on a glyph's side. `--grammar-card-padding-inline`, `--grammar-card-padding-block` and `--grammar-overlay-inset`
472
+ read `--grammar-inset`. Vendor parts take their step in `src/common/components-spacing.css`. Tabs are the underline
473
+ bar everywhere, the first label on the content edge.
474
+
475
+ An app writes no spacing value; it names the relation of the blocks it places:
476
+
477
+ - `Stack` (`gap`: `identity` | `inline` | `row` | `section` | `region`, required; `align`; `measure`: `full` |
478
+ `reading` | `form`; `as`): blocks in the block axis.
479
+ - `Cluster` (`gap`: `identity` | `inline` | `row` | `section`, the same on both axes; `wrap`, default true; `align`;
480
+ `justify`): items side by side that wrap, a chip set, a toolbar group.
481
+ - `Grid` (`kind="cards"` with `columns?: 2 | 3 | 4`, the section gap on both axes; `kind="form"` renders `FormGrid`;
482
+ `kind="main-rail"` renders `PrimaryRailLayout` with its props).
483
+
484
+ None takes a `className` or a `style`.
467
485
 
468
486
  ```tsx
469
487
  <Page measure="reading" header={{ breadcrumbs, title: "Profile", tabs: { label: "Profile", items, selectedKey, onSelect } }}>
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schema": "starci/grammar-build-stamp@1",
3
3
  "package": "@starci/grammar",
4
- "version": "0.17.1",
4
+ "version": "0.19.0",
5
5
  "algorithm": "starci/grammar-source-digest@1",
6
- "sourceDigest": "sha256:1415cc469a0bd9438d2bfe9cdbee3ba143d5c7d458ed3e10938f140f4e0b89f4",
7
- "distDigest": "sha256:dc7ca20b82316d1cb1efa88121ad1d834ffa10f38948319079f1f2ffd8acfd38",
8
- "builtAt": "2026-10-31T12:36:26.842Z"
6
+ "sourceDigest": "sha256:cbb2fa9919a6912261ffbebc3a6f3e5efe7aaa8110f1d928768d787c1d6d7ef1",
7
+ "distDigest": "sha256:6da9e874645d92ab40db8717793e26a5fa08aef70c7e9163716bafb2c95b9f36",
8
+ "builtAt": "2026-10-31T16:16:39.196Z"
9
9
  }
@@ -278,7 +278,7 @@
278
278
  inline-size: 100%;
279
279
  min-width: 0;
280
280
  flex-direction: column;
281
- gap: var(--grammar-section-gap, 1rem);
281
+ gap: var(--grammar-row-gap, 0.75rem);
282
282
  container-type: inline-size;
283
283
  }
284
284
 
@@ -287,7 +287,7 @@
287
287
  min-width: 0;
288
288
  grid-template-columns: minmax(0, 1fr);
289
289
  grid-auto-rows: auto;
290
- gap: 0 var(--grammar-section-gap, 1rem);
290
+ gap: 0 var(--grammar-row-gap, 0.75rem);
291
291
  align-items: start;
292
292
  }
293
293
 
@@ -302,7 +302,7 @@
302
302
  }
303
303
 
304
304
  :is(.starci-core-form-grid-row > [data-component]:not(:first-child), .starci-core-form-grid-row > .starci-core-field-skeleton-scope:not(:first-child) > [data-component]) {
305
- margin-block-start: var(--grammar-section-gap, 1rem);
305
+ margin-block-start: var(--grammar-row-gap, 0.75rem);
306
306
  }
307
307
 
308
308
  :is(.starci-core-form-grid-row > [data-component], .starci-core-form-grid-row > .starci-core-field-skeleton-scope > [data-component]) > :is([data-slot="label"], [data-grammar-field-label]) {
@@ -426,7 +426,7 @@
426
426
 
427
427
  [data-grammar-orientation="horizontal"] > .starci-core-choice-group-options {
428
428
  flex-flow: row wrap;
429
- gap: 0.75rem 1rem;
429
+ gap: var(--grammar-row-gap, 0.75rem);
430
430
  }
431
431
 
432
432
  /*
@@ -571,7 +571,7 @@
571
571
  display: flex;
572
572
  min-width: 0;
573
573
  flex-direction: column;
574
- gap: var(--grammar-section-gap, 1rem);
574
+ gap: var(--grammar-row-gap, 0.75rem);
575
575
  }
576
576
 
577
577
  .starci-core-fieldset-actions {
@@ -586,7 +586,7 @@
586
586
  display: flex;
587
587
  min-width: 0;
588
588
  flex-direction: column;
589
- gap: var(--grammar-region-gap, 1.5rem);
589
+ gap: var(--grammar-row-gap, 0.75rem);
590
590
  }
591
591
 
592
592
  /* --- FileDropzone: a native file input stretched over the drop target --- */
@@ -599,7 +599,7 @@
599
599
  justify-content: center;
600
600
  border: 2px dashed var(--border, ButtonBorder);
601
601
  border-radius: var(--field-radius, calc(var(--radius, 0.5rem) * 1.5));
602
- padding: 1.5rem;
602
+ padding: var(--grammar-inset-open, 1.5rem);
603
603
  background: var(--field-background, var(--surface, Canvas));
604
604
  color: var(--muted, GrayText);
605
605
  text-align: center;
@@ -656,7 +656,7 @@
656
656
 
657
657
  .starci-core-file-dropzone-files {
658
658
  margin: 0;
659
- padding-inline-start: 1rem;
659
+ padding-inline-start: var(--grammar-section-gap, 1.5rem);
660
660
  color: var(--foreground, CanvasText);
661
661
  font-size: 0.875rem;
662
662
  overflow-wrap: anywhere;
@@ -303,6 +303,26 @@
303
303
  color: var(--danger-soft-foreground, var(--danger, CanvasText));
304
304
  }
305
305
 
306
+ /* A paused step: a dashed circle in the warning ink, so the state is a form as well as a colour. */
307
+ .starci-core-step[data-grammar-step-state="paused"] .starci-core-step-marker {
308
+ border-style: dashed;
309
+ border-color: var(--warning, Mark);
310
+ color: var(--warning-soft-foreground, var(--warning, CanvasText));
311
+ }
312
+
313
+ /* Dense (a list row): a 20px marker, 12px type, no copy padding, and the connector re-aimed at the smaller circle. */
314
+ .starci-core-stepper[data-grammar-stepper-size="dense"] .starci-core-step-body { padding: 0; gap: var(--grammar-identity-gap, 0.25rem); }
315
+ .starci-core-stepper[data-grammar-stepper-size="dense"] .starci-core-step-marker { width: 1.25rem; height: 1.25rem; border-width: 1.5px; font-size: 0.75rem; }
316
+ .starci-core-stepper[data-grammar-stepper-size="dense"] .starci-core-step-copy { padding-block-start: 0; gap: 0; }
317
+ .starci-core-stepper[data-grammar-stepper-size="dense"] .starci-core-step-label { font-size: 0.75rem; }
318
+ .starci-core-stepper[data-grammar-stepper-size="dense"] .starci-core-step-description { font-size: 0.75rem; }
319
+ .starci-core-stepper[data-grammar-stepper-size="dense"][data-grammar-stepper-orientation="horizontal"] .starci-core-step:not(:last-child)::after {
320
+ inset-block-start: calc(0.625rem - 1px);
321
+ inset-inline-start: calc(50% + 1rem);
322
+ inset-inline-end: calc(-50% + 1rem);
323
+ }
324
+ .starci-core-stepper[data-grammar-stepper-size="dense"] .starci-core-step-control { min-block-size: 0; }
325
+
306
326
  /* Loading stepper (knowledge STATE-8): a static Skeleton circle for the mark, labels stay real. */
307
327
  .starci-core-step-skeleton-marker { border: 0; background: transparent; }
308
328
  .starci-core-step-skeleton-label { display: block; inline-size: 5rem; }
@@ -313,7 +333,7 @@
313
333
  display: flex;
314
334
  min-width: 0;
315
335
  flex-direction: column;
316
- gap: 0.125rem;
336
+ gap: var(--grammar-identity-gap, 0.25rem);
317
337
  padding-block-start: 0.25rem;
318
338
  }
319
339
 
@@ -354,9 +374,9 @@
354
374
  align-items: center;
355
375
  gap: var(--grammar-row-gap, 0.75rem);
356
376
  padding-block: 0.5rem;
357
- padding-inline: var(--grammar-page-inset, 1rem);
358
- padding-inline-start: max(var(--grammar-page-inset, 1rem), env(safe-area-inset-left));
359
- padding-inline-end: max(var(--grammar-page-inset, 1rem), env(safe-area-inset-right));
377
+ padding-inline: var(--grammar-page-inset, 0.75rem);
378
+ padding-inline-start: max(var(--grammar-page-inset, 0.75rem), env(safe-area-inset-left));
379
+ padding-inline-end: max(var(--grammar-page-inset, 0.75rem), env(safe-area-inset-right));
360
380
  border-block-end: 1px solid var(--separator, var(--border, CanvasText));
361
381
  background: var(--surface, Canvas);
362
382
  color: var(--foreground, CanvasText);
@@ -466,11 +486,11 @@
466
486
  }
467
487
 
468
488
  .grammar-common-root:has(.starci-core-bottom-nav[data-grammar-bottom-nav-position="fixed"]) .starci-core-footer {
469
- padding-block-end: calc(var(--grammar-section-gap, 1rem) + max(var(--starci-core-bottom-nav-offset), env(safe-area-inset-bottom, 0px)));
489
+ padding-block-end: calc(var(--grammar-inset, 0.75rem) + max(var(--starci-core-bottom-nav-offset), env(safe-area-inset-bottom, 0px)));
470
490
  }
471
491
 
472
492
  .grammar-common-root:has(.starci-core-bottom-nav[data-grammar-bottom-nav-position="fixed"]) .starci-core-toaster:is([data-placement="bottom-start"], [data-placement="bottom"], [data-placement="bottom-end"]) {
473
- bottom: max(var(--grammar-page-inset, 1rem), env(safe-area-inset-bottom, 0px), calc(var(--starci-core-bottom-nav-offset) + var(--grammar-page-inset, 1rem)));
493
+ bottom: max(var(--grammar-page-inset, 0.75rem), env(safe-area-inset-bottom, 0px), calc(var(--starci-core-bottom-nav-offset) + var(--grammar-page-inset, 0.75rem)));
474
494
  }
475
495
 
476
496
  .starci-core-bottom-nav-list {
@@ -494,7 +514,7 @@
494
514
  flex-direction: column;
495
515
  align-items: center;
496
516
  justify-content: center;
497
- gap: 0.125rem;
517
+ gap: var(--grammar-identity-gap, 0.25rem);
498
518
  padding: 0.25rem;
499
519
  border: 0;
500
520
  border-radius: var(--radius, 0.5rem);
@@ -570,9 +590,9 @@
570
590
  display: flex;
571
591
  min-width: 0;
572
592
  flex-direction: column;
573
- gap: var(--grammar-region-gap, 1.5rem);
574
- padding-block: var(--grammar-region-gap, 1.5rem) calc(var(--grammar-section-gap, 1rem) + env(safe-area-inset-bottom, 0px));
575
- padding-inline: var(--grammar-page-inset, 1rem);
593
+ gap: var(--grammar-section-gap, 1.5rem);
594
+ padding-block: var(--grammar-section-gap, 1.5rem) calc(var(--grammar-inset, 0.75rem) + env(safe-area-inset-bottom, 0px));
595
+ padding-inline: var(--grammar-page-inset, 0.75rem);
576
596
  border-block-start: 1px solid var(--separator, var(--border, CanvasText));
577
597
  color: var(--foreground, CanvasText);
578
598
  }
@@ -581,7 +601,7 @@
581
601
  display: grid;
582
602
  min-width: 0;
583
603
  grid-template-columns: minmax(0, 1fr);
584
- gap: var(--grammar-region-gap, 1.5rem);
604
+ gap: var(--grammar-region-gap, 2rem);
585
605
  }
586
606
 
587
607
  @media (min-width: 48rem) {
@@ -602,7 +622,7 @@
602
622
  display: grid;
603
623
  min-width: 0;
604
624
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
605
- gap: var(--grammar-section-gap, 1rem);
625
+ gap: var(--grammar-section-gap, 1.5rem);
606
626
  }
607
627
 
608
628
  .starci-core-footer-group {
@@ -945,7 +965,7 @@
945
965
  min-block-size: 8rem;
946
966
  align-items: center;
947
967
  justify-content: center;
948
- padding: var(--grammar-section-gap, 1rem);
968
+ padding: var(--grammar-inset, 0.75rem);
949
969
  color: var(--muted, GrayText);
950
970
  white-space: normal;
951
971
  text-align: center;
@@ -990,7 +1010,7 @@
990
1010
  .starci-core-data-table-secondary {
991
1011
  display: flex;
992
1012
  flex-direction: column;
993
- gap: 0.125rem;
1013
+ gap: var(--grammar-identity-gap, 0.25rem);
994
1014
  margin-block-start: 0.25rem;
995
1015
  color: var(--muted, GrayText);
996
1016
  font-size: 0.875em;
@@ -1245,7 +1265,7 @@
1245
1265
  display: flex;
1246
1266
  align-items: center;
1247
1267
  padding-inline: 0.5rem;
1248
- padding-block: 0.375rem;
1268
+ padding-block: var(--grammar-inset-nested, 0.5rem);
1249
1269
  pointer-events: none;
1250
1270
  }
1251
1271
 
@@ -1259,7 +1279,7 @@
1259
1279
  .starci-core-list-box-skeleton-option .starci-core-skeleton-group { gap: 0; }
1260
1280
 
1261
1281
  .starci-core-list-box-empty {
1262
- padding: var(--grammar-section-gap, 1rem);
1282
+ padding: var(--grammar-inset, 0.75rem);
1263
1283
  color: var(--muted, GrayText);
1264
1284
  }
1265
1285
 
@@ -1442,7 +1462,7 @@
1442
1462
  .starci-core-rating {
1443
1463
  display: inline-flex;
1444
1464
  align-items: center;
1445
- gap: 0.125rem;
1465
+ gap: var(--grammar-identity-gap, 0.25rem);
1446
1466
  }
1447
1467
 
1448
1468
  .starci-core-rating-item {
@@ -1519,7 +1539,7 @@
1519
1539
  min-width: 0;
1520
1540
  grid-template-columns: 1.5rem minmax(0, 1fr);
1521
1541
  gap: var(--grammar-row-gap, 0.75rem);
1522
- padding-block-end: var(--grammar-section-gap, 1rem);
1542
+ padding-block-end: var(--grammar-row-gap, 0.75rem);
1523
1543
  }
1524
1544
 
1525
1545
  .starci-core-timeline-item:not(:last-child)::before {
@@ -1605,7 +1625,7 @@
1605
1625
  display: flex;
1606
1626
  min-width: 0;
1607
1627
  flex-direction: column;
1608
- gap: 0.125rem;
1628
+ gap: var(--grammar-identity-gap, 0.25rem);
1609
1629
  }
1610
1630
 
1611
1631
  /* The list title scale (the ListBox and panel row label, text-sm), not whatever size the page body inherits. */
@@ -1777,7 +1797,7 @@
1777
1797
  align-items: center;
1778
1798
  gap: 0.5rem;
1779
1799
  max-inline-size: 14rem;
1780
- padding: 0.375rem 0.875rem;
1800
+ padding: var(--grammar-inset-nested, 0.5rem) var(--grammar-inset, 0.75rem);
1781
1801
  border: 1px solid var(--separator, CanvasText);
1782
1802
  border-radius: 999px;
1783
1803
  background-color: var(--surface, Canvas);
@@ -1805,7 +1825,7 @@
1805
1825
  }
1806
1826
 
1807
1827
  .starci-core-flow-graph-header {
1808
- padding: 0.125rem 0.625rem;
1828
+ padding: 0 var(--grammar-inset-nested, 0.5rem);
1809
1829
  color: var(--muted, GrayText);
1810
1830
  font-size: 0.6875rem;
1811
1831
  font-weight: 600;
@@ -1817,15 +1837,106 @@
1817
1837
 
1818
1838
  .starci-core-flow-graph-header[data-direction="TB"] { text-align: end; }
1819
1839
 
1820
- .starci-core-flow-graph-node[data-size="sm"] { padding: 0.25rem 0.625rem; font-size: 0.75rem; }
1821
- .starci-core-flow-graph-node[data-size="lg"] { padding: 0.625rem 1.25rem; font-size: 1rem; font-weight: 600; }
1822
- .starci-core-flow-graph-node[data-tone="accent"] { --starci-flow-tint: var(--accent-soft, transparent); --starci-flow-ink: var(--accent-soft-foreground, CanvasText); }
1823
- .starci-core-flow-graph-node[data-tone="success"] { --starci-flow-tint: var(--success-soft, transparent); --starci-flow-ink: var(--success-soft-foreground, CanvasText); }
1824
- .starci-core-flow-graph-node[data-tone="warning"] { --starci-flow-tint: var(--warning-soft, transparent); --starci-flow-ink: var(--warning-soft-foreground, CanvasText); }
1825
- .starci-core-flow-graph-node[data-tone="danger"] { --starci-flow-tint: var(--danger-soft, transparent); --starci-flow-ink: var(--danger-soft-foreground, CanvasText); }
1826
- .starci-core-flow-graph-node[data-tone="muted"] { --starci-flow-tint: transparent; --starci-flow-ink: var(--muted, GrayText); }
1840
+ .starci-core-flow-graph-node[data-size="sm"] { padding: var(--grammar-identity-gap, 0.25rem) var(--grammar-inset-nested, 0.5rem); font-size: 0.75rem; }
1841
+ .starci-core-flow-graph-node[data-size="lg"] { padding: var(--grammar-inset, 0.75rem) var(--grammar-section-gap, 1.5rem); font-size: 1rem; font-weight: 600; }
1842
+ /* A tone gives four values: its tint, its ink over the tint, its full colour (`base`) and the ink over the full colour. */
1843
+ .starci-core-flow-graph-node[data-tone="neutral"] { --starci-flow-base: var(--muted, GrayText); --starci-flow-on-base: var(--surface, Canvas); }
1844
+ .starci-core-flow-graph-node[data-tone="accent"] { --starci-flow-tint: var(--accent-soft, transparent); --starci-flow-ink: var(--accent-soft-foreground, CanvasText); --starci-flow-base: var(--accent, Highlight); --starci-flow-on-base: var(--accent-foreground, HighlightText); }
1845
+ .starci-core-flow-graph-node[data-tone="success"] { --starci-flow-tint: var(--success-soft, transparent); --starci-flow-ink: var(--success-soft-foreground, CanvasText); --starci-flow-base: var(--success, CanvasText); --starci-flow-on-base: var(--success-foreground, Canvas); }
1846
+ .starci-core-flow-graph-node[data-tone="warning"] { --starci-flow-tint: var(--warning-soft, transparent); --starci-flow-ink: var(--warning-soft-foreground, CanvasText); --starci-flow-base: var(--warning, CanvasText); --starci-flow-on-base: var(--warning-foreground, Canvas); }
1847
+ .starci-core-flow-graph-node[data-tone="danger"] { --starci-flow-tint: var(--danger-soft, transparent); --starci-flow-ink: var(--danger-soft-foreground, CanvasText); --starci-flow-base: var(--danger, CanvasText); --starci-flow-on-base: var(--danger-foreground, Canvas); }
1848
+ .starci-core-flow-graph-node[data-tone="info"] { --starci-flow-tint: color-mix(in oklab, var(--info, CanvasText) 15%, transparent); --starci-flow-ink: color-mix(in oklab, var(--info, CanvasText) 70%, var(--foreground, CanvasText) 30%); --starci-flow-base: var(--info, CanvasText); --starci-flow-on-base: var(--info-foreground, Canvas); }
1849
+ .starci-core-flow-graph-node[data-tone="muted"] { --starci-flow-tint: transparent; --starci-flow-ink: var(--muted, GrayText); --starci-flow-base: color-mix(in oklab, var(--muted, GrayText) 55%, transparent); --starci-flow-on-base: var(--surface, Canvas); }
1827
1850
  .starci-core-flow-graph-node[data-placeholder="true"] { border-style: dashed; }
1828
1851
 
1852
+ /*
1853
+ * Bubbles (`form="bubble"`): a disc whose width is the node's `diameter`, carrying a short mark and a caption; its name
1854
+ * shows in the tip. The text keeps one screen size: it is the 12px floor divided by the viewport zoom, which the figure
1855
+ * publishes as `--starci-flow-zoom`, and it drops out when the zoom leaves no room for it (caption under 0.42, mark under 0.5).
1856
+ */
1857
+ .starci-core-flow-graph-node[data-form="bubble"] {
1858
+ box-sizing: border-box;
1859
+ inline-size: var(--starci-flow-diameter, 3rem);
1860
+ block-size: var(--starci-flow-diameter, 3rem);
1861
+ max-inline-size: none;
1862
+ padding: 0;
1863
+ justify-content: center;
1864
+ border: 2px solid var(--starci-flow-base, var(--separator, CanvasText));
1865
+ border-radius: 50%;
1866
+ background-image: linear-gradient(var(--starci-flow-tint), var(--starci-flow-tint));
1867
+ color: var(--starci-flow-ink);
1868
+ font-variant-numeric: tabular-nums;
1869
+ transition: opacity 0.2s ease;
1870
+ }
1871
+ .starci-core-flow-graph-disc {
1872
+ position: absolute;
1873
+ inset: 0;
1874
+ display: grid;
1875
+ place-items: center;
1876
+ align-content: center;
1877
+ border-radius: 50%;
1878
+ overflow: hidden;
1879
+ text-align: center;
1880
+ font-size: calc(0.75rem / var(--starci-flow-zoom, 1));
1881
+ font-weight: 600;
1882
+ line-height: 1.2;
1883
+ }
1884
+ .starci-core-flow-graph-mark,
1885
+ .starci-core-flow-graph-caption { display: block; max-inline-size: 100%; overflow: hidden; text-overflow: clip; white-space: nowrap; }
1886
+ .starci-core-flow-graph-caption { font-weight: 500; }
1887
+ .starci-core-flow-graph[data-grammar-flow-mark="off"] .starci-core-flow-graph-mark,
1888
+ .starci-core-flow-graph[data-grammar-flow-caption="off"] .starci-core-flow-graph-caption { display: none; }
1889
+ .starci-core-flow-graph-node[data-form="bubble"][data-root="true"] { --starci-flow-base: var(--foreground, CanvasText); --starci-flow-on-base: var(--surface, Canvas); }
1890
+
1891
+ /* A status never rests on colour alone: the fill is its form. */
1892
+ .starci-core-flow-graph-node[data-fill="solid"] { background-color: var(--starci-flow-base, var(--foreground, CanvasText)); background-image: none; color: var(--starci-flow-on-base, var(--surface, Canvas)); }
1893
+ .starci-core-flow-graph-node[data-fill="half"] { background-image: linear-gradient(to top, color-mix(in oklab, var(--starci-flow-base, var(--foreground, CanvasText)) 42%, transparent) 50%, var(--starci-flow-tint) 50%); }
1894
+ .starci-core-flow-graph-node[data-fill="outline"] { border-style: dashed; background-image: none; color: var(--starci-flow-base, var(--foreground, CanvasText)); }
1895
+ .starci-core-flow-graph-node[data-fill="hatched"] { border-style: dotted; background-image: repeating-linear-gradient(135deg, var(--starci-flow-tint) 0 3px, transparent 3px 6px); }
1896
+
1897
+ /* A status ring: the conic arcs fill the disc and the inner disc, on the surface, leaves a band of them. */
1898
+ .starci-core-flow-graph-node[data-ring="true"] { border-color: transparent; background-color: transparent; background-image: var(--starci-flow-ring); }
1899
+ .starci-core-flow-graph-node[data-ring="true"] > .starci-core-flow-graph-disc,
1900
+ .starci-core-flow-graph-node[data-ring="true"] > .starci-core-flow-graph-link > .starci-core-flow-graph-disc {
1901
+ inset: 7px;
1902
+ background: var(--surface, Canvas);
1903
+ color: var(--foreground, CanvasText);
1904
+ box-shadow: var(--surface-shadow, none);
1905
+ }
1906
+
1907
+ /* A filtered-out node stays in place and in the tab order. */
1908
+ .starci-core-flow-graph-node[data-dimmed="true"] { opacity: 0.16; }
1909
+ .starci-core-flow-graph .starci-core-flow-graph-edge-dimmed { opacity: 0.2; }
1910
+ .starci-core-flow-graph .starci-core-flow-graph-edge-strong .react-flow__edge-path { stroke: var(--foreground, CanvasText); stroke-width: 2; }
1911
+ .starci-core-flow-graph-node[data-form="bubble"][data-grammar-selected="true"] { outline: 3px solid var(--accent, Highlight); outline-offset: 3px; }
1912
+ .starci-core-flow-graph-node[data-expandable="true"] { cursor: pointer; }
1913
+
1914
+ /* The orbit's dashed ring guides sit under the nodes and take no pointer. */
1915
+ .starci-core-flow-graph .starci-core-flow-graph-guide-node { pointer-events: none; }
1916
+ .starci-core-flow-graph-guide { display: block; pointer-events: none; }
1917
+ .starci-core-flow-graph-guide circle { fill: none; stroke: var(--separator, CanvasText); stroke-width: 1; stroke-dasharray: 2 6; }
1918
+
1919
+ /* The tip names a bubble on hover and focus, painted from the Tooltip's tokens. */
1920
+ .starci-core-flow-graph-tip {
1921
+ position: absolute;
1922
+ z-index: 8;
1923
+ display: flex;
1924
+ flex-direction: column;
1925
+ max-inline-size: 16rem;
1926
+ padding: var(--grammar-inset-nested, 0.5rem);
1927
+ border-radius: min(32px, var(--radius-xl, 1rem));
1928
+ background: var(--overlay, Canvas);
1929
+ box-shadow: var(--overlay-shadow, none);
1930
+ color: var(--overlay-foreground, CanvasText);
1931
+ font-size: 0.75rem;
1932
+ line-height: 1.4;
1933
+ pointer-events: none;
1934
+ transform: translate(-50%, calc(-100% - var(--grammar-inline-gap, 0.5rem)));
1935
+ }
1936
+ .starci-core-flow-graph-tip[data-placement="bottom"] { transform: translate(-50%, var(--grammar-inline-gap, 0.5rem)); }
1937
+ .starci-core-flow-graph-tip-label { font-weight: 600; }
1938
+ .starci-core-flow-graph-tip-description { color: var(--muted, GrayText); }
1939
+
1829
1940
  /*
1830
1941
  * Node shapes (the flowchart outlines). `stadium` is the default pill above. A diamond cannot carry a CSS border, so
1831
1942
  * its outline and fill are two clipped layers behind the copy: the outer layer in the separator colour, the inner one
@@ -1847,7 +1958,7 @@
1847
1958
  isolation: isolate;
1848
1959
  justify-content: center;
1849
1960
  min-block-size: 4rem;
1850
- padding: 1rem 2rem;
1961
+ padding: var(--grammar-inset, 0.75rem) var(--grammar-section-gap, 1.5rem);
1851
1962
  border-color: transparent;
1852
1963
  background: none;
1853
1964
  text-align: center;
@@ -1900,7 +2011,7 @@
1900
2011
  .starci-core-flow-graph-count {
1901
2012
  flex: 0 0 auto;
1902
2013
  min-inline-size: 1.25rem;
1903
- padding: 0 0.375rem;
2014
+ padding: 0 var(--grammar-identity-gap, 0.25rem);
1904
2015
  border-radius: 999px;
1905
2016
  background: var(--surface, Canvas);
1906
2017
  color: var(--foreground, CanvasText);
@@ -1931,7 +2042,7 @@
1931
2042
  justify-content: center;
1932
2043
  inline-size: 2rem;
1933
2044
  block-size: 2rem;
1934
- padding: 0.375rem;
2045
+ padding: var(--grammar-inset-nested, 0.5rem);
1935
2046
  border: 0;
1936
2047
  background: transparent;
1937
2048
  color: var(--foreground, CanvasText);
@@ -1945,6 +2056,7 @@
1945
2056
 
1946
2057
  @media (prefers-reduced-motion: reduce) {
1947
2058
  .starci-core-flow-graph .react-flow__edge.animated path { animation: none; }
2059
+ .starci-core-flow-graph-node[data-form="bubble"] { transition: none; }
1948
2060
  }
1949
2061
 
1950
2062
  @media (forced-colors: active) {
@@ -1974,7 +2086,7 @@
1974
2086
  display: grid;
1975
2087
  min-block-size: var(--starci-mermaid-height);
1976
2088
  place-items: center;
1977
- padding: var(--grammar-section-gap, 1rem);
2089
+ padding: var(--grammar-inset, 0.75rem);
1978
2090
  overflow-x: auto;
1979
2091
  border-radius: inherit;
1980
2092
  outline: none;
@@ -1987,7 +2099,7 @@
1987
2099
 
1988
2100
  .starci-core-mermaid-diagram-canvas > .starci-core-skeleton {
1989
2101
  inline-size: 100%;
1990
- block-size: calc(var(--starci-mermaid-height) - 2 * var(--grammar-section-gap, 1rem));
2102
+ block-size: calc(var(--starci-mermaid-height) - 2 * var(--grammar-section-gap, 1.5rem));
1991
2103
  aspect-ratio: auto;
1992
2104
  }
1993
2105
 
@@ -2000,7 +2112,7 @@
2000
2112
  min-block-size: var(--starci-mermaid-height);
2001
2113
  flex-direction: column;
2002
2114
  gap: var(--grammar-row-gap, 0.75rem);
2003
- padding: var(--grammar-section-gap, 1rem);
2115
+ padding: var(--grammar-inset, 0.75rem);
2004
2116
  }
2005
2117
 
2006
2118
  /*
@@ -41,7 +41,7 @@
41
41
  display: flex;
42
42
  align-items: center;
43
43
  justify-content: center;
44
- padding: var(--grammar-page-inset, 1rem);
44
+ padding: var(--grammar-page-inset, 0.75rem);
45
45
  background: color-mix(in srgb, var(--foreground, CanvasText) 40%, transparent);
46
46
  overscroll-behavior: contain;
47
47
  }
@@ -106,7 +106,7 @@
106
106
  min-width: 0;
107
107
  flex: 1;
108
108
  flex-direction: column;
109
- gap: var(--grammar-section-gap, 1rem);
109
+ gap: var(--grammar-row-gap, 0.75rem);
110
110
  overflow: hidden;
111
111
  color: var(--surface-foreground, CanvasText);
112
112
  }
@@ -393,25 +393,25 @@
393
393
  position: fixed;
394
394
  z-index: 60;
395
395
  display: flex;
396
- width: min(24rem, calc(100vw - 2 * var(--grammar-page-inset, 1rem)));
396
+ width: min(24rem, calc(100vw - 2 * var(--grammar-page-inset, 0.75rem)));
397
397
  flex-direction: column;
398
398
  pointer-events: none;
399
399
  }
400
400
 
401
401
  .starci-core-toaster:is([data-placement="top-start"], [data-placement="top"], [data-placement="top-end"]) {
402
- top: var(--grammar-page-inset, 1rem);
402
+ top: var(--grammar-page-inset, 0.75rem);
403
403
  }
404
404
 
405
405
  .starci-core-toaster:is([data-placement="bottom-start"], [data-placement="bottom"], [data-placement="bottom-end"]) {
406
- bottom: max(var(--grammar-page-inset, 1rem), env(safe-area-inset-bottom));
406
+ bottom: max(var(--grammar-page-inset, 0.75rem), env(safe-area-inset-bottom));
407
407
  }
408
408
 
409
409
  .starci-core-toaster:is([data-placement="top-start"], [data-placement="bottom-start"]) {
410
- inset-inline-start: var(--grammar-page-inset, 1rem);
410
+ inset-inline-start: var(--grammar-page-inset, 0.75rem);
411
411
  }
412
412
 
413
413
  .starci-core-toaster:is([data-placement="top-end"], [data-placement="bottom-end"]) {
414
- inset-inline-end: var(--grammar-page-inset, 1rem);
414
+ inset-inline-end: var(--grammar-page-inset, 0.75rem);
415
415
  }
416
416
 
417
417
  .starci-core-toaster:is([data-placement="top"], [data-placement="bottom"]) {
@@ -441,8 +441,6 @@
441
441
  display: grid;
442
442
  grid-template-columns: auto minmax(0, 1fr) auto auto;
443
443
  align-items: start;
444
- gap: 0.5rem 0.75rem;
445
- padding: 0.75rem 0.5rem 0.75rem 1rem;
446
444
  border-radius: var(--starci-core-surface-radius, 1rem);
447
445
  background: var(--surface, Canvas);
448
446
  color: var(--surface-foreground, CanvasText);
@@ -452,14 +450,14 @@
452
450
 
453
451
  .starci-core-toast-indicator {
454
452
  display: inline-flex;
455
- margin-block-start: 0.125rem;
453
+ margin-block-start: var(--grammar-identity-gap, 0.25rem);
456
454
  color: var(--starci-core-overlay-tone);
457
455
  }
458
456
 
459
457
  .starci-core-toast-indicator[data-grammar-toast-indicator] {
460
458
  width: 0.625rem;
461
459
  height: 0.625rem;
462
- margin-block-start: 0.375rem;
460
+ margin-block-start: var(--grammar-identity-gap, 0.25rem);
463
461
  border-radius: 999px;
464
462
  background: var(--starci-core-overlay-tone);
465
463
  }
@@ -468,7 +466,7 @@
468
466
  display: flex;
469
467
  min-width: 0;
470
468
  flex-direction: column;
471
- gap: 0.125rem;
469
+ gap: var(--grammar-identity-gap, 0.25rem);
472
470
  align-self: center;
473
471
  }
474
472
 
@@ -493,7 +491,7 @@
493
491
 
494
492
  @media (max-width: 40rem) {
495
493
  .starci-core-toaster {
496
- inset-inline: var(--grammar-page-inset, 1rem);
494
+ inset-inline: var(--grammar-page-inset, 0.75rem);
497
495
  width: auto;
498
496
  margin-inline: 0;
499
497
  }
@@ -622,7 +620,7 @@
622
620
  min-width: 1.5rem;
623
621
  align-items: center;
624
622
  justify-content: center;
625
- gap: 0.125rem;
623
+ gap: var(--grammar-identity-gap, 0.25rem);
626
624
  border: 1px solid var(--border, CanvasText);
627
625
  white-space: nowrap;
628
626
  }
@@ -795,7 +793,7 @@
795
793
  width: 100%;
796
794
  min-width: 0;
797
795
  flex-direction: column;
798
- gap: 0.375rem;
796
+ gap: var(--grammar-identity-gap, 0.25rem);
799
797
  }
800
798
 
801
799
  .starci-core-meter-header {