efficient-web-builder 6.0.0 → 7.1.1

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 (118) hide show
  1. package/CHANGELOG.md +238 -0
  2. package/README.md +42 -0
  3. package/dist/components/ApplyCornersToSiblings.js +1 -1
  4. package/dist/components/Banner.js +1 -1
  5. package/dist/components/BlockGapField.js +1 -1
  6. package/dist/components/BlogPostSource.js +1 -1
  7. package/dist/components/Breadcrumb.js +1 -1
  8. package/dist/components/CardShadowField.js +1 -1
  9. package/dist/components/CategoryBar.js +1 -1
  10. package/dist/components/ChatSettingsPanel.js +1 -1
  11. package/dist/components/ClearColoursDialog.js +1 -1
  12. package/dist/components/CornersField.js +1 -1
  13. package/dist/components/DocBody.js +5 -5
  14. package/dist/components/DuplicateToPageMenu.js +1 -1
  15. package/dist/components/FAQ.js +1 -1
  16. package/dist/components/Flex.js +1 -1
  17. package/dist/components/Footprint.js +3 -3
  18. package/dist/components/HeroImageField.js +1 -1
  19. package/dist/components/HeroSection.js +2 -2
  20. package/dist/components/HeroSingle.js +1 -1
  21. package/dist/components/HeroSlide.js +1 -1
  22. package/dist/components/HoverEffectField.js +1 -1
  23. package/dist/components/ImageFocusRow.js +1 -1
  24. package/dist/components/IntegrationsSection.js +1 -1
  25. package/dist/components/IntroBleed.js +1 -1
  26. package/dist/components/KeyFeatures.js +1 -1
  27. package/dist/components/LocationsGroupedField.js +1 -1
  28. package/dist/components/MediaUploadField.js +1 -1
  29. package/dist/components/MultiSelectField.js +1 -1
  30. package/dist/components/PagesPanel.js +1 -1
  31. package/dist/components/PartInkControl.js +1 -1
  32. package/dist/components/PartResetDot.js +1 -1
  33. package/dist/components/PartSettingsPanel.js +1 -1
  34. package/dist/components/PartTextBox.js +1 -1
  35. package/dist/components/PhotographyField.js +1 -1
  36. package/dist/components/ProcessSteps.js +1 -1
  37. package/dist/components/SearchSelectField.d.ts +1 -1
  38. package/dist/components/SearchSelectField.js +1 -1
  39. package/dist/components/SectionEditor.js +1 -1
  40. package/dist/components/SectionTextEntries.js +1 -1
  41. package/dist/components/SectionThemePicker.js +1 -1
  42. package/dist/components/Series.js +1 -1
  43. package/dist/components/ShadowColourField.js +1 -1
  44. package/dist/components/SideSpaceField.js +1 -1
  45. package/dist/components/SiteFields.js +1 -1
  46. package/dist/components/SiteWidthField.js +1 -1
  47. package/dist/components/SlotChildRows.js +1 -1
  48. package/dist/components/SocialLinksPanel.js +1 -1
  49. package/dist/components/SourceStatus.js +1 -1
  50. package/dist/components/SpecStrip.js +1 -1
  51. package/dist/components/Split.js +1 -1
  52. package/dist/components/StandaloneSectionEditor.js +1 -1
  53. package/dist/components/StatsParagraph.js +1 -1
  54. package/dist/components/StorefrontLinkField.js +1 -1
  55. package/dist/components/StylesPanel.js +1 -1
  56. package/dist/components/SwitchToBoxFooter.js +1 -1
  57. package/dist/components/Testimonials.js +1 -1
  58. package/dist/components/TextStyleRow.js +1 -1
  59. package/dist/components/TextStylesField.js +1 -1
  60. package/dist/components/ThemeCard.js +1 -1
  61. package/dist/components/ThemeColourField.js +1 -1
  62. package/dist/components/ThemeField.js +1 -1
  63. package/dist/components/TickerSection.js +1 -1
  64. package/dist/components/TickerSettingsPanel.js +1 -1
  65. package/dist/components/TitleWithButtons.js +1 -1
  66. package/dist/components/WebsiteScreenParts.js +1 -1
  67. package/dist/components/WebsiteSettingsPanel.js +1 -1
  68. package/dist/components/WidthFollowField.js +1 -1
  69. package/dist/components/preview-devices.js +1 -1
  70. package/dist/components/storefront-editor.css +93 -123
  71. package/dist/components/useLocationsTree.js +1 -1
  72. package/dist/lib/content/ink.d.ts +36 -0
  73. package/dist/lib/content/ink.js +1 -0
  74. package/dist/lib/content/section-surface.js +1 -1
  75. package/dist/lib/editor-words-catalogue.d.ts +1178 -0
  76. package/dist/lib/editor-words-catalogue.js +11 -0
  77. package/dist/lib/editor-words.d.ts +15 -0
  78. package/dist/lib/editor-words.js +1 -0
  79. package/dist/lib/links/link-picker-setup.js +1 -1
  80. package/dist/lib/page-rows.d.ts +8 -0
  81. package/dist/lib/page-rows.js +1 -0
  82. package/dist/lib/puck/ColourControl.js +1 -1
  83. package/dist/lib/puck/MarkupToolbar.js +1 -1
  84. package/dist/lib/puck/apply-card-corners.js +1 -1
  85. package/dist/lib/puck/blocks/general/Breadcrumb/render.js +1 -1
  86. package/dist/lib/puck/blocks/general/Cards/render.js +1 -1
  87. package/dist/lib/puck/blocks/general/CategoryBar/render.js +1 -1
  88. package/dist/lib/puck/blocks/general/Chart/render.js +2 -2
  89. package/dist/lib/puck/blocks/general/DocBody/render.js +1 -1
  90. package/dist/lib/puck/blocks/general/DocMasthead/render.js +1 -1
  91. package/dist/lib/puck/blocks/general/DocTabs/render.js +1 -1
  92. package/dist/lib/puck/blocks/general/FAQ/render.js +1 -1
  93. package/dist/lib/puck/blocks/general/Footprint/render.js +2 -2
  94. package/dist/lib/puck/blocks/general/KeyFeatures/render.js +1 -1
  95. package/dist/lib/puck/blocks/general/Logos/render.js +1 -1
  96. package/dist/lib/puck/blocks/general/Masthead/render.js +1 -1
  97. package/dist/lib/puck/blocks/general/OrderSummary/render.js +1 -1
  98. package/dist/lib/puck/blocks/general/PaymentBadges/render.js +1 -1
  99. package/dist/lib/puck/blocks/general/ProcessSteps/render.js +1 -1
  100. package/dist/lib/puck/blocks/general/Series/render.js +1 -1
  101. package/dist/lib/puck/blocks/general/StatsParagraph/render.js +1 -1
  102. package/dist/lib/puck/blocks/general/Testimonials/render.js +1 -1
  103. package/dist/lib/puck/blocks/general/Timeline/render.js +2 -2
  104. package/dist/lib/puck/blocks/general/TimelineTrack/render.js +1 -1
  105. package/dist/lib/puck/blocks/scaffold.js +1 -1
  106. package/dist/lib/puck/controls.js +1 -1
  107. package/dist/lib/puck/footer-block-row-name.js +1 -1
  108. package/dist/lib/puck/page-root-fields.js +1 -1
  109. package/dist/lib/puck/theme-swatches.js +1 -1
  110. package/dist/lib/source-rules/editor-words.d.ts +26 -0
  111. package/dist/lib/source-rules/editor-words.js +3 -0
  112. package/dist/lib/source-rules/mirrored-rows-agree.js +1 -1
  113. package/dist/lib/source-rules/part-style-naming.js +1 -1
  114. package/dist/lib/source-rules/support/label-extraction.js +2 -2
  115. package/dist/lib/theme-save-message.js +1 -1
  116. package/package.json +6 -5
  117. package/dist/components/HeadingAudit.d.ts +0 -1
  118. package/dist/components/HeadingAudit.js +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,243 @@
1
1
  # Changelog
2
2
 
3
+ ## 7.1.1 - 2026-10-14
4
+
5
+ - **"Duplicate to page" has no trailing dots** (owner ruling, 2026-10-14:
6
+ "…" means only that words were cut, so no button, link or menu label ends in
7
+ it). The menu heading now says the phrase the menu already names itself by,
8
+ so the editor's catalogue has one phrase fewer: `"Duplicate to page…"` is
9
+ gone. No host has a word for it (searched efficient-storefront,
10
+ efficient-admin-portal, efficient-storefront-blocks and efficient-easel).
11
+ - **`efficient-shell-ui-check` counts it now.** The dev shell is 7.19.0 and
12
+ `ui-kit.config.json` names `ellipsis-label`, which reads the phrase handed to
13
+ `say()`. Peer ranges are unchanged.
14
+
15
+ ## 7.1.0 - 2026-10-14
16
+
17
+ - **The Layers head names the open page** ("About us", not "/about"; owner,
18
+ 2026-10-14, editor audit item 10). The Pages head's second line names the
19
+ website, so the Layers head's names the page: each says what its panel is
20
+ about. The website hands the screen `useDocumentLabel`; the address stays
21
+ as the fallback while the list loads.
22
+ - **The page list is held once** (`lib/page-rows`): the Pages panel and the
23
+ Layers head read the same rows from one `listPages()` per site, instead of
24
+ the panel keeping its own copy.
25
+
26
+ ## 7.0.1 - 2026-10-14
27
+
28
+ - **The Pages panel has one way to add a page** (Lewis, 2026-10-14). The bottom
29
+ "+ New page" button and its `.sf-pages-new` rules are gone. The panel head's
30
+ New page icon button already did the same thing. An empty list now says
31
+ "No pages yet. Add one with + above." and points at that head button. A host
32
+ that translated "No pages yet" gives the new sentence instead.
33
+ - **Pages list rows keep a 4px gap** (`.sf-pgroup-body`, `var(--space-4)`), the
34
+ shell Tree's own row gap. A hovered row next to the selected one is now two
35
+ rounded shapes, not one block.
36
+ - **The add dialog's busy button keeps saying "Add page"** and shows the shell
37
+ Button's spinner (`loading`). The "Adding…" label is gone (no trailing dots on
38
+ a button). The words catalogue is rewritten: 1,176 phrases.
39
+ - The new guard `scripts/test-pages-panel-one-add.ts` holds all three.
40
+
41
+ ## 7.0.0 - 2026-10-14
42
+
43
+ - **The Layers panel no longer shows the heading outline** (owner,
44
+ 2026-10-14, editor audit). The box under the tree — the heading count,
45
+ "No H1 — add one for SEO." and the list of headings by level — is removed,
46
+ not moved: it is not a layer, "H1" and "SEO" are not a merchant's words, and
47
+ on an empty page it read as an error. The website hands the screen no
48
+ `treeFoot` any more.
49
+ - **Breaking: `components/HeadingAudit` is deleted** (it was reachable through
50
+ the package's `./*` export). No host imports it. Its four words are out of
51
+ the editor's catalogue, and the `.sf-tree-tool` rule is gone with it.
52
+ - `lib/puck/heading-outline` stays: the one-H1 rule's join and the
53
+ storefront's guards still read it.
54
+
55
+ ## 6.1.1 - 2026-10-14
56
+
57
+ - **The editor's stylesheet reads the shell's tokens instead of restating
58
+ them** (the duplicate scan of 2026-10-13, GG for Lewis). Every text size
59
+ reads a shell step token, as the shell's own `ui.css` does:
60
+ `var(--text-sm)` for 14, `--text-xs` for 12, `--text-2xs` for 11, and so on
61
+ (80 rules). The settings panel's side padding reads `var(--space-16)`, and
62
+ `var(--space-8)` in the narrow step. Same pixels; one place each. The theme
63
+ card's preview keeps its own px (a drawing).
64
+ - **Two rules no markup draws are deleted**: `.sf-addempty-sub` and
65
+ `.sf-readability-head`. Neither class appears in any repository's source
66
+ (web-builder, easel, storefront, storefront-blocks, admin and the portals);
67
+ the only hits were old CSS copies inlined in storefront's
68
+ `scripts/harness/*.html` snapshots.
69
+ - The padding guards' reader (`scripts/panel-body-padding.ts`) resolves the
70
+ shell's `--space-*` scale from the installed shell's `ui.css`, in px.
71
+
72
+ ## 6.1.0 - 2026-10-14
73
+
74
+ **The editor speaks its host's language.** A host that runs in another
75
+ language hands the editor its words, and the editor says them where it draws
76
+ them (programme: Foyerboard on EFFICIENT, editor languages step 3). In English
77
+ nothing reads differently.
78
+
79
+ - **One seam, `lib/editor-words`.** `configureEditorWords(words, locale,
80
+ easel)` lays a host's words over the English, during its frame's render, as
81
+ it configures `lib/web-runtime`; `say(english, values)` and
82
+ `sayCount(one, other, n)` answer the host's word, or the English. The words
83
+ are keyed by the English they replace: a plain word is a string, a sentence
84
+ names its values in braces (`"Delete {name}": "{name} löschen"`), and a
85
+ host may give a function of the values for a plural or an agreement. A word
86
+ left out, or empty, reads in English. The language is fixed for the life of
87
+ a page (harness UI-25), and plain functions read the words as well as
88
+ components, so they are the module's state.
89
+ - **The English stays the key; it is translated where it is shown.** A
90
+ settings row's label is still the literal its control builder is given, so
91
+ `field-groups` (`bucketOf`, `LABEL_BUCKET`, `RETIRED`), `field-noun`,
92
+ `ThemeCard`'s `ROW_ICON` and the label rules in `lib/source-rules` read the
93
+ same English. Inside `lib/puck/controls` every row says its label, hint,
94
+ placeholder, note and choices at render; the group headings, the Swatches'
95
+ names, the reset marks' details, the focal and placement grids' cells, the
96
+ text lists and the coordinates field do too.
97
+ - **Panels and dialogs** — Pages, Site styles, Styles, Theme, the part
98
+ settings, the Locations field, the text styles, Duplicate to page, Clear
99
+ colours, the chat, social and ticker panels, the section editor's head —
100
+ **block names and Add-panel blurbs** (said in the screen's block look:
101
+ `labelAtLevel`, `descriptions`, `categoryLabel`, and an Add target's
102
+ choices), the **preview devices**, and the **placeholders drawn on the
103
+ canvas while editing** (`phPrev`, and the scaffold rows of the general
104
+ blocks) read the seam.
105
+ - **A host's places are phrases of their own**: "Storefront ▸ Sites ▸
106
+ Settings", "Storefront → Blog" and "the admin" are each a phrase passed into
107
+ the sentence that names them, so a host whose menu is elsewhere renames them
108
+ once, in English too.
109
+ - **What is written into a page stays English** (harness UI-25): a block's
110
+ default content, a saved name, a seeded section. What a visitor reads on the
111
+ published page is never the editor's words.
112
+ - **Easel's words reach the parts that cannot read easel's provider.** A reset
113
+ mark (`SettingsDefaultReset`) drawn inside a control builder's closure is
114
+ handed the panel words the host's frame passed with `configureEditorWords`
115
+ (`editorEaselWords()`); the picture box, the Locations tree and the ticker's
116
+ Layers row are handed `useEaselStrings()`'s. The screen's own leaves (the
117
+ save pill, the right-click menu, Copy / Paste style, the Add panel's tiles)
118
+ are efficient-easel 3.1.0's to hand, and are with it.
119
+ - **The phrases are a typed catalogue**, `lib/editor-words-catalogue`
120
+ (`WEB_BUILDER_WORDS`, `WebBuilderPhrase`, 1,181 phrases), written from the
121
+ source by `pnpm run words` and held to it by the new tree-wide rule
122
+ `lib/source-rules/editor-words` (`scripts/test-editor-words.ts`). A host
123
+ types its words as `EditorWordsFor<WebBuilderPhrase>`. The rule reads a
124
+ phrase a builder or `say` is given as a literal; one built at run time
125
+ (`${kind} colour`) is not read and stays English, which is why such phrases
126
+ are now written whole (`"Padding top"`, `"{value} on desktop"`).
127
+ - **Peer `efficient-shell ^5.6.0 || ^6.4.0 || ^7.7.0`** (was `^5.6.0`), as
128
+ efficient-easel 3.0.0 has it; dev `^7.7.0`. The typecheck, every guard and
129
+ the build pass on 7.x.
130
+ - **Peer `efficient-easel ^2.0.0 || ^3.1.0`** (was `^2.0.0`). 3.1.0 is the
131
+ easel whose screen says its last words from the catalogue, hands its own
132
+ leaves their words and names a block row through the layer's
133
+ `labelAtLevel`, so a host on it gets the whole editor in its language; on
134
+ 2.x the screen's own parts and a block row's name stay English. Checked
135
+ against a local build of 3.1.0 before it was published: the typecheck, the
136
+ boundary, the screen rule and all 150 other guards pass (the one that fails
137
+ is the dev pin's own, which the injected build does not satisfy by design).
138
+ Dev stays on 2.2.0 until 3.1.0 is on npm.
139
+
140
+ - **The colour row's hex box is the swatch's small rung** (`size="sm"`).
141
+ From efficient-shell 7.11.0 the shell's `HexInput` defaults to a larger
142
+ rung, and in the row's fixed `.sf-colour-hexwrap` (4.75rem) `#FFFFFF` showed
143
+ as `#FFFFF` (the value was unchanged). A shell before the prop draws the
144
+ small box it always did. `test-one-colour-control` §5 holds the box to the
145
+ shell's own small rung.
146
+
147
+ ### For hosts
148
+
149
+ - A host in English needs nothing. A host in another language calls
150
+ `configureEditorWords(words, locale, useEaselStrings())` while its frame
151
+ renders (efficient-storefront-blocks does it for its `WebsiteEditor`'s
152
+ `words` / `locale`), and wraps the editor in efficient-easel's
153
+ `EaselStringsProvider`.
154
+ - The storefront's own guards that read a builder file's text are unaffected:
155
+ every word a guard pinned is still the English literal, now inside `say("…")`
156
+ where it is drawn. Three guards here read through `say("…")` now
157
+ (`mirrored-rows-agree`, `part-style-naming`, the panel label reader).
158
+
159
+ ### Guards
160
+
161
+ - New rule `lib/source-rules/editor-words` (`scripts/test-editor-words.ts`):
162
+ the reader on a fixture, a floor of 900 phrases, and the catalogue equal to
163
+ what the source says.
164
+ - New `scripts/test-editor-words-say.tsx`: a host's word, its values, a
165
+ function word, the English for a word left out, empty or throwing; a
166
+ control's row drawn in the host's words while its field keeps its English
167
+ label; a canvas placeholder said only while editing; and the English back
168
+ byte for byte with no words.
169
+ - `test-shell-peer-range` holds the wider range and a 7.x dev shell;
170
+ `test-panel-parts-from-the-shell` holds every range to start at 5.6.0.
171
+ - `support/label-extraction`'s JSX label reader, `mirrored-rows-agree`,
172
+ `part-style-naming`, `test-part-follows-style`, `test-clear-formatting` and
173
+ `test-save-as-template-action` read `label={say("…")}` as the label they
174
+ read before.
175
+
176
+ ## 6.0.3 - 2026-10-13
177
+
178
+ - **A block on a dark picture paints light ink, words and placeholders
179
+ alike** (card [FIX] Dark Text On Dark Hero Boxes, audit item 28). A
180
+ Heading or Text dropped into a Hero's box painted the page's ink (#111 on
181
+ a light site) on the dark overlay: the layer set `color` only, and a block
182
+ paints `text-fg` / `text-fg-dim` / `border-line`, which read the TOKENS
183
+ `--fg` / `--fg-dim` / `--site-line`. Every layer that puts blocks on a
184
+ darkened picture now sets the tokens too: the Hero's box layer, a Hero
185
+ slide's group layer, a Box with a darkened background image (it set no ink
186
+ at all before), the Banner and Split copy on a photograph, and IntroBleed.
187
+ The empty Heading and Text placeholders follow, as they are the same
188
+ elements.
189
+ - **One list of ink tokens, `lib/content/ink`.** `SECTION_PALETTE` names each
190
+ colour a section theme hands its band, with its token and whether it is ink;
191
+ `inkStyle(source)` is the one way a layer changes the ink, and
192
+ `ON_MEDIA_INK_STYLE` is the on-media ink (`--overlay-fg`, its dim and its
193
+ rule as thinned mixes, no raw hex). `sectionAccentVars` now emits its
194
+ `--sf-sec-*` from the same list, byte for byte as before.
195
+ - **Split's body on a photograph** is `text-fg-dim` again (the on-media dim);
196
+ the `opacity-85` patch that stood in for it is gone.
197
+ - Host follow-up: a host's `.sf-section-accent` / `.sf-header-theme` map each
198
+ `--sf-sec-<role>` onto its token; the storefront's own guard should pin that
199
+ list to `SECTION_PALETTE` when it moves to this release. A published hero
200
+ with boxes, every slide, and a Banner, Split or IntroBleed on a photograph
201
+ carry the new style attribute, so the storefront's byte baseline moves.
202
+
203
+ ## 6.0.2 - 2026-10-13
204
+
205
+ - **Every text size in the editor's stylesheet is a type step** (9 / 11 /
206
+ 12 / 14 / 16 / 18 / 20 / 24 / 30; Lewis, 2026-10-12). Each move is the one
207
+ the 10-12 type ruling names. GG confirmed on 2026-10-13 that "never smaller"
208
+ is about spacing and heights, not type.
209
+ - 10 → 11 ("other 10px → 11"): the dropdown caret, the page's Draft tag,
210
+ the chooser's meta line, the theme warning, the readability head.
211
+ - 10.5 → 11 (the same rule): the add card's description, the part chip,
212
+ the gallery count.
213
+ - 11.5 → 12 ("12 = table heads, grey meta (11.5, 12.5 → 12)"): the
214
+ site-wide row's description, the form summary warning, the part-style
215
+ state, the gallery filter.
216
+ - 12.5 → 12 (the same rule; "add-section card name 12.5 → 12"): the
217
+ site-wide row's name, the add card's name and its Add button, the empty
218
+ state's line.
219
+ - 15 → 16 ("15/17 → 16"): the add card's plus and the large A of the text
220
+ scale.
221
+ - The theme card's preview keeps its own sizes (7.5 / 8.5 / 9.5px): it is a
222
+ picture of a page, drawn small, and its type is the drawing's.
223
+
224
+ ## 6.0.1 - 2026-10-13
225
+
226
+ - **The settings panel's side padding is 16** (was 14). The spacing scale
227
+ Lewis ruled on 2026-10-12 is 2 / 4 / 8 / 12 / 16 / …, and a value off it
228
+ goes up, never down (2026-10-13). The narrow step stays 8. The content
229
+ width card 66212 protects (241px) now needs a 285px panel, so the
230
+ container line moves from `< 280px` to `< 284px`, for the body padding
231
+ and for the segmented strip's narrow padding together (one edge, one
232
+ number). At the 320 default the tab strip is 277 with a scrollbar, still
233
+ clear of the 270 its widest tier needs.
234
+ - **The editor's 13px text is 14** (the type steps: 9 / 11 / 12 / 14 / 16 /
235
+ …; 13 → 14 by name). The inspector's inputs, textareas and selects, the
236
+ dropdown trigger, the page list's search and its new-page row, the
237
+ chooser and its items, the read-only field, the preview pin's label, the
238
+ column step's value, the note, and the part-style options: 14 rules.
239
+ easel's own 13px rules move in easel's pull request.
240
+
3
241
  ## 6.0.0 - 2026-10-13
4
242
 
5
243
  - **Breaking: efficient-shell ^5.6.0** (peer; was `^4.171.0 || ^5.0.0`). The
package/README.md CHANGED
@@ -32,6 +32,48 @@ What a host adds, it hands over on the editor's session
32
32
 
33
33
  The storefront's are assembled in its `lib/storefront-editor.ts`.
34
34
 
35
+ ## The editor's words in a host's language
36
+
37
+ The editor speaks English, and a host that runs in another language lays its
38
+ own words over it (6.1.0). The English stays in the code, where it is said,
39
+ and many of the words are also keys (a row is filed under its tab and given
40
+ its glyph by its English label), so it is translated where it is SHOWN:
41
+
42
+ - **The words are keyed by the English they replace.**
43
+ `efficient-web-builder/lib/editor-words-catalogue` exports
44
+ `WEB_BUILDER_WORDS`, every phrase the editor says (a settings row's label,
45
+ hint and choices, a group heading, a panel's and a dialog's words, a block's
46
+ name and its Add-panel blurb, a placeholder drawn on the canvas while
47
+ editing), and the type of its keys, `WebBuilderPhrase`. It is written from
48
+ the source by `pnpm run words` and held to it by
49
+ `scripts/test-editor-words.ts`, so a host typing its catalogue as
50
+ `EditorWordsFor<WebBuilderPhrase>` fails its typecheck on a phrase it lacks.
51
+ - **A sentence with a value names it in braces**, in the English and in the
52
+ host's word alike: `"Delete {name}": "{name} löschen"`. A host may give a
53
+ function of the values instead, for a plural or an agreement the braces
54
+ cannot say (`"{n} pages": ({ n }) => …`). A count is two phrases
55
+ (`sayCount("{n} page", "{n} pages", n)`).
56
+ - **Names of a host's places are phrases of their own**, so a host renames
57
+ them once, in English too: the editor says "Storefront ▸ Sites ▸ Settings"
58
+ and "the admin", where an EFFICIENT admin has them, and a host whose menu
59
+ is elsewhere says its own.
60
+ - **The host hands its words once, while its frame renders**, with
61
+ `configureEditorWords(words, locale)` from `lib/editor-words` — as it
62
+ configures `lib/web-runtime`. The words are fixed for the life of a page
63
+ (harness UI-25), one editor is mounted at a time, and they are read by plain
64
+ functions as well as components, so they are the module's state, not a
65
+ React context. A word left out reads in English.
66
+ - **What is written into a page stays English**: a block's default content, a
67
+ saved name. And what a visitor reads on the published page is never the
68
+ editor's words.
69
+ - **The engine's words are efficient-easel's** (`EaselStringsProvider`); a host
70
+ wraps the editor in it. The parts the builder draws itself that take
71
+ easel's words as a prop (a picture box, a Locations tree) are handed
72
+ `useEaselStrings()`'s.
73
+
74
+ `efficient-storefront-blocks` adds the phrases of its own blocks the same way
75
+ and takes a host's words on its `WebsiteEditor` (`words`, `locale`).
76
+
35
77
  Two checks hold the rule, both in CI and both without an allow-list:
36
78
 
37
79
  - `pnpm run typecheck` — `@/…` points at `src/` and nowhere else, so an import
@@ -1 +1 @@
1
- "use client";import{jsx as l}from"react/jsx-runtime";import{useCallback as i,useEffect as a,useRef as m,useState as f}from"react";import{useEasel as C}from"efficient-easel/easel";import{SettingsAction as d}from"efficient-easel/panel";import{applyCorners as A,applyCornersDoneLabel as S,applyCornersNote as y,planApplyCorners as b}from"../lib/puck/apply-card-corners.js";import{useSectionOps as _}from"../lib/puck/use-section-ops.js";const x=2200,E="apply-corners-to-siblings",L="Apply to all cards";function P({plan:e,doneCount:n,onPress:r}){return l(d,{id:E,label:L,note:y(e),disabled:!e.can,done:n!==null,doneLabel:S(n??0),onPress:r})}function N(){const{appState:e,config:n}=C(u=>u),r=_(),[p,s]=f(null),o=m(null);a(()=>()=>{o.current&&clearTimeout(o.current)},[]);const t=b(n,e.data.content??[],e.ui.itemSelector),c=i(()=>{A(t,r.replaceComponent),t.can&&(s(t.others),o.current&&clearTimeout(o.current),o.current=setTimeout(()=>s(null),x))},[t,r]);return l(P,{plan:t,doneCount:p,onPress:c})}export{x as APPLY_CORNERS_DONE_MS,L as APPLY_CORNERS_LABEL,P as ApplyCornersButton,N as default};
1
+ "use client";import{jsx as l}from"react/jsx-runtime";import{useCallback as u,useEffect as a,useRef as m,useState as f}from"react";import{useEasel as C}from"efficient-easel/easel";import{SettingsAction as d}from"efficient-easel/panel";import{say as y}from"../lib/editor-words.js";import{applyCorners as A,applyCornersDoneLabel as S,applyCornersNote as b,planApplyCorners as _}from"../lib/puck/apply-card-corners.js";import{useSectionOps as x}from"../lib/puck/use-section-ops.js";const E=2200,L="apply-corners-to-siblings",P="Apply to all cards";function N({plan:e,doneCount:n,onPress:r}){return l(d,{id:L,label:y(P),note:b(e),disabled:!e.can,done:n!==null,doneLabel:S(n??0),onPress:r})}function O(){const{appState:e,config:n}=C(i=>i),r=x(),[p,s]=f(null),o=m(null);a(()=>()=>{o.current&&clearTimeout(o.current)},[]);const t=_(n,e.data.content??[],e.ui.itemSelector),c=u(()=>{A(t,r.replaceComponent),t.can&&(s(t.others),o.current&&clearTimeout(o.current),o.current=setTimeout(()=>s(null),E))},[t,r]);return l(N,{plan:t,doneCount:p,onPress:c})}export{E as APPLY_CORNERS_DONE_MS,P as APPLY_CORNERS_LABEL,N as ApplyCornersButton,O as default};
@@ -1 +1 @@
1
- import{jsx as e,jsxs as r}from"react/jsx-runtime";import h from"./BlockFrame.js";import f from"./Btn.js";import{media as v}from"../lib/media.js";import g from"./SectionEyebrow.js";import{renderGold as t,renderHeadline as u,splitParagraphs as N}from"./goldPhrases.js";function b({eyebrow:i,title:s,body:l,ctaText:n,ctaHref:d,imageSrc:a,contained:m,overlay:o=60}){const c=Math.min(Math.max(o,0),100)/100;return e(h,{contained:m,className:"py-6",children:r("div",{className:"sf-card relative overflow-hidden border border-line bg-surface px-10 py-16 max-[800px]:px-6 max-[800px]:py-10",children:[a&&r("div",{className:"absolute inset-0","aria-hidden":!0,children:[e("img",{src:v(a),alt:"",className:"h-full w-full object-cover"}),e("div",{className:"absolute inset-0",style:{background:`rgba(0,0,0,${c})`}})]}),r("div",{className:"relative z-[1] flex max-w-[680px] flex-col gap-5",style:a?{color:"var(--overlay-fg)"}:void 0,children:[i&&e(g,{text:i}),s&&e("h2",{className:"font-heading2 text-[length:min(var(--size-headingMedium,44px),var(--sf-heading-max,9999px))] font-medium leading-[1.05] tracking-[-0.03em] max-[800px]:text-[length:min(var(--size-headingMedium,32px),var(--sf-heading-max,9999px))]",children:u(s,!1)}),l&&N(l).map((p,x)=>e("p",{className:"text-[length:var(--size-body,16px)] leading-[1.6] opacity-85",children:t(p)},x)),n&&e("div",{className:"mt-2 self-start",children:e(f,{href:d||"#",variant:"primary",children:t(n)})})]})]})})}export{b as default};
1
+ import{jsx as e,jsxs as r}from"react/jsx-runtime";import h from"./BlockFrame.js";import f from"./Btn.js";import{media as g}from"../lib/media.js";import v from"./SectionEyebrow.js";import{renderGold as t,renderHeadline as u,splitParagraphs as N}from"./goldPhrases.js";import{ON_MEDIA_INK_STYLE as b}from"../lib/content/ink.js";function y({eyebrow:i,title:s,body:n,ctaText:l,ctaHref:m,imageSrc:a,contained:d,overlay:o=60}){const p=Math.min(Math.max(o,0),100)/100;return e(h,{contained:d,className:"py-6",children:r("div",{className:"sf-card relative overflow-hidden border border-line bg-surface px-10 py-16 max-[800px]:px-6 max-[800px]:py-10",children:[a&&r("div",{className:"absolute inset-0","aria-hidden":!0,children:[e("img",{src:g(a),alt:"",className:"h-full w-full object-cover"}),e("div",{className:"absolute inset-0",style:{background:`rgba(0,0,0,${p})`}})]}),r("div",{className:"relative z-[1] flex max-w-[680px] flex-col gap-5",style:a?b:void 0,children:[i&&e(v,{text:i}),s&&e("h2",{className:"font-heading2 text-[length:min(var(--size-headingMedium,44px),var(--sf-heading-max,9999px))] font-medium leading-[1.05] tracking-[-0.03em] max-[800px]:text-[length:min(var(--size-headingMedium,32px),var(--sf-heading-max,9999px))]",children:u(s,!1)}),n&&N(n).map((c,x)=>e("p",{className:"text-[length:var(--size-body,16px)] leading-[1.6] opacity-85",children:t(c)},x)),l&&e("div",{className:"mt-2 self-start",children:e(f,{href:m||"#",variant:"primary",children:t(l)})})]})]})})}export{y as default};
@@ -1 +1 @@
1
- "use client";import{jsx as a}from"react/jsx-runtime";import{SettingsDefaultReset as s,SettingsField as r}from"efficient-easel/panel";import{AlignSeparateIcon as c}from"efficient-easel/icons";import{CONTAINER_GAP_BUILT_IN as d,SITE_BLOCK_GAP as t}from"../lib/content/container-gap.js";import{Slider as p}from"../lib/puck/controls.js";const m="The space between the blocks inside a row, a grid or a box. Default means each one keeps the spacing it already has.",o="Block gap",f=d.Grid;function u({value:n,onChange:i,showHint:l=!0}){const e=n===null;return a(r,{label:o,labelId:"sf-block-gap",icon:a(c,{}),value:e?void 0:`${n}px`,changed:!e,action:a(s,{label:o,changed:!e,detail:"each block's own spacing",onReset:()=>i(null)}),hint:l?m:void 0,children:a(p,{min:t.min,max:t.max,step:t.step,value:e?f:n,readout:!1,ariaLabel:o,onChange:i})})}export{u as default};
1
+ "use client";import{jsx as t}from"react/jsx-runtime";import{SettingsDefaultReset as r,SettingsField as c}from"efficient-easel/panel";import{AlignSeparateIcon as d}from"efficient-easel/icons";import{CONTAINER_GAP_BUILT_IN as p,SITE_BLOCK_GAP as o}from"../lib/content/container-gap.js";import{say as e,editorEaselWords as m}from"../lib/editor-words.js";import{Slider as f}from"../lib/puck/controls.js";const u="The space between the blocks inside a row, a grid or a box. Default means each one keeps the spacing it already has.",i="Block gap",_=p.Grid;function g({value:n,onChange:l,showHint:s=!0}){const a=n===null;return t(c,{label:e(i),labelId:"sf-block-gap",icon:t(d,{}),value:a?void 0:`${n}px`,changed:!a,action:t(r,{words:m()?.panel,label:e(i),changed:!a,detail:e("each block's own spacing"),onReset:()=>l(null)}),hint:s?e(u):void 0,children:t(f,{min:o.min,max:o.max,step:o.step,value:a?_:n,readout:!1,ariaLabel:e(i),onChange:l})})}export{g as default};
@@ -1 +1 @@
1
- "use client";import{jsx as t,Fragment as l,jsxs as B}from"react/jsx-runtime";import{useContext as y}from"react";import{SettingsField as c}from"efficient-easel/panel";import{FieldSelect as d}from"efficient-easel/fields";import{BlogPostIcon as g}from"efficient-easel/icons";import{StorefrontDocumentContext as x}from"./StorefrontDocumentContext.js";import{loadStorefrontBlogPosts as L}from"./useStorefrontBlogPosts.js";import{useSnapshotSource as N}from"./useSnapshotSource.js";import P from"./SourceStatus.js";function C(o){if(!o)return"";const e=new Date(o);return Number.isNaN(e.getTime())?"":e.toLocaleDateString("en-US",{year:"numeric",month:"short",day:"2-digit"})}function f(o){return{slug:o.slug,image:o.coverImage,title:o.title,dek:o.excerpt,date:C(o.publishedAt),href:o.href}}function D({value:o,onChange:e}){const r=y(x);return r&&!r.capabilities.hasBlog?o?.slug?t(F,{value:o,onChange:e}):t(l,{}):t(T,{value:o,onChange:e})}function F({value:o,onChange:e}){return t("div",{children:t(c,{label:"Blog post source",icon:t(g,{}),hint:t(l,{children:"Linked to a blog post. This editor has no blog to pick another from; choose “None (manual)” to author the section yourself."}),children:t(d,{value:o.slug,ariaLabel:"Blog post source",onChange:r=>r?void 0:e(null),options:[{label:"None (manual)",value:""},{label:o.title||o.slug,value:o.slug}]})})})}function T({value:o,onChange:e}){const{rows:r,loading:h,refreshing:i,errored:u,refresh:m}=N({load:L,resolve:(n,s)=>n.find(a=>a.slug===s.slug),snapshot:f,value:o,onChange:e}),p=n=>{if(!n){e(null);return}const s=(r??[]).find(a=>a.slug===n);s&&e(f(s))},b=o?.slug&&!(r??[]).some(n=>n.slug===o.slug),S=u?"error":i?"loading":"idle",k=u?"Couldn’t reach Storefront → Blog.":i?"Syncing from Storefront → Blog…":`Linked to “${o?.title||o?.slug||""}”`;return B("div",{children:[t(c,{label:"Blog post source",icon:t(g,{}),hint:o?.slug?void 0:t(l,{children:"Pick a post from Storefront → Blog to auto-fill this section, or keep “None (manual)” to author it yourself below."}),children:t(d,{value:o?.slug??"",ariaLabel:"Blog post source",onChange:p,readOnly:h,options:[{label:"None (manual)",value:""},...b?[{label:`${o.title||o.slug} (unavailable)`,value:o.slug}]:[],...(r??[]).map(n=>({label:n.title,value:n.slug}))]})}),o?.slug?t(P,{status:S,statusText:k,onRefresh:m,busy:i,hint:t(l,{children:"Auto-fills the cover image, headline, dek, date and link from this post each time you open the editor. Those fields are hidden while a post is linked; the remaining fields stay yours to set."})}):null]})}export{D as default};
1
+ "use client";import{jsx as o,Fragment as i,jsxs as B}from"react/jsx-runtime";import{useContext as P}from"react";import{SettingsField as m}from"efficient-easel/panel";import{FieldSelect as g}from"efficient-easel/fields";import{BlogPostIcon as f}from"efficient-easel/icons";import{StorefrontDocumentContext as L}from"./StorefrontDocumentContext.js";import{loadStorefrontBlogPosts as N}from"./useStorefrontBlogPosts.js";import{useSnapshotSource as F}from"./useSnapshotSource.js";import T from"./SourceStatus.js";import{say as t}from"../lib/editor-words.js";function j(e){if(!e)return"";const n=new Date(e);return Number.isNaN(n.getTime())?"":n.toLocaleDateString("en-US",{year:"numeric",month:"short",day:"2-digit"})}function h(e){return{slug:e.slug,image:e.coverImage,title:e.title,dek:e.excerpt,date:j(e.publishedAt),href:e.href}}function w({value:e,onChange:n}){const r=P(L);return r&&!r.capabilities.hasBlog?e?.slug?o(C,{value:e,onChange:n}):o(i,{}):o(D,{value:e,onChange:n})}function C({value:e,onChange:n}){return o("div",{children:o(m,{label:t("Blog post source"),icon:o(f,{}),hint:o(i,{children:t("Linked to a blog post. This editor has no blog to pick another from; choose “None (manual)” to author the section yourself.")}),children:o(g,{value:e.slug,ariaLabel:t("Blog post source"),onChange:r=>r?void 0:n(null),options:[{label:t("None (manual)"),value:""},{label:e.title||e.slug,value:e.slug}]})})})}function D({value:e,onChange:n}){const{rows:r,loading:p,refreshing:a,errored:d,refresh:b}=F({load:N,resolve:(s,l)=>s.find(c=>c.slug===l.slug),snapshot:h,value:e,onChange:n}),k=s=>{if(!s){n(null);return}const l=(r??[]).find(c=>c.slug===s);l&&n(h(l))},S=e?.slug&&!(r??[]).some(s=>s.slug===e.slug),y=d?"error":a?"loading":"idle",u=t("Storefront → Blog"),x=d?t("Couldn’t reach {place}.",{place:u}):a?t("Syncing from {place}…",{place:u}):t("Linked to “{name}”",{name:e?.title||e?.slug||""});return B("div",{children:[o(m,{label:t("Blog post source"),icon:o(f,{}),hint:e?.slug?void 0:o(i,{children:t("Pick a post from {place} to auto-fill this section, or keep “None (manual)” to author it yourself below.",{place:u})}),children:o(g,{value:e?.slug??"",ariaLabel:t("Blog post source"),onChange:k,readOnly:p,options:[{label:t("None (manual)"),value:""},...S?[{label:t("{name} (unavailable)",{name:e.title||e.slug}),value:e.slug}]:[],...(r??[]).map(s=>({label:s.title,value:s.slug}))]})}),e?.slug?o(T,{status:y,statusText:x,onRefresh:b,busy:a,hint:o(i,{children:t("Auto-fills the cover image, headline, dek, date and link from this post each time you open the editor. Those fields are hidden while a post is linked; the remaining fields stay yours to set.")})}):null]})}export{w as default};
@@ -1 +1 @@
1
- import{jsx as r,jsxs as s,Fragment as c}from"react/jsx-runtime";import{Fragment as f}from"react";import{SiteLink as x}from"../lib/site-runtime.js";import{renderChrome as m,renderGold as d}from"./goldPhrases.js";function h({items:p,meta:n,editing:o=!1}){const l=(p??[]).filter(e=>e?.label?.trim()),t=l.length>0?l:o?[{label:"Breadcrumb"}]:[];return t.length===0&&!n?null:s("nav",{className:"flex items-center gap-4 border-b border-line px-[var(--sf-site-gutter,40px)] py-6 font-mono text-[length:var(--size-label,13px)] uppercase tracking-[0.2em] text-fg-dim max-[720px]:px-[min(20px,var(--sf-site-gutter,40px))] max-[720px]:py-4",children:[t.map((e,a)=>{const i=a===t.length-1;return s(f,{children:[a>0&&r("span",{className:"opacity-40",children:"/"}),e.href&&!i?r(x,{href:e.href,className:"hover:text-fg",children:m(e.label)}):r("span",{className:i?"text-fg":void 0,children:m(e.label)})]},a)}),n&&s(c,{children:[r("span",{className:"opacity-40",children:"·"}),r("span",{children:d(n)})]})]})}export{h as default};
1
+ import{jsx as r,jsxs as s,Fragment as c}from"react/jsx-runtime";import{Fragment as f}from"react";import{SiteLink as x}from"../lib/site-runtime.js";import{renderChrome as m,renderGold as d}from"./goldPhrases.js";import{say as h}from"../lib/editor-words.js";function g({items:o,meta:t,editing:p=!1}){const l=(o??[]).filter(e=>e?.label?.trim()),a=l.length>0?l:p?[{label:h("Breadcrumb")}]:[];return a.length===0&&!t?null:s("nav",{className:"flex items-center gap-4 border-b border-line px-[var(--sf-site-gutter,40px)] py-6 font-mono text-[length:var(--size-label,13px)] uppercase tracking-[0.2em] text-fg-dim max-[720px]:px-[min(20px,var(--sf-site-gutter,40px))] max-[720px]:py-4",children:[a.map((e,n)=>{const i=n===a.length-1;return s(f,{children:[n>0&&r("span",{className:"opacity-40",children:"/"}),e.href&&!i?r(x,{href:e.href,className:"hover:text-fg",children:m(e.label)}):r("span",{className:i?"text-fg":void 0,children:m(e.label)})]},n)}),t&&s(c,{children:[r("span",{className:"opacity-40",children:"·"}),r("span",{children:d(t)})]})]})}export{g as default};
@@ -1 +1 @@
1
- "use client";import{jsx as e}from"react/jsx-runtime";import{SettingsDefaultReset as r,SettingsField as c,SettingsSegmented as p}from"efficient-easel/panel";import{ShadowIcon as f}from"efficient-easel/icons";import{CARD_SHADOWS as x,DEFAULT_CARD_SHADOW as i}from"../lib/theme.js";const n={none:"None",soft:"Soft",lifted:"Lifted"},h="Applies to cards — products, brands, journal entries and filter cells. Round things like swatches and status dots are never shadowed.",u={none:"none",soft:"0 1px 1px rgba(20, 20, 22, 0.10), 0 2px 4px -1px rgba(20, 20, 22, 0.14)",lifted:"0 1px 2px rgba(20, 20, 22, 0.10), 0 4px 7px -2px rgba(20, 20, 22, 0.30)"},S={labelsPx:90.25,widestLabelPx:34.6};function b({value:a,onChange:o,showHint:l=!0}){const s=a===null,d=a??i;return e(c,{label:"Card shadow",icon:e(f,{}),value:n[d],changed:!s,action:e(r,{label:"Card shadow",changed:!s,detail:n[i],onReset:()=>o(null)}),hint:l?h:void 0,children:e(p,{options:x.map(t=>({id:t,label:n[t],icon:e("span",{className:"sf-shadow-chip",style:{boxShadow:u[t]}})})),value:d,ariaLabel:"Card shadow",fit:S,onChange:o})})}export{b as default};
1
+ "use client";import{jsx as t}from"react/jsx-runtime";import{SettingsDefaultReset as p,SettingsField as c,SettingsSegmented as f}from"efficient-easel/panel";import{ShadowIcon as x}from"efficient-easel/icons";import{say as e,editorEaselWords as h}from"../lib/editor-words.js";import{CARD_SHADOWS as u,DEFAULT_CARD_SHADOW as r}from"../lib/theme.js";const a={none:"None",soft:"Soft",lifted:"Lifted"},S="Applies to cards — products, brands, journal entries and filter cells. Round things like swatches and status dots are never shadowed.",m={none:"none",soft:"0 1px 1px rgba(20, 20, 22, 0.10), 0 2px 4px -1px rgba(20, 20, 22, 0.14)",lifted:"0 1px 2px rgba(20, 20, 22, 0.10), 0 4px 7px -2px rgba(20, 20, 22, 0.30)"},b={labelsPx:90.25,widestLabelPx:34.6};function A({value:n,onChange:s,showHint:l=!0}){const d=n===null,i=n??r;return t(c,{label:e("Card shadow"),icon:t(x,{}),value:e(a[i]),changed:!d,action:t(p,{words:h()?.panel,label:e("Card shadow"),changed:!d,detail:e(a[r]),onReset:()=>s(null)}),hint:l?e(S):void 0,children:t(f,{options:u.map(o=>({id:o,label:e(a[o]),icon:t("span",{className:"sf-shadow-chip",style:{boxShadow:m[o]}})})),value:i,ariaLabel:e("Card shadow"),fit:b,onChange:s})})}export{A as default};
@@ -1 +1 @@
1
- import{jsx as r,jsxs as n}from"react/jsx-runtime";import{SiteLink as g}from"../lib/site-runtime.js";import{renderChrome as l}from"./goldPhrases.js";import{STICKY_UNDERHEADER_CLASS as u}from"../lib/puck/header-clearance.js";function v({tabs:m,activeTab:o,links:d,sticky:b=!0,editing:h=!1}){const i=(m??[]).filter(e=>e?.label?.trim()),a=i.length>0?i:h?[{label:"Tab"}]:[],s=(d??[]).filter(e=>e?.label?.trim());if(a.length===0&&s.length===0)return null;const c=o?a.findIndex(e=>e.label.toLowerCase()===o.toLowerCase()):-1,f=c===-1?0:c;return r("nav",{className:`${b?`${u} sticky top-[var(--sf-header-h,88px)] z-40 `:""}border-b border-line px-[var(--sf-site-gutter,40px)] backdrop-blur-md max-[1100px]:px-[min(16px,var(--sf-site-gutter,40px))]`,style:{background:"color-mix(in oklab, var(--bg) 92%, transparent)"},children:n("div",{className:"mx-auto flex max-w-[calc(var(--sf-site-width,1600px)_-_var(--sf-site-gutter,40px)*2)] items-stretch gap-0 overflow-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",children:[a.map((e,t)=>{const p=t===f,x=e.count?.trim()?e.count:"";return n("span",{className:["relative whitespace-nowrap border-r border-line px-[22px] py-[18px] font-mono text-[length:var(--size-label,13px)] uppercase tracking-[0.22em] transition-colors",t===0?"border-l border-line":"",p?"text-fg":"text-fg-dim hover:text-fg"].join(" "),children:[l(e.label),x&&r("span",{className:"ml-2 text-[length:var(--size-body,12px)] text-accent opacity-70",children:l(x)}),p&&r("span",{className:"absolute inset-x-0 -bottom-px h-[2px] bg-accent"})]},t)}),s.length>0&&r("div",{className:"ml-auto flex border-l border-line",children:s.map((e,t)=>n(g,{href:e.href||"#",className:"flex items-center gap-2.5 px-[22px] py-[18px] font-mono text-[length:var(--size-label,13px)] uppercase tracking-[0.22em] text-fg-dim hover:text-fg",children:[l(e.label)," ",r("span",{className:"text-accent",children:"↗"})]},t))})]})})}export{v as default};
1
+ import{jsx as r,jsxs as n}from"react/jsx-runtime";import{SiteLink as g}from"../lib/site-runtime.js";import{renderChrome as o}from"./goldPhrases.js";import{STICKY_UNDERHEADER_CLASS as u}from"../lib/puck/header-clearance.js";import{say as v}from"../lib/editor-words.js";function k({tabs:m,activeTab:l,links:d,sticky:b=!0,editing:f=!1}){const i=(m??[]).filter(e=>e?.label?.trim()),a=i.length>0?i:f?[{label:v("Tab")}]:[],s=(d??[]).filter(e=>e?.label?.trim());if(a.length===0&&s.length===0)return null;const c=l?a.findIndex(e=>e.label.toLowerCase()===l.toLowerCase()):-1,h=c===-1?0:c;return r("nav",{className:`${b?`${u} sticky top-[var(--sf-header-h,88px)] z-40 `:""}border-b border-line px-[var(--sf-site-gutter,40px)] backdrop-blur-md max-[1100px]:px-[min(16px,var(--sf-site-gutter,40px))]`,style:{background:"color-mix(in oklab, var(--bg) 92%, transparent)"},children:n("div",{className:"mx-auto flex max-w-[calc(var(--sf-site-width,1600px)_-_var(--sf-site-gutter,40px)*2)] items-stretch gap-0 overflow-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",children:[a.map((e,t)=>{const p=t===h,x=e.count?.trim()?e.count:"";return n("span",{className:["relative whitespace-nowrap border-r border-line px-[22px] py-[18px] font-mono text-[length:var(--size-label,13px)] uppercase tracking-[0.22em] transition-colors",t===0?"border-l border-line":"",p?"text-fg":"text-fg-dim hover:text-fg"].join(" "),children:[o(e.label),x&&r("span",{className:"ml-2 text-[length:var(--size-body,12px)] text-accent opacity-70",children:o(x)}),p&&r("span",{className:"absolute inset-x-0 -bottom-px h-[2px] bg-accent"})]},t)}),s.length>0&&r("div",{className:"ml-auto flex border-l border-line",children:s.map((e,t)=>n(g,{href:e.href||"#",className:"flex items-center gap-2.5 px-[22px] py-[18px] font-mono text-[length:var(--size-label,13px)] uppercase tracking-[0.22em] text-fg-dim hover:text-fg",children:[o(e.label)," ",r("span",{className:"text-accent",children:"↗"})]},t))})]})})}export{k as default};
@@ -1 +1 @@
1
- "use client";import{jsx as e,Fragment as b,jsxs as d}from"react/jsx-runtime";import{useEffect as m,useId as f,useState as g}from"react";import{usePreviewChat as p}from"./PreviewChatContext.js";import{SettingsField as u,SettingsSwitch as w}from"efficient-easel/panel";import{EyeIcon as C,TypeIcon as E}from"efficient-easel/icons";import{GroupLabel as L}from"../lib/puck/controls.js";const y="Chat with us",T="We usually reply within a few minutes",v="Hi! Ask us anything — about a wheel, an order, or fitment. Leave your details and we’ll reply.";function s({label:t,value:a,placeholder:i,onCommit:l,multiline:c=!1}){const[o,r]=g(a);m(()=>r(a),[a]);const h=()=>{o!==a&&l(o)};return e(u,{label:t,icon:e(E,{}),children:c?e("textarea",{value:o,placeholder:i,rows:3,onChange:n=>r(n.target.value),onBlur:h,"aria-label":t}):e("input",{type:"text",value:o,placeholder:i,onChange:n=>r(n.target.value),onBlur:h,onKeyDown:n=>{n.key==="Enter"&&n.currentTarget.blur()},"aria-label":t,autoComplete:"off"})})}function x(){const{chat:t,setChat:a}=p(),i=f();return d(b,{children:[e(u,{label:"Show the chat bubble",labelId:i,icon:e(C,{}),action:e(w,{checked:t.enabled,onChange:l=>a({enabled:l}),ariaLabelledBy:i}),hint:t.enabled?"The floating chat bubble shows in the corner of every page.":"The chat bubble is hidden across the whole site."}),e(L,{label:"Wording"}),e(s,{label:"Title",value:t.title,placeholder:y,onCommit:l=>a({title:l})}),e(s,{label:"Subtitle",value:t.subtitle,placeholder:T,onCommit:l=>a({subtitle:l})}),e(s,{label:"Greeting message",value:t.greeting,placeholder:v,onCommit:l=>a({greeting:l}),multiline:!0}),e("p",{className:"es-field-hint sf-hint-keep",children:"Leave a field blank to use the built-in wording."})]})}export{x as default};
1
+ "use client";import{jsx as e,Fragment as b,jsxs as d}from"react/jsx-runtime";import{useEffect as f,useId as g,useState as p}from"react";import{usePreviewChat as w}from"./PreviewChatContext.js";import{SettingsField as c,SettingsSwitch as C}from"efficient-easel/panel";import{EyeIcon as y,TypeIcon as E}from"efficient-easel/icons";import{GroupLabel as L}from"../lib/puck/controls.js";import{say as n}from"../lib/editor-words.js";const T="Chat with us",v="We usually reply within a few minutes",x="Hi! Ask us anything — about a wheel, an order, or fitment. Leave your details and we’ll reply.";function h({label:t,value:a,placeholder:i,onCommit:l,multiline:m=!1}){const[r,s]=p(a);f(()=>s(a),[a]);const u=()=>{r!==a&&l(r)};return e(c,{label:t,icon:e(E,{}),children:m?e("textarea",{value:r,placeholder:i,rows:3,onChange:o=>s(o.target.value),onBlur:u,"aria-label":t}):e("input",{type:"text",value:r,placeholder:i,onChange:o=>s(o.target.value),onBlur:u,onKeyDown:o=>{o.key==="Enter"&&o.currentTarget.blur()},"aria-label":t,autoComplete:"off"})})}function I(){const{chat:t,setChat:a}=w(),i=g();return d(b,{children:[e(c,{label:n("Show the chat bubble"),labelId:i,icon:e(y,{}),action:e(C,{checked:t.enabled,onChange:l=>a({enabled:l}),ariaLabelledBy:i}),hint:t.enabled?n("The floating chat bubble shows in the corner of every page."):n("The chat bubble is hidden across the whole site.")}),e(L,{label:"Wording"}),e(h,{label:n("Title"),value:t.title,placeholder:T,onCommit:l=>a({title:l})}),e(h,{label:n("Subtitle"),value:t.subtitle,placeholder:v,onCommit:l=>a({subtitle:l})}),e(h,{label:n("Greeting message"),value:t.greeting,placeholder:x,onCommit:l=>a({greeting:l}),multiline:!0}),e("p",{className:"es-field-hint sf-hint-keep",children:n("Leave a field blank to use the built-in wording.")})]})}export{I as default};
@@ -1 +1 @@
1
- "use client";import{jsxs as r,jsx as o,Fragment as a}from"react/jsx-runtime";import{Button as t,Dialog as i}from"efficient-shell/ui";function l(e){return e===1?"1 color theme":`${e} color themes`}function m({count:e,onCancel:s,onConfirm:n}){return r(i,{open:!0,onClose:s,role:"alertdialog",size:"sm",title:"Clear this site’s colors?",footer:r(a,{children:[o(t,{variant:"secondary",onClick:s,children:"Cancel"}),o(t,{variant:"danger",onClick:n,children:"Clear colors"})]}),children:[r("p",{children:["This site has ",l(e)," of its own. Clearing takes them away and puts back the four the shop supplies."]}),o("p",{className:"mt-2 font-medium",children:"This cannot be undone."})]})}export{m as ClearColoursDialog};
1
+ "use client";import{jsx as e,Fragment as i,jsxs as n}from"react/jsx-runtime";import{Button as r,Dialog as l}from"efficient-shell/ui";import{say as s,sayCount as h}from"../lib/editor-words.js";function c(t){return h("This site has {n} color theme of its own. Clearing takes them away and puts back the four the shop supplies.","This site has {n} color themes of its own. Clearing takes them away and puts back the four the shop supplies.",t)}function d({count:t,onCancel:o,onConfirm:a}){return n(l,{open:!0,onClose:o,role:"alertdialog",size:"sm",title:s("Clear this site’s colors?"),footer:n(i,{children:[e(r,{variant:"secondary",onClick:o,children:s("Cancel")}),e(r,{variant:"danger",onClick:a,children:s("Clear colors")})]}),children:[e("p",{children:c(t)}),e("p",{className:"mt-2 font-medium",children:s("This cannot be undone.")})]})}export{d as ClearColoursDialog};
@@ -1 +1 @@
1
- "use client";import{jsx as e}from"react/jsx-runtime";import{SettingsDefaultReset as a,SettingsField as l}from"efficient-easel/panel";import{SquareIcon as u}from"efficient-easel/icons";import{CORNER_RADIUS_MAX as d,CORNER_RADIUS_MIN as r}from"../lib/theme.js";import{Slider as c}from"../lib/puck/controls.js";const p="Applies to panels, cards, inputs and buttons. Round things — color swatches, switches, status dots — stay round.",o="Corners";function R({value:t,onChange:s,showHint:i=!0}){const n=t===null;return e(l,{label:o,icon:e(u,{}),value:n?void 0:`${t}px`,changed:!n,action:e(a,{label:o,changed:!n,detail:"the built-in corners",onReset:()=>s(null)}),hint:i?p:void 0,children:e(c,{min:r,max:d,step:1,value:t??r,readout:!1,ariaLabel:o,onChange:s})})}export{o as CORNERS_LABEL,R as default};
1
+ "use client";import{jsx as t}from"react/jsx-runtime";import{SettingsDefaultReset as l,SettingsField as d}from"efficient-easel/panel";import{SquareIcon as u}from"efficient-easel/icons";import{CORNER_RADIUS_MAX as c,CORNER_RADIUS_MIN as i}from"../lib/theme.js";import{say as e,editorEaselWords as p}from"../lib/editor-words.js";import{Slider as m}from"../lib/puck/controls.js";const R="Applies to panels, cards, inputs and buttons. Round things — color swatches, switches, status dots — stay round.",s="Corners";function f({value:n,onChange:r,showHint:a=!0}){const o=n===null;return t(d,{label:e(s),icon:t(u,{}),value:o?void 0:`${n}px`,changed:!o,action:t(l,{words:p()?.panel,label:e(s),changed:!o,detail:e("the built-in corners"),onReset:()=>r(null)}),hint:a?e(R):void 0,children:t(m,{min:i,max:c,step:1,value:n??i,readout:!1,ariaLabel:e(s),onChange:r})})}export{s as CORNERS_LABEL,f as default};
@@ -1,7 +1,7 @@
1
- import{jsx as r,jsxs as m}from"react/jsx-runtime";import{renderGold as c}from"./goldPhrases.js";import{storefrontLines as b}from"../lib/markup.js";function u(i,l){return i.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||`section-${l+1}`}function y(i){const l=[];for(const p of i.replace(/\r\n?/g,`
2
- `).split(/\n[ \t]*\n/))p.split(`
3
- `).map(a=>a.replace(/\s+$/,"")).filter(a=>a.trim()!=="").forEach((a,d)=>{const x=b(a)[0].bullet,n=l.at(-1),e=d===0||!n||n.type==="ul"!==x;if(x){const t=a.slice(2);!e&&n?.type==="ul"?n.items.push(t):l.push({type:"ul",items:[t]})}else!e&&n?.type==="p"?n.text+=`
4
- ${a}`:l.push({type:"p",text:a})});return l}const v=[{label:"First section",heading:"Your *first* section.",body:`Write the opening paragraph here.
1
+ import{jsx as r,jsxs as f}from"react/jsx-runtime";import{renderGold as c}from"./goldPhrases.js";import{storefrontLines as u}from"../lib/markup.js";import{say as p}from"../lib/editor-words.js";function y(o,s){return o.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||`section-${s+1}`}function v(o){const s=[];for(const d of o.replace(/\r\n?/g,`
2
+ `).split(/\n[ \t]*\n/))d.split(`
3
+ `).map(a=>a.replace(/\s+$/,"")).filter(a=>a.trim()!=="").forEach((a,x)=>{const m=u(a)[0].bullet,n=s.at(-1),e=x===0||!n||n.type==="ul"!==m;if(m){const t=a.slice(2);!e&&n?.type==="ul"?n.items.push(t):s.push({type:"ul",items:[t]})}else!e&&n?.type==="p"?n.text+=`
4
+ ${a}`:s.push({type:"p",text:a})});return s}const w=()=>[{label:p("First section"),heading:p("Your *first* section."),body:p(`Write the opening paragraph here.
5
5
 
6
6
  - Start a line with a dash for a bullet.
7
- - Add as many as you like.`},{label:"Second section",heading:"Your *second* section.",body:"Leave a blank line between paragraphs to start a new one."}],w=120;function N({tocTitle:i,sections:l,editing:p=!1,stickyTop:f=w}){const a=(l??[]).filter(e=>(e.heading??"").trim()!==""||(e.label??"").trim()!==""||(e.body??"").trim()!=="");if(a.length===0&&!p)return null;const d=a.length===0&&p?v:a,x=new Set,n=d.map((e,t)=>{const o=u(e.label||e.heading||"",t);let s=o,g=2;for(;x.has(s);)s=`${o}-${g++}`;return x.add(s),s});return r("section",{className:"relative z-[2] bg-bg",children:m("div",{className:"mx-auto grid max-w-[var(--sf-site-width,1600px)] grid-cols-[240px_1fr] items-start gap-20 border-b border-line px-[var(--sf-site-gutter,40px)] py-20 max-[900px]:grid-cols-1 max-[900px]:gap-10 max-[900px]:px-[min(24px,var(--sf-site-gutter,40px))] max-[900px]:py-[60px]",children:[m("aside",{className:"sticky flex flex-col self-start max-[900px]:static",style:{top:f},children:[i&&r("h4",{className:"mb-[18px] font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.3em] text-accent",children:c(i)}),d.map((e,t)=>m("a",{href:`#${n[t]}`,className:`flex items-center justify-between gap-3 border-b border-line py-2.5 font-mono text-[length:var(--size-label,13px)] uppercase tracking-[0.18em] text-fg-dim transition-colors hover:text-fg${t===0?" border-t":""}`,children:[r("span",{children:c(e.label||"Section")}),r("span",{className:"text-accent",children:String(t+1).padStart(2,"0")})]},n[t]))]}),r("div",{className:"flex flex-col gap-16",children:d.map((e,t)=>m("article",{id:n[t],className:"flex flex-col gap-[18px]",style:{scrollMarginTop:f},children:[m("span",{className:"font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.3em] text-accent",children:[String(t+1).padStart(2,"0")," · ",c(e.label||"Section")]}),e.heading&&r("h2",{className:"mb-2 border-b border-line pb-[18px] font-heading2 text-[length:min(var(--size-headingMedium,clamp(28px,3.5vw,44px)),var(--sf-heading-max,9999px))] font-medium uppercase leading-[1.04] tracking-[-0.025em]",children:c(e.heading)}),y(e.body??"").map((o,s)=>o.type==="ul"?r("ul",{className:"flex max-w-[680px] list-none flex-col gap-2.5 pl-0",children:o.items.map((g,h)=>r("li",{className:"relative pl-6 font-serif text-[length:var(--size-body,17px)] leading-[1.65] text-fg-dim before:absolute before:-top-0.5 before:left-1.5 before:text-[length:var(--size-body,24px)] before:leading-none before:text-accent before:content-['·']",children:c(g)},h))},s):r("p",{className:"max-w-[680px] font-serif text-[length:var(--size-body,17px)] leading-[1.7] text-fg-dim",children:c(o.text)},s))]},n[t]))})]})})}export{N as default,u as sectionAnchorId};
7
+ - Add as many as you like.`)},{label:p("Second section"),heading:p("Your *second* section."),body:p("Leave a blank line between paragraphs to start a new one.")}],N=120;function S({tocTitle:o,sections:s,editing:d=!1,stickyTop:g=N}){const a=(s??[]).filter(e=>(e.heading??"").trim()!==""||(e.label??"").trim()!==""||(e.body??"").trim()!=="");if(a.length===0&&!d)return null;const x=a.length===0&&d?w():a,m=new Set,n=x.map((e,t)=>{const i=y(e.label||e.heading||"",t);let l=i,h=2;for(;m.has(l);)l=`${i}-${h++}`;return m.add(l),l});return r("section",{className:"relative z-[2] bg-bg",children:f("div",{className:"mx-auto grid max-w-[var(--sf-site-width,1600px)] grid-cols-[240px_1fr] items-start gap-20 border-b border-line px-[var(--sf-site-gutter,40px)] py-20 max-[900px]:grid-cols-1 max-[900px]:gap-10 max-[900px]:px-[min(24px,var(--sf-site-gutter,40px))] max-[900px]:py-[60px]",children:[f("aside",{className:"sticky flex flex-col self-start max-[900px]:static",style:{top:g},children:[o&&r("h4",{className:"mb-[18px] font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.3em] text-accent",children:c(o)}),x.map((e,t)=>f("a",{href:`#${n[t]}`,className:`flex items-center justify-between gap-3 border-b border-line py-2.5 font-mono text-[length:var(--size-label,13px)] uppercase tracking-[0.18em] text-fg-dim transition-colors hover:text-fg${t===0?" border-t":""}`,children:[r("span",{children:c(e.label||"Section")}),r("span",{className:"text-accent",children:String(t+1).padStart(2,"0")})]},n[t]))]}),r("div",{className:"flex flex-col gap-16",children:x.map((e,t)=>f("article",{id:n[t],className:"flex flex-col gap-[18px]",style:{scrollMarginTop:g},children:[f("span",{className:"font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.3em] text-accent",children:[String(t+1).padStart(2,"0")," · ",c(e.label||"Section")]}),e.heading&&r("h2",{className:"mb-2 border-b border-line pb-[18px] font-heading2 text-[length:min(var(--size-headingMedium,clamp(28px,3.5vw,44px)),var(--sf-heading-max,9999px))] font-medium uppercase leading-[1.04] tracking-[-0.025em]",children:c(e.heading)}),v(e.body??"").map((i,l)=>i.type==="ul"?r("ul",{className:"flex max-w-[680px] list-none flex-col gap-2.5 pl-0",children:i.items.map((h,b)=>r("li",{className:"relative pl-6 font-serif text-[length:var(--size-body,17px)] leading-[1.65] text-fg-dim before:absolute before:-top-0.5 before:left-1.5 before:text-[length:var(--size-body,24px)] before:leading-none before:text-accent before:content-['·']",children:c(h)},b))},l):r("p",{className:"max-w-[680px] font-serif text-[length:var(--size-body,17px)] leading-[1.7] text-fg-dim",children:c(i.text)},l))]},n[t]))})]})})}export{S as default,y as sectionAnchorId};
@@ -1 +1 @@
1
- "use client";import{jsx as t,jsxs as f,Fragment as _}from"react/jsx-runtime";import{useCallback as A,useRef as L,useState as o}from"react";import{InspectorIconButton as z}from"efficient-easel/chrome";import{PopupMenu as W,PopupMenuItem as $,PopupMenuLabel as l}from"efficient-shell/ui";import{CopyToPageIcon as H}from"efficient-easel/icons";import{backendOf as E}from"../lib/website-session.js";import{useEditorSession as Y}from"../lib/editor-session-context.js";import{cloneNodeWithFreshIds as q}from"../lib/puck/section-doc.js";import{FOOTER_TYPE as G}from"../lib/puck/content-tree.js";function J({node:M,currentSlug:N,siteId:a,tokenRef:g,runAuthed:T,onNotify:D}){const[i,s]=o(!1),[h,y]=o(null),n=Y(),[I,c]=o(!1),[u,x]=o(null),b=L(null),[d,j]=o(null),B=A(async()=>{if(n.usesBridgedToken&&!g.current){c(!0);return}try{y(await E(n).pages.list(a?{siteId:a}:void 0))}catch{c(!0)}},[g,a,n]),F=()=>{if(i){s(!1);return}const e=b.current?.getBoundingClientRect();e&&j({top:e.bottom+6,right:Math.max(8,window.innerWidth-e.right)}),y(null),c(!1),B(),s(!0)},P=(h??[]).filter(e=>e.slug!==N),O=async e=>{if(u)return;x(e.slug);const k=q(M),R=n.documentHooks?.storedNode?.(k)??k,w=E(n).pages,C=await T(async()=>{const p=await w.load({id:e.id});if(!p)throw new Error(`Page "${e.slug}" no longer exists.`);const m=p.layout??{},r=Array.isArray(m.content)?[...m.content]:[],v=r.findIndex(S=>S?.type===G);r.splice(v===-1?r.length:v,0,R),await w.save(e.id,{layout:{root:{},zones:{},...m,content:r}},{version:p.version,clientId:""})});x(null),s(!1),D?.(C?`Duplicated to “${e.label}”`:"Couldn’t duplicate the section — please retry",C?"success":"error")};return f(_,{children:[t("span",{"data-menu-toggle":!0,style:{display:"contents"},children:t(z,{buttonRef:b,label:"Duplicate to another page",hasPopup:"menu",expanded:i,onClick:F,children:t(H,{size:15})})}),i&&d&&t(W,{portal:!0,style:{top:d.top,right:d.right},onClose:()=>s(!1),children:f("div",{role:"menu","aria-label":"Duplicate to page",children:[t(l,{children:"Duplicate to page…"}),I?t(l,{children:"Couldn’t load pages"}):h===null?t(l,{children:"Loading…"}):P.length===0?t(l,{children:"No other pages"}):P.map(e=>f($,{role:"menuitem",disabled:u!==null,onClick:()=>{O(e)},children:[t("span",{className:"min-w-0 flex-1 truncate",children:e.label}),u===e.slug?t("span",{className:"shrink-0 text-xs",children:"Copying…"}):e.draft&&t("span",{className:"shrink-0 text-xs",children:"draft"})]},e.id))]})})]})}export{J as default};
1
+ "use client";import{jsx as t,jsxs as g,Fragment as A}from"react/jsx-runtime";import{useCallback as L,useRef as z,useState as s}from"react";import{InspectorIconButton as W}from"efficient-easel/chrome";import{PopupMenu as H,PopupMenuItem as Y,PopupMenuLabel as a}from"efficient-shell/ui";import{CopyToPageIcon as $}from"efficient-easel/icons";import{backendOf as M}from"../lib/website-session.js";import{useEditorSession as q}from"../lib/editor-session-context.js";import{say as n}from"../lib/editor-words.js";import{cloneNodeWithFreshIds as G}from"../lib/puck/section-doc.js";import{FOOTER_TYPE as J}from"../lib/puck/content-tree.js";function K({node:N,currentSlug:T,siteId:i,tokenRef:h,runAuthed:D,onNotify:I}){const[c,r]=s(!1),[y,x]=s(null),o=q(),[j,u]=s(!1),[d,b]=s(null),P=z(null),[p,B]=s(null),F=L(async()=>{if(o.usesBridgedToken&&!h.current){u(!0);return}try{x(await M(o).pages.list(i?{siteId:i}:void 0))}catch{u(!0)}},[h,i,o]),O=()=>{if(c){r(!1);return}const e=P.current?.getBoundingClientRect();e&&B({top:e.bottom+6,right:Math.max(8,window.innerWidth-e.right)}),x(null),u(!1),F(),r(!0)},k=(y??[]).filter(e=>e.slug!==T),R=async e=>{if(d)return;b(e.slug);const w=G(N),S=o.documentHooks?.storedNode?.(w)??w,C=M(o).pages,v=await D(async()=>{const m=await C.load({id:e.id});if(!m)throw new Error(`Page "${e.slug}" no longer exists.`);const f=m.layout??{},l=Array.isArray(f.content)?[...f.content]:[],E=l.findIndex(_=>_?.type===J);l.splice(E===-1?l.length:E,0,S),await C.save(e.id,{layout:{root:{},zones:{},...f,content:l}},{version:m.version,clientId:""})});b(null),r(!1),I?.(v?n("Duplicated to “{name}”",{name:e.label}):n("Couldn’t duplicate the section — please retry"),v?"success":"error")};return g(A,{children:[t("span",{"data-menu-toggle":!0,style:{display:"contents"},children:t(W,{buttonRef:P,label:n("Duplicate to another page"),hasPopup:"menu",expanded:c,onClick:O,children:t($,{size:15})})}),c&&p&&t(H,{portal:!0,style:{top:p.top,right:p.right},onClose:()=>r(!1),children:g("div",{role:"menu","aria-label":n("Duplicate to page"),children:[t(a,{children:n("Duplicate to page")}),j?t(a,{children:n("Couldn’t load pages")}):y===null?t(a,{children:n("Loading…")}):k.length===0?t(a,{children:n("No other pages")}):k.map(e=>g(Y,{role:"menuitem",disabled:d!==null,onClick:()=>{R(e)},children:[t("span",{className:"min-w-0 flex-1 truncate",children:e.label}),d===e.slug?t("span",{className:"shrink-0 text-xs",children:n("Copying…")}):e.draft&&t("span",{className:"shrink-0 text-xs",children:n("draft")})]},e.id))]})})]})}export{K as default};
@@ -1 +1 @@
1
- import{Fragment as q,jsx as e,jsxs as c}from"react/jsx-runtime";import{stripMarkup as v}from"../lib/markup.js";import{renderGold as i,renderHeadline as A}from"./goldPhrases.js";import E from"./SectionHeader.js";import F from"./SectionTextPartBridge.js";import{TEXT_REGION_ATTR as S,regionStyle as R,textRegionPartKey as T}from"../lib/sections/text-regions.js";import{FAQ_REGIONS as z}from"../lib/sections/faq-text-regions.js";import{SIZE_STEP_OF_ROLE as I}from"../lib/fonts.js";function h(t){return v(t).trim()}function m(t,r){return t?{[S]:r}:{}}function x(t,r){const a=t?.[T(r)]?.role;return R(a,z[r],I)}function O({editing:t,nodeId:r,children:a}){return!t||!r?e(q,{children:a}):e(F,{nodeId:r,sectionType:"FAQ",children:a})}const j="relative z-[2] bg-bg px-[var(--sf-site-gutter,40px)] py-[120px] max-[800px]:px-[min(20px,var(--sf-site-gutter,40px))] max-[800px]:py-[70px]",Q="relative z-[2] bg-bg px-[var(--sf-site-gutter,40px)] pt-0 pb-[120px] max-[800px]:px-[min(20px,var(--sf-site-gutter,40px))] max-[800px]:pt-0 max-[800px]:pb-[70px]";function k({num:t,title:r,link:a,items:y,defaultOpen:_=!0,editing:s=!1,omitHead:d=!1,nodeId:b,partStyles:o}){const p=(y??[]).filter(n=>(n.question??"").trim()!==""),w=s&&p.length===0;if(p.length===0&&!s)return null;const u=w?[{question:"Add your first question",answer:"Write the answer here."},{question:"Add another question",answer:"Write the answer here."}]:p,f=u.filter(n=>(n.answer??"").trim()!=="").map(n=>({"@type":"Question",name:h(n.question??""),acceptedAnswer:{"@type":"Answer",text:h(n.answer??"")}})),g=!s&&f.length>0?JSON.stringify({"@context":"https://schema.org","@type":"FAQPage",mainEntity:f}).replace(/</g,"\\u003c"):null,N=r?A(r,!0):void 0;return c("section",{className:d?Q:j,children:[e("style",{children:".faq-acc__ico{transition:transform .2s ease}details[open]>summary .faq-acc__ico{transform:rotate(45deg)}"}),g&&e("script",{type:"application/ld+json",dangerouslySetInnerHTML:{__html:g}}),d?null:e(E,{num:t!==void 0?i(t):void 0,title:N,link:a?{text:i(a.text),href:a.href}:void 0,accent:!0}),e(O,{editing:s,nodeId:b,children:e("div",{children:u.map((n,l)=>c("details",{open:l===0&&_,className:"border-b border-line",children:[c("summary",{className:"flex cursor-pointer list-none items-center gap-6 py-7 [&::-webkit-details-marker]:hidden max-[800px]:gap-4 max-[800px]:py-5",children:[e("span",{className:"font-mono text-[length:var(--size-label,14px)] tracking-[0.2em] text-accent",...m(s,"number"),style:x(o,"number"),children:String(l+1).padStart(2,"0")}),e("span",{className:"flex-1 font-display text-[length:var(--size-body,18px)] font-medium uppercase tracking-[0.02em] text-fg max-[800px]:text-[length:var(--size-body,15px)]",...m(s,"question"),style:x(o,"question"),children:i(n.question??"")}),e("span",{"aria-hidden":!0,className:"faq-acc__ico shrink-0 text-[length:var(--size-body,22px)] leading-none text-fg-dim",children:"+"})]}),n.answer&&e("div",{className:"pb-8 pl-11 pr-8 max-[800px]:pl-0 max-[800px]:pr-0",children:e("p",{className:"max-w-[760px] text-[length:var(--size-body,15px)] leading-[1.7] text-fg-dim",...m(s,"answer"),style:x(o,"answer"),children:i(n.answer)})})]},l))})})]})}export{k as default};
1
+ import{Fragment as v,jsx as e,jsxs as c}from"react/jsx-runtime";import{stripMarkup as A}from"../lib/markup.js";import{renderGold as i,renderHeadline as E}from"./goldPhrases.js";import F from"./SectionHeader.js";import S from"./SectionTextPartBridge.js";import{TEXT_REGION_ATTR as R,regionStyle as T,textRegionPartKey as z}from"../lib/sections/text-regions.js";import{FAQ_REGIONS as I}from"../lib/sections/faq-text-regions.js";import{SIZE_STEP_OF_ROLE as O}from"../lib/fonts.js";import{say as o}from"../lib/editor-words.js";function y(t){return A(t).trim()}function x(t,r){return t?{[R]:r}:{}}function d(t,r){const a=t?.[z(r)]?.role;return T(a,I[r],O)}function j({editing:t,nodeId:r,children:a}){return!t||!r?e(v,{children:a}):e(S,{nodeId:r,sectionType:"FAQ",children:a})}const Q="relative z-[2] bg-bg px-[var(--sf-site-gutter,40px)] py-[120px] max-[800px]:px-[min(20px,var(--sf-site-gutter,40px))] max-[800px]:py-[70px]",k="relative z-[2] bg-bg px-[var(--sf-site-gutter,40px)] pt-0 pb-[120px] max-[800px]:px-[min(20px,var(--sf-site-gutter,40px))] max-[800px]:pt-0 max-[800px]:pb-[70px]";function L({num:t,title:r,link:a,items:_,defaultOpen:b=!0,editing:s=!1,omitHead:u=!1,nodeId:w,partStyles:p}){const l=(_??[]).filter(n=>(n.question??"").trim()!==""),N=s&&l.length===0;if(l.length===0&&!s)return null;const f=N?[{question:o("Add your first question"),answer:o("Write the answer here.")},{question:o("Add another question"),answer:o("Write the answer here.")}]:l,g=f.filter(n=>(n.answer??"").trim()!=="").map(n=>({"@type":"Question",name:y(n.question??""),acceptedAnswer:{"@type":"Answer",text:y(n.answer??"")}})),h=!s&&g.length>0?JSON.stringify({"@context":"https://schema.org","@type":"FAQPage",mainEntity:g}).replace(/</g,"\\u003c"):null,q=r?E(r,!0):void 0;return c("section",{className:u?k:Q,children:[e("style",{children:".faq-acc__ico{transition:transform .2s ease}details[open]>summary .faq-acc__ico{transform:rotate(45deg)}"}),h&&e("script",{type:"application/ld+json",dangerouslySetInnerHTML:{__html:h}}),u?null:e(F,{num:t!==void 0?i(t):void 0,title:q,link:a?{text:i(a.text),href:a.href}:void 0,accent:!0}),e(j,{editing:s,nodeId:w,children:e("div",{children:f.map((n,m)=>c("details",{open:m===0&&b,className:"border-b border-line",children:[c("summary",{className:"flex cursor-pointer list-none items-center gap-6 py-7 [&::-webkit-details-marker]:hidden max-[800px]:gap-4 max-[800px]:py-5",children:[e("span",{className:"font-mono text-[length:var(--size-label,14px)] tracking-[0.2em] text-accent",...x(s,"number"),style:d(p,"number"),children:String(m+1).padStart(2,"0")}),e("span",{className:"flex-1 font-display text-[length:var(--size-body,18px)] font-medium uppercase tracking-[0.02em] text-fg max-[800px]:text-[length:var(--size-body,15px)]",...x(s,"question"),style:d(p,"question"),children:i(n.question??"")}),e("span",{"aria-hidden":!0,className:"faq-acc__ico shrink-0 text-[length:var(--size-body,22px)] leading-none text-fg-dim",children:"+"})]}),n.answer&&e("div",{className:"pb-8 pl-11 pr-8 max-[800px]:pl-0 max-[800px]:pr-0",children:e("p",{className:"max-w-[760px] text-[length:var(--size-body,15px)] leading-[1.7] text-fg-dim",...x(s,"answer"),style:d(p,"answer"),children:i(n.answer)})})]},m))})})]})}export{L as default};
@@ -1 +1 @@
1
- import{jsx as s,jsxs as X,Fragment as T}from"react/jsx-runtime";import U from"./BoxTabs.js";import q from"./SiteGutterFrame.js";import{BoxSurfaceProvider as J}from"./BoxSurfaceContext.js";import{useInSiteHeader as K}from"./PhoneMenuTools.js";import{FRAME_BASE as Q,FRAME_GUTTER as V}from"./BlockFrame.js";import{containerGapProps as Y}from"../lib/content/container-gap.js";import{boxLayoutClasses as Z,boxBlockSpacingClass as ee,boxPhoneRowClasses as se,boxShareClasses as oe,boxWrapClass as re}from"../lib/content/box-layout.js";import{BOX_CARD_PAD as e,BOX_OWNS_SIDES_CLASS as te,boxDarkenStyle as ae,boxPlacementClasses as ne,CARD_SHADOW_CLASS as le,isBackgroundBox as ie}from"../lib/content/box-background.js";import{sectionPadAttrs as ce}from"../lib/content/section-padding.js";function me({direction:r,label:N,gap:g,justify:v,align:P,wrap:w,share:B,phoneRow:E,blockSpacing:F,bgImage:l,darken:j,darkenStyle:D,placement:H,padTop:i,padBottom:c,padTopPhone:m,padBottomPhone:f,padLeft:u,padRight:d,padLeftPhone:b,padRightPhone:p,contained:O=!0,fillHeight:R=!1,content:z}){const t=ie(l),x=K(),y=[oe(B),ee(F)].filter(Boolean).join(" "),G=y?` ${y}`:"",$=["flex",r==="row"?`flex-row ${se(E)}`:"flex-col",re(w),t?ne(H??"center",r):Z(v,P)].join(" ")+G,o=R||t?" h-full":"",S=t||x?"":" py-6",L=`${Q}${S} ${V}${o}`,W=x?o.trim():`w-full${S}${o}`,a=Y("Flex",g,r==="row"?"inline":"block"),C=s(z,{className:`${$}${a.className}${o}`,style:a.style,minEmptyHeight:120}),h=r==="tabs"?s(U,{className:`${$}${a.className}${o}`,style:a.style,children:C}):C,I=N?.trim()||void 0,_=M=>s(q,{contained:O,framedClass:L,fillClass:W,tabLabel:I,children:M});if(!t)return _(h);const n=ae(j,D),A=ce(typeof i=="number"?i:e.desktop,typeof c=="number"?c:e.desktop,typeof m=="number"?m:e.phone,typeof f=="number"?f:e.phone,typeof u=="number"?u:e.side,typeof d=="number"?d:e.side,typeof b=="number"?b:e.sidePhone,typeof p=="number"?p:e.sidePhone),k=s("div",{className:`relative z-[1] ${te} ${A?.className??""}`.trimEnd(),style:A?.style,children:h});return _(s(T,{children:X("div",{className:`relative isolate overflow-hidden ${le}${o}`,style:{backgroundImage:`url(${l})`,backgroundSize:"cover",backgroundPosition:"center"},children:[n&&s("div",{className:"absolute inset-0 z-0",style:n,"aria-hidden":!0}),n?s(J,{children:k}):k]})}))}export{me as default};
1
+ import{jsx as s,jsxs as T,Fragment as X}from"react/jsx-runtime";import K from"./BoxTabs.js";import U from"./SiteGutterFrame.js";import{BoxSurfaceProvider as Y}from"./BoxSurfaceContext.js";import{useInSiteHeader as q}from"./PhoneMenuTools.js";import{FRAME_BASE as J,FRAME_GUTTER as Q}from"./BlockFrame.js";import{containerGapProps as V}from"../lib/content/container-gap.js";import{boxLayoutClasses as Z,boxBlockSpacingClass as ee,boxPhoneRowClasses as se,boxShareClasses as oe,boxWrapClass as re}from"../lib/content/box-layout.js";import{BOX_CARD_PAD as e,BOX_OWNS_SIDES_CLASS as te,boxDarkenStyle as ae,boxPlacementClasses as ne,CARD_SHADOW_CLASS as le,isBackgroundBox as ie}from"../lib/content/box-background.js";import{sectionPadAttrs as me}from"../lib/content/section-padding.js";import{ON_MEDIA_INK_STYLE as ce}from"../lib/content/ink.js";function fe({direction:r,label:k,gap:E,justify:g,align:v,wrap:P,share:w,phoneRow:B,blockSpacing:D,bgImage:i,darken:F,darkenStyle:j,placement:O,padTop:m,padBottom:c,padTopPhone:f,padBottomPhone:u,padLeft:d,padRight:b,padLeftPhone:p,padRightPhone:y,contained:H=!0,fillHeight:I=!1,content:L}){const t=ie(i),x=q(),S=[oe(w),ee(D)].filter(Boolean).join(" "),R=S?` ${S}`:"",$=["flex",r==="row"?`flex-row ${se(B)}`:"flex-col",re(P),t?ne(O??"center",r):Z(g,v)].join(" ")+R,o=I||t?" h-full":"",C=t||x?"":" py-6",z=`${J}${C} ${Q}${o}`,G=x?o.trim():`w-full${C}${o}`,a=V("Flex",E,r==="row"?"inline":"block"),_=s(L,{className:`${$}${a.className}${o}`,style:a.style,minEmptyHeight:120}),h=r==="tabs"?s(K,{className:`${$}${a.className}${o}`,style:a.style,children:_}):_,M=k?.trim()||void 0,A=W=>s(U,{contained:H,framedClass:z,fillClass:G,tabLabel:M,children:W});if(!t)return A(h);const n=ae(F,j),l=me(typeof m=="number"?m:e.desktop,typeof c=="number"?c:e.desktop,typeof f=="number"?f:e.phone,typeof u=="number"?u:e.phone,typeof d=="number"?d:e.side,typeof b=="number"?b:e.side,typeof p=="number"?p:e.sidePhone,typeof y=="number"?y:e.sidePhone),N=s("div",{className:`relative z-[1] ${te} ${l?.className??""}`.trimEnd(),style:n?{...l?.style,...ce}:l?.style,children:h});return A(s(X,{children:T("div",{className:`relative isolate overflow-hidden ${le}${o}`,style:{backgroundImage:`url(${i})`,backgroundSize:"cover",backgroundPosition:"center"},children:[n&&s("div",{className:"absolute inset-0 z-0",style:n,"aria-hidden":!0}),n?s(Y,{children:N}):N]})}))}export{fe as default};
@@ -1,4 +1,4 @@
1
- "use client";import{jsx as r,Fragment as nt,jsxs as w}from"react/jsx-runtime";import{useEffect as S,useRef as f,useState as P}from"react";import{MAP_PALETTE as Q,mapBackgroundFor as F,mapMarkerColorFor as U,mapPaletteFor as H,mapStylesFor as X,watchMapTheme as rt}from"../lib/map-styles.js";import{hasMapCoords as J,loadGoogleMaps as ot}from"../lib/maps-loader.js";import{resolveMapId as at,resolveMapsKey as st,useSiteMapId as lt,useSiteMapsKey as it}from"../lib/maps-config.js";import{bucketByGroup as ct,groupColsClass as pt}from"../lib/locations-grid.js";import ut from"./EditorialSectionHead.js";import{LocationGroupHeader as dt}from"./LocationGroupHeader.js";import{renderGold as mt,renderHeadline as ft}from"./goldPhrases.js";import gt from"./PartStyledText.js";const xt=11,bt=6,q=60;function G(t){return t.replace(/[&<>"']/g,o=>o==="&"?"&amp;":o==="<"?"&lt;":o===">"?"&gt;":o==='"'?"&quot;":"&#39;")}function V(t,o,u){return{path:o.SymbolPath.CIRCLE,scale:t.hq?xt:bt,fillColor:U(u),fillOpacity:1,strokeColor:U(u),strokeOpacity:t.hq?.3:.25,strokeWeight:t.hq?14:9}}function Y(t,o,u,e,l,M){l.forEach(a=>a.setMap(null)),l.clear();const g=new o.LatLngBounds;return e.forEach((a,x)=>{if(!J(a.lat,a.lng))return;g.extend({lat:a.lat,lng:a.lng});const m=new o.Marker({map:t,position:{lat:a.lat,lng:a.lng},icon:V(a,o,u),title:a.city,zIndex:a.hq?100:10,cursor:"pointer"});m.addListener("click",()=>M(x)),l.set(x,m)}),g}function yt(t){return`
1
+ "use client";import{jsx as r,Fragment as nt,jsxs as w}from"react/jsx-runtime";import{useEffect as S,useRef as f,useState as P}from"react";import{MAP_PALETTE as Q,mapBackgroundFor as F,mapMarkerColorFor as U,mapPaletteFor as H,mapStylesFor as X,watchMapTheme as rt}from"../lib/map-styles.js";import{hasMapCoords as J,loadGoogleMaps as ot}from"../lib/maps-loader.js";import{resolveMapId as at,resolveMapsKey as st,useSiteMapId as lt,useSiteMapsKey as it}from"../lib/maps-config.js";import{bucketByGroup as ct,groupColsClass as pt}from"../lib/locations-grid.js";import ut from"./EditorialSectionHead.js";import{LocationGroupHeader as dt}from"./LocationGroupHeader.js";import{renderGold as mt,renderHeadline as ft}from"./goldPhrases.js";import gt from"./PartStyledText.js";import{say as xt}from"../lib/editor-words.js";const bt=11,yt=6,q=60;function G(t){return t.replace(/[&<>"']/g,o=>o==="&"?"&amp;":o==="<"?"&lt;":o===">"?"&gt;":o==='"'?"&quot;":"&#39;")}function V(t,o,u){return{path:o.SymbolPath.CIRCLE,scale:t.hq?bt:yt,fillColor:U(u),fillOpacity:1,strokeColor:U(u),strokeOpacity:t.hq?.3:.25,strokeWeight:t.hq?14:9}}function Y(t,o,u,e,l,M){l.forEach(a=>a.setMap(null)),l.clear();const g=new o.LatLngBounds;return e.forEach((a,x)=>{if(!J(a.lat,a.lng))return;g.extend({lat:a.lat,lng:a.lng});const m=new o.Marker({map:t,position:{lat:a.lat,lng:a.lng},icon:V(a,o,u),title:a.city,zIndex:a.hq?100:10,cursor:"pointer"});m.addListener("click",()=>M(x)),l.set(x,m)}),g}function vt(t){return`
2
2
  .footprint-map .gm-style { font-family: var(--font-role-label), var(--font-mono-fallback) !important; }
3
3
  .footprint-map .gm-style .gm-style-iw-c {
4
4
  background: ${t.bg} !important;
@@ -16,7 +16,7 @@
16
16
  .footprint-map .gm-style button[aria-label="Close"] > span {
17
17
  background-color: ${t.closeX} !important;
18
18
  }
19
- `}function T({pal:t}){const o=t?t.marker:"var(--accent)",u=t?t.info.bg:"var(--site-surface)",e=t?t.background:"var(--bg)",l=t?`color-mix(in oklab, ${t.info.fgDim} 12%, transparent)`:"color-mix(in oklab, var(--fg) 3%, transparent)";return w(nt,{children:[r("div",{className:"pointer-events-none absolute inset-0",style:{backgroundImage:`radial-gradient(60% 70% at 50% 50%, color-mix(in oklab, ${o} 8%, ${u}) 0%, ${u} 60%, ${e} 100%)`}}),r("div",{className:"pointer-events-none absolute inset-0",style:{backgroundImage:`repeating-linear-gradient(0deg, transparent 0 22px, ${l} 22px 23px), repeating-linear-gradient(90deg, transparent 0 22px, ${l} 22px 23px)`}})]})}function vt({sectionNumber:t="",heading:o="",tagline:u="",offices:e=[],apiKey:l="",mapId:M="",mapTheme:g="auto",padTop:a,padBottom:x,editing:m,omitHead:$=!1}){const L=typeof a=="number"?a:120,z=typeof x=="number"?x:120,[b,h]=P(null),v=e.length===0&&!t&&!o&&!u;if(S(()=>{h(n=>n!==null&&n>=e.length?null:n)},[e]),v&&!m)return null;const N=ct(e,n=>String(n.group??""),()=>"");return r("section",{className:"relative z-[2] bg-bg",children:w("div",{className:"mx-auto max-w-[var(--sf-site-width,1600px)] border-b border-line px-[var(--sf-site-gutter,40px)] max-[1100px]:px-[min(24px,var(--sf-site-gutter,40px))] max-[800px]:px-[min(20px,var(--sf-site-gutter,40px))]",style:{paddingTop:L,paddingBottom:z},children:[!$&&(t||o||u)&&r(ut,{num:t,title:o?ft(o):void 0,desc:u||void 0,spaceBelow:"mb-12"}),r(ht,{offices:e,apiKey:l,mapId:M,mapTheme:g,activeIndex:b,onSelect:h}),e.length>0&&r("div",{className:"mt-12 flex flex-col gap-8",children:N.map((n,C)=>w("div",{children:[n.key&&r(dt,{groupKey:n.key,count:n.items.length}),r("div",{className:["grid gap-[2px] bg-bg",pt(n.items.length),n.items.length>2?"max-[1100px]:grid-cols-2 max-[700px]:grid-cols-1":n.items.length===2?"max-[700px]:grid-cols-1":""].join(" "),children:n.items.map(({item:i,index:k})=>{const A=b===k;return w("button",{type:"button",onClick:()=>h(k),"aria-pressed":A,className:["flex flex-col gap-1.5 bg-bg px-6 py-5 text-left transition-colors outline outline-1 -outline-offset-1",A?"outline-accent":"outline-transparent hover:outline-line"].join(" "),children:[w("span",{className:"flex items-center gap-2.5 font-mono text-[length:var(--size-label,11px)] uppercase tracking-[0.28em]",children:[i.hq&&r("span",{className:"border border-accent bg-accent px-1.5 py-0.5 text-[length:var(--size-body,10px)] text-bg",children:"HQ"}),i.role&&r("span",{className:"text-accent",children:i.role})]}),i.city&&r("span",{className:"font-display text-[length:var(--size-body,18px)] font-medium leading-[1.2] tracking-[-0.01em] text-fg",children:r(gt,{value:i.city,children:i.city})}),i.country&&r("span",{className:"font-mono text-[length:var(--size-label,11px)] uppercase tracking-[0.2em] text-fg-dim",children:i.country}),i.address&&r("span",{className:"font-mono text-[length:var(--size-label,11px)] uppercase tracking-[0.2em] text-fg-dim",children:mt(i.address)})]},k)})})]},n.key?`g:${n.key}`:`ungrouped:${C}`))}),e.length===0&&m&&r("p",{className:"mt-8 font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.24em] text-fg-dim",children:"Add an office in the inspector"})]})})}function ht({offices:t,apiKey:o,mapId:u,mapTheme:e,activeIndex:l,onSelect:M}){const g=st(o,it()),a=at(u,lt()),x=!!g&&t.some(c=>J(c.lat,c.lng)),m=e!=="auto",$=H(e),L=f(null),z=f(null),b=f(null),h=f(null),v=f(new Map),N=f(null),n=f(null),C=f(null),i=f(M);i.current=M;const k=f(t);k.current=t;const A=f(l);A.current=l;const[_,D]=P(!1),[j,K]=P(Q.dark),[tt,W]=P(Q.dark.background);return S(()=>{K(H(e)),W(F(e))},[e]),S(()=>{if(!x)return;const c=L.current;if(!c)return;let d=!1,p=null;const I=async()=>{try{const s=await ot(g);if(d||!z.current)return;const y=new s.Map(z.current,{center:{lat:25,lng:0},zoom:2,minZoom:1,maxZoom:5,disableDefaultUI:!0,gestureHandling:"cooperative",keyboardShortcuts:!1,clickableIcons:!1,backgroundColor:F(e),...a?{mapId:a}:{styles:X(e)}});b.current=y,h.current=s;const R=Y(y,s,e,k.current,v.current,B=>i.current(B));n.current=R,R.isEmpty()||y.fitBounds(R,q);const O=new s.InfoWindow({disableAutoPan:!0});O.addListener("closeclick",()=>i.current(null)),N.current=O,y.addListener("click",()=>i.current(null)),y.addListener("tilesloaded",()=>{d||D(!0)}),e==="auto"&&!a&&(p=rt(()=>{d||!b.current||(b.current.setOptions({styles:X(e),backgroundColor:F(e)}),v.current.forEach((B,et)=>{const Z=k.current[et];Z&&B.setIcon(V(Z,s,e))}),K(H(e)),W(F(e)))}))}catch(s){console.error("[FootprintMap] Maps SDK failed:",s)}},E=new IntersectionObserver(s=>{for(const y of s)if(y.isIntersecting){E.disconnect(),I();break}},{rootMargin:"200px"});return E.observe(c),()=>{d=!0,E.disconnect(),p?.(),N.current?.close(),N.current=null,b.current=null,h.current=null,v.current.forEach(s=>s.setMap(null)),v.current.clear(),n.current=null,D(!1)}},[x,g,a,e]),S(()=>{const c=b.current,d=h.current;if(!c||!d)return;const p=Y(c,d,e,t,v.current,I=>i.current(I));n.current=p,A.current===null&&!p.isEmpty()&&c.fitBounds(p,q)},[t,e]),S(()=>{const c=b.current,d=N.current;if(!c||!d)return;if(l===null){d.close(),C.current!==null&&n.current&&!n.current.isEmpty()&&c.fitBounds(n.current,q),C.current=null;return}const p=t[l],I=v.current.get(l);if(!p||!I){d.close(),C.current=l;return}c.panTo({lat:p.lat,lng:p.lng});const E=c.getZoom();E!=null&&E<4&&c.setZoom(4);const s=j.info,y=p.hq?`<span style="background:${s.accent};color:${s.bg};padding:1px 6px;font-size:10px;letter-spacing:0.18em;">HQ</span>`:"",R=p.role?`<span style="color:${s.accent};">${G(p.role)}</span>`:"";d.setContent(`
19
+ `}function T({pal:t}){const o=t?t.marker:"var(--accent)",u=t?t.info.bg:"var(--site-surface)",e=t?t.background:"var(--bg)",l=t?`color-mix(in oklab, ${t.info.fgDim} 12%, transparent)`:"color-mix(in oklab, var(--fg) 3%, transparent)";return w(nt,{children:[r("div",{className:"pointer-events-none absolute inset-0",style:{backgroundImage:`radial-gradient(60% 70% at 50% 50%, color-mix(in oklab, ${o} 8%, ${u}) 0%, ${u} 60%, ${e} 100%)`}}),r("div",{className:"pointer-events-none absolute inset-0",style:{backgroundImage:`repeating-linear-gradient(0deg, transparent 0 22px, ${l} 22px 23px), repeating-linear-gradient(90deg, transparent 0 22px, ${l} 22px 23px)`}})]})}function ht({sectionNumber:t="",heading:o="",tagline:u="",offices:e=[],apiKey:l="",mapId:M="",mapTheme:g="auto",padTop:a,padBottom:x,editing:m,omitHead:$=!1}){const L=typeof a=="number"?a:120,z=typeof x=="number"?x:120,[b,h]=P(null),v=e.length===0&&!t&&!o&&!u;if(S(()=>{h(n=>n!==null&&n>=e.length?null:n)},[e]),v&&!m)return null;const N=ct(e,n=>String(n.group??""),()=>"");return r("section",{className:"relative z-[2] bg-bg",children:w("div",{className:"mx-auto max-w-[var(--sf-site-width,1600px)] border-b border-line px-[var(--sf-site-gutter,40px)] max-[1100px]:px-[min(24px,var(--sf-site-gutter,40px))] max-[800px]:px-[min(20px,var(--sf-site-gutter,40px))]",style:{paddingTop:L,paddingBottom:z},children:[!$&&(t||o||u)&&r(ut,{num:t,title:o?ft(o):void 0,desc:u||void 0,spaceBelow:"mb-12"}),r(kt,{offices:e,apiKey:l,mapId:M,mapTheme:g,activeIndex:b,onSelect:h}),e.length>0&&r("div",{className:"mt-12 flex flex-col gap-8",children:N.map((n,C)=>w("div",{children:[n.key&&r(dt,{groupKey:n.key,count:n.items.length}),r("div",{className:["grid gap-[2px] bg-bg",pt(n.items.length),n.items.length>2?"max-[1100px]:grid-cols-2 max-[700px]:grid-cols-1":n.items.length===2?"max-[700px]:grid-cols-1":""].join(" "),children:n.items.map(({item:i,index:k})=>{const A=b===k;return w("button",{type:"button",onClick:()=>h(k),"aria-pressed":A,className:["flex flex-col gap-1.5 bg-bg px-6 py-5 text-left transition-colors outline outline-1 -outline-offset-1",A?"outline-accent":"outline-transparent hover:outline-line"].join(" "),children:[w("span",{className:"flex items-center gap-2.5 font-mono text-[length:var(--size-label,11px)] uppercase tracking-[0.28em]",children:[i.hq&&r("span",{className:"border border-accent bg-accent px-1.5 py-0.5 text-[length:var(--size-body,10px)] text-bg",children:"HQ"}),i.role&&r("span",{className:"text-accent",children:i.role})]}),i.city&&r("span",{className:"font-display text-[length:var(--size-body,18px)] font-medium leading-[1.2] tracking-[-0.01em] text-fg",children:r(gt,{value:i.city,children:i.city})}),i.country&&r("span",{className:"font-mono text-[length:var(--size-label,11px)] uppercase tracking-[0.2em] text-fg-dim",children:i.country}),i.address&&r("span",{className:"font-mono text-[length:var(--size-label,11px)] uppercase tracking-[0.2em] text-fg-dim",children:mt(i.address)})]},k)})})]},n.key?`g:${n.key}`:`ungrouped:${C}`))}),e.length===0&&m&&r("p",{className:"mt-8 font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.24em] text-fg-dim",children:xt("Add an office in the inspector")})]})})}function kt({offices:t,apiKey:o,mapId:u,mapTheme:e,activeIndex:l,onSelect:M}){const g=st(o,it()),a=at(u,lt()),x=!!g&&t.some(c=>J(c.lat,c.lng)),m=e!=="auto",$=H(e),L=f(null),z=f(null),b=f(null),h=f(null),v=f(new Map),N=f(null),n=f(null),C=f(null),i=f(M);i.current=M;const k=f(t);k.current=t;const A=f(l);A.current=l;const[_,D]=P(!1),[j,K]=P(Q.dark),[tt,W]=P(Q.dark.background);return S(()=>{K(H(e)),W(F(e))},[e]),S(()=>{if(!x)return;const c=L.current;if(!c)return;let d=!1,p=null;const I=async()=>{try{const s=await ot(g);if(d||!z.current)return;const y=new s.Map(z.current,{center:{lat:25,lng:0},zoom:2,minZoom:1,maxZoom:5,disableDefaultUI:!0,gestureHandling:"cooperative",keyboardShortcuts:!1,clickableIcons:!1,backgroundColor:F(e),...a?{mapId:a}:{styles:X(e)}});b.current=y,h.current=s;const R=Y(y,s,e,k.current,v.current,B=>i.current(B));n.current=R,R.isEmpty()||y.fitBounds(R,q);const O=new s.InfoWindow({disableAutoPan:!0});O.addListener("closeclick",()=>i.current(null)),N.current=O,y.addListener("click",()=>i.current(null)),y.addListener("tilesloaded",()=>{d||D(!0)}),e==="auto"&&!a&&(p=rt(()=>{d||!b.current||(b.current.setOptions({styles:X(e),backgroundColor:F(e)}),v.current.forEach((B,et)=>{const Z=k.current[et];Z&&B.setIcon(V(Z,s,e))}),K(H(e)),W(F(e)))}))}catch(s){console.error("[FootprintMap] Maps SDK failed:",s)}},E=new IntersectionObserver(s=>{for(const y of s)if(y.isIntersecting){E.disconnect(),I();break}},{rootMargin:"200px"});return E.observe(c),()=>{d=!0,E.disconnect(),p?.(),N.current?.close(),N.current=null,b.current=null,h.current=null,v.current.forEach(s=>s.setMap(null)),v.current.clear(),n.current=null,D(!1)}},[x,g,a,e]),S(()=>{const c=b.current,d=h.current;if(!c||!d)return;const p=Y(c,d,e,t,v.current,I=>i.current(I));n.current=p,A.current===null&&!p.isEmpty()&&c.fitBounds(p,q)},[t,e]),S(()=>{const c=b.current,d=N.current;if(!c||!d)return;if(l===null){d.close(),C.current!==null&&n.current&&!n.current.isEmpty()&&c.fitBounds(n.current,q),C.current=null;return}const p=t[l],I=v.current.get(l);if(!p||!I){d.close(),C.current=l;return}c.panTo({lat:p.lat,lng:p.lng});const E=c.getZoom();E!=null&&E<4&&c.setZoom(4);const s=j.info,y=p.hq?`<span style="background:${s.accent};color:${s.bg};padding:1px 6px;font-size:10px;letter-spacing:0.18em;">HQ</span>`:"",R=p.role?`<span style="color:${s.accent};">${G(p.role)}</span>`:"";d.setContent(`
20
20
  <div style="padding:10px 14px 12px;min-width:180px;color:${s.fg};">
21
21
  <div style="display:flex;align-items:center;gap:8px;font-family:var(--font-role-label),var(--font-mono-fallback);font-size:11px;letter-spacing:0.28em;text-transform:uppercase;">
22
22
  ${y}${R}
@@ -24,4 +24,4 @@
24
24
  <div style="font-family:var(--font-display),Georgia,serif;font-size:20px;font-weight:500;line-height:1.2;margin-top:6px;color:${s.fg};">${G(p.city)}</div>
25
25
  <div style="font-family:var(--font-role-label),var(--font-mono-fallback);font-size:11px;letter-spacing:0.2em;text-transform:uppercase;color:${s.fgDim};margin-top:4px;">${G(p.country)}</div>
26
26
  </div>
27
- `),d.open({map:c,anchor:I}),C.current=l},[l,_,j,t]),x?w("div",{ref:L,className:"sf-card footprint-map relative aspect-[16/9] w-full overflow-hidden border border-line max-[720px]:aspect-[4/5]",style:{backgroundColor:tt},children:[r("style",{dangerouslySetInnerHTML:{__html:yt(j.info)}}),r(T,{pal:m?$:void 0}),r("div",{ref:z,className:"gmap-canvas absolute inset-0 transition-opacity duration-500",style:{opacity:_?1:0}}),r("div",{className:["pointer-events-none absolute inset-0 flex items-center justify-center font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.24em] text-fg-dim transition-opacity duration-500",_?"opacity-0":"opacity-100"].join(" "),children:"Loading map…"})]}):w("div",{className:["relative aspect-[16/9] w-full overflow-hidden border border-line max-[720px]:aspect-[4/5]",m?"":"bg-bg"].join(" "),style:m?{backgroundColor:$.background}:void 0,children:[r(T,{pal:m?$:void 0}),r("div",{className:["absolute inset-0 flex items-center justify-center px-6 text-center font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.24em]",m?"":"text-fg-dim"].join(" "),style:m?{color:$.info.fgDim}:void 0,children:g?"Add office coordinates to plot the map":"Add a Google Maps API key to show the map"})]})}export{vt as default};
27
+ `),d.open({map:c,anchor:I}),C.current=l},[l,_,j,t]),x?w("div",{ref:L,className:"sf-card footprint-map relative aspect-[16/9] w-full overflow-hidden border border-line max-[720px]:aspect-[4/5]",style:{backgroundColor:tt},children:[r("style",{dangerouslySetInnerHTML:{__html:vt(j.info)}}),r(T,{pal:m?$:void 0}),r("div",{ref:z,className:"gmap-canvas absolute inset-0 transition-opacity duration-500",style:{opacity:_?1:0}}),r("div",{className:["pointer-events-none absolute inset-0 flex items-center justify-center font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.24em] text-fg-dim transition-opacity duration-500",_?"opacity-0":"opacity-100"].join(" "),children:"Loading map…"})]}):w("div",{className:["relative aspect-[16/9] w-full overflow-hidden border border-line max-[720px]:aspect-[4/5]",m?"":"bg-bg"].join(" "),style:m?{backgroundColor:$.background}:void 0,children:[r(T,{pal:m?$:void 0}),r("div",{className:["absolute inset-0 flex items-center justify-center px-6 text-center font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.24em]",m?"":"text-fg-dim"].join(" "),style:m?{color:$.info.fgDim}:void 0,children:g?"Add office coordinates to plot the map":"Add a Google Maps API key to show the map"})]})}export{ht as default};
@@ -1 +1 @@
1
- "use client";import{jsx as t}from"react/jsx-runtime";import{SettingsDefaultReset as f,SettingsField as h,SettingsSegmented as g}from"efficient-easel/panel";import{WIDTH_CHOICE_LABEL as d}from"../lib/content/section-width.js";import{ImageIcon as c}from"efficient-easel/icons";const l="Hero image",o=[{followsWidth:!1,label:d.full},{followsWidth:!0,label:d.page}],u="Whether the big picture at the top of a page runs edge to edge, or sits inside the page width. The words follow the page width either way.";function p({value:i,onChange:a,showHint:r=!0}){const n=i===null,s=i??!1;return t(h,{label:l,icon:t(c,{}),value:o.find(e=>e.followsWidth===s).label,changed:!n,action:t(f,{label:l,changed:!n,detail:o.find(e=>!e.followsWidth).label,onReset:()=>a(null)}),hint:r?u:void 0,children:t(g,{options:o.map(e=>({id:e.followsWidth,label:e.label})),value:s,ariaLabel:l,onChange:a})})}export{l as HERO_IMAGE_LABEL,p as default};
1
+ "use client";import{jsx as l}from"react/jsx-runtime";import{SettingsDefaultReset as h,SettingsField as g,SettingsSegmented as p}from"efficient-easel/panel";import{say as e,editorEaselWords as c}from"../lib/editor-words.js";import{WIDTH_CHOICE_LABEL as d}from"../lib/content/section-width.js";import{ImageIcon as m}from"efficient-easel/icons";const o="Hero image",i=[{followsWidth:!1,label:d.full},{followsWidth:!0,label:d.page}],u="Whether the big picture at the top of a page runs edge to edge, or sits inside the page width. The words follow the page width either way.";function w({value:a,onChange:n,showHint:f=!0}){const s=a===null,r=a??!1;return l(g,{label:e(o),icon:l(m,{}),value:e(i.find(t=>t.followsWidth===r).label),changed:!s,action:l(h,{words:c()?.panel,label:e(o),changed:!s,detail:e(i.find(t=>!t.followsWidth).label),onReset:()=>n(null)}),hint:f?e(u):void 0,children:l(p,{options:i.map(t=>({id:t.followsWidth,label:e(t.label)})),value:r,ariaLabel:e(o),onChange:n})})}export{o as HERO_IMAGE_LABEL,w as default};