@sonordev/site-kit 8.11.0 → 8.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/agent-manifest.json +1 -1
  3. package/dist/{AddressAutocomplete-SQDJJOEF.js → AddressAutocomplete-6DQ3IP5C.js} +1 -1
  4. package/dist/{AnalyticsProvider-JDVLH5OV.js → AnalyticsProvider-RC2RG4YW.js} +4 -4
  5. package/dist/{ArticleViewTracker-L6TFJ35X.js → ArticleViewTracker-FNCRH3SB.js} +1 -1
  6. package/dist/{BlocksPopup-5Q47LRN2.js → BlocksPopup-AJCDT5WM.js} +3 -3
  7. package/dist/{ChatWidget-4ILVGTK6.js → ChatWidget-CM5OGMJQ.js} +5 -5
  8. package/dist/{FileField-TLS53TPW.js → FileField-QWS5ASLB.js} +1 -1
  9. package/dist/{FormSpotlight-JPLIRN3H.js → FormSpotlight-7Y6B2YZM.js} +1 -1
  10. package/dist/{FormStage-UBP57NY7.js → FormStage-T3N27SID.js} +1 -1
  11. package/dist/{ManagedForm-PFGM5X5H.js → ManagedForm-WUULZELM.js} +6 -6
  12. package/dist/{ManagedNewsletterForm-GJU5KCGA.js → ManagedNewsletterForm-GGA3JOOI.js} +8 -8
  13. package/dist/{SignalCore-DGKCVQ74.js → SignalCore-ZMKDW44U.js} +3 -3
  14. package/dist/{SiteChat-EP25SEMP.js → SiteChat-PM4RIZR4.js} +1 -1
  15. package/dist/{SiteDesignReporter-NE6BQSVZ.js → SiteDesignReporter-SALYTWV2.js} +4 -4
  16. package/dist/{SitePopups-XJLZPV4L.js → SitePopups-VDSDWZHD.js} +4 -4
  17. package/dist/{SitemapSync-35EAR2YA.js → SitemapSync-6UDJHS2F.js} +2 -2
  18. package/dist/_client/booking-widget.js +3 -3
  19. package/dist/_client/reviewed-signal-experiment.js +1 -1
  20. package/dist/affiliates/index.js +1 -1
  21. package/dist/analytics/index.js +4 -4
  22. package/dist/api-4AAR6BZO.js +5 -0
  23. package/dist/articles/index.js +1 -1
  24. package/dist/articles/server-ui.js +3 -3
  25. package/dist/articles/server.js +2 -2
  26. package/dist/chat/index.js +6 -6
  27. package/dist/{chunk-N26GRGIP.js → chunk-27264MSX.js} +2 -2
  28. package/dist/{chunk-CNQMSQYQ.js → chunk-2MJYGVKB.js} +1 -1
  29. package/dist/{chunk-DHGOFK5P.js → chunk-2QAG7ALW.js} +1 -1
  30. package/dist/{chunk-XHEUPCCU.js → chunk-5UAX6PHO.js} +1 -1
  31. package/dist/{chunk-UK4ANQUS.js → chunk-675DTMSR.js} +1 -1
  32. package/dist/{chunk-3LGE3LRC.js → chunk-7PW7N7P4.js} +1 -1
  33. package/dist/{chunk-2URLL3Z2.js → chunk-C55LIDVW.js} +1 -1
  34. package/dist/{chunk-IDLJEFVB.js → chunk-CNCAQEUJ.js} +1 -1
  35. package/dist/{chunk-UEF4AC76.js → chunk-DFCLELM2.js} +2 -2
  36. package/dist/{chunk-BHJTT63Y.js → chunk-DICLVB5P.js} +1 -1
  37. package/dist/{chunk-CIZ7Z3NU.js → chunk-DUIGCLQM.js} +1 -1
  38. package/dist/{chunk-XVB5V7D3.js → chunk-GHW37LVB.js} +2 -2
  39. package/dist/{chunk-GZYIJ3JI.js → chunk-H4KPWR7P.js} +3 -3
  40. package/dist/{chunk-7OTHRX45.js → chunk-M2ZGSI6K.js} +1 -1
  41. package/dist/{chunk-I5S2XA7H.js → chunk-M34VOQ5J.js} +8 -8
  42. package/dist/{chunk-KWHR7PNA.js → chunk-M3VJAE24.js} +1 -1
  43. package/dist/{chunk-52MFDOBB.js → chunk-NX4FETUK.js} +1 -1
  44. package/dist/{chunk-JXH56PY3.js → chunk-O3CAXGY5.js} +2 -2
  45. package/dist/{chunk-5LCY7QKK.js → chunk-T2Z7KEIH.js} +1 -1
  46. package/dist/{chunk-OPJZSLGK.js → chunk-TPB5PTWW.js} +2 -2
  47. package/dist/{chunk-LY3Q6RU5.js → chunk-TXB4ULAM.js} +1 -1
  48. package/dist/{chunk-FW63D6W3.js → chunk-UFHEND2H.js} +1 -1
  49. package/dist/{chunk-NAO63YOG.js → chunk-UTVFF7VT.js} +1 -1
  50. package/dist/{chunk-DHMBDPGA.js → chunk-VF2X5T4H.js} +2 -2
  51. package/dist/{chunk-R53G2IKP.js → chunk-VZSIIKLR.js} +1 -1
  52. package/dist/{chunk-JYRBGL33.js → chunk-WX4XX3BZ.js} +1 -1
  53. package/dist/{chunk-CCZHAC4K.js → chunk-Y4MQTSYY.js} +3 -3
  54. package/dist/{chunk-VA5NLFSL.js → chunk-YOPBFEDG.js} +1 -1
  55. package/dist/{chunk-ZWTDI2JU.js → chunk-YPTUKSF7.js} +1 -1
  56. package/dist/{chunk-3VKJJNQU.js → chunk-YYV3YHUX.js} +1 -1
  57. package/dist/{chunk-HWYJ3O7D.js → chunk-Z42KXUTK.js} +1 -1
  58. package/dist/{controls-YQ3MGZJG.js → controls-GLVRT5EM.js} +2 -2
  59. package/dist/fleet/index.js +3 -3
  60. package/dist/forms/index.js +8 -8
  61. package/dist/forms/server.js +2 -2
  62. package/dist/images/index.js +2 -2
  63. package/dist/index.js +1 -1
  64. package/dist/landing/server.js +5 -5
  65. package/dist/layout/client.js +6 -6
  66. package/dist/layout/index.js +7 -7
  67. package/dist/llms/index.js +4 -4
  68. package/dist/maps/index.js +3 -3
  69. package/dist/mcp/sonor.js +5 -5
  70. package/dist/resonance/ConsentPreferences.d.ts +7 -3
  71. package/dist/resonance/consent-preferences-styles.d.ts +2 -0
  72. package/dist/resonance/index.js +144 -50
  73. package/dist/revalidate/index.js +2 -2
  74. package/dist/runtime/index.js +1 -1
  75. package/dist/{runtime-core-WSKES5UP.js → runtime-core-4WL4V4QE.js} +1 -1
  76. package/dist/seo/client.js +2 -2
  77. package/dist/seo/index.js +2 -2
  78. package/dist/seo/llms.js +4 -4
  79. package/dist/seo/register-sitemap-cli.js +1 -1
  80. package/dist/seo/sitemap.js +2 -2
  81. package/dist/server/index.js +2 -2
  82. package/dist/shared/version.d.ts +1 -1
  83. package/dist/signal/index.js +2 -2
  84. package/dist/sitemap/index.js +2 -2
  85. package/dist/{sonor-fetch-runtime-JDVDCTKI.js → sonor-fetch-runtime-QO4DR7GR.js} +2 -2
  86. package/dist/sync/index.js +3 -3
  87. package/dist/website/images.js +2 -2
  88. package/dist/website/index.js +4 -4
  89. package/dist/website/landing/server.js +5 -5
  90. package/dist/website/popups.js +4 -4
  91. package/dist/{writeLLMsTxt-SYLPYWUX.js → writeLLMsTxt-EKWOSHXU.js} +1 -1
  92. package/package.json +1 -1
  93. package/src/resonance/README.md +33 -0
  94. package/dist/api-AHFVE5HN.js +0 -5
  95. /package/dist/{chunk-ES52W5EE.js → chunk-2YSX4ZUC.js} +0 -0
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { getConsentAdapter, registerResonanceConsentAdapter } from '../chunk-WYNDBJG7.js';
3
3
  export { getResonanceMeasurementStatus, recordResonanceExposure, registerResonanceConsentAdapter } from '../chunk-WYNDBJG7.js';
4
- export { ResonanceRuntime } from '../chunk-VA5NLFSL.js';
4
+ export { ResonanceRuntime } from '../chunk-YOPBFEDG.js';
5
5
  import '../chunk-MDBXGN6W.js';
6
6
  import '../chunk-YC7ELZS3.js';
7
7
  import '../chunk-KMNHNCVG.js';
@@ -9,7 +9,7 @@ import '../chunk-MOACHHOJ.js';
9
9
  import { MEASUREMENT_PURPOSES, unknownChoices } from '../chunk-FRY6PBT6.js';
10
10
  import '../chunk-PKBMQBKP.js';
11
11
  import { useState, useSyncExternalStore, useRef, useId, useEffect } from 'react';
12
- import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
12
+ import { jsxs, jsx } from 'react/jsx-runtime';
13
13
 
14
14
  // src/resonance/first-party-consent.ts
15
15
  var storageKey = "sonor:privacy-preference:v1";
@@ -53,7 +53,8 @@ function createFirstPartyConsentAdapter(options = {}) {
53
53
  for (const listener of viewListeners) listener();
54
54
  }
55
55
  clearTimeout(timer);
56
- const expiry = Math.min(Date.parse(context?.configExpiresAt ?? ""), Date.parse(selected?.expiresAt ?? ""));
56
+ const deadlines = [context?.configExpiresAt, selected?.expiresAt].map((value) => Date.parse(value ?? "")).filter(Number.isFinite);
57
+ const expiry = Math.min(...deadlines);
57
58
  if (Number.isFinite(expiry) && expiry > now()) {
58
59
  timer = setTimeout(() => {
59
60
  publish();
@@ -147,18 +148,85 @@ function createFirstPartyConsentAdapter(options = {}) {
147
148
  }
148
149
  };
149
150
  }
150
- var buttonStyle = { border: "1px solid currentColor", borderRadius: 6, padding: "10px 14px", cursor: "pointer", minHeight: 44 };
151
- function ResonanceConsentPreferences({ privacyUrl = "/privacy", maxAgeDays = 180 }) {
151
+
152
+ // src/resonance/consent-preferences-styles.ts
153
+ var consentPreferencesStyles = `
154
+ .sk-consent { display: contents; }
155
+ .sk-consent button, .sk-consent input { font: inherit; }
156
+ .sk-consent button { min-height: 44px; cursor: pointer; }
157
+ .sk-consent button:disabled, .sk-consent fieldset:disabled { opacity: .55; }
158
+ .sk-consent button:disabled { cursor: default; }
159
+ .sk-consent :is(button, input, a):focus-visible {
160
+ outline: 2px solid var(--sk-consent-focus, #225588); outline-offset: 3px;
161
+ }
162
+ .sk-consent__launcher--floating {
163
+ position: fixed; bottom: max(12px, env(safe-area-inset-bottom)); left: 12px; z-index: 1000;
164
+ border: 1px solid var(--sk-consent-border, currentColor); border-radius: var(--sk-consent-radius, 6px);
165
+ padding: 10px 14px; background: var(--sk-consent-surface, #fff); color: var(--sk-consent-foreground, #222);
166
+ }
167
+ .sk-consent__launcher--inline {
168
+ display: inline-flex; align-items: center; border: 0; padding: 0; background: transparent;
169
+ color: inherit; text-decoration: underline; text-underline-offset: .2em;
170
+ }
171
+ .sk-consent__prompt, .sk-consent__dialog {
172
+ box-sizing: border-box; padding: 24px; border: 1px solid var(--sk-consent-border, #777);
173
+ border-radius: var(--sk-consent-radius, 10px); background: var(--sk-consent-surface, #fff);
174
+ color: var(--sk-consent-foreground, #222); font: var(--sk-consent-font, 16px/1.5 system-ui, sans-serif);
175
+ }
176
+ .sk-consent__dialog {
177
+ max-width: 460px; width: calc(100% - 32px); max-height: calc(100dvh - 32px);
178
+ overflow-y: auto; margin: auto;
179
+ }
180
+ .sk-consent__prompt {
181
+ position: fixed; z-index: 1000; width: min(420px, calc(100% - 32px));
182
+ left: var(--sk-consent-prompt-inset, 16px);
183
+ bottom: var(--sk-consent-prompt-bottom, max(16px, env(safe-area-inset-bottom)));
184
+ max-height: calc(100dvh - var(--sk-consent-prompt-bottom, 16px) - 16px);
185
+ overflow-y: auto;
186
+ }
187
+ .sk-consent__heading { color: var(--sk-consent-accent, inherit); font-size: 22px; line-height: 1.25; margin: 0 0 12px; }
188
+ .sk-consent__prompt .sk-consent__heading { font-size: 20px; padding-right: 32px; }
189
+ .sk-consent__prompt p, .sk-consent__dialog p { margin: 12px 0; }
190
+ .sk-consent__description, .sk-consent small { color: var(--sk-consent-muted, inherit); }
191
+ .sk-consent__fields { border: 0; margin: 16px 0; padding: 0; }
192
+ .sk-consent__fields legend { font-weight: 600; }
193
+ .sk-consent__setting { display: flex; align-items: start; gap: 10px; padding: 12px 0; min-height: 44px; box-sizing: border-box; }
194
+ .sk-consent__setting input { flex: 0 0 auto; margin-top: 5px; }
195
+ .sk-consent__actions { display: flex; flex-wrap: wrap; gap: 10px; }
196
+ .sk-consent__actions button, .sk-consent__dismiss {
197
+ border: 1px solid var(--sk-consent-border, currentColor); border-radius: var(--sk-consent-radius, 6px);
198
+ padding: 10px 14px; color: inherit; background: transparent;
199
+ }
200
+ .sk-consent__prompt .sk-consent__actions { display: grid; grid-template-columns: 1fr 1fr; }
201
+ .sk-consent__customize { grid-column: 1 / -1; }
202
+ .sk-consent__dismiss { position: absolute; right: 8px; top: 8px; width: 44px; padding: 8px; }
203
+ .sk-consent__policy { display: inline-block; color: inherit; margin-top: 16px; text-underline-offset: .2em; }
204
+ .sk-consent__status:empty { display: none; }
205
+ .sk-consent__status { font: inherit; }
206
+ `;
207
+ function ResonanceConsentPreferences({
208
+ privacyUrl = "/privacy",
209
+ maxAgeDays = 180,
210
+ launcherPlacement = "floating",
211
+ launcherLabel = "Privacy choices",
212
+ promptMode = "dialog",
213
+ className = ""
214
+ }) {
152
215
  const [owner] = useState(() => createFirstPartyConsentAdapter({ maxAgeDays }));
153
216
  const status = useSyncExternalStore(owner.subscribe, owner.getSnapshot, owner.getServerSnapshot);
154
217
  const [active, setActive] = useState(false);
155
218
  const [open, setOpen] = useState(false);
219
+ const [dismissed, setDismissed] = useState(false);
156
220
  const [analytics, setAnalytics] = useState(false);
157
221
  const [advertising, setAdvertising] = useState(false);
222
+ const [notice, setNotice] = useState("");
158
223
  const dialog = useRef(null);
159
224
  const launcher = useRef(null);
225
+ const prompt = useRef(null);
226
+ const invoker = useRef(null);
160
227
  const titleId = useId();
161
228
  const descriptionId = useId();
229
+ const promptTitleId = useId();
162
230
  useEffect(() => {
163
231
  if (getConsentAdapter()) return;
164
232
  const unregister = registerResonanceConsentAdapter(owner.adapter);
@@ -170,10 +238,14 @@ function ResonanceConsentPreferences({ privacyUrl = "/privacy", maxAgeDays = 180
170
238
  };
171
239
  }, [owner]);
172
240
  useEffect(() => {
173
- setAnalytics(status.analytics);
174
- setAdvertising(status.advertising);
175
- if (status.contextReady && !status.hasPreference) setOpen(true);
176
- }, [status]);
241
+ if (status.hasPreference) setDismissed(false);
242
+ if (promptMode === "dialog" && status.contextReady && !status.hasPreference && !dialog.current?.open) {
243
+ invoker.current = launcher.current;
244
+ setAnalytics(status.analytics);
245
+ setAdvertising(status.advertising);
246
+ setOpen(true);
247
+ }
248
+ }, [promptMode, status.contextReady, status.hasPreference, status.analytics, status.advertising]);
177
249
  useEffect(() => {
178
250
  const element = dialog.current;
179
251
  if (!element || !active) return;
@@ -181,42 +253,75 @@ function ResonanceConsentPreferences({ privacyUrl = "/privacy", maxAgeDays = 180
181
253
  else if (!open && element.open) element.close();
182
254
  }, [active, open]);
183
255
  function restoreFocus() {
184
- if (launcher.current?.isConnected) launcher.current.focus({ preventScroll: true });
256
+ const target = invoker.current?.isConnected ? invoker.current : launcher.current;
257
+ if (target?.isConnected) target.focus({ preventScroll: true });
185
258
  }
186
259
  function close() {
187
260
  dialog.current?.close();
188
261
  setOpen(false);
189
262
  restoreFocus();
190
263
  }
264
+ function customize(source) {
265
+ invoker.current = source;
266
+ const current = owner.getSnapshot();
267
+ setAnalytics(current.analytics);
268
+ setAdvertising(current.advertising);
269
+ setNotice("");
270
+ setOpen(true);
271
+ }
191
272
  function save(nextAnalytics, nextAdvertising) {
192
- if (owner.save(nextAnalytics, nextAdvertising)) close();
273
+ if (!owner.save(nextAnalytics, nextAdvertising)) {
274
+ setNotice("Settings changed. Review your choices and try again when settings are available.");
275
+ return;
276
+ }
277
+ if (invoker.current && prompt.current?.contains(invoker.current)) invoker.current = launcher.current;
278
+ setNotice("Your choices are applied on this page.");
279
+ close();
280
+ }
281
+ function chooseFromPrompt(source, accepted) {
282
+ invoker.current = source;
283
+ save(accepted, accepted);
284
+ }
285
+ function dismissPrompt() {
286
+ setDismissed(true);
287
+ invoker.current = launcher.current;
288
+ restoreFocus();
193
289
  }
194
290
  if (!active) return null;
195
- return /* @__PURE__ */ jsxs(Fragment, { children: [
291
+ const showPrompt = promptMode === "panel" && status.contextReady && !status.hasPreference && !dismissed;
292
+ return /* @__PURE__ */ jsxs("div", { className: `sk-consent ${className}`, children: [
293
+ /* @__PURE__ */ jsx("style", { children: consentPreferencesStyles }),
196
294
  /* @__PURE__ */ jsx(
197
295
  "button",
198
296
  {
199
297
  ref: launcher,
200
298
  type: "button",
201
- onClick: () => setOpen(true),
299
+ onClick: (event) => customize(event.currentTarget),
202
300
  "aria-haspopup": "dialog",
203
- style: {
204
- ...buttonStyle,
205
- position: "fixed",
206
- bottom: "max(12px, env(safe-area-inset-bottom))",
207
- left: 12,
208
- zIndex: 1e3,
209
- background: "#fff",
210
- color: "#222",
211
- font: "inherit"
212
- },
213
- children: "Privacy choices"
301
+ className: `sk-consent__launcher sk-consent__launcher--${launcherPlacement}`,
302
+ children: launcherLabel
214
303
  }
215
304
  ),
305
+ showPrompt && /* @__PURE__ */ jsxs("section", { ref: prompt, className: "sk-consent__prompt", "aria-labelledby": promptTitleId, children: [
306
+ /* @__PURE__ */ jsx("h2", { id: promptTitleId, className: "sk-consent__heading", children: "Optional privacy settings" }),
307
+ /* @__PURE__ */ jsxs("p", { className: "sk-consent__description", children: [
308
+ "Choose whether to allow visit measurement and advertising. You can change your choices in ",
309
+ launcherLabel,
310
+ "."
311
+ ] }),
312
+ /* @__PURE__ */ jsx("button", { type: "button", "aria-label": "Dismiss privacy prompt", className: "sk-consent__dismiss", onClick: dismissPrompt, children: "\xD7" }),
313
+ /* @__PURE__ */ jsxs("div", { className: "sk-consent__actions", children: [
314
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: (event) => chooseFromPrompt(event.currentTarget, true), children: "Accept optional" }),
315
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: (event) => chooseFromPrompt(event.currentTarget, false), children: "Reject optional" }),
316
+ /* @__PURE__ */ jsx("button", { type: "button", className: "sk-consent__customize", onClick: (event) => customize(event.currentTarget), children: "Customize" })
317
+ ] }),
318
+ /* @__PURE__ */ jsx("a", { className: "sk-consent__policy", href: privacyUrl, children: "Read our privacy policy" })
319
+ ] }),
216
320
  /* @__PURE__ */ jsxs(
217
321
  "dialog",
218
322
  {
219
323
  ref: dialog,
324
+ className: "sk-consent__dialog",
220
325
  "aria-labelledby": titleId,
221
326
  "aria-describedby": descriptionId,
222
327
  onCancel: (event) => {
@@ -228,26 +333,13 @@ function ResonanceConsentPreferences({ privacyUrl = "/privacy", maxAgeDays = 180
228
333
  setOpen(false);
229
334
  restoreFocus();
230
335
  },
231
- style: {
232
- maxWidth: 460,
233
- width: "calc(100% - 32px)",
234
- maxHeight: "calc(100dvh - 32px)",
235
- overflowY: "auto",
236
- margin: "auto",
237
- padding: 24,
238
- border: "1px solid #777",
239
- borderRadius: 10,
240
- background: "#fff",
241
- color: "#222",
242
- font: "16px/1.5 system-ui, sans-serif"
243
- },
244
336
  children: [
245
- /* @__PURE__ */ jsx("h2", { id: titleId, style: { fontSize: 22, margin: "0 0 12px" }, children: "Your privacy choices" }),
246
- /* @__PURE__ */ jsx("p", { id: descriptionId, children: "Choose whether this site may measure visits and use advertising services. You can change your choices here at any time." }),
247
- !status.contextReady && /* @__PURE__ */ jsx("p", { role: "status", children: "Optional measurement settings are unavailable right now. Advertising stays off while settings load." }),
248
- /* @__PURE__ */ jsxs("fieldset", { disabled: !status.contextReady, style: { border: 0, margin: "16px 0", padding: 0 }, children: [
249
- /* @__PURE__ */ jsx("legend", { style: { fontWeight: 600 }, children: "Optional purposes" }),
250
- /* @__PURE__ */ jsxs("label", { style: { display: "flex", alignItems: "start", gap: 10, padding: "12px 0", minHeight: 44 }, children: [
337
+ /* @__PURE__ */ jsx("h2", { id: titleId, className: "sk-consent__heading", children: "Your privacy choices" }),
338
+ /* @__PURE__ */ jsx("p", { id: descriptionId, className: "sk-consent__description", children: "Choose whether this site may measure visits and use advertising services. You can change your choices here at any time." }),
339
+ !status.contextReady && /* @__PURE__ */ jsx("p", { className: "sk-consent__status", role: "status", children: "Optional measurement settings are unavailable right now. Advertising stays off while settings load." }),
340
+ /* @__PURE__ */ jsxs("fieldset", { disabled: !status.contextReady, className: "sk-consent__fields", children: [
341
+ /* @__PURE__ */ jsx("legend", { children: "Optional purposes" }),
342
+ /* @__PURE__ */ jsxs("label", { className: "sk-consent__setting", children: [
251
343
  /* @__PURE__ */ jsx("input", { type: "checkbox", checked: analytics, onChange: (event) => setAnalytics(event.target.checked) }),
252
344
  /* @__PURE__ */ jsxs("span", { children: [
253
345
  "Visit measurement",
@@ -255,7 +347,7 @@ function ResonanceConsentPreferences({ privacyUrl = "/privacy", maxAgeDays = 180
255
347
  /* @__PURE__ */ jsx("small", { children: "Help this site understand page views and interactions." })
256
348
  ] })
257
349
  ] }),
258
- /* @__PURE__ */ jsxs("label", { style: { display: "flex", alignItems: "start", gap: 10, padding: "12px 0", minHeight: 44 }, children: [
350
+ /* @__PURE__ */ jsxs("label", { className: "sk-consent__setting", children: [
259
351
  /* @__PURE__ */ jsx("input", { type: "checkbox", checked: advertising, onChange: (event) => setAdvertising(event.target.checked) }),
260
352
  /* @__PURE__ */ jsxs("span", { children: [
261
353
  "Advertising",
@@ -264,15 +356,17 @@ function ResonanceConsentPreferences({ privacyUrl = "/privacy", maxAgeDays = 180
264
356
  ] })
265
357
  ] })
266
358
  ] }),
267
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexWrap: "wrap", gap: 10 }, children: [
268
- /* @__PURE__ */ jsx("button", { type: "button", style: buttonStyle, disabled: !status.contextReady, onClick: () => save(analytics, advertising), children: "Save choices" }),
269
- /* @__PURE__ */ jsx("button", { type: "button", style: buttonStyle, disabled: !status.contextReady, onClick: () => save(false, false), children: "Decline optional" }),
270
- /* @__PURE__ */ jsx("button", { type: "button", style: buttonStyle, onClick: close, children: "Close" })
359
+ /* @__PURE__ */ jsxs("div", { className: "sk-consent__actions", children: [
360
+ /* @__PURE__ */ jsx("button", { type: "button", disabled: !status.contextReady, onClick: () => save(analytics, advertising), children: promptMode === "panel" ? "Save preferences" : "Save choices" }),
361
+ /* @__PURE__ */ jsx("button", { type: "button", disabled: !status.contextReady, onClick: () => save(false, false), children: promptMode === "panel" ? "Reject optional" : "Decline optional" }),
362
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: close, children: "Close" })
271
363
  ] }),
272
- /* @__PURE__ */ jsx("p", { style: { marginBottom: 0 }, children: /* @__PURE__ */ jsx("a", { href: privacyUrl, children: "Read our privacy policy" }) })
364
+ /* @__PURE__ */ jsx("a", { className: "sk-consent__policy", href: privacyUrl, children: "Read our privacy policy" }),
365
+ notice && /* @__PURE__ */ jsx("p", { className: "sk-consent__status", role: "status", children: notice })
273
366
  ]
274
367
  }
275
- )
368
+ ),
369
+ !open && notice && /* @__PURE__ */ jsx("span", { className: "sk-consent__status", role: "status", children: notice })
276
370
  ] });
277
371
  }
278
372
 
@@ -1,7 +1,7 @@
1
- import { createSeoRevalidationHandler } from '../chunk-5LCY7QKK.js';
1
+ import { createSeoRevalidationHandler } from '../chunk-T2Z7KEIH.js';
2
2
  import '../chunk-XTRZGXB2.js';
3
3
  export { SITE_CACHE_TAGS, SITE_REVALIDATE_PATH, isSiteRevalidatePong } from '../chunk-HY7ZEU2C.js';
4
- import '../chunk-CNQMSQYQ.js';
4
+ import '../chunk-2MJYGVKB.js';
5
5
  import '../chunk-PKBMQBKP.js';
6
6
 
7
7
  // src/revalidate/index.ts
@@ -1,7 +1,7 @@
1
1
  import '../chunk-B7T2SFUF.js';
2
2
  export { readAnalyticsIdentity } from '../chunk-PVY7X5UR.js';
3
3
  import '../chunk-5IVD6G77.js';
4
- export { sonorFetch } from '../chunk-7OTHRX45.js';
4
+ export { sonorFetch } from '../chunk-M2ZGSI6K.js';
5
5
  export { DEFAULT_SONOR_API_URL, getClientApiConfig, readPublishedApiConfig } from '../chunk-W7OPEMGJ.js';
6
6
  import '../chunk-FRY6PBT6.js';
7
7
  import '../chunk-PKBMQBKP.js';
@@ -9,7 +9,7 @@ import { isNormalizedSiteHost } from './chunk-NZOJHERI.js';
9
9
  import { advertisingPurposesPermitted, experimentMeasurementPermitted } from './chunk-KMNHNCVG.js';
10
10
  import { resolveAnalyticsTarget, isFramed, isPrivatePreview, isLocalHostname } from './chunk-7643MCD2.js';
11
11
  import './chunk-64XODP7M.js';
12
- import { sonorFetch } from './chunk-7OTHRX45.js';
12
+ import { sonorFetch } from './chunk-M2ZGSI6K.js';
13
13
  import './chunk-W7OPEMGJ.js';
14
14
  import './chunk-A4CC4ZG3.js';
15
15
  import './chunk-MOACHHOJ.js';
@@ -1,8 +1,8 @@
1
1
  'use client';
2
- export { SitemapSync } from '../chunk-JYRBGL33.js';
2
+ export { SitemapSync } from '../chunk-WX4XX3BZ.js';
3
3
  import '../chunk-YVKRYQRG.js';
4
4
  import '../chunk-7643MCD2.js';
5
5
  import '../chunk-64XODP7M.js';
6
- import '../chunk-7OTHRX45.js';
6
+ import '../chunk-M2ZGSI6K.js';
7
7
  import '../chunk-W7OPEMGJ.js';
8
8
  import '../chunk-PKBMQBKP.js';
package/dist/seo/index.js CHANGED
@@ -2,7 +2,7 @@ export { ManagedNoScripts, ManagedScripts } from '../chunk-MVLMXC44.js';
2
2
  import { pickManagedLlmSchemaForJsonLd } from '../chunk-XAZC2TUD.js';
3
3
  import { withoutSchemaPlaceholders } from '../chunk-VWWITFWM.js';
4
4
  import { safeJsonLd } from '../chunk-4KZ6W3ZH.js';
5
- export { SitemapSync } from '../chunk-JYRBGL33.js';
5
+ export { SitemapSync } from '../chunk-WX4XX3BZ.js';
6
6
  import { getManagedMetadata, mergeAlternates } from '../chunk-3W3MN6Z7.js';
7
7
  export { generateSitemap, getABVariant, getManagedMetadata, getManagedMetadataWithAB, getRedirect, getRobotsDirective, isIndexable } from '../chunk-3W3MN6Z7.js';
8
8
  import { getSchemaMarkups, getSEOPageData, getEntityEnhancedSchema, getFAQData, getInternalLinks, pruneSchemaTypes } from '../chunk-STIVFCWM.js';
@@ -18,7 +18,7 @@ import '../chunk-DIKNRN34.js';
18
18
  import '../chunk-FESKKJAS.js';
19
19
  import '../chunk-7643MCD2.js';
20
20
  import '../chunk-64XODP7M.js';
21
- import '../chunk-7OTHRX45.js';
21
+ import '../chunk-M2ZGSI6K.js';
22
22
  import '../chunk-W7OPEMGJ.js';
23
23
  import '../chunk-A4CC4ZG3.js';
24
24
  import '../chunk-PKBMQBKP.js';
package/dist/seo/llms.js CHANGED
@@ -1,10 +1,10 @@
1
- export { createLLMsFullTxtHandler, createLLMsTxtHandler, formatSourcedFactsSection, llmsResponseHeaders } from '../chunk-CIZ7Z3NU.js';
1
+ export { createLLMsFullTxtHandler, createLLMsTxtHandler, formatSourcedFactsSection, llmsResponseHeaders } from '../chunk-DUIGCLQM.js';
2
2
  export { buildAiDiscoveryHeaders, wantsLlmsDiscoveryLink } from '../chunk-MTDVV5P3.js';
3
3
  export { AI_RETRIEVAL_CRAWLERS, AI_TRAINING_CRAWLERS, buildAiCrawlerRules, createRobotsTxtHandler } from '../chunk-QLTDFUJ7.js';
4
- export { LEGACY_PUBLICATION_PATH, SEO_REVALIDATION_MAX_BODY_BYTES, SEO_REVALIDATION_MAX_ITEMS, createLlmsRevalidateHandler, createSeoRevalidationHandler, parseSeoRevalidationPayload } from '../chunk-5LCY7QKK.js';
4
+ export { LEGACY_PUBLICATION_PATH, SEO_REVALIDATION_MAX_BODY_BYTES, SEO_REVALIDATION_MAX_ITEMS, createLlmsRevalidateHandler, createSeoRevalidationHandler, parseSeoRevalidationPayload } from '../chunk-T2Z7KEIH.js';
5
5
  export { AEOBlock, AEOCitedContent, AEOClaim, AEOComparison, AEODefinition, AEOEntity, AEOProvenanceList, AEOStep, AEOSteps, AEOSummary, DEFAULT_SPEAKABLE_SELECTORS, SpeakableSchema, createSpeakableSchema, getSpeakableSelectorsForPage } from '../chunk-PSAG45YF.js';
6
6
  import '../chunk-XTRZGXB2.js';
7
- export { AGENT_ACCESS_HEADING, appendAgentAccessSection, describesMcp, detectMcpServer, formatAgentAccessSection, generateLLMsFullTxt, generateLLMsTxt, writeLLMsTxtToPublic } from '../chunk-ES52W5EE.js';
7
+ export { AGENT_ACCESS_HEADING, appendAgentAccessSection, describesMcp, detectMcpServer, formatAgentAccessSection, generateLLMsFullTxt, generateLLMsTxt, writeLLMsTxtToPublic } from '../chunk-2YSX4ZUC.js';
8
8
  export { getBusinessInfo, getFAQItems, getLLMsData, getOptimizedLLMsTxt, getPageSummaries, getServices } from '../chunk-EK7P62DT.js';
9
9
  import '../chunk-7DYMM6SS.js';
10
10
  import '../chunk-IBHBEUZV.js';
@@ -16,7 +16,7 @@ import '../chunk-ISRBSTB7.js';
16
16
  import '../chunk-MJP63SSS.js';
17
17
  import '../chunk-YVKRYQRG.js';
18
18
  import '../chunk-FESKKJAS.js';
19
- import '../chunk-CNQMSQYQ.js';
19
+ import '../chunk-2MJYGVKB.js';
20
20
  import '../chunk-W7OPEMGJ.js';
21
21
  import '../chunk-A4CC4ZG3.js';
22
22
  import '../chunk-PKBMQBKP.js';
@@ -458,7 +458,7 @@ async function registerLocalSitemap(options) {
458
458
  }
459
459
  if (options.writeLlms && result.success) {
460
460
  try {
461
- const { writeLLMsTxtToPublic } = await import('../writeLLMsTxt-SYLPYWUX.js');
461
+ const { writeLLMsTxtToPublic } = await import('../writeLLMsTxt-EKWOSHXU.js');
462
462
  const written = await writeLLMsTxtToPublic({
463
463
  // Resolved here too: the sync's own `site` is scoped to the branch that
464
464
  // posts, and this runs on the skip path as well.
@@ -1,5 +1,5 @@
1
- export { createSitemap, getPortfolioPaths, syncSitemapToPortal } from '../chunk-HWYJ3O7D.js';
2
- import '../chunk-ES52W5EE.js';
1
+ export { createSitemap, getPortfolioPaths, syncSitemapToPortal } from '../chunk-Z42KXUTK.js';
2
+ import '../chunk-2YSX4ZUC.js';
3
3
  import '../chunk-EK7P62DT.js';
4
4
  import '../chunk-Z22QVHT4.js';
5
5
  import '../chunk-7DYMM6SS.js';
@@ -1,8 +1,8 @@
1
1
  import '../chunk-34ZYJTQR.js';
2
- export { resolveClientCredential } from '../chunk-UK4ANQUS.js';
2
+ export { resolveClientCredential } from '../chunk-675DTMSR.js';
3
3
  import '../chunk-UAYAJPEP.js';
4
4
  export { SITE_CACHE_TAGS } from '../chunk-HY7ZEU2C.js';
5
5
  export { isNextControlFlow, requireServerApiConfig, resolveApiOrigin, resolveServerApiConfig, serverApiFetch } from '../chunk-7VMA3KCN.js';
6
6
  import '../chunk-MH2DSGC6.js';
7
- import '../chunk-CNQMSQYQ.js';
7
+ import '../chunk-2MJYGVKB.js';
8
8
  import '../chunk-PKBMQBKP.js';
@@ -7,4 +7,4 @@
7
7
  * it matches package.json at publish time, and version.test.ts asserts it in
8
8
  * dev, so it can't silently drift.
9
9
  */
10
- export declare const SITE_KIT_VERSION = "8.11.0";
10
+ export declare const SITE_KIT_VERSION = "8.12.0";
@@ -1,6 +1,6 @@
1
1
  'use client';
2
- export { ExperimentConversion, SignalExperiment, useExperimentVariant } from '../chunk-KWHR7PNA.js';
3
- export { SignalBridge, useSignal, useSignalConfig, useSignalEvent, useSignalExperiment, useSignalOutcome } from '../chunk-R53G2IKP.js';
2
+ export { ExperimentConversion, SignalExperiment, useExperimentVariant } from '../chunk-M3VJAE24.js';
3
+ export { SignalBridge, useSignal, useSignalConfig, useSignalEvent, useSignalExperiment, useSignalOutcome } from '../chunk-VZSIIKLR.js';
4
4
  import '../chunk-MOACHHOJ.js';
5
5
  import '../chunk-FRY6PBT6.js';
6
6
  import '../chunk-PKBMQBKP.js';
@@ -1,5 +1,5 @@
1
- export { createSitemap, getPortfolioPaths, syncSitemapToPortal } from '../chunk-HWYJ3O7D.js';
2
- import '../chunk-ES52W5EE.js';
1
+ export { createSitemap, getPortfolioPaths, syncSitemapToPortal } from '../chunk-Z42KXUTK.js';
2
+ import '../chunk-2YSX4ZUC.js';
3
3
  import '../chunk-EK7P62DT.js';
4
4
  import '../chunk-Z22QVHT4.js';
5
5
  import '../chunk-7DYMM6SS.js';
@@ -1,7 +1,7 @@
1
- import { getFreshCredential, refreshSiteToken } from './chunk-3LGE3LRC.js';
1
+ import { getFreshCredential, refreshSiteToken } from './chunk-7PW7N7P4.js';
2
2
  import { isSiteToken, credentialBreakerId } from './chunk-TPHOIE4I.js';
3
3
  import { isTokenMode } from './chunk-D4M5K6JN.js';
4
- import { SITE_KIT_VERSION } from './chunk-CNQMSQYQ.js';
4
+ import { SITE_KIT_VERSION } from './chunk-2MJYGVKB.js';
5
5
  import './chunk-PKBMQBKP.js';
6
6
 
7
7
  // src/shared/fetchWithRetry.ts
@@ -1,8 +1,8 @@
1
- export { BookingWidget } from '../chunk-3VKJJNQU.js';
2
- export { createBooking, createSlotHold, detectTimezone, fetchAvailability, fetchAvailableDates, fetchBookingTypeDetails, fetchBookingTypes, formatDate, formatDuration, formatTime, prepareMeeting, releaseSlotHold } from '../chunk-LY3Q6RU5.js';
1
+ export { BookingWidget } from '../chunk-YYV3YHUX.js';
2
+ export { createBooking, createSlotHold, detectTimezone, fetchAvailability, fetchAvailableDates, fetchBookingTypeDetails, fetchBookingTypes, formatDate, formatDuration, formatTime, prepareMeeting, releaseSlotHold } from '../chunk-TXB4ULAM.js';
3
3
  import '../chunk-PVY7X5UR.js';
4
4
  import '../chunk-5IVD6G77.js';
5
- import '../chunk-7OTHRX45.js';
5
+ import '../chunk-M2ZGSI6K.js';
6
6
  import '../chunk-W7OPEMGJ.js';
7
7
  import '../chunk-FRY6PBT6.js';
8
8
  import '../chunk-PKBMQBKP.js';
@@ -1,4 +1,4 @@
1
- export { ManagedImage, assignImageToSlot, clearImageSlot, fetchManagedImage, fetchManagedImages, listImageFiles, uploadImage } from '../chunk-52MFDOBB.js';
1
+ export { ManagedImage, assignImageToSlot, clearImageSlot, fetchManagedImage, fetchManagedImages, listImageFiles, uploadImage } from '../chunk-NX4FETUK.js';
2
2
  export { ManagedFavicon, getManagedFaviconUrl } from '../chunk-GW7GSBO7.js';
3
- import '../chunk-7OTHRX45.js';
3
+ import '../chunk-M2ZGSI6K.js';
4
4
  import '../chunk-PKBMQBKP.js';
@@ -1,14 +1,14 @@
1
1
  'use client';
2
- export { SiteDesignReporter } from '../chunk-2URLL3Z2.js';
3
- export { SITE_DESIGN_PATH, cleanFontStack, colorToHex, getSiteDesignTokens, measureSiteDesign, readDeclaredDesign, sendSiteDesign } from '../chunk-OPJZSLGK.js';
2
+ export { SiteDesignReporter } from '../chunk-C55LIDVW.js';
3
+ export { SITE_DESIGN_PATH, cleanFontStack, colorToHex, getSiteDesignTokens, measureSiteDesign, readDeclaredDesign, sendSiteDesign } from '../chunk-TPB5PTWW.js';
4
4
  export { DEFAULT_POPUP_TOKENS, PopupBlocks } from '../chunk-52KO4LKD.js';
5
5
  import '../chunk-XLAHRN4B.js';
6
6
  import '../chunk-XHYE24HU.js';
7
7
  import '../chunk-YC7ELZS3.js';
8
8
  import '../chunk-S22FSH7C.js';
9
- import '../chunk-CNQMSQYQ.js';
9
+ import '../chunk-2MJYGVKB.js';
10
10
  import '../chunk-7643MCD2.js';
11
11
  import '../chunk-64XODP7M.js';
12
- import '../chunk-7OTHRX45.js';
12
+ import '../chunk-M2ZGSI6K.js';
13
13
  import '../chunk-W7OPEMGJ.js';
14
14
  import '../chunk-PKBMQBKP.js';
@@ -1,12 +1,12 @@
1
- export { FunnelPageContent, ManagedFunnelPage, getPublishedFunnelPage, managedFunnelMetadata } from '../../chunk-N26GRGIP.js';
1
+ export { FunnelPageContent, ManagedFunnelPage, getPublishedFunnelPage, managedFunnelMetadata } from '../../chunk-27264MSX.js';
2
2
  import '../../chunk-XS2ORMUG.js';
3
3
  import '../../chunk-XJZLXUTG.js';
4
4
  import '../../chunk-7IZUUXXO.js';
5
5
  export { normalizeCampaignSlug } from '../../chunk-2QPVU63T.js';
6
6
  export { landingPageMetadata } from '../../chunk-JESLPEST.js';
7
7
  import '../../chunk-XHYE24HU.js';
8
- import '../../chunk-XHEUPCCU.js';
9
- import '../../chunk-IDLJEFVB.js';
8
+ import '../../chunk-5UAX6PHO.js';
9
+ import '../../chunk-CNCAQEUJ.js';
10
10
  import '../../chunk-C2FZBUSS.js';
11
11
  import '../../chunk-YC7ELZS3.js';
12
12
  import '../../chunk-HHEDRZOJ.js';
@@ -19,8 +19,8 @@ import '../../chunk-7VMA3KCN.js';
19
19
  import '../../chunk-MH2DSGC6.js';
20
20
  import '../../chunk-S22FSH7C.js';
21
21
  import '../../chunk-A4CC4ZG3.js';
22
- import '../../chunk-KWHR7PNA.js';
23
- import '../../chunk-R53G2IKP.js';
22
+ import '../../chunk-M3VJAE24.js';
23
+ import '../../chunk-VZSIIKLR.js';
24
24
  import '../../chunk-MOACHHOJ.js';
25
25
  import '../../chunk-FRY6PBT6.js';
26
26
  import '../../chunk-PKBMQBKP.js';
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- export { SitePopups } from '../chunk-CCZHAC4K.js';
2
+ export { SitePopups } from '../chunk-Y4MQTSYY.js';
3
3
  import '../chunk-27FISYK4.js';
4
4
  export { DEFAULT_POPUP_TOKENS, PopupBlocks } from '../chunk-52KO4LKD.js';
5
5
  import '../chunk-XLAHRN4B.js';
@@ -7,17 +7,17 @@ import '../chunk-XHYE24HU.js';
7
7
  import '../chunk-PVY7X5UR.js';
8
8
  import '../chunk-S22FSH7C.js';
9
9
  import '../chunk-JT7GHHMV.js';
10
- import '../chunk-UEF4AC76.js';
10
+ import '../chunk-DFCLELM2.js';
11
11
  import '../chunk-NHDRA4Y5.js';
12
12
  import '../chunk-D4M5K6JN.js';
13
13
  import '../chunk-GJWI74ZZ.js';
14
14
  import '../chunk-NZOJHERI.js';
15
15
  import '../chunk-KMNHNCVG.js';
16
16
  import '../chunk-5IVD6G77.js';
17
- import '../chunk-CNQMSQYQ.js';
17
+ import '../chunk-2MJYGVKB.js';
18
18
  import '../chunk-7643MCD2.js';
19
19
  import '../chunk-64XODP7M.js';
20
- import '../chunk-7OTHRX45.js';
20
+ import '../chunk-M2ZGSI6K.js';
21
21
  import '../chunk-W7OPEMGJ.js';
22
22
  import '../chunk-A4CC4ZG3.js';
23
23
  import '../chunk-FRY6PBT6.js';
@@ -1,4 +1,4 @@
1
- export { writeLLMsTxtToPublic } from './chunk-ES52W5EE.js';
1
+ export { writeLLMsTxtToPublic } from './chunk-2YSX4ZUC.js';
2
2
  import './chunk-EK7P62DT.js';
3
3
  import './chunk-7DYMM6SS.js';
4
4
  import './chunk-IBHBEUZV.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sonordev/site-kit",
3
- "version": "8.11.0",
3
+ "version": "8.12.0",
4
4
  "type": "module",
5
5
  "packageManager": "pnpm@11.5.3",
6
6
  "description": "Sonor's Next.js foundation: SEO, analytics, forms, chat, booking, AI visibility and agent tools. Add @sonordev/press-kit for publications or @sonordev/commerce-kit for selling. Legacy article imports remain compatible in 8.x.",
@@ -27,6 +27,39 @@ A choice needs a fresh, enabled native configuration. The component waits while
27
27
 
28
28
  This component records preferences through the native consent owner. Configure the real page rules, destinations and approved visit feed before verifying advertising delivery.
29
29
 
30
+ ### Branded footer settings
31
+
32
+ Mount the same leaf in the footer to keep one preference owner. Existing callers retain the floating launcher and automatic dialog. The optional presentation props are `launcherPlacement` (`floating` or `inline`), `launcherLabel`, `promptMode` (`dialog` or `panel`), and `className`.
33
+
34
+ ```tsx
35
+ <ResonanceConsentPreferences
36
+ launcherPlacement="inline"
37
+ launcherLabel="Privacy settings"
38
+ promptMode="panel"
39
+ className="workshop-consent"
40
+ />
41
+ ```
42
+
43
+ Panel mode presents equal Accept optional, Reject optional and Customize actions when the native configuration is ready and no current preference exists. It opens without moving focus. Dismissal lasts for this mount and records no choice. Customize opens the existing native dialog. Closing returns focus to the initiating control, or the footer launcher if that control disappears after a choice. Unsaved settings remain visible when configuration becomes unavailable. Every save rechecks the owner, and a failed save stays open for review and retry. The saved message describes the local preference on this page.
44
+
45
+ Style the root with inherited `--sk-consent-surface`, `--sk-consent-foreground`, `--sk-consent-muted`, `--sk-consent-border`, `--sk-consent-accent`, `--sk-consent-focus`, `--sk-consent-radius`, and `--sk-consent-font` tokens. The font token accepts a complete CSS font value, including `inherit`. Prompt placement uses `--sk-consent-prompt-inset` and `--sk-consent-prompt-bottom`. Stable slot classes are `sk-consent__launcher`, `sk-consent__prompt`, `sk-consent__dialog`, `sk-consent__actions`, and `sk-consent__status`.
46
+
47
+ ```css
48
+ .workshop-consent {
49
+ --sk-consent-surface: #18201e;
50
+ --sk-consent-foreground: #fff8e8;
51
+ --sk-consent-muted: #d8d0bf;
52
+ --sk-consent-border: #8b806a;
53
+ --sk-consent-accent: #f0cd84;
54
+ --sk-consent-focus: #f0cd84;
55
+ --sk-consent-font: inherit;
56
+ }
57
+ ```
58
+
59
+ For a site using the shared Echo chat, `--sk-echo-offset-bottom` is the existing placement input for the launcher and popup. A narrow layout can reserve the 60px launcher plus a gap beneath the privacy prompt by setting its bottom token from that value, safe-area inset and `--sk-vv-layout-gap`. Keep the existing CTA owner in charge of writing the Echo offset.
60
+
61
+ `[data-sk-echo-panel]` is the supported styling marker for an open Echo panel. At a narrow breakpoint, a site can hide only its nonmodal `sk-consent__prompt` while that marker exists, using a CSS `:has()` rule. The native privacy dialog remains independent. Verify chat open and closed states, short viewports, text zoom and focused content before shipping the placement.
62
+
30
63
  ## Connect your consent manager
31
64
 
32
65
  Register one adapter for the consent manager your site already uses. The
@@ -1,5 +0,0 @@
1
- export { fetchMapsConfig, fetchNearbyPlaces } from './chunk-FW63D6W3.js';
2
- import './chunk-MH2DSGC6.js';
3
- import './chunk-7OTHRX45.js';
4
- import './chunk-W7OPEMGJ.js';
5
- import './chunk-PKBMQBKP.js';
File without changes