efficient-web-builder 2.0.0 → 3.0.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 (145) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +7 -0
  3. package/dist/components/BlogPostSource.js +1 -1
  4. package/dist/components/Breadcrumb.js +1 -1
  5. package/dist/components/Btn.js +1 -1
  6. package/dist/components/CardShadowField.js +1 -1
  7. package/dist/components/CategoryBar.js +1 -1
  8. package/dist/components/ChartSection.js +1 -1
  9. package/dist/components/ChatSettingsPanel.js +1 -1
  10. package/dist/components/ClearColoursDialog.js +1 -1
  11. package/dist/components/ContactLocations.js +5 -5
  12. package/dist/components/CornersField.js +1 -1
  13. package/dist/components/DocBody.js +1 -1
  14. package/dist/components/DuplicateToPageMenu.js +1 -1
  15. package/dist/components/FeaturedCover.js +1 -1
  16. package/dist/components/FieldGlyph.js +1 -1
  17. package/dist/components/Footprint.js +1 -1
  18. package/dist/components/HeadingAudit.js +1 -1
  19. package/dist/components/HeroSection.js +1 -1
  20. package/dist/components/HoverEffectField.js +1 -1
  21. package/dist/components/ImageBlock.js +1 -1
  22. package/dist/components/ImageFocusRow.js +1 -1
  23. package/dist/components/LocationsGroupedField.js +1 -1
  24. package/dist/components/LongRead.js +1 -1
  25. package/dist/components/MediaUploadField.js +1 -1
  26. package/dist/components/MockupSection.js +1 -1
  27. package/dist/components/PagesPanel.js +1 -1
  28. package/dist/components/PartInkControl.js +1 -1
  29. package/dist/components/PartResetDot.js +1 -1
  30. package/dist/components/PartSettingsPanel.js +1 -1
  31. package/dist/components/PostCard.js +1 -1
  32. package/dist/components/ProcessSteps.js +1 -1
  33. package/dist/components/RMark.js +1 -1
  34. package/dist/components/Reel.js +1 -1
  35. package/dist/components/SectionEditor.js +1 -1
  36. package/dist/components/SectionHeader.js +1 -1
  37. package/dist/components/SectionThumbnail.js +1 -1
  38. package/dist/components/SlotChildRows.js +1 -1
  39. package/dist/components/SourceStatus.js +1 -1
  40. package/dist/components/Split.js +1 -1
  41. package/dist/components/StandaloneSectionEditor.js +1 -1
  42. package/dist/components/StylesPanel.js +1 -1
  43. package/dist/components/SwitchToBoxFooter.js +1 -1
  44. package/dist/components/Testimonials.js +1 -1
  45. package/dist/components/TextStyleRow.js +1 -1
  46. package/dist/components/TextStylesField.js +1 -1
  47. package/dist/components/ThemeCard.js +1 -1
  48. package/dist/components/ThemeColourField.js +1 -1
  49. package/dist/components/ThemeField.js +1 -1
  50. package/dist/components/TickerSettingsPanel.js +1 -1
  51. package/dist/components/Timeline.js +1 -1
  52. package/dist/components/TimelineTrack.js +1 -1
  53. package/dist/components/TitleWithButtons.js +1 -1
  54. package/dist/components/WebsiteScreenParts.js +1 -1
  55. package/dist/components/WebsiteSettingsPanel.d.ts +5 -9
  56. package/dist/components/WebsiteSettingsPanel.js +1 -1
  57. package/dist/components/goldPhrases.js +1 -1
  58. package/dist/components/storefront-editor.css +262 -542
  59. package/dist/lib/content/contact.js +3 -3
  60. package/dist/lib/content/subscribe.js +1 -1
  61. package/dist/lib/puck/ColourControl.d.ts +5 -0
  62. package/dist/lib/puck/ColourControl.js +1 -1
  63. package/dist/lib/puck/MarkupToolbar.js +1 -1
  64. package/dist/lib/puck/blocks/general/CategoryBar/fields.js +1 -1
  65. package/dist/lib/puck/blocks/general/Chart/fields.js +1 -1
  66. package/dist/lib/puck/blocks/general/FAQ/render.js +1 -1
  67. package/dist/lib/puck/blocks/general/FeaturedCover/render.js +1 -1
  68. package/dist/lib/puck/blocks/general/Footprint/fields.js +1 -1
  69. package/dist/lib/puck/blocks/general/IntroBleed/render.js +1 -1
  70. package/dist/lib/puck/blocks/general/LogoBlock/fields.js +1 -1
  71. package/dist/lib/puck/blocks/general/LongRead/render.js +1 -1
  72. package/dist/lib/puck/blocks/general/Masthead/render.js +1 -1
  73. package/dist/lib/puck/blocks/general/Mockup/fields.js +1 -1
  74. package/dist/lib/puck/blocks/general/OrderSummary/render.js +1 -1
  75. package/dist/lib/puck/blocks/general/ParagraphsA/render.js +1 -1
  76. package/dist/lib/puck/blocks/general/PostCard/render.js +1 -1
  77. package/dist/lib/puck/blocks/general/ProcessSteps/render.js +1 -1
  78. package/dist/lib/puck/blocks/general/Series/render.js +1 -1
  79. package/dist/lib/puck/blocks/general/Split/render.js +1 -1
  80. package/dist/lib/puck/blocks/general/Testimonials/fields.js +1 -1
  81. package/dist/lib/puck/blocks/general/Testimonials/render.js +1 -1
  82. package/dist/lib/puck/blocks/general/TimelineTrack/render.js +1 -1
  83. package/dist/lib/puck/blocks/placeholders.js +1 -1
  84. package/dist/lib/puck/controls.d.ts +0 -10
  85. package/dist/lib/puck/controls.js +1 -1
  86. package/dist/lib/puck/field-groups.js +1 -1
  87. package/dist/lib/puck/footer-block-row-name.js +1 -1
  88. package/dist/lib/puck/geocode-resolve.js +1 -1
  89. package/dist/lib/puck/locations-tree.js +2 -2
  90. package/dist/lib/puck/neutralization-contracts.js +1 -1
  91. package/dist/lib/puck/part-rows.js +1 -1
  92. package/dist/lib/puck/section-catalog.js +1 -1
  93. package/dist/lib/puck/section-descriptions.js +1 -1
  94. package/dist/lib/sections/divider-style.js +1 -1
  95. package/dist/lib/sections/product-card-style.js +1 -1
  96. package/dist/lib/sections/ticker-style.js +1 -1
  97. package/dist/lib/source-rules/accent-token-name.js +2 -2
  98. package/dist/lib/source-rules/button-corners.js +1 -1
  99. package/dist/lib/source-rules/editor-chrome-note.js +2 -2
  100. package/dist/lib/source-rules/editor-context-menu.js +2 -2
  101. package/dist/lib/source-rules/editor-puck-drag-off.js +2 -2
  102. package/dist/lib/source-rules/efficient-easel-version.js +1 -1
  103. package/dist/lib/source-rules/every-button-follows-the-button-colour.js +4 -4
  104. package/dist/lib/source-rules/flat-theme-palette.js +2 -2
  105. package/dist/lib/source-rules/full-width-means-full.js +1 -1
  106. package/dist/lib/source-rules/heading-ceiling-sweep.js +3 -3
  107. package/dist/lib/source-rules/label-hints.js +4 -4
  108. package/dist/lib/source-rules/label-vocabulary.js +5 -5
  109. package/dist/lib/source-rules/mirrored-rows-agree.js +4 -4
  110. package/dist/lib/source-rules/no-comment-in-jsx.js +2 -2
  111. package/dist/lib/source-rules/no-glyph-rows.js +2 -2
  112. package/dist/lib/source-rules/one-colour-control.js +2 -2
  113. package/dist/lib/source-rules/one-dropdown.js +2 -2
  114. package/dist/lib/source-rules/one-editable-list.js +3 -3
  115. package/dist/lib/source-rules/one-editor-list.js +1 -1
  116. package/dist/lib/source-rules/one-right-click.js +2 -2
  117. package/dist/lib/source-rules/one-switch.js +1 -1
  118. package/dist/lib/source-rules/one-up-level-link.js +7 -7
  119. package/dist/lib/source-rules/panel-colour-tokens.js +7 -7
  120. package/dist/lib/source-rules/panel-no-divider-lines.js +5 -5
  121. package/dist/lib/source-rules/panel-one-arrow.js +1 -1
  122. package/dist/lib/source-rules/panel-one-hover.js +2 -2
  123. package/dist/lib/source-rules/panel-rows-use-the-shared-row.js +5 -5
  124. package/dist/lib/source-rules/part-slider-fills-row.js +8 -8
  125. package/dist/lib/source-rules/part-style-naming.js +1 -1
  126. package/dist/lib/source-rules/puck-document-surfaces.js +6 -6
  127. package/dist/lib/source-rules/reduced-motion.js +2 -2
  128. package/dist/lib/source-rules/route-content-width.js +2 -2
  129. package/dist/lib/source-rules/rule.js +1 -1
  130. package/dist/lib/source-rules/section-theme-preview.js +2 -2
  131. package/dist/lib/source-rules/site-square-by-default.js +1 -1
  132. package/dist/lib/source-rules/site-width.js +2 -2
  133. package/dist/lib/source-rules/size-token-paints.js +2 -2
  134. package/dist/lib/source-rules/social-icons-import.js +1 -1
  135. package/dist/lib/source-rules/source-status-is-shared.js +1 -1
  136. package/dist/lib/source-rules/style-face-matches-size.js +3 -3
  137. package/dist/lib/source-rules/support/label-extraction.js +5 -5
  138. package/dist/lib/source-rules/support/part-slider-call-sites.js +4 -4
  139. package/dist/lib/source-rules/support/registry-source.js +1 -1
  140. package/dist/lib/source-rules/theme-mode-controls-gone.js +3 -3
  141. package/dist/lib/source-rules/upload-intake-guard.js +2 -2
  142. package/dist/lib/source-rules/word-styles.js +2 -2
  143. package/package.json +7 -7
  144. package/dist/lib/puck/slider-typing.d.ts +0 -9
  145. package/dist/lib/puck/slider-typing.js +0 -1
@@ -14,11 +14,24 @@
14
14
  * left/right panels, device frame, inspector).
15
15
  *
16
16
  * Everything is scoped under `.sf-editor` so it can never leak into the public
17
- * storefront. Colour is driven entirely by the `--sf-*` tokens below so the
18
- * chrome themes in ONE place. The editor is deliberately light-only — it does
19
- * NOT follow the admin portal's active theme. The admin still bridges its theme
20
- * into the iframe, but lib/editor-theme.ts pins the editor to the light base
21
- * regardless (see that file), so there is no dark block.
17
+ * storefront. Colour is driven entirely by tokens so the chrome themes in ONE
18
+ * place — and that place is efficient-shell (harness UI-24,
19
+ * Efficient-Pty-Ltd/efficient-easel#199). Every chrome rule reads a `--sf-*`
20
+ * (aliased by efficient-easel's `screen.css` onto `--es-*`, which easel aliases
21
+ * onto the shell's tokens), an `--es-*`, or a shell token directly
22
+ * (`--line-strong`, `--status-*`, `--radius-*`), so the chrome FOLLOWS THE
23
+ * SHELL'S THEME, dark included, with no dark block of its own: the shell swaps
24
+ * its tokens under `<html data-theme="dark">`. The theme reaches that attribute
25
+ * through efficient-easel's `chrome-theme` (re-exported as lib/editor-theme.ts),
26
+ * which pinned the editor light up to easel 0.106.0 and stamps the bridged
27
+ * theme from easel #200 on. Hosts load `efficient-shell/ui.css` before this
28
+ * sheet, or the shell's tokens resolve to nothing.
29
+ *
30
+ * NO COLOUR LITERAL AND NO LITERAL RADIUS IN A CHROME RULE
31
+ * (`scripts/test-editor-css-tokens.ts`). Two regions are not chrome and are
32
+ * marked `@tokens canvas-begin/end` and `@tokens output-begin/end`: rules that
33
+ * paint inside the canvas frame (the website being edited, where no token is
34
+ * defined), and the section covers' palette (a miniature of a section, output).
22
35
  *
23
36
  * WHERE THE VALUES LIVE (card 66034). They no longer live here. Every `--sf-*`
24
37
  * colour, radius, shadow and font below is now an ALIAS onto the `--es-*` shell
@@ -62,28 +75,10 @@
62
75
  }
63
76
 
64
77
  /* ── Top bar ─────────────────────────────────────────────────────────────
65
- The bar, its clusters, the icon buttons, the divider, the device switch and
66
- the ghost / primary buttons are editor-shell's (efficient-easel/chrome.css,
67
- `.es-topbar` and friends), lifted from this sheet and shared with the
68
- campaign editor (redesign PR 2a). Only the website's own additions follow. */
69
-
70
- /* Reuse-component buttons in the top bar inherit the ghost look */
71
- .sf-actions-slot > button {
72
- display: flex;
73
- align-items: center;
74
- gap: 6px;
75
- padding: 7px 12px;
76
- border: 1px solid var(--sf-btn-border);
77
- background: var(--sf-panel);
78
- border-radius: 8px;
79
- cursor: pointer;
80
- font-size: 13px;
81
- font-weight: 500;
82
- color: var(--sf-btn-text);
83
- }
84
- .sf-actions-slot > button:hover {
85
- background: var(--sf-hover);
86
- }
78
+ The bar and its clusters are efficient-easel's (chrome.css, `.es-topbar` and
79
+ friends), shared with the campaign editor (redesign PR 2a); its buttons and
80
+ device switch are efficient-shell's since easel 1.0 (UI-24).
81
+ Only the website's own additions follow. */
87
82
 
88
83
  /* ── Body / floating panels ───────────────────────────────────────────── */
89
84
  /* The panel's title and, beside it, WHICH SITE it is styling (card 66167).
@@ -102,6 +97,7 @@
102
97
  wrapping, because a two-line head would move every row in the panel below it;
103
98
  the whole name is on the `title`, along with where to change it. */
104
99
  .sf-panel-site {
100
+ display: block;
105
101
  margin: 0;
106
102
  min-width: 0;
107
103
  overflow: hidden;
@@ -111,6 +107,22 @@
111
107
  white-space: nowrap;
112
108
  text-overflow: ellipsis;
113
109
  }
110
+ /* The site's name sits in the shell's Tooltip (harness UI-24), which carries
111
+ the whole name and where to change it. Its wrapper is block-level and gives
112
+ way like the name did, so the name still ellipsises inside it rather than
113
+ the wrapper's inline box ending the ellipsis. */
114
+ .sf-name-tip {
115
+ position: relative;
116
+ display: block;
117
+ min-width: 0;
118
+ max-width: 100%;
119
+ }
120
+ .sf-name-text {
121
+ display: block;
122
+ overflow: hidden;
123
+ white-space: nowrap;
124
+ text-overflow: ellipsis;
125
+ }
114
126
  .sf-panel-site::before {
115
127
  content: "·";
116
128
  margin-right: 6px;
@@ -120,7 +132,7 @@
120
132
  height: 24px;
121
133
  border: none;
122
134
  background: transparent;
123
- border-radius: 6px;
135
+ border-radius: var(--radius-md);
124
136
  cursor: pointer;
125
137
  color: var(--sf-muted);
126
138
  display: flex;
@@ -238,7 +250,7 @@
238
250
  top: 50%;
239
251
  height: 2px;
240
252
  transform: translateY(-50%);
241
- border-radius: 2px;
253
+ border-radius: calc(var(--radius-sm) / 2);
242
254
  background: var(--sf-accent);
243
255
  opacity: 0;
244
256
  transition: opacity 0.12s;
@@ -252,7 +264,7 @@
252
264
  height: 18px;
253
265
  border-radius: 50%;
254
266
  background: var(--sf-accent);
255
- color: #fff;
267
+ color: var(--sf-on-accent);
256
268
  display: flex;
257
269
  align-items: center;
258
270
  justify-content: center;
@@ -322,7 +334,7 @@
322
334
  margin-top: 16px;
323
335
  padding: 10px;
324
336
  border: 1px solid var(--sf-divider);
325
- border-radius: 10px;
337
+ border-radius: var(--radius-xl);
326
338
  }
327
339
  .sf-addside-sitewide-glyph {
328
340
  flex: none;
@@ -330,7 +342,7 @@
330
342
  place-items: center;
331
343
  width: 28px;
332
344
  height: 28px;
333
- border-radius: 7px;
345
+ border-radius: var(--radius-lg);
334
346
  background: var(--sf-hover);
335
347
  color: var(--sf-muted);
336
348
  }
@@ -369,7 +381,7 @@
369
381
  margin: 18px 0 6px;
370
382
  padding: 12px;
371
383
  border: 1px solid var(--sf-divider);
372
- border-radius: 10px;
384
+ border-radius: var(--radius-xl);
373
385
  }
374
386
  .sf-box-footer-switch-text {
375
387
  margin: 0;
@@ -387,6 +399,12 @@
387
399
  * colour literal lives in the .tsx (doc/no-hardcoding.md R3); the admin mirror
388
400
  * defines the same block in src/index.css. Light-only: the picker + the console
389
401
  * catalog both sit on a light surface. */
402
+ /* @tokens output-begin — the section COVERS' palette. A cover is a miniature
403
+ picture of a kind of SECTION (output), drawn the same in every tenant and in
404
+ the admin's catalog; it is not chrome and does not follow the shell theme,
405
+ exactly as the canvas paper stays white (UI-24 leaves rendered output its own
406
+ look). The card around a cover is easel's `.es-add-*` chrome, on shell tokens.
407
+ `scripts/test-editor-css-tokens.ts` skips this region. */
390
408
  .sf-thumb {
391
409
  --sf-thumb-card: #ffffff;
392
410
  --sf-thumb-thumb-bg: #fcfdff;
@@ -448,6 +466,7 @@
448
466
  colour here rather than an `rgba()` sitting in the component. */
449
467
  --sf-thumb-scrim: rgba(0, 0, 0, 0.55);
450
468
  }
469
+ /* @tokens output-end */
451
470
  /* Drives the Ticker cover's scrolling strip. */
452
471
  @keyframes sf-thumb-marquee {
453
472
  from {
@@ -458,90 +477,6 @@
458
477
  }
459
478
  }
460
479
 
461
- /* ── The one destructive confirm in the panel (Reset color) ─────────────────
462
- [ADD] Reset On Every Site Style. Every other reset on Site styles clears a
463
- number or a rung; this one deletes a merchant's authored palettes, so it asks
464
- first (owner's ruling 2026-09-05, option B).
465
-
466
- THE COLOURS LIVE HERE RATHER THAN ON THE COMPONENT, and that is the rule
467
- rather than a preference. `test-panel-colour-tokens` sweeps `components/` for
468
- literals — "a literal there is a private copy of one step of that scale: it
469
- does not move when the scale moves". The older `LeaveConfirmDialog` spells its
470
- palette in Tailwind literals and predates that sweep; a second dialog copying
471
- it would have passed review and failed the gate.
472
-
473
- The destructive button takes the `--sf-danger*` family that already exists,
474
- not a red of its own. The radius is the 9px the theme cards and the add-card
475
- button already wear, so the dialog reads as the same object as the rest of the
476
- panel rather than a new shape. */
477
- .sf-cleardlg-veil {
478
- position: fixed;
479
- inset: 0;
480
- z-index: 200;
481
- display: flex;
482
- align-items: center;
483
- justify-content: center;
484
- padding: 16px;
485
- background: var(--sf-addcard-veil);
486
- }
487
- .sf-cleardlg {
488
- width: 100%;
489
- max-width: 360px;
490
- box-sizing: border-box;
491
- padding: 20px;
492
- border: 1px solid var(--sf-line);
493
- border-radius: 9px;
494
- background: var(--sf-panel);
495
- color: var(--sf-text);
496
- box-shadow: 0 20px 50px var(--sf-accent-shadow);
497
- }
498
- .sf-cleardlg-title {
499
- margin: 0;
500
- font-size: 15px;
501
- font-weight: 600;
502
- color: var(--sf-text);
503
- }
504
- .sf-cleardlg-body {
505
- margin: 8px 0 0;
506
- font-size: 13px;
507
- line-height: 1.6;
508
- color: var(--sf-muted);
509
- }
510
- /* The one line a merchant must not skim. NOT red — the button already carries
511
- the danger colour, and a red sentence over a red button is the panel shouting
512
- twice. Full-strength ink and a heavier weight instead. */
513
- .sf-cleardlg-final {
514
- margin: 8px 0 0;
515
- font-size: 13px;
516
- line-height: 1.6;
517
- font-weight: 500;
518
- color: var(--sf-text);
519
- }
520
- .sf-cleardlg-btns {
521
- display: flex;
522
- justify-content: flex-end;
523
- gap: 8px;
524
- margin-top: 18px;
525
- }
526
- .sf-cleardlg-btn {
527
- font-family: var(--sf-font);
528
- font-size: 13px;
529
- padding: 7px 14px;
530
- border-radius: var(--sf-field-radius);
531
- border: 1px solid var(--sf-btn-border);
532
- background: var(--sf-panel);
533
- color: var(--sf-btn-text);
534
- cursor: pointer;
535
- }
536
- .sf-cleardlg-btn:hover { background: var(--sf-hover); }
537
- .sf-cleardlg-btn--danger {
538
- font-weight: 600;
539
- border-color: var(--sf-danger-strong);
540
- background: var(--sf-danger-strong);
541
- color: var(--sf-on-accent);
542
- }
543
- .sf-cleardlg-btn--danger:hover { filter: brightness(0.94); }
544
-
545
480
  .sf-addcard-overlay {
546
481
  position: absolute;
547
482
  inset: 0;
@@ -564,8 +499,8 @@
564
499
  align-items: center;
565
500
  gap: 6px;
566
501
  background: var(--sf-accent);
567
- color: #fff;
568
- border-radius: 9px;
502
+ color: var(--sf-on-accent);
503
+ border-radius: var(--radius-lg);
569
504
  padding: 8px 14px;
570
505
  font-size: 12.5px;
571
506
  font-weight: 600;
@@ -650,6 +585,13 @@
650
585
  src>`) — see components/EditorPreviewSurface. No editor-side frame CSS is
651
586
  needed; the ribbon is styled inline in that component. */
652
587
 
588
+ /* @tokens canvas-begin — the rules from here to `@tokens canvas-end` paint
589
+ INSIDE the canvas frame, on the website being edited: that document has no
590
+ `.es-editor` root, so no shell or `--es-*` token is defined there, and each
591
+ colour carries its own fallback (see the note at B below). Harness UI-24
592
+ leaves the canvas to the page; `scripts/test-editor-css-tokens.ts` skips
593
+ this region and counts everything else. */
594
+
653
595
  /* ── One dashed outline on hover, not a field of them (card 66395) ─────────
654
596
  The owner, 2026-09-01: hovering a section shows too many dashed lines. He
655
597
  wants to find the hover target directly, and ONE is enough.
@@ -927,6 +869,8 @@
927
869
  outline: none;
928
870
  }
929
871
 
872
+ /* @tokens canvas-end */
873
+
930
874
  /* ── Canvas "Add section" seams (overlay between preview sections) ──────────
931
875
  Absolutely positioned over .sf-preview-host (the iframe's box), one band per
932
876
  seam between top-level sections, placed at the boundary Y measured from the
@@ -1050,83 +994,6 @@
1050
994
  }
1051
995
  }
1052
996
 
1053
- /* "Duplicate to another page" dropdown — portalled to <body> and pinned under
1054
- the inspector button (see DuplicateToPageMenu), so it's position:fixed and
1055
- right-aligned. Shares the floating-menu language of .sf-blockmenu. */
1056
- .sf-dupmenu-backdrop {
1057
- position: fixed;
1058
- inset: 0;
1059
- z-index: 1000;
1060
- }
1061
- .sf-dupmenu {
1062
- position: fixed;
1063
- z-index: 1001;
1064
- display: flex;
1065
- flex-direction: column;
1066
- gap: 1px;
1067
- min-width: 200px;
1068
- max-width: 280px;
1069
- max-height: 60vh;
1070
- overflow: auto;
1071
- padding: 4px;
1072
- border: 1px solid var(--sf-line);
1073
- border-radius: 8px;
1074
- background: var(--sf-panel);
1075
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
1076
- }
1077
- .sf-dupmenu-head {
1078
- padding: 6px 9px 4px;
1079
- font-size: 11px;
1080
- font-weight: 600;
1081
- letter-spacing: 0.02em;
1082
- color: var(--sf-muted);
1083
- }
1084
- .sf-dupmenu-empty {
1085
- padding: 8px 9px;
1086
- font-size: 13px;
1087
- color: var(--sf-muted);
1088
- }
1089
- .sf-dupmenu-item {
1090
- display: flex;
1091
- align-items: center;
1092
- gap: 8px;
1093
- width: 100%;
1094
- padding: 7px 9px;
1095
- border: none;
1096
- background: transparent;
1097
- border-radius: 6px;
1098
- cursor: pointer;
1099
- font-size: 13px;
1100
- text-align: left;
1101
- color: var(--sf-text);
1102
- }
1103
- .sf-dupmenu-item:hover:not(:disabled) {
1104
- background: var(--sf-accent-tint);
1105
- color: var(--sf-accent);
1106
- }
1107
- .sf-dupmenu-item:disabled {
1108
- cursor: default;
1109
- opacity: 0.6;
1110
- }
1111
- .sf-dupmenu-name {
1112
- flex: 1;
1113
- min-width: 0;
1114
- overflow: hidden;
1115
- text-overflow: ellipsis;
1116
- white-space: nowrap;
1117
- }
1118
- /* Here the note is a word trailing a menu row ("draft", "Copying…"), not a
1119
- block: no inset of its own, and it takes the ROW's leading rather than the
1120
- note's, or a draft row would stand 4px taller than a published one and the
1121
- menu would lose its even rhythm. It stays quiet against the page name beside
1122
- it by COLOUR, which is the note's own and the whole reason one class can
1123
- serve both places. Placement only; the note style itself is .sf-note. */
1124
- .sf-dupmenu-item > .sf-note {
1125
- padding: 0;
1126
- flex: none;
1127
- line-height: inherit;
1128
- }
1129
-
1130
997
  /* Floating-styled form controls for Puck's native fields */
1131
998
  .sf-insp-body input[type="text"],
1132
999
  .sf-insp-body input[type="number"],
@@ -1430,10 +1297,11 @@
1430
1297
  flex: none;
1431
1298
  display: flex;
1432
1299
  overflow: hidden;
1433
- border-radius: 4px;
1300
+ border-radius: var(--radius-sm);
1434
1301
  /* A hairline of the merchant's own ink would vanish on a theme whose paper is
1435
- that ink. A translucent black keeps the chip's edge on every palette. */
1436
- border: 1px solid rgba(0, 0, 0, 0.14);
1302
+ that ink. The shell's strong line keeps the chip's edge on every palette,
1303
+ against the panel it sits on in either theme. */
1304
+ border: 1px solid var(--line-strong);
1437
1305
  }
1438
1306
  .sf-secttheme-chip i {
1439
1307
  display: block;
@@ -1545,7 +1413,7 @@
1545
1413
  .sf-secttheme-done:focus-visible {
1546
1414
  outline: 2px solid var(--sf-accent);
1547
1415
  outline-offset: -2px;
1548
- border-radius: 4px;
1416
+ border-radius: var(--radius-sm);
1549
1417
  }
1550
1418
  .sf-secttheme-sep {
1551
1419
  height: 1px;
@@ -1664,7 +1532,7 @@
1664
1532
  margin: 0 12px 8px;
1665
1533
  padding: 7px 10px;
1666
1534
  background: var(--sf-track-bg);
1667
- border-radius: 8px;
1535
+ border-radius: var(--radius-lg);
1668
1536
  color: var(--sf-muted);
1669
1537
  }
1670
1538
  .sf-pages-search-input {
@@ -1765,8 +1633,9 @@
1765
1633
  background: var(--sf-accent-tint);
1766
1634
  }
1767
1635
  .sf-badge-lightbox {
1768
- color: #047857; /* emerald-700 — matches the Console's Lightbox pill */
1769
- background: #ecfdf5; /* emerald-50 */
1636
+ /* The shell's success wash — the green the Console's Lightbox pill wears. */
1637
+ color: var(--status-success-soft-ink);
1638
+ background: var(--status-success-soft);
1770
1639
  }
1771
1640
  .sf-page-edit {
1772
1641
  flex: none;
@@ -1774,7 +1643,7 @@
1774
1643
  height: 24px;
1775
1644
  border: none;
1776
1645
  background: transparent;
1777
- border-radius: 6px;
1646
+ border-radius: var(--radius-md);
1778
1647
  color: var(--sf-muted);
1779
1648
  display: none;
1780
1649
  align-items: center;
@@ -1848,7 +1717,7 @@
1848
1717
  height: 22px;
1849
1718
  border: none;
1850
1719
  background: transparent;
1851
- border-radius: 5px;
1720
+ border-radius: var(--radius-md);
1852
1721
  cursor: pointer;
1853
1722
  color: var(--sf-danger);
1854
1723
  display: flex;
@@ -2308,7 +2177,7 @@
2308
2177
  height: 22px;
2309
2178
  border: none;
2310
2179
  background: transparent;
2311
- border-radius: 5px;
2180
+ border-radius: var(--radius-md);
2312
2181
  cursor: pointer;
2313
2182
  color: var(--sf-muted);
2314
2183
  display: flex;
@@ -2352,8 +2221,9 @@
2352
2221
 
2353
2222
  `.sf-markup` — the container class this repo's own bar carried — is GONE from
2354
2223
  these selectors, because nothing renders it any more (swept: no `"sf-markup"`
2355
- class in app / components / lib). `.sf-markup-btn` stays: the Clear button is
2356
- still the host's own and still takes its metrics from it. */
2224
+ class in app / components / lib). `.sf-markup-btn` went too: the Clear
2225
+ button is the shell's `IconButton` (harness UI-24), with the shell's own
2226
+ metrics, hover and :disabled look. */
2357
2227
  .sf-copybox > .es-copy-bar,
2358
2228
  .es-list-above > .es-copy-bar {
2359
2229
  display: flex;
@@ -2391,7 +2261,6 @@
2391
2261
  wide as its word. `has-glyph` is the shell's own marker for the difference
2392
2262
  (editor-shell 0.62.0), so the two cases are told apart by what they hold
2393
2263
  rather than by a list of style names kept in step by hand. */
2394
- .sf-markup-btn,
2395
2264
  .sf-copybox .es-copy-mark.has-glyph,
2396
2265
  .es-list-above .es-copy-mark.has-glyph {
2397
2266
  width: 24px;
@@ -2405,39 +2274,12 @@
2405
2274
  line-height: 1;
2406
2275
  white-space: nowrap;
2407
2276
  }
2408
- .sf-markup-btn,
2277
+ /* A mark is the shell's IconButton since efficient-easel 1.0 (UI-24): its
2278
+ ground, ink, hover, press, pressed (`aria-pressed`) and disabled looks are
2279
+ the shell's. Only its height is this row's: the bar is 22px tall. */
2409
2280
  .sf-copybox .es-copy-mark,
2410
2281
  .es-list-above .es-copy-mark {
2411
2282
  height: 22px;
2412
- border: none;
2413
- background: transparent;
2414
- border-radius: 5px;
2415
- cursor: pointer;
2416
- color: var(--sf-icon);
2417
- display: flex;
2418
- align-items: center;
2419
- justify-content: center;
2420
- opacity: 0.75;
2421
- transition: background 0.12s, color 0.12s, opacity 0.12s;
2422
- }
2423
- .sf-markup-btn:hover:not(:disabled),
2424
- .sf-copybox .es-copy-mark:hover:not(:disabled),
2425
- .es-list-above .es-copy-mark:hover:not(:disabled) {
2426
- background: var(--sf-hover);
2427
- color: var(--sf-text-secondary);
2428
- opacity: 1;
2429
- }
2430
- .sf-markup-btn:active:not(:disabled),
2431
- .sf-copybox .es-copy-mark:active:not(:disabled),
2432
- .es-list-above .es-copy-mark:active:not(:disabled) {
2433
- background: var(--sf-accent-tint);
2434
- color: var(--sf-accent-text);
2435
- }
2436
- .sf-markup-btn:disabled,
2437
- .sf-copybox .es-copy-mark:disabled,
2438
- .es-list-above .es-copy-mark:disabled {
2439
- color: var(--sf-disabled);
2440
- cursor: default;
2441
2283
  }
2442
2284
  /* The fifth button — clear formatting (card 8809) — pushed away from the four.
2443
2285
  The four ADD a mark to a selection; this one TAKES every mark off the whole
@@ -2445,8 +2287,8 @@
2445
2287
  the whole separation: `margin-left: auto` makes the bar's own `.es-divider-v`
2446
2288
  eat the free space, which IS the flexible spacer, and carries the button after
2447
2289
  it to the far end. No new class, no new line and no new colour — the divider
2448
- already draws itself in --sf-divider, and the button takes `.sf-markup-btn`
2449
- for its metrics, its hover and its :disabled look.
2290
+ already draws itself in --sf-divider, and the button is the shell's
2291
+ `IconButton`, with its metrics, its hover and its :disabled look.
2450
2292
 
2451
2293
  The height is the one adjustment: `.es-divider-v` is 22px for the 30px icon
2452
2294
  buttons of the top toolbar, which in a 22px row would run edge to edge and
@@ -2532,16 +2374,6 @@
2532
2374
  .sf-copybox:not(:focus-within) > .es-copy-bar {
2533
2375
  display: none;
2534
2376
  }
2535
- /* PRESSED: the selection already wears this mark, so a press takes it off. The
2536
- same tint as the :active press above, held — under the pointer too, which is
2537
- where it is right after the press (the hover rule would otherwise win). */
2538
- .sf-copybox .es-copy-mark.is-active,
2539
- .sf-copybox .es-copy-mark.is-active:hover:not(:disabled),
2540
- .es-list-above .es-copy-mark.is-active,
2541
- .es-list-above .es-copy-mark.is-active:hover:not(:disabled) {
2542
- background: var(--sf-accent-tint);
2543
- color: var(--sf-accent-text);
2544
- }
2545
2377
  /* The input gives up its own frame to the wrapper. The `.sf-insp-body` +
2546
2378
  attribute selectors are not decoration: the base control rule near the top of
2547
2379
  this file is `.sf-insp-body input[type="text"]`, which ties on specificity
@@ -2577,11 +2409,11 @@
2577
2409
  .sf-swatch {
2578
2410
  width: 26px;
2579
2411
  height: 26px;
2580
- border-radius: 7px;
2412
+ border-radius: var(--radius-lg);
2581
2413
  border: none;
2582
2414
  cursor: pointer;
2583
2415
  padding: 0;
2584
- box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.18);
2416
+ box-shadow: inset 0 0 0 1px var(--line-strong);
2585
2417
  }
2586
2418
  .sf-swatch.is-active {
2587
2419
  box-shadow: 0 0 0 2px var(--sf-panel), 0 0 0 4px var(--sf-accent);
@@ -2593,69 +2425,59 @@
2593
2425
  margin-left: 2px;
2594
2426
  }
2595
2427
 
2596
- /* ── THE colour control — efficient-easel's `ColourPicker` (blueprint 4.8) ──
2597
- One swatch, one typeable hex, and — pressing the swatch — the package's own
2598
- window under the row: the site's theme colours, a saturation square, a hue
2599
- strip and the hex. Its look is `efficient-easel/pick.css`; what is here is
2600
- only how the shop's rows hold it. There used to be two colour controls in
2601
- this file's rows, then one on `<input type="color">`, whose window was the
2602
- operating system's. See lib/puck/ColourControl.tsx.
2428
+ /* ── THE colour control — efficient-shell's `ColorPicker` (harness UI-24) ──
2429
+ One swatch, one typeable hex (the shell's `HexInput`), and — pressing the
2430
+ swatch — the shell's picker in a popover: the site's theme colours as its
2431
+ presets, a saturation square, a hue strip and the hex. Its look is the
2432
+ shell's; what is here is only how the shop's rows hold it. Before UI-24 it
2433
+ was efficient-easel's `ColourPicker` (blueprint 4.8), and before that
2434
+ `<input type="color">`, whose window was the operating system's. See
2435
+ lib/puck/ColourControl.tsx.
2603
2436
 
2604
2437
  THE ROW HOLDS THE PICKER IN ITS LABEL LINE — `SettingsField`'s `action`
2605
- slot (`colorInput()` too, since [MERGE] One Settings Row And Hint) — so the picker's own box steps aside
2606
- (`display: contents`) and its two halves become items of that line: the
2607
- swatch and hex stay at the end of the label line (`margin-left: auto`, which
2608
- the shell gives `.es-field-value` and nothing else), and the window wraps
2609
- onto a line of its own at the row's FULL width. Left inside the picker's box
2610
- it would open in the few pixels the action slot has. */
2611
- .es-field-head:has(> .es-pick-colour) {
2612
- flex-wrap: wrap;
2613
- }
2614
- .es-field-head > .es-pick-colour {
2615
- display: contents;
2438
+ slot — and the swatch and hex sit at the end of that line (`margin-left:
2439
+ auto`, which the shell gives `.es-field-value` and nothing else). The window
2440
+ is a popover now, so the line no longer wraps to make room for it. */
2441
+ .sf-colour {
2442
+ display: inline-flex;
2443
+ align-items: center;
2444
+ gap: 6px;
2445
+ min-width: 0;
2616
2446
  }
2617
- .es-field-head > .es-pick-colour > .es-pick-colour-row {
2447
+ .es-field-head > .sf-colour {
2618
2448
  margin-left: auto;
2619
2449
  }
2620
- .es-field-head > .es-pick-colour > .es-pick-window {
2621
- flex: 1 0 100%;
2622
- min-width: 0;
2623
- /* LAST on the line, whatever follows it in the markup: the row's Default /
2624
- Reset part (easel's `SettingsDefaultReset`, owner 2026-10-06) comes after
2625
- the picker, and without this an open window pushed Reset onto a line of
2626
- its own UNDER the window, away from the swatch it belongs beside. */
2627
- order: 1;
2628
- }
2629
-
2630
- /* ⚠️ THE HEX BOX MUST OUT-SPECIFY THE SHARED INSPECTOR INPUT. `.sf-insp-body
2631
- input[type="text"]` (0,2,1) is `width: 100%`, `padding: 8px 11px`,
2632
- `font-size: 13px` — a full-width form field — and it beats the package's
2633
- one-class rule. Inherited here it would eat the row and shove the swatch out
2634
- of it, and at 13px `#DDDDDD` rendered `#DDDDD`, one digit short, on a control
2635
- whose whole job is to show a hex exactly (the 2026-09-04 drawing). So EVERY
2636
- property the shared rule sets is restated, at the package's own values, on
2637
- a selector that wins on specificity (0,3,1) and not on source order. */
2638
- .sf-insp-body input[type="text"].es-pick-hex {
2639
- width: var(--es-pick-readout-w);
2640
- border: 1px solid transparent;
2641
- background: transparent;
2642
- border-radius: 6px;
2643
- padding: 4px 6px;
2644
- font-size: 12px;
2645
- color: var(--es-text);
2646
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
2450
+ /* Seven monospaced characters and the box's padding — no wider, so a two-word
2451
+ label keeps its line beside the swatch. */
2452
+ .sf-colour-hexwrap {
2647
2453
  flex: none;
2454
+ width: 4.75rem;
2648
2455
  }
2649
- .sf-insp-body input[type="text"].es-pick-hex:hover {
2650
- border-color: var(--es-input-border);
2651
- }
2652
- .sf-insp-body input[type="text"].es-pick-hex:focus {
2653
- outline: none;
2654
- border-color: var(--es-accent);
2655
- box-shadow: none;
2656
- background: var(--es-panel);
2456
+ .sf-colour-caption {
2457
+ font-size: 12px;
2458
+ color: var(--sf-muted);
2459
+ white-space: nowrap;
2657
2460
  }
2658
2461
 
2462
+ /* ⚠️ THE SHELL'S FIELDS MUST NOT WEAR THE INSPECTOR'S OLD INPUT RULE.
2463
+ `.sf-insp-body input[type="text"]` (0,2,1, below the shell in no layer) is
2464
+ `width: 100%`, `padding: 8px 11px`, `font-size: 13px` — the box Puck's native
2465
+ fields were given — and an unlayered rule beats every shell utility whatever
2466
+ its specificity. Inside the shell's own controls it is rolled back to the
2467
+ shell's layer, so the hex box and the slider's number look as the shell
2468
+ draws them everywhere else. */
2469
+ .sf-editor .sf-insp-body :is(.sf-slider, .sf-colour) input[type="text"],
2470
+ .sf-editor .sf-insp-body :is(.sf-slider, .sf-colour) input[type="text"]:focus,
2471
+ .sf-editor .sf-insp-body :is(.sf-slider, .sf-colour) input::placeholder {
2472
+ all: revert-layer;
2473
+ }
2474
+ /* The colour a row FOLLOWS, shown in its box in grey (option E): the grey is
2475
+ what says the setting has no colour of its own. After the roll-back above,
2476
+ and more specific than it, so the roll-back does not take the grey away. */
2477
+ .sf-editor .sf-colour input.sf-colour-hex.is-inherited,
2478
+ .sf-editor .sf-insp-body .sf-colour input[type="text"].sf-colour-hex.is-inherited {
2479
+ color: var(--sf-muted);
2480
+ }
2659
2481
 
2660
2482
  /* ── Site styles' own controls (card 66148) ───────────────────────────────────
2661
2483
  These three shapes -- a dropdown, a strip of choice rungs, and the small text
@@ -2735,7 +2557,7 @@
2735
2557
  border: 1px solid var(--sf-divider);
2736
2558
  border-radius: var(--sf-field-radius);
2737
2559
  background: var(--sf-panel);
2738
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);
2560
+ box-shadow: var(--es-menu-shadow);
2739
2561
  }
2740
2562
  /* The font menu is long enough to need its own ceiling; the theme menu is not. */
2741
2563
  .sf-chooser-menu--tall {
@@ -2924,7 +2746,7 @@
2924
2746
  ON THE TAB PANEL'S FIRST CHILD, whatever that child is. A rule per tab would
2925
2747
  be three statements of one fact, and would go stale the first time a tab's
2926
2748
  opening control changes — which is precisely how these three drifted apart. */
2927
- #sf-site-styles-tabpanel > :first-child {
2749
+ .sf-site-styles-tabpanel > :first-child {
2928
2750
  margin-top: var(--es-rhythm-group-first);
2929
2751
  }
2930
2752
 
@@ -2991,6 +2813,14 @@
2991
2813
  text-overflow: ellipsis;
2992
2814
  white-space: nowrap;
2993
2815
  }
2816
+ /* The sample in the shell's Tooltip (UI-24): the wrapper takes the sample's
2817
+ place at the end of the line and gives way, and the sample ellipsises in it. */
2818
+ .sf-style-sample-tip {
2819
+ position: relative;
2820
+ display: flex;
2821
+ min-width: 0;
2822
+ margin-left: auto;
2823
+ }
2994
2824
  /* Where Done used to sit. The editor autosaves, so the thing to say here is
2995
2825
  "stored", and it says it once and goes quiet again. */
2996
2826
  .sf-style-slot {
@@ -3060,6 +2890,35 @@
3060
2890
  .sf-rung--glyph:disabled:hover {
3061
2891
  background: var(--sf-field-bg);
3062
2892
  }
2893
+ /* The B and the I each sit in the shell's Tooltip (harness UI-24), whose
2894
+ wrapper is the strip's flex item now: it takes the glyph's 34px and the
2895
+ rung fills it. */
2896
+ .sf-rung-tip {
2897
+ position: relative;
2898
+ display: inline-flex;
2899
+ flex: 0 0 34px;
2900
+ }
2901
+ .sf-rung-tip .sf-rung--glyph {
2902
+ flex: 1;
2903
+ }
2904
+ /* A DISABLED BUTTON GETS NO HOVER AND NO FOCUS, so a reason written on it is
2905
+ never shown. The reason sits on a focusable wrapper instead, and the button
2906
+ lets the pointer through to it. Used wherever a disabled control explains
2907
+ itself through the shell's Tooltip. */
2908
+ .sf-tip-disabled {
2909
+ display: inline-flex;
2910
+ }
2911
+ .sf-rung-tip > .sf-tip-disabled {
2912
+ flex: 1;
2913
+ }
2914
+ .sf-tip-disabled > button:disabled {
2915
+ pointer-events: none;
2916
+ }
2917
+ .sf-tip-disabled:focus-visible {
2918
+ outline: 2px solid var(--sf-accent);
2919
+ outline-offset: 2px;
2920
+ border-radius: var(--radius-sm);
2921
+ }
3063
2922
  /* One theme's swatch in the preset dropdown, and one colour row's chip. */
3064
2923
  /* ── The theme list: one card per theme, each a small page in its own palette ──
3065
2924
  Card 66304. Sized against the RIGHT panel's real widths — 320px default and
@@ -3087,7 +2946,7 @@
3087
2946
  .sf-theme-card,
3088
2947
  .sf-secttheme-btn {
3089
2948
  border: 1px solid var(--sf-input-border);
3090
- border-radius: 9px;
2949
+ border-radius: var(--radius-lg);
3091
2950
  overflow: hidden;
3092
2951
  background: var(--sf-panel);
3093
2952
  }
@@ -3156,7 +3015,7 @@
3156
3015
  font-weight: 700;
3157
3016
  letter-spacing: 0.02em;
3158
3017
  padding: 5px 11px;
3159
- border-radius: 4px;
3018
+ border-radius: var(--radius-sm);
3160
3019
  white-space: nowrap;
3161
3020
  flex: none;
3162
3021
  }
@@ -3164,7 +3023,7 @@
3164
3023
  flex: 1;
3165
3024
  min-width: 0;
3166
3025
  height: 22px;
3167
- border-radius: 3px;
3026
+ border-radius: var(--radius-sm);
3168
3027
  border: 1px solid;
3169
3028
  display: flex;
3170
3029
  align-items: center;
@@ -3201,32 +3060,18 @@
3201
3060
  margin-left: auto;
3202
3061
  font-size: 10px;
3203
3062
  font-weight: 700;
3204
- color: #b45309;
3063
+ color: var(--status-warning-soft-ink);
3205
3064
  white-space: nowrap;
3206
3065
  }
3207
3066
  .sf-theme-warn + .sf-theme-edit { margin-left: 8px; }
3208
3067
  /* With no warning beside it the Edit still sits hard right. */
3209
3068
  .sf-theme-name { margin-right: auto; }
3210
3069
 
3070
+ /* The Edit / Done button is efficient-shell's `Button` (harness UI-24): its
3071
+ look is the shell's, `secondary` folded and `primary` open. This class only
3072
+ keeps it from shrinking in the foot. */
3211
3073
  .sf-theme-edit {
3212
3074
  flex: none;
3213
- border: 1px solid var(--sf-input-border);
3214
- background: var(--sf-panel);
3215
- border-radius: 4px;
3216
- font: inherit;
3217
- font-size: 10px;
3218
- font-weight: 700;
3219
- letter-spacing: 0.03em;
3220
- color: var(--sf-text-secondary);
3221
- padding: 3px 8px;
3222
- line-height: 1.3;
3223
- cursor: pointer;
3224
- }
3225
- .sf-theme-edit:hover { border-color: var(--sf-muted); color: var(--sf-text); }
3226
- .sf-theme-edit.is-open {
3227
- background: var(--sf-accent);
3228
- border-color: var(--sf-accent);
3229
- color: #fff;
3230
3075
  }
3231
3076
 
3232
3077
  /* This theme's own colours, inside this theme's own card. The first row carries
@@ -3236,20 +3081,10 @@
3236
3081
  .sf-theme-colours[hidden] { display: none; }
3237
3082
  .sf-theme-colours > .es-field:first-child { margin-top: var(--es-rhythm-group-first); }
3238
3083
 
3084
+ /* The shell's `link` Button (harness UI-24); placement only. */
3239
3085
  .sf-theme-reset {
3240
- width: 100%;
3241
3086
  margin-top: var(--es-rhythm-between-tight);
3242
- border: 0;
3243
- background: none;
3244
- font: inherit;
3245
- font-size: 10.5px;
3246
- font-weight: 600;
3247
- color: var(--sf-accent-text);
3248
- padding: 0;
3249
- cursor: pointer;
3250
- text-align: left;
3251
3087
  }
3252
- .sf-theme-reset:disabled { color: var(--sf-muted); cursor: default; }
3253
3088
 
3254
3089
  .sf-theme-add {
3255
3090
  display: flex;
@@ -3409,8 +3244,8 @@
3409
3244
  width: 9px;
3410
3245
  height: 9px;
3411
3246
  flex: none;
3412
- border-radius: 2px;
3413
- box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.18);
3247
+ border-radius: calc(var(--radius-sm) / 2);
3248
+ box-shadow: inset 0 0 0 1px var(--line-strong);
3414
3249
  }
3415
3250
 
3416
3251
  /* The nine-square grid moved into `efficient-easel/panel` as `SettingsPlacement`
@@ -3460,68 +3295,14 @@
3460
3295
  flex: 1;
3461
3296
  min-width: 0;
3462
3297
  }
3463
- /* The range was the one control still drawn entirely by the browser — a native
3464
- track and thumb tinted with `accent-color`, sitting beside a panel of bespoke
3465
- toggles, segmenteds and swatches. It read as an unstyled leftover at every OS,
3466
- and each OS drew it differently. Drawn explicitly here instead, off the same
3467
- tokens as its neighbours: a 4px rail on the field track colour, and a white
3468
- thumb ringed in the accent (the shell's control shape — cf. `.es-switch`).
3469
-
3470
- `accent-color` is deliberately dropped: with an explicit thumb + track it only
3471
- fights the rules below. The two vendor thumb selectors CANNOT be combined into
3472
- one rule — a selector list is discarded wholesale by any browser that doesn't
3473
- recognise one member, which would leave the thumb unstyled. */
3474
- .sf-slider input[type="range"] {
3298
+ /* The track and the typed number are efficient-shell's `Slider` (harness
3299
+ UI-24); until it had one, this sheet drew a 4px rail and a ringed thumb on a
3300
+ native range here, and a readout that was pressed to type into (card
3301
+ 66275). The shell's control does both, so those rules went with them. Its
3302
+ box fills `.sf-slider` like the input did. */
3303
+ .sf-slider > .sf-slider-track {
3475
3304
  flex: 1;
3476
3305
  min-width: 0;
3477
- height: 18px;
3478
- margin: 0;
3479
- padding: 0;
3480
- background: transparent;
3481
- cursor: pointer;
3482
- -webkit-appearance: none;
3483
- appearance: none;
3484
- }
3485
- .sf-slider input[type="range"]::-webkit-slider-runnable-track {
3486
- height: 4px;
3487
- border-radius: 999px;
3488
- background: var(--sf-track-bg);
3489
- box-shadow: inset 0 0 0 1px var(--sf-input-border);
3490
- }
3491
- .sf-slider input[type="range"]::-moz-range-track {
3492
- height: 4px;
3493
- border-radius: 999px;
3494
- background: var(--sf-track-bg);
3495
- box-shadow: inset 0 0 0 1px var(--sf-input-border);
3496
- }
3497
- .sf-slider input[type="range"]::-webkit-slider-thumb {
3498
- -webkit-appearance: none;
3499
- appearance: none;
3500
- width: 14px;
3501
- height: 14px;
3502
- /* Centre the thumb on a 4px track: (4 - 14) / 2. Webkit lays the thumb out
3503
- from the track's top edge, so without this it hangs below the rail. */
3504
- margin-top: -5px;
3505
- border-radius: 50%;
3506
- background: var(--sf-knob);
3507
- border: 2px solid var(--sf-accent);
3508
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
3509
- transition: transform 0.12s;
3510
- }
3511
- .sf-slider input[type="range"]::-moz-range-thumb {
3512
- width: 14px;
3513
- height: 14px;
3514
- border-radius: 50%;
3515
- background: var(--sf-knob);
3516
- border: 2px solid var(--sf-accent);
3517
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
3518
- transition: transform 0.12s;
3519
- }
3520
- .sf-slider input[type="range"]:hover::-webkit-slider-thumb {
3521
- transform: scale(1.12);
3522
- }
3523
- .sf-slider input[type="range"]:hover::-moz-range-thumb {
3524
- transform: scale(1.12);
3525
3306
  }
3526
3307
  /* THE NOTCH — where the style is, on a track that overrides it (card 66116).
3527
3308
  Drag away from it to override; land on it and the override is cleared, so the
@@ -3542,57 +3323,11 @@
3542
3323
  width: 2px;
3543
3324
  height: 10px;
3544
3325
  margin-top: -5px;
3545
- border-radius: 1px;
3326
+ border-radius: calc(var(--radius-sm) / 2);
3546
3327
  background: var(--sf-input-border);
3547
3328
  pointer-events: none;
3548
3329
  }
3549
3330
 
3550
- /* THE NUMBER, AND IT IS A BUTTON (card 66275). A track that runs 200–900 in
3551
- tens has seventy stops on it, and 380 is one of them — reachable by dragging
3552
- only if the merchant lands the thumb on exactly the right pixel. So the
3553
- readout is pressed to type into, which means it is a real <button> and needs
3554
- the browser's own button drawing taken off it: everything below the first
3555
- five declarations is undoing an appearance, not adding one. It must measure
3556
- exactly what the <span> measured, because `.es-field-value` and this box sit
3557
- in the same 42px column and a merchant reads down it. */
3558
- .sf-slider-val {
3559
- font-size: 12px;
3560
- color: var(--sf-icon);
3561
- font-variant-numeric: tabular-nums;
3562
- min-width: 42px;
3563
- text-align: right;
3564
- padding: 0;
3565
- border: none;
3566
- background: none;
3567
- font-family: inherit;
3568
- line-height: inherit;
3569
- }
3570
- button.sf-slider-val {
3571
- cursor: text;
3572
- border-radius: 4px;
3573
- }
3574
- button.sf-slider-val:hover {
3575
- color: var(--sf-text);
3576
- box-shadow: inset 0 0 0 1px var(--sf-input-border);
3577
- }
3578
- /* The same box, being typed into. It keeps the readout's own width and
3579
- alignment so the row does not shift under the merchant's caret the moment
3580
- they click it — a number that jumps sideways as it becomes editable reads as
3581
- two different controls. */
3582
- input.sf-slider-typed {
3583
- width: 42px;
3584
- color: var(--sf-text);
3585
- box-shadow: inset 0 0 0 1px var(--sf-accent);
3586
- border-radius: 4px;
3587
- padding: 1px 3px;
3588
- /* iOS zooms a focused input under 16px unless the page says otherwise; the
3589
- editor is desktop-first but the panel is reachable on a tablet. */
3590
- touch-action: manipulation;
3591
- }
3592
- input.sf-slider-typed:focus {
3593
- outline: none;
3594
- }
3595
-
3596
3331
  /* Pinned editor-only toggle bar — sits BETWEEN the inspector head and the
3597
3332
  scrolling body (SectionEditor), so an aid like "Preview hover state" stays
3598
3333
  put at the top while the fields below it scroll. `flex: none` keeps it from
@@ -3623,34 +3358,14 @@ input.sf-slider-typed:focus {
3623
3358
  }
3624
3359
 
3625
3360
  /* Columns-per-line stepper — a compact − N + shown for the view the footer's
3626
- "Columns per line" control is currently editing (the top-bar device). */
3361
+ "Columns per line" control is currently editing (the top-bar device). The −
3362
+ and + are the shell's `IconButton` (harness UI-24); this only lines the three
3363
+ parts up and sets the number. */
3627
3364
  .sf-colstep {
3628
3365
  display: flex;
3629
3366
  align-items: center;
3630
3367
  gap: 6px;
3631
3368
  }
3632
- .sf-colstep-btn {
3633
- width: 22px;
3634
- height: 22px;
3635
- padding: 0;
3636
- border: 1px solid var(--sf-line);
3637
- border-radius: 6px;
3638
- background: transparent;
3639
- color: var(--sf-text);
3640
- font-size: 15px;
3641
- line-height: 1;
3642
- cursor: pointer;
3643
- display: flex;
3644
- align-items: center;
3645
- justify-content: center;
3646
- }
3647
- .sf-colstep-btn:hover:not(:disabled) {
3648
- border-color: var(--sf-accent);
3649
- }
3650
- .sf-colstep-btn:disabled {
3651
- opacity: 0.4;
3652
- cursor: default;
3653
- }
3654
3369
  .sf-colstep-value {
3655
3370
  min-width: 14px;
3656
3371
  text-align: center;
@@ -3659,19 +3374,13 @@ input.sf-slider-typed:focus {
3659
3374
  color: var(--sf-text);
3660
3375
  }
3661
3376
 
3662
- /* Checkbox — a literal tick box tinted to the editor accent. The row is the
3663
- shell's SettingsField with the box in its `action` slot (label left, box
3664
- right); the label points at the box, so the text is clickable. */
3377
+ /* Checkbox — a literal tick box. The row is easel's SettingsField with the box
3378
+ in its `action` slot (label left, box right); the label points at the box, so
3379
+ the text is clickable. The box is efficient-shell's `Checkbox` (harness
3380
+ UI-24), drawn and focus-ringed by the shell, so no rule here reaches it. */
3665
3381
  .sf-checkbox {
3666
3382
  cursor: pointer;
3667
3383
  }
3668
- .sf-checkbox input[type="checkbox"] {
3669
- width: 17px;
3670
- height: 17px;
3671
- margin: 0;
3672
- accent-color: var(--sf-accent);
3673
- cursor: pointer;
3674
- }
3675
3384
 
3676
3385
  /* A field group greyed out because a higher-level setting overrides it — e.g. the
3677
3386
  manual ProductPicker (Category + Product) when the Product Grid's Products
@@ -3689,15 +3398,14 @@ input.sf-slider-typed:focus {
3689
3398
 
3690
3399
  `:focus-visible`, not `:focus`, so a mouse click never paints a ring — the
3691
3400
  reason the outline was dropped in the first place. Same 2px accent ring as
3692
- the top bar's `.es-btn-ghost` (editor-shell) / `.sf-addcard`, so focus reads identically across the whole
3693
- editor rather than per-control. */
3401
+ `.sf-addcard`, so focus reads identically across the whole editor rather
3402
+ than per-control. (The top bar's buttons are the shell's `Button` since
3403
+ efficient-easel 1.0 and ring themselves.) */
3694
3404
  .sf-seg2 button:focus-visible,
3695
3405
  .sf-readability-suggestion:focus-visible,
3696
3406
  .sf-swatch:focus-visible,
3697
3407
  .sf-field-eye:focus-visible,
3698
- .sf-slider input[type="range"]:focus-visible,
3699
- .sf-insp-body select:focus-visible,
3700
- .sf-checkbox input[type="checkbox"]:focus-visible {
3408
+ .sf-insp-body select:focus-visible {
3701
3409
  outline: 2px solid var(--sf-accent);
3702
3410
  outline-offset: 2px;
3703
3411
  }
@@ -3716,14 +3424,14 @@ input.sf-slider-typed:focus {
3716
3424
  gap: 3px;
3717
3425
  background: var(--sf-track-bg);
3718
3426
  border: 1px solid var(--sf-input-border);
3719
- border-radius: 9px;
3427
+ border-radius: var(--radius-lg);
3720
3428
  padding: 3px;
3721
3429
  }
3722
3430
  .sf-seg2 button {
3723
3431
  flex: 1;
3724
3432
  border: none;
3725
3433
  background: transparent;
3726
- border-radius: 6px;
3434
+ border-radius: var(--radius-md);
3727
3435
  padding: 6px 8px;
3728
3436
  font-size: 12px;
3729
3437
  font-weight: 500;
@@ -3817,7 +3525,7 @@ input.sf-slider-typed:focus {
3817
3525
  color: var(--sf-text-strong);
3818
3526
  /* Softer and shorter than the old flat 0.14 drop: the active segment should
3819
3527
  look lifted off the track, not outlined against it. */
3820
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.04);
3528
+ box-shadow: var(--es-raised-shadow);
3821
3529
  }
3822
3530
  /* ─── A control the panel has taken out of force (card 66216) ─────────────
3823
3531
  The setting still exists and still shows the merchant's saved answer; another
@@ -3826,11 +3534,9 @@ input.sf-slider-typed:focus {
3826
3534
  Background. DIMMED, NEVER HIDDEN: a control that disappears reads as a feature
3827
3535
  that was removed, and leaves the merchant no way to see what to change.
3828
3536
 
3829
- `--sf-disabled` is the panel's existing token for exactly this — the same dim
3830
- the top bar's icon and ghost buttons wear (editor-shell's `.es-iconbtn` /
3831
- `.es-btn-ghost`, lifted from this sheet); this is the same state on
3832
- the control that had no rule for it (the switch is efficient-easel's
3833
- `.es-switch`, which dims itself). The track keeps its border and the
3537
+ `--sf-disabled` is the panel's existing token for exactly this; this is the
3538
+ same state on the control that had no rule for it (the switch is the
3539
+ shell's `Switch` since efficient-easel 1.0, which dims itself). The track keeps its border and the
3834
3540
  pressed segment keeps its lift, so the strip still READS as a strip with an
3835
3541
  answer in it — it is quieter, not broken. */
3836
3542
  .sf-seg2[data-disabled] {
@@ -3884,6 +3590,11 @@ input.sf-list-marking {
3884
3590
  flex-direction: column;
3885
3591
  gap: 9px;
3886
3592
  }
3593
+ /* The shell's Button in the column (SourceStatus): its own width, not the
3594
+ column's. Placement only; the look is the shell's (harness UI-24). */
3595
+ .sf-blogsource-action {
3596
+ align-self: flex-start;
3597
+ }
3887
3598
  .sf-blogsource-status {
3888
3599
  margin: 0;
3889
3600
  font-size: 12px;
@@ -3895,8 +3606,8 @@ input.sf-list-marking {
3895
3606
  /* The "Open" control inside a source note — the route the sentence names.
3896
3607
  [ADD] Source Note Opens The Record, 2026-09-06.
3897
3608
 
3898
- AN INLINE BUTTON, NOT A BLOCK ONE (`.sf-blogsource-btn` is the block form on
3899
- the product inspector). This one sits INSIDE the sentence, at the end, so it
3609
+ AN INLINE BUTTON, NOT A BLOCK ONE (the shell's `Button` beside the status is
3610
+ the block form, `SourceStatus`). This one sits INSIDE the sentence, at the end, so it
3900
3611
  inherits the caption's 11px and only its colour and underline say it is
3901
3612
  pressable — a 12px pill on the end of an 11px line would be a second control
3902
3613
  grammar for the same act.
@@ -3919,28 +3630,7 @@ input.sf-list-marking {
3919
3630
  .sf-source-open:focus-visible {
3920
3631
  outline: 2px solid var(--sf-accent);
3921
3632
  outline-offset: 2px;
3922
- border-radius: 2px;
3923
- }
3924
-
3925
- .sf-blogsource-btn {
3926
- align-self: flex-start;
3927
- padding: 7px 14px;
3928
- border: 1px solid var(--sf-input-border);
3929
- border-radius: 8px;
3930
- background: transparent;
3931
- color: var(--sf-text-secondary);
3932
- font-size: 12px;
3933
- font-weight: 500;
3934
- cursor: pointer;
3935
- transition: border-color 0.12s, color 0.12s, background 0.12s;
3936
- }
3937
- .sf-blogsource-btn:hover:not(:disabled) {
3938
- border-color: var(--sf-accent);
3939
- color: var(--sf-accent);
3940
- }
3941
- .sf-blogsource-btn:disabled {
3942
- opacity: 0.55;
3943
- cursor: default;
3633
+ border-radius: calc(var(--radius-sm) / 2);
3944
3634
  }
3945
3635
 
3946
3636
  /* Form picker — the "which form does this section show" control. Reuses the
@@ -3986,7 +3676,7 @@ input.sf-list-marking {
3986
3676
  border: 1px solid var(--sf-btn-border);
3987
3677
  background: var(--sf-panel);
3988
3678
  color: var(--sf-btn-text);
3989
- border-radius: 6px;
3679
+ border-radius: var(--radius-md);
3990
3680
  padding: 4px 10px;
3991
3681
  font-size: 12px;
3992
3682
  font-weight: 500;
@@ -4028,7 +3718,7 @@ ul.sf-image-status {
4028
3718
  because of its old name, `.sf-media-note` (card 66121). */
4029
3719
  .sf-image-slot-inert {
4030
3720
  border: 1.5px dashed var(--sf-input-border);
4031
- border-radius: 9px;
3721
+ border-radius: var(--radius-lg);
4032
3722
  padding: 12px 14px;
4033
3723
  font-size: 12px;
4034
3724
  line-height: 1.5;
@@ -4065,7 +3755,7 @@ ul.sf-image-status {
4065
3755
  min-height: 150px;
4066
3756
  padding: 0;
4067
3757
  border: 1px solid var(--sf-line);
4068
- border-radius: 6px;
3758
+ border-radius: var(--radius-md);
4069
3759
  background: var(--sf-preview-bg);
4070
3760
  cursor: crosshair;
4071
3761
  touch-action: none;
@@ -4095,7 +3785,7 @@ ul.sf-image-status {
4095
3785
  }
4096
3786
  .sf-focus-cross {
4097
3787
  position: absolute;
4098
- background: rgb(255 255 255 / 45%);
3788
+ background: color-mix(in srgb, var(--status-on-solid) 45%, transparent);
4099
3789
  pointer-events: none;
4100
3790
  }
4101
3791
  .sf-focus-cross--h {
@@ -4110,14 +3800,16 @@ ul.sf-image-status {
4110
3800
  }
4111
3801
  /* Drawn in white with a dark ring rather than in a panel token: it sits on a
4112
3802
  merchant's own photograph, which can be any colour, and a token that read
4113
- correctly against the panel would vanish against half of them. */
3803
+ correctly against the panel would vanish against half of them. White and the
3804
+ ring are the shell's own on-media pair (`--status-on-solid`, the media
3805
+ scrim), which do not change with the theme for the same reason. */
4114
3806
  .sf-focus-marker {
4115
3807
  position: absolute;
4116
3808
  width: 18px;
4117
3809
  height: 18px;
4118
- border: 2px solid #fff;
3810
+ border: 2px solid var(--status-on-solid);
4119
3811
  border-radius: 50%;
4120
- box-shadow: 0 0 0 1.5px rgb(0 0 0 / 55%), 0 1px 5px rgb(0 0 0 / 50%);
3812
+ box-shadow: 0 0 0 1.5px var(--rosh-media-scrim-button), 0 1px 5px var(--rosh-media-scrim-button);
4121
3813
  transform: translate(-50%, -50%);
4122
3814
  pointer-events: none;
4123
3815
  }
@@ -4147,8 +3839,7 @@ ul.sf-image-status {
4147
3839
  THE chrome's quiet sentence: what a panel says when it has something to tell
4148
3840
  the merchant and nothing to let them edit. A whole-panel stand-in for
4149
3841
  settings that belong to a website the open document does not have; "No pages
4150
- yet"; "Loading…"; the word "draft" trailing a row in the duplicate-to-page
4151
- menu. One voice, so it is one class.
3842
+ yet"; "Loading…". One voice, so it is one class.
4152
3843
 
4153
3844
  It was six classes at five sizes — .sf-pages-note, .sf-sm-note,
4154
3845
  .sf-panel-note, .sf-dupmenu-note, and two more that were only counted as
@@ -4198,7 +3889,7 @@ ul.sf-image-status {
4198
3889
  .sf-note--error {
4199
3890
  color: var(--sf-danger);
4200
3891
  background: var(--sf-danger-tint);
4201
- border-radius: 6px;
3892
+ border-radius: var(--radius-md);
4202
3893
  }
4203
3894
  /* Where this section's words come from, when they are not the merchant's to
4204
3895
  type (card 66089, owner ruling 7). A MODIFIER rather than a second note
@@ -4231,7 +3922,7 @@ ul.sf-image-status {
4231
3922
  margin: 0 0 4px;
4232
3923
  padding: 11px 13px;
4233
3924
  border: 1px solid var(--sf-divider);
4234
- border-radius: 12px;
3925
+ border-radius: var(--radius-xl);
4235
3926
  background: var(--sf-track-bg);
4236
3927
  }
4237
3928
  /* Marked only once something has actually been overridden, so the card is quiet
@@ -4250,7 +3941,7 @@ ul.sf-image-status {
4250
3941
  /* Rounds ITSELF against the card's left corners, so the card no longer needs
4251
3942
  `overflow: hidden` to hide the square ends. Left radii only — the right side
4252
3943
  is a straight edge into the card. */
4253
- border-radius: 12px 0 0 12px;
3944
+ border-radius: var(--radius-xl) 0 0 var(--radius-xl);
4254
3945
  }
4255
3946
  /* The card's own caption — the panel's small-label voice, so it reads as a
4256
3947
  heading for the name below rather than as another name. */
@@ -4331,7 +4022,7 @@ ul.sf-image-status {
4331
4022
  .sf-part-style-trigger:focus-visible {
4332
4023
  outline: 2px solid var(--sf-accent);
4333
4024
  outline-offset: 2px;
4334
- border-radius: 3px;
4025
+ border-radius: var(--radius-sm);
4335
4026
  }
4336
4027
  /* Sits over the card's own content rather than pushing it down: the card is a
4337
4028
  summary and a menu that grew it would move the controls under it while a
@@ -4341,7 +4032,7 @@ ul.sf-image-status {
4341
4032
  z-index: 30;
4342
4033
  left: 0;
4343
4034
  right: 0;
4344
- box-shadow: 0 8px 24px rgb(0 0 0 / 0.18);
4035
+ box-shadow: var(--es-menu-shadow);
4345
4036
  }
4346
4037
  .sf-part-style-option {
4347
4038
  display: flex;
@@ -4508,8 +4199,7 @@ ul.sf-image-status {
4508
4199
  slot now holds — without it the row keeps drawing `style` at full strength
4509
4200
  and the one visual difference between "set" and "following the style"
4510
4201
  disappears. */
4511
- .sf-part-field.is-style-value > .es-field-head > .es-field-value,
4512
- .sf-part-field.is-style-value > .es-field-head > .es-field-value > .sf-slider-val {
4202
+ .sf-part-field.is-style-value > .es-field-head > .es-field-value {
4513
4203
  color: var(--sf-muted);
4514
4204
  }
4515
4205
 
@@ -4539,10 +4229,29 @@ ul.sf-image-status {
4539
4229
  gap: 8px;
4540
4230
  min-width: 0;
4541
4231
  }
4542
- /* In this column the picker keeps its own box: the window opens under the
4543
- dropdown at the column's width, and the swatch and hex sit at its end. */
4544
- .sf-part-ink > .es-pick-colour > .es-pick-colour-row {
4545
- align-self: flex-end;
4232
+ /* The part panel's Color row: its name and what it holds on one line, the
4233
+ theme's swatches and Any color under it — the shell's swatches and picker. */
4234
+ .sf-part-ink-top {
4235
+ display: flex;
4236
+ align-items: baseline;
4237
+ gap: 8px;
4238
+ }
4239
+ .sf-part-ink-label {
4240
+ font-size: 11px;
4241
+ font-weight: 600;
4242
+ color: var(--sf-muted);
4243
+ }
4244
+ .sf-part-ink-read {
4245
+ margin-left: auto;
4246
+ font-size: 12px;
4247
+ color: var(--sf-text-secondary);
4248
+ font-variant-numeric: tabular-nums;
4249
+ }
4250
+ .sf-part-ink-swatches {
4251
+ display: flex;
4252
+ flex-wrap: wrap;
4253
+ align-items: center;
4254
+ gap: 6px;
4546
4255
  }
4547
4256
 
4548
4257
  /* The override mark, half two — and it IS the reset button, which is why it is
@@ -4773,10 +4482,21 @@ ul.sf-image-status {
4773
4482
  flex: 1 1 auto;
4774
4483
  min-width: 0;
4775
4484
  }
4485
+ /* Each tile sits in the shell's Tooltip (harness UI-24); its wrapper is the
4486
+ grid item now, and the tile fills it. */
4487
+ .sf-tile-tip {
4488
+ position: relative;
4489
+ display: flex;
4490
+ min-width: 0;
4491
+ }
4492
+ .sf-tile-tip > button {
4493
+ flex: 1;
4494
+ min-width: 0;
4495
+ }
4776
4496
  .sf-gallery-layouts button {
4777
4497
  border: 1px solid var(--sf-input-border);
4778
4498
  background: var(--sf-field-bg);
4779
- border-radius: 7px;
4499
+ border-radius: var(--radius-lg);
4780
4500
  padding: 5px;
4781
4501
  cursor: pointer;
4782
4502
  transition: border-color 0.12s, box-shadow 0.12s, background 0.12s;
@@ -4802,7 +4522,7 @@ ul.sf-image-status {
4802
4522
  .sf-gallery-pic i {
4803
4523
  display: block;
4804
4524
  background: var(--sf-track-bg);
4805
- border-radius: 1.5px;
4525
+ border-radius: calc(var(--radius-sm) / 2);
4806
4526
  }
4807
4527
  .sf-gallery-layouts button[aria-pressed="true"] .sf-gallery-pic i {
4808
4528
  background: var(--sf-accent);