@ohhwells/bridge 0.1.83 → 0.1.84-next.249

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.
package/dist/index.js CHANGED
@@ -69,6 +69,7 @@ function isRenderableTree(value) {
69
69
 
70
70
  // src/lib/ai-sections-store.ts
71
71
  var AI_SECTIONS_KEY = "__ohw_ai_sections";
72
+ var AI_SLOT_KEY_PREFIX = "ai.";
72
73
  var EMPTY_AI_SECTIONS = { v: 1, sections: [] };
73
74
  function parseAiSectionsState(raw) {
74
75
  if (!raw) return EMPTY_AI_SECTIONS;
@@ -112,6 +113,63 @@ function applyTreeToState(state, payload) {
112
113
  const others = state.sections.filter((existing) => existing.id !== entry.id);
113
114
  return { ...state, v: 1, sections: [...others, entry] };
114
115
  }
116
+ function foldAlignIntoTrees(state, store) {
117
+ const byId = new Map(state.sections.map((entry) => [entry.id, entry]));
118
+ const nextTrees = /* @__PURE__ */ new Map();
119
+ const treeFor = (id) => {
120
+ const cloned = nextTrees.get(id);
121
+ if (cloned) return cloned;
122
+ const entry = byId.get(id);
123
+ if (!entry) return void 0;
124
+ const fresh = {
125
+ ...entry.tree,
126
+ rows: entry.tree.rows.map((row) => ({ ...row, blocks: row.blocks.map((block) => ({ ...block })) }))
127
+ };
128
+ nextTrees.set(id, fresh);
129
+ return fresh;
130
+ };
131
+ const nodes = {};
132
+ for (const [key, override] of Object.entries(store.nodes)) {
133
+ const match = override.align !== void 0 && key.startsWith(AI_SLOT_KEY_PREFIX) ? key.match(/^ai\.(.+?)\.r(\d+)\.b(\d+)(?:\.|$)/) : null;
134
+ const tree = match ? treeFor(match[1]) : void 0;
135
+ const block = match && tree ? tree.rows[Number(match[2])]?.blocks[Number(match[3])] : void 0;
136
+ if (!block) {
137
+ nodes[key] = override;
138
+ continue;
139
+ }
140
+ block.align = override.align;
141
+ const rest = { ...override };
142
+ delete rest.align;
143
+ if (Object.keys(rest).length > 0) nodes[key] = rest;
144
+ }
145
+ const sections = {};
146
+ for (const [sectionId, override] of Object.entries(store.sections)) {
147
+ const tree = override.align !== void 0 ? treeFor(sectionId) : void 0;
148
+ if (!tree) {
149
+ sections[sectionId] = override;
150
+ continue;
151
+ }
152
+ for (const row of tree.rows) {
153
+ for (const block of row.blocks) block.align = override.align;
154
+ }
155
+ const rest = { ...override };
156
+ delete rest.align;
157
+ if (Object.keys(rest).length > 0) sections[sectionId] = rest;
158
+ }
159
+ if (nextTrees.size === 0) return { state, store, changed: false };
160
+ return {
161
+ state: {
162
+ ...state,
163
+ v: 1,
164
+ sections: state.sections.map((entry) => {
165
+ const tree = nextTrees.get(entry.id);
166
+ return tree ? { ...entry, tree } : entry;
167
+ })
168
+ },
169
+ store: { v: 1, sections, nodes },
170
+ changed: true
171
+ };
172
+ }
115
173
  function removeFromState(state, id) {
116
174
  return { ...state, v: 1, sections: state.sections.filter((entry) => entry.id !== id) };
117
175
  }
@@ -183,6 +241,17 @@ var BRAND_VAR_PREFIX = "--ohw-brand-";
183
241
  var BRAND_VAR_NAMES = ["primary", "accent", "light", "dark", "surface", "border", "muted"].map(
184
242
  (role) => `${BRAND_VAR_PREFIX}${role}`
185
243
  );
244
+ var LEGACY_BRAND_VAR_NAMES = [
245
+ "primary",
246
+ "accent",
247
+ "background",
248
+ "text",
249
+ "text-muted",
250
+ "surface",
251
+ "border",
252
+ "on-primary",
253
+ "navbar-background"
254
+ ].map((role) => `--brand-${role}`);
186
255
  var FONT_VARS = {
187
256
  heading: ["--font-heading", "--font-display", "--brand-font-heading"],
188
257
  body: ["--font-body", "--brand-font-body"]
@@ -191,14 +260,29 @@ var BRAND_FONT_LINK_ID = "ohw-brand-fonts";
191
260
  function brandColorVars(kit) {
192
261
  const { dark, primary, accent, light } = kit.palette;
193
262
  const mix = (a, aPct, b) => `color-mix(in srgb, ${a} ${aPct}%, ${b})`;
263
+ const surface = mix(light, 95, dark);
264
+ const border = mix(light, 85, dark);
265
+ const muted = mix(dark, 62, light);
194
266
  return {
195
267
  [`${BRAND_VAR_PREFIX}primary`]: primary,
196
268
  [`${BRAND_VAR_PREFIX}accent`]: accent,
197
269
  [`${BRAND_VAR_PREFIX}light`]: light,
198
270
  [`${BRAND_VAR_PREFIX}dark`]: dark,
199
- [`${BRAND_VAR_PREFIX}surface`]: mix(light, 95, dark),
200
- [`${BRAND_VAR_PREFIX}border`]: mix(light, 85, dark),
201
- [`${BRAND_VAR_PREFIX}muted`]: mix(dark, 62, light)
271
+ [`${BRAND_VAR_PREFIX}surface`]: surface,
272
+ [`${BRAND_VAR_PREFIX}border`]: border,
273
+ [`${BRAND_VAR_PREFIX}muted`]: muted,
274
+ // The BrandProvider contract every template actually renders from (see LEGACY_BRAND_VAR_NAMES).
275
+ "--brand-primary": primary,
276
+ "--brand-accent": accent,
277
+ "--brand-background": light,
278
+ "--brand-text": dark,
279
+ "--brand-text-muted": muted,
280
+ "--brand-surface": surface,
281
+ "--brand-border": border,
282
+ // Buttons/bands painted in the primary colour assume it's dark/saturated enough to need
283
+ // light text on top — the same assumption LOGO_IMAGE's light-on-dark navbar mark makes.
284
+ "--brand-on-primary": light,
285
+ "--brand-navbar-background": dark
202
286
  };
203
287
  }
204
288
  function parseBrandKit(raw) {
@@ -239,7 +323,7 @@ function loadBrandFonts(families) {
239
323
  function applyBrandToDom(kit) {
240
324
  const root = document.documentElement;
241
325
  if (!kit) {
242
- for (const name of BRAND_VAR_NAMES) root.style.removeProperty(name);
326
+ for (const name of [...BRAND_VAR_NAMES, ...LEGACY_BRAND_VAR_NAMES]) root.style.removeProperty(name);
243
327
  for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
244
328
  document.getElementById(BRAND_FONT_LINK_ID)?.remove();
245
329
  return;
@@ -304,6 +388,9 @@ function styleSheetCss() {
304
388
  `[data-ohw-style-spacing="${spacing}"] { padding-top: ${px}px !important; padding-bottom: ${px}px !important; }`
305
389
  );
306
390
  }
391
+ for (const align of ["left", "center", "right"]) {
392
+ rules.push(`[data-ohw-style-align="${align}"] { text-align: ${align} !important; }`);
393
+ }
307
394
  return rules.join("\n");
308
395
  }
309
396
  var STYLE_FONT_LINK_ID = "ohw-style-fonts";
@@ -330,10 +417,24 @@ var SECTION_ATTRS = {
330
417
  textDistribution: "data-ohw-style-distribution",
331
418
  headlineScale: "data-ohw-style-headline",
332
419
  imageAspect: "data-ohw-style-aspect",
333
- spacing: "data-ohw-style-spacing"
420
+ spacing: "data-ohw-style-spacing",
421
+ align: "data-ohw-style-align"
334
422
  };
335
423
  var NODE_WROTE_ATTR = "data-ohw-style-node";
336
- var NODE_PROPS = ["color", "font-family", "font-size", "background"];
424
+ var NODE_PROPS = [
425
+ "color",
426
+ "font-family",
427
+ "font-size",
428
+ "background",
429
+ "text-align",
430
+ "justify-content",
431
+ "align-items"
432
+ ];
433
+ var ALIGN_JUSTIFY = {
434
+ left: "flex-start",
435
+ center: "center",
436
+ right: "flex-end"
437
+ };
337
438
  function saveInline(el, prop) {
338
439
  const attr = `data-ohw-style-prev-${prop}`;
339
440
  if (el.hasAttribute(attr)) return;
@@ -377,6 +478,10 @@ function clearNodeProps(root) {
377
478
  function buttonSurfaceOf(el) {
378
479
  return el.closest("a, button") ?? el;
379
480
  }
481
+ function alignSubjectOf(el) {
482
+ const button = el.closest('[data-ohw-role="button"]');
483
+ return button?.parentElement ?? el;
484
+ }
380
485
  function applyStylesToDom(store) {
381
486
  ensureStyleSheet();
382
487
  clearSectionAttrs(document);
@@ -422,6 +527,18 @@ function applyStylesToDom(store) {
422
527
  el.style.setProperty("font-size", `${override.fontSize}px`, "important");
423
528
  el.setAttribute(NODE_WROTE_ATTR, "");
424
529
  }
530
+ if (override.align !== void 0) {
531
+ const subject = alignSubjectOf(el);
532
+ saveInline(subject, "text-align");
533
+ saveInline(subject, "justify-content");
534
+ subject.style.setProperty("text-align", override.align, "important");
535
+ subject.style.setProperty(
536
+ "justify-content",
537
+ ALIGN_JUSTIFY[override.align] ?? "flex-start",
538
+ "important"
539
+ );
540
+ subject.setAttribute(NODE_WROTE_ATTR, "");
541
+ }
425
542
  if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
426
543
  const surface = buttonSurfaceOf(el);
427
544
  if (override.buttonBackground !== void 0) {
@@ -517,6 +634,22 @@ function accentBandContext(brand) {
517
634
  function textAttrs(ctx, path) {
518
635
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
519
636
  }
637
+ var AI_RESPONSIVE_CSS = [
638
+ "@media (max-width: 960px) {",
639
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
640
+ ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
641
+ "}",
642
+ "@media (max-width: 640px) {",
643
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
644
+ " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
645
+ // Group containers flatten to a column on phones; span placements come along for free.
646
+ " [data-ai-responsive] [data-ai-group] { display: flex !important; flex-direction: column !important; }",
647
+ " [data-ai-responsive] [data-ai-group] > * { grid-column: auto !important; }",
648
+ " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
649
+ " [data-ai-responsive] { overflow-x: hidden; }",
650
+ " [data-ai-responsive] img { max-width: 100%; }",
651
+ "}"
652
+ ].join("\n");
520
653
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
521
654
  function MediaBox({
522
655
  refValue,
@@ -529,13 +662,17 @@ function MediaBox({
529
662
  const url = refValue ? ctx.resolveMedia(refValue) : null;
530
663
  const isIcon = /^(lucide|simple):/.test(refValue);
531
664
  const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
532
- const editAttrs = ctx.keyFor && editPath && !isIcon ? { "data-ohw-key": ctx.keyFor(editPath), "data-ohw-editable": "image" } : {};
665
+ const editAttrs = ctx.keyFor && editPath ? {
666
+ "data-ohw-key": ctx.keyFor(editPath),
667
+ "data-ohw-editable": isIcon ? "icon" : "image"
668
+ } : {};
533
669
  if (isIcon) {
534
670
  const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
535
671
  return /* @__PURE__ */ jsx(
536
672
  "span",
537
673
  {
538
674
  "data-ai-icon": refValue,
675
+ ...editAttrs,
539
676
  style: {
540
677
  display: "inline-flex",
541
678
  width: 48,
@@ -630,7 +767,7 @@ function TextBlock({ slots, ctx, path }) {
630
767
  }
631
768
  function SectionHeaderBlock({ node, ctx, path }) {
632
769
  const slots = node.slots ?? {};
633
- const align = slots.alignment === "center" ? "center" : "left";
770
+ const align = node.align ?? (slots.alignment === "center" ? "center" : "left");
634
771
  const children = node.children ?? [];
635
772
  const buttonRowIdx = children.findIndex((c) => c.type === "button-row");
636
773
  const buttonRow = buttonRowIdx >= 0 ? children[buttonRowIdx] : void 0;
@@ -674,7 +811,7 @@ function SectionHeaderBlock({ node, ctx, path }) {
674
811
  display: "flex",
675
812
  gap: AI_TREE_TOKENS.spacing6,
676
813
  marginTop: AI_TREE_TOKENS.spacing8,
677
- justifyContent: align === "center" ? "center" : "flex-start"
814
+ justifyContent: align === "center" ? "center" : align === "right" ? "flex-end" : "flex-start"
678
815
  },
679
816
  children: (buttonRow.children ?? []).map((button, i) => /* @__PURE__ */ jsx(
680
817
  ButtonEl,
@@ -1007,7 +1144,7 @@ function CardBlock({ node, ctx, path }) {
1007
1144
  editPath: `${path}.media`
1008
1145
  }
1009
1146
  ) : null;
1010
- const centered = slots.alignment === "center";
1147
+ const centered = (node.align ?? slots.alignment) === "center";
1011
1148
  const content = /* @__PURE__ */ jsxs(
1012
1149
  "div",
1013
1150
  {
@@ -1421,7 +1558,7 @@ function CollectionBlock({ node, ctx, path }) {
1421
1558
  return /* @__PURE__ */ jsx(
1422
1559
  "div",
1423
1560
  {
1424
- "data-ai-grid": "",
1561
+ "data-ai-grid": String(itemsPerRow),
1425
1562
  style: {
1426
1563
  display: "grid",
1427
1564
  gridTemplateColumns: `repeat(${itemsPerRow}, 1fr)`,
@@ -1726,11 +1863,25 @@ function AiTreeRenderer({
1726
1863
  }
1727
1864
  })();
1728
1865
  const distributed = !isOverlay && settings.textDistribution;
1866
+ const rowAlignItems = (rowAlign) => {
1867
+ if (rowAlign === "top") return "start";
1868
+ if (rowAlign === "bottom") return "end";
1869
+ if (rowAlign === "center" || rowAlign === "stretch") return rowAlign;
1870
+ if (distributed === "space-between") return "stretch";
1871
+ return (distributed ?? settings.verticalPosition) === "top" ? "start" : "center";
1872
+ };
1873
+ const cellAlignStyle = (blockAlign) => blockAlign ? {
1874
+ display: "flex",
1875
+ flexDirection: "column",
1876
+ alignItems: blockAlign === "left" ? "flex-start" : blockAlign === "right" ? "flex-end" : "center",
1877
+ textAlign: blockAlign
1878
+ } : {};
1729
1879
  return /* @__PURE__ */ jsxs(
1730
1880
  "section",
1731
1881
  {
1732
1882
  "data-ai-section": tree.tag ?? "",
1733
1883
  ...bgAttrs,
1884
+ "data-ai-responsive": "",
1734
1885
  style: {
1735
1886
  position: "relative",
1736
1887
  padding: `${pad}px 0`,
@@ -1741,12 +1892,13 @@ function AiTreeRenderer({
1741
1892
  color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
1742
1893
  },
1743
1894
  children: [
1744
- isOverlay && backgroundUrl && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
1895
+ /* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
1745
1896
  /* @__PURE__ */ jsx("style", { children: AI_MOBILE_CSS }),
1897
+ isOverlay && backgroundUrl && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
1746
1898
  /* @__PURE__ */ jsx(
1747
1899
  "div",
1748
1900
  {
1749
- "data-ai-container": "",
1901
+ "data-ai-section-inner": "",
1750
1902
  style: {
1751
1903
  position: "relative",
1752
1904
  maxWidth: AI_TREE_TOKENS.sectionMaxWidth,
@@ -1757,12 +1909,12 @@ function AiTreeRenderer({
1757
1909
  children: tree.rows.map((row, r2) => /* @__PURE__ */ jsx(
1758
1910
  "div",
1759
1911
  {
1760
- "data-ai-row": "",
1912
+ "data-ai-columns": "",
1761
1913
  style: {
1762
1914
  display: "grid",
1763
1915
  gridTemplateColumns: "repeat(12, 1fr)",
1764
1916
  gap: AI_TREE_TOKENS.spacing6,
1765
- alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
1917
+ alignItems: rowAlignItems(row.align),
1766
1918
  marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
1767
1919
  },
1768
1920
  children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ jsx(
@@ -1772,6 +1924,8 @@ function AiTreeRenderer({
1772
1924
  style: {
1773
1925
  gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
1774
1926
  minWidth: 0,
1927
+ // Horizontal placement of the block's content within its column.
1928
+ ...cellAlignStyle(block.align),
1775
1929
  // space-between: each column becomes a flex column whose content spreads over
1776
1930
  // the full row height instead of clumping at the top.
1777
1931
  ...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
@@ -7359,6 +7513,9 @@ function applyFieldType(wrapper, type) {
7359
7513
  el.style.removeProperty("min-height");
7360
7514
  el.style.removeProperty("resize");
7361
7515
  el.removeAttribute("rows");
7516
+ if (el.className) {
7517
+ el.className = el.className.split(/\s+/).filter((token) => !/textarea/i.test(token)).join(" ");
7518
+ }
7362
7519
  };
7363
7520
  const applyDefaults = (el) => {
7364
7521
  el.setAttribute("placeholder", defaults.placeholder);
@@ -7780,6 +7937,7 @@ function MediaOverlay({
7780
7937
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
7781
7938
  );
7782
7939
  }, [isVideo]);
7940
+ const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
7783
7941
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
7784
7942
  const box = {
7785
7943
  position: "fixed",
@@ -7909,17 +8067,17 @@ function MediaOverlay({
7909
8067
  },
7910
8068
  children: [
7911
8069
  isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
7912
- isVideo ? "Replace video" : "Replace image"
8070
+ replaceLabel
7913
8071
  ]
7914
8072
  }
7915
8073
  ),
7916
- replaceMode === "none" ? null : /* @__PURE__ */ jsxs7(
8074
+ showChrome && replaceMode !== "none" && /* @__PURE__ */ jsxs7(
7917
8075
  Button,
7918
8076
  {
7919
8077
  "data-ohw-media-overlay": "",
7920
8078
  variant: "outline",
7921
8079
  size: "sm",
7922
- "aria-label": isVideo ? "Replace video" : "Replace image",
8080
+ "aria-label": replaceLabel,
7923
8081
  className: "gap-1.5 cursor-pointer hover:bg-background",
7924
8082
  style: {
7925
8083
  ...OVERLAY_BUTTON_STYLE,
@@ -7942,7 +8100,7 @@ function MediaOverlay({
7942
8100
  },
7943
8101
  children: [
7944
8102
  isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
7945
- replaceMode === "full" ? isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image" : null
8103
+ replaceMode === "full" ? replaceLabel : null
7946
8104
  ]
7947
8105
  }
7948
8106
  )
@@ -8148,6 +8306,27 @@ function deleteSectionInstance(instanceId, currentPath, existingEntries) {
8148
8306
  function restoreSectionInstance(instanceId, currentPath, existingEntries) {
8149
8307
  return setSectionRemoved(instanceId, currentPath, existingEntries, false);
8150
8308
  }
8309
+ function duplicateSectionInstance(instanceId, newId, currentPath, existingEntries) {
8310
+ const original = findByInstanceId(instanceId);
8311
+ if (!original) return null;
8312
+ const clone = original.cloneNode(true);
8313
+ clone.setAttribute("data-ohw-instance", newId);
8314
+ const keyRekeys = rekeySectionSubtree(clone, newId);
8315
+ original.insertAdjacentElement("afterend", clone);
8316
+ const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
8317
+ const entries = topLevelSections().map((el, order) => {
8318
+ const id = instanceIdOf(el);
8319
+ return {
8320
+ instanceId: id,
8321
+ type: el.getAttribute("data-ohw-section") ?? "",
8322
+ order,
8323
+ pagePath: currentPath,
8324
+ ...byId.get(id)?.removed ? { removed: true } : {}
8325
+ };
8326
+ });
8327
+ applyPersistedOrder(entries);
8328
+ return { entries, keyRekeys };
8329
+ }
8151
8330
  function newInstanceId() {
8152
8331
  return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `instance-${Date.now()}-${Math.random().toString(36).slice(2)}`;
8153
8332
  }
@@ -8162,14 +8341,20 @@ function getPageSectionOrderEntries(raw, currentPath) {
8162
8341
  }
8163
8342
  function rekeySectionSubtree(root, instanceId) {
8164
8343
  const suffix = `::${instanceId}`;
8344
+ const pairs = [];
8165
8345
  const rekey = (el, attr) => {
8166
8346
  const current = el.getAttribute(attr);
8167
- if (current) el.setAttribute(attr, `${current}${suffix}`);
8347
+ if (!current) return;
8348
+ const base = current.includes("::") ? current.slice(0, current.indexOf("::")) : current;
8349
+ const next = `${base}${suffix}`;
8350
+ el.setAttribute(attr, next);
8351
+ pairs.push({ from: current, to: next });
8168
8352
  };
8169
8353
  if (root.hasAttribute("data-ohw-key")) rekey(root, "data-ohw-key");
8170
8354
  if (root.hasAttribute("data-ohw-href-key")) rekey(root, "data-ohw-href-key");
8171
8355
  root.querySelectorAll("[data-ohw-key]").forEach((el) => rekey(el, "data-ohw-key"));
8172
8356
  root.querySelectorAll("[data-ohw-href-key]").forEach((el) => rekey(el, "data-ohw-href-key"));
8357
+ return pairs;
8173
8358
  }
8174
8359
  function initSectionInstancesFromContent(content, currentPath) {
8175
8360
  document.querySelectorAll("[data-ohw-section]:not([data-ohw-instance])").forEach((el) => {
@@ -11609,7 +11794,7 @@ function applySocialsDisplayToRow(row, display) {
11609
11794
  const icon = item.querySelector(ICON_SELECTOR);
11610
11795
  if (label) label.style.display = display.text ? "" : "none";
11611
11796
  if (icon) icon.style.display = display.icon ? "" : "none";
11612
- layOutIconAndLabel(item, Boolean(display.text && display.icon));
11797
+ layOutIconAndLabel(item, Boolean(display.text));
11613
11798
  });
11614
11799
  allowRowToWrap(row);
11615
11800
  }
@@ -11621,6 +11806,9 @@ function layOutIconAndLabel(item, on) {
11621
11806
  item.style.gap = "";
11622
11807
  item.style.whiteSpace = "";
11623
11808
  item.style.flex = "";
11809
+ item.style.width = "";
11810
+ item.style.height = "";
11811
+ item.style.padding = "";
11624
11812
  return;
11625
11813
  }
11626
11814
  item.style.display = on ? "inline-flex" : "";
@@ -11628,6 +11816,9 @@ function layOutIconAndLabel(item, on) {
11628
11816
  item.style.gap = on ? "8px" : "";
11629
11817
  item.style.whiteSpace = on ? "nowrap" : "";
11630
11818
  item.style.flex = on ? "0 0 auto" : "";
11819
+ item.style.width = on ? "auto" : "";
11820
+ item.style.height = on ? "auto" : "";
11821
+ item.style.padding = on ? "0 12px" : "";
11631
11822
  }
11632
11823
  function allowRowToWrap(row) {
11633
11824
  const display = row.ownerDocument.defaultView?.getComputedStyle(row).display ?? "";
@@ -12583,6 +12774,15 @@ function resolveLogoDisplayText(text) {
12583
12774
  function isFooterLogoRoot(root) {
12584
12775
  return Boolean(root.closest("footer") || root.closest('[data-ohw-section="footer"]'));
12585
12776
  }
12777
+ var NAV_IMAGE_KEYS = ["nav-logo-image", "logo-image"];
12778
+ var FOOTER_IMAGE_KEYS = ["footer-logo", "footer-logo-image", "footer-logo-img"];
12779
+ function firstNonEmptyContentValue(content, keys) {
12780
+ for (const key of keys) {
12781
+ const value = content[key];
12782
+ if (typeof value === "string" && value.trim()) return value.trim();
12783
+ }
12784
+ return null;
12785
+ }
12586
12786
  function imageKeyForRoot(root) {
12587
12787
  return isFooterLogoRoot(root) ? "footer-logo" : "nav-logo-image";
12588
12788
  }
@@ -12632,9 +12832,10 @@ function applyLogoIdentity(text, isPlaceholder) {
12632
12832
  });
12633
12833
  return display;
12634
12834
  }
12635
- function applyLogoImage(url, alt) {
12835
+ function applyLogoImage(url, alt, placement) {
12636
12836
  const displayAlt = resolveLogoDisplayText(alt);
12637
12837
  document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
12838
+ if (placement && (isFooterLogoRoot(root) ? "footer" : "navbar") !== placement) return;
12638
12839
  ensureLogoHrefKey(root);
12639
12840
  const imageKey = imageKeyForRoot(root);
12640
12841
  const textKey = textKeyForRoot(root);
@@ -12755,7 +12956,12 @@ function applyLogoFromContent(content) {
12755
12956
  const logoImageUrl = typeof rawLogoImage === "string" && rawLogoImage.trim() ? rawLogoImage.trim() : null;
12756
12957
  const imageExplicitlyCleared = LOGO_IMAGE_KEYS.some((key) => key in content) && !logoImageUrl;
12757
12958
  const logoIsPlaceholder = LOGO_PLACEHOLDER_KEY in content ? content[LOGO_PLACEHOLDER_KEY] !== "false" : !logoImageUrl && (!logoText.trim() || logoText === PLACEHOLDER_BUSINESS_NAME);
12758
- if (logoImageUrl) {
12959
+ const navImageUrl = firstNonEmptyContentValue(content, NAV_IMAGE_KEYS);
12960
+ const footerImageUrl = firstNonEmptyContentValue(content, FOOTER_IMAGE_KEYS);
12961
+ if (navImageUrl && footerImageUrl && navImageUrl !== footerImageUrl) {
12962
+ applyLogoImage(navImageUrl, logoAlt, "navbar");
12963
+ applyLogoImage(footerImageUrl, logoAlt, "footer");
12964
+ } else if (logoImageUrl) {
12759
12965
  applyLogoImage(logoImageUrl, logoAlt);
12760
12966
  } else {
12761
12967
  if (imageExplicitlyCleared) applyLogoImage(null, logoAlt);
@@ -12886,6 +13092,7 @@ function readLogoSizeState(content, placement) {
12886
13092
  function getLogoElement(el) {
12887
13093
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
12888
13094
  if (marked) return marked;
13095
+ if (el.closest('[data-ohw-editable="icon"]')) return null;
12889
13096
  const root = el.closest("nav, [data-ohw-nav-root], footer");
12890
13097
  if (!root) return null;
12891
13098
  const anchor = el.closest("a");
@@ -14820,21 +15027,10 @@ function parseSchedulingInsertAfter(insertAfter) {
14820
15027
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
14821
15028
  };
14822
15029
  }
14823
- function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
14824
- const parsed = parseSchedulingInsertAfter(insertAfter);
14825
- const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
14826
- const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
14827
- return { effectiveInsertAfter, insertBefore };
14828
- }
14829
- function getSchedulingMountPoint(insertAfter) {
14830
- const { anchor } = parseSchedulingInsertAfter(insertAfter);
14831
- let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
14832
- if (!anchorEl && anchor === "scheduling") {
14833
- const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
14834
- anchorEl = widgets.at(-1) ?? null;
14835
- }
14836
- if (!anchorEl) return null;
14837
- return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
15030
+ function resolveEntryAnchor(entry) {
15031
+ if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
15032
+ const parsed = parseSchedulingInsertAfter(entry.insertAfter);
15033
+ return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
14838
15034
  }
14839
15035
  function schedulingMountDepth(insertAfter) {
14840
15036
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -14851,8 +15047,7 @@ function getPageSchedulingEntries(raw) {
14851
15047
  }
14852
15048
  }
14853
15049
  function isSchedulingWidgetMissing(entry) {
14854
- const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
14855
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
15050
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
14856
15051
  }
14857
15052
  function hasMissingSchedulingWidgets(entries) {
14858
15053
  return entries.some(isSchedulingWidgetMissing);
@@ -14882,16 +15077,17 @@ function initSectionsFromContent(content, removeExisting = false) {
14882
15077
  } catch {
14883
15078
  }
14884
15079
  }
14885
- function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
14886
- const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
14887
- const sectionId = schedulingSectionId(effectiveInsertAfter);
15080
+ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
15081
+ const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
15082
+ const sectionId = schedulingSectionId(widgetId);
14888
15083
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
14889
- const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
14890
- if (!mountPoint) return false;
15084
+ const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
15085
+ if (!anchorEl) return false;
15086
+ const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
14891
15087
  const container = document.createElement("div");
14892
15088
  container.dataset.ohwSectionContainer = "scheduling";
14893
- if (insertBefore) {
14894
- const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
15089
+ if (beforeId) {
15090
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
14895
15091
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
14896
15092
  if (!beforePoint) return false;
14897
15093
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -14902,19 +15098,25 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
14902
15098
  }
14903
15099
  tail.insertAdjacentElement("afterend", container);
14904
15100
  }
14905
- const root = createRoot2(container);
14906
- flushSync2(() => {
14907
- root.render(
14908
- /* @__PURE__ */ jsx33(
14909
- SchedulingWidget,
14910
- {
14911
- notifyOnConnect,
14912
- initialScheduleId: scheduleId,
14913
- insertAfter: effectiveInsertAfter
14914
- }
14915
- )
14916
- );
14917
- });
15101
+ try {
15102
+ const root = createRoot2(container);
15103
+ flushSync2(() => {
15104
+ root.render(
15105
+ /* @__PURE__ */ jsx33(
15106
+ SchedulingWidget,
15107
+ {
15108
+ notifyOnConnect,
15109
+ initialScheduleId: scheduleId,
15110
+ insertAfter: widgetId
15111
+ }
15112
+ )
15113
+ );
15114
+ });
15115
+ } catch (err) {
15116
+ console.error("[ow:scheduling] render threw", err);
15117
+ container.remove();
15118
+ return false;
15119
+ }
14918
15120
  const tracker = getSectionsTracker();
14919
15121
  let sections = [];
14920
15122
  try {
@@ -14922,10 +15124,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
14922
15124
  } catch {
14923
15125
  }
14924
15126
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
14925
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
15127
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
14926
15128
  sections.push({
14927
15129
  type: "scheduling",
14928
- insertAfter: effectiveInsertAfter,
15130
+ insertAfter: widgetId,
15131
+ anchorId,
15132
+ beforeId: beforeId ?? null,
14929
15133
  pagePath: window.location.pathname,
14930
15134
  ...scheduleId ? { scheduleId } : {}
14931
15135
  });
@@ -14939,7 +15143,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
14939
15143
  for (let i = pending.length - 1; i >= 0; i--) {
14940
15144
  const entry = pending[i];
14941
15145
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
14942
- if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId ?? null)) {
15146
+ const { anchorId, beforeId } = resolveEntryAnchor(entry);
15147
+ if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
14943
15148
  pending.splice(i, 1);
14944
15149
  }
14945
15150
  }
@@ -15097,6 +15302,11 @@ function applyLinkByKey(key, val) {
15097
15302
  hrefAnchors.forEach((el) => applyLinkHref(el, val));
15098
15303
  }
15099
15304
  }
15305
+ function isInsideLinkEditor(target) {
15306
+ return Boolean(
15307
+ target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest("[data-ohw-section-picker]") || target.closest("[data-ohw-navbar-container-chrome]") || target.closest("[data-ohw-navbar-add-button]") || target.closest("[data-ohw-footer-container-chrome]") || target.closest("[data-ohw-footer-add-button]") || target.closest("[data-ohw-more-menu]") || target.closest('[data-slot="dropdown-menu-content"]') || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
15308
+ );
15309
+ }
15100
15310
  function isInsideFloatingPanel(target) {
15101
15311
  return Boolean(target.closest("[data-ohw-floating-panel]"));
15102
15312
  }
@@ -15104,11 +15314,6 @@ function isPointOverFloatingPanel(clientX, clientY) {
15104
15314
  const el = document.elementFromPoint(clientX, clientY);
15105
15315
  return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
15106
15316
  }
15107
- function isInsideLinkEditor(target) {
15108
- return Boolean(
15109
- target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest("[data-ohw-section-picker]") || target.closest("[data-ohw-navbar-container-chrome]") || target.closest("[data-ohw-navbar-add-button]") || target.closest("[data-ohw-footer-container-chrome]") || target.closest("[data-ohw-footer-add-button]") || target.closest("[data-ohw-more-menu]") || target.closest('[data-slot="dropdown-menu-content"]') || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
15110
- );
15111
- }
15112
15317
  function getHrefKeyFromElement(el) {
15113
15318
  if (!el) return null;
15114
15319
  const anchor = el.closest("[data-ohw-href-key]");
@@ -15367,7 +15572,7 @@ function getNavigationSelectionParent(el) {
15367
15572
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
15368
15573
  return getFooterLinksContainer();
15369
15574
  }
15370
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
15575
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isFooterLinksContainer(el) || isInferredFooterGroup2(el)) {
15371
15576
  return getNavigationRoot(el);
15372
15577
  }
15373
15578
  return null;
@@ -15582,7 +15787,6 @@ var ICONS = {
15582
15787
  insertUnorderedList: '<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>',
15583
15788
  insertOrderedList: '<line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>'
15584
15789
  };
15585
- var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
15586
15790
  var SELECTION_CHROME_GAP2 = 4;
15587
15791
  var TOOLBAR_STROKE_GAP2 = 4;
15588
15792
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -15962,6 +16166,8 @@ function StateToggle({
15962
16166
  );
15963
16167
  }
15964
16168
  var contentCache = /* @__PURE__ */ new Map();
16169
+ var fetchedContentPaths = /* @__PURE__ */ new Set();
16170
+ var brandingCache = /* @__PURE__ */ new Map();
15965
16171
  var OHW_LOADER_STYLE = {
15966
16172
  position: "fixed",
15967
16173
  inset: 0,
@@ -15999,6 +16205,89 @@ function OhwLoaderSpinner() {
15999
16205
  )
16000
16206
  ] });
16001
16207
  }
16208
+ function OhwBrandMark() {
16209
+ return /* @__PURE__ */ jsxs20(
16210
+ "svg",
16211
+ {
16212
+ width: "16",
16213
+ height: "16",
16214
+ viewBox: "0 0 48 48",
16215
+ fill: "none",
16216
+ "aria-hidden": true,
16217
+ style: { display: "block", flexShrink: 0 },
16218
+ xmlns: "http://www.w3.org/2000/svg",
16219
+ children: [
16220
+ /* @__PURE__ */ jsx33(
16221
+ "mask",
16222
+ {
16223
+ id: "ohw-badge-mark",
16224
+ style: { maskType: "luminance" },
16225
+ maskUnits: "userSpaceOnUse",
16226
+ x: "0",
16227
+ y: "0",
16228
+ width: "48",
16229
+ height: "48",
16230
+ children: /* @__PURE__ */ jsx33("path", { d: "M23.8741 48C37.0594 48 47.7481 37.2548 47.7481 24C47.7481 10.7452 37.0594 0 23.8741 0C10.6888 0 0 10.7452 0 24C0 37.2548 10.6888 48 23.8741 48Z", fill: "white" })
16231
+ }
16232
+ ),
16233
+ /* @__PURE__ */ jsxs20("g", { mask: "url(#ohw-badge-mark)", children: [
16234
+ /* @__PURE__ */ jsx33("path", { d: "M23.8731 48.0497C37.0584 48.0497 47.7472 37.3046 47.7472 24.0497C47.7472 10.7949 37.0584 0.0497208 23.8731 0.0497208C10.6878 0.0497208 -0.000976562 10.7949 -0.000976562 24.0497C-0.000976562 37.3046 10.6878 48.0497 23.8731 48.0497Z", fill: "#0078E5" }),
16235
+ /* @__PURE__ */ jsx33("path", { d: "M17.1307 14.7172C13.1687 14.7172 9.38102 18.1154 8.65114 22.34C8.38885 23.8488 8.5598 25.2581 9.06929 26.4451C6.20005 29.1677 1.77216 27.8721 -1.40212 26.1536C-2.73618 25.4317 -3.92695 27.4745 -2.59037 28.1981C1.33389 30.3226 6.86037 31.6621 10.4402 28.4188C11.4718 29.3859 12.867 29.9621 14.4894 29.9621C18.4161 29.9621 22.2038 26.5318 22.9337 22.34C23.6636 18.1162 21.0566 14.7172 17.1298 14.7172H17.1307ZM19.9798 22.34C19.5281 25.0399 17.2689 27.231 14.9754 27.231C12.6466 27.231 11.1877 25.0399 11.6394 22.34C12.1262 19.6401 14.3151 17.4482 16.6438 17.4482C18.9374 17.4482 20.4667 19.6401 19.9798 22.34Z", fill: "white" }),
16236
+ /* @__PURE__ */ jsx33("path", { d: "M40.0017 27.0262C39.1797 27.081 38.2721 26.995 37.4668 26.7415C37.3344 26.6993 37.28 26.5401 37.3529 26.4205C37.5959 26.0212 37.8255 25.6152 38.009 25.1889C38.1071 24.9918 38.2018 24.793 38.2897 24.5908C38.3274 24.5041 38.4163 24.451 38.5101 24.4619C38.63 24.4754 38.7054 24.4821 38.8881 24.4821L39.1529 24.4796L39.8283 24.4543C45.9229 24.0492 50.4765 20.4319 54.8466 16.9014C56.0172 15.9554 57.6932 17.6208 56.5116 18.5752C51.7687 22.4065 47.1966 26.5081 40.9319 26.9689", fill: "white" }),
16237
+ /* @__PURE__ */ jsx33("path", { d: "M37.9687 24.27C38.4472 23.1319 38.7656 21.9045 38.9609 20.6991C39.5927 16.76 38.5058 14.2193 36.1553 14.2193C34.1835 14.2193 33.1469 17.2427 32.9199 18.8694C32.743 19.9872 32.5914 22.1219 33.6028 23.9524C33.7553 24.259 34.15 24.7712 34.471 25.1259C34.5447 25.2067 34.6746 25.2 34.7349 25.1082C34.9528 24.7788 35.1615 24.4039 35.3584 24.0257C35.5444 23.6677 35.5888 23.6138 35.8587 23.0207C35.8838 22.966 35.8813 22.9002 35.8478 22.8505C35.5888 22.4597 35.2168 21.9787 35.1204 21.4614C34.9184 20.4455 34.9436 19.2257 35.2218 18.1078C35.4413 17.3118 35.7195 16.7844 35.9039 16.5106C35.9466 16.4466 36.0279 16.4129 36.0975 16.4449C36.369 16.5671 36.5827 16.8838 36.7385 17.396C37.0167 18.2089 37.0167 19.3782 36.814 20.6999C36.6991 21.5271 36.4771 22.3729 36.1746 23.1673C36.1293 23.308 36.0757 23.4461 36.0187 23.5826C36.0187 23.5868 36.0187 23.591 36.0187 23.5961C35.9911 23.6946 35.9207 23.8067 35.8846 23.901C35.5536 24.5497 35.2344 25.1697 34.8439 25.7838C34.8388 25.7863 34.8346 25.7914 34.8296 25.7931C34.6528 26.0525 34.4718 26.2901 34.2866 26.4965C34.2774 26.5099 34.2682 26.5234 34.2589 26.5369C34.2405 26.5638 34.2179 26.5815 34.1944 26.595C33.5064 27.3212 32.7665 27.6893 32.0123 27.6893C31.8606 27.6893 31.6838 27.664 31.507 27.4349C31.3042 27.1299 30.85 26.0879 31.2539 22.9112C31.4785 21.3949 31.8011 20.0268 31.931 19.5408C31.9579 19.4413 31.8908 19.3419 31.7886 19.3293L30.0062 19.1162C29.9241 19.1061 29.8478 19.1566 29.8252 19.2366C29.2704 21.1615 27.0305 27.6885 24.9599 27.6885C24.328 27.6885 24.1512 26.6211 24.1001 26.2909C23.775 23.6264 25.528 18.5492 29.5302 16.2267C29.6048 16.1838 29.635 16.0928 29.5998 16.0136L28.9042 14.467C28.8632 14.3752 28.7501 14.3389 28.6638 14.3886C25.8079 16.0414 24.1847 18.5231 23.2915 20.3436C22.2046 22.5793 21.6993 25.0189 21.9515 26.8738C22.1795 28.7794 23.1398 29.872 24.6054 29.872C26.0543 29.872 27.4369 28.9066 28.7098 27.0187C28.7953 26.8915 28.9889 26.9311 29.0149 27.0819C29.2646 28.5283 29.9811 29.872 31.6579 29.872C33.5282 29.872 35.3232 28.7288 36.7134 26.6447C36.7712 26.5874 36.7972 26.5411 36.8181 26.4906C36.8232 26.4931 36.8282 26.4948 36.8332 26.4973C37.01 26.2025 37.181 25.9051 37.3444 25.6027C37.4508 25.4005 37.5572 25.1992 37.6586 24.9945C37.7181 24.874 37.7743 24.7527 37.8262 24.6289C37.838 24.6002 37.8547 24.5665 37.8706 24.5337", fill: "white" }),
16238
+ /* @__PURE__ */ jsx33("path", { d: "M30.5839 31.6397C25.7546 34.8577 19.4773 34.7853 14.6907 31.5243C13.5368 30.7384 12.5044 32.6498 13.6474 33.4281C19.034 37.0985 26.3077 37.096 31.7218 33.488C32.8791 32.7172 31.7478 30.8639 30.5839 31.6397Z", fill: "white" })
16239
+ ] })
16240
+ ]
16241
+ }
16242
+ );
16243
+ }
16244
+ var OHW_BADGE_STYLE = {
16245
+ position: "fixed",
16246
+ left: 20,
16247
+ bottom: 20,
16248
+ zIndex: 2147483e3,
16249
+ boxSizing: "border-box",
16250
+ display: "inline-flex",
16251
+ alignItems: "center",
16252
+ gap: 0,
16253
+ padding: "6px 8px",
16254
+ margin: 0,
16255
+ background: "#ffffff",
16256
+ border: "1px solid #e7e5e4",
16257
+ borderRadius: 9999,
16258
+ boxShadow: "0 1px 3px rgba(0, 0, 0, 0.1)",
16259
+ color: "#0c0a09",
16260
+ textDecoration: "none",
16261
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
16262
+ };
16263
+ var OHW_BADGE_LABEL_STYLE = {
16264
+ padding: "0 4px",
16265
+ fontSize: 14,
16266
+ lineHeight: "24px",
16267
+ fontWeight: 500,
16268
+ fontStyle: "normal",
16269
+ letterSpacing: "normal",
16270
+ textTransform: "none",
16271
+ color: "#0c0a09",
16272
+ whiteSpace: "nowrap"
16273
+ };
16274
+ function MadeWithOhhWells() {
16275
+ return /* @__PURE__ */ jsxs20(
16276
+ "a",
16277
+ {
16278
+ href: "https://ohhwells.com",
16279
+ target: "_blank",
16280
+ rel: "noopener noreferrer",
16281
+ "aria-label": "Made with OhhWells",
16282
+ "data-ohw-badge": "",
16283
+ style: OHW_BADGE_STYLE,
16284
+ children: [
16285
+ /* @__PURE__ */ jsx33(OhwBrandMark, {}),
16286
+ /* @__PURE__ */ jsx33("span", { style: OHW_BADGE_LABEL_STYLE, children: "Made with OhhWells" })
16287
+ ]
16288
+ }
16289
+ );
16290
+ }
16002
16291
  var OHW_LOADER_PREHYDRATE_SCRIPT = `(function(){try{var p=location.hostname.split(".");var fromHost=p.length>=3&&p[0]!=="www"?p[0]:"";var fromQuery=new URLSearchParams(location.search).get("subdomain")||"";if(!fromHost&&!fromQuery)return;var e=document.getElementById("ohw-loader");if(e)e.style.display="flex"}catch(e){}})();`;
16003
16292
  function resolveSubdomain(subdomainFromQuery) {
16004
16293
  if (subdomainFromQuery) return subdomainFromQuery;
@@ -16058,6 +16347,7 @@ function OhhwellsBridge() {
16058
16347
  }
16059
16348
  }, []);
16060
16349
  const [fetchState, setFetchState] = useState13("idle");
16350
+ const [showBranding, setShowBranding] = useState13(false);
16061
16351
  const autoSaveTimers = useRef10(/* @__PURE__ */ new Map());
16062
16352
  const activeElRef = useRef10(null);
16063
16353
  const pointerHeldRef = useRef10(false);
@@ -16406,13 +16696,6 @@ function OhhwellsBridge() {
16406
16696
  const [isItemDragging, setIsItemDragging] = useState13(false);
16407
16697
  const [isFooterFrameSelection, setIsFooterFrameSelection] = useState13(false);
16408
16698
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
16409
- const [floatingPanel, setFloatingPanel] = useState13(null);
16410
- const floatingPanelOpenRef = useRef10(false);
16411
- floatingPanelOpenRef.current = floatingPanel !== null;
16412
- const [floatingPanelPos, setFloatingPanelPos] = useState13(null);
16413
- const [logoSizeDraft, setLogoSizeDraft] = useState13(null);
16414
- const [editorViewport, setEditorViewport] = useState13("desktop");
16415
- const [parentScrollSnap, setParentScrollSnap] = useState13(null);
16416
16699
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState13(null);
16417
16700
  const [footerHeadingVisible, setFooterHeadingVisible] = useState13(null);
16418
16701
  const footerDragRef = useRef10(null);
@@ -16430,6 +16713,13 @@ function OhhwellsBridge() {
16430
16713
  const brandKitRef = useRef10("");
16431
16714
  const stylesRef = useRef10("");
16432
16715
  const pendingDeleteUndoRef = useRef10(null);
16716
+ const [floatingPanel, setFloatingPanel] = useState13(null);
16717
+ const floatingPanelOpenRef = useRef10(false);
16718
+ const setFloatingPanelRef = useRef10(setFloatingPanel);
16719
+ const [floatingPanelPos, setFloatingPanelPos] = useState13(null);
16720
+ const [logoSizeDraft, setLogoSizeDraft] = useState13(null);
16721
+ const [editorViewport, setEditorViewport] = useState13("desktop");
16722
+ const [parentScrollSnap, setParentScrollSnap] = useState13(null);
16433
16723
  const [sitePages, setSitePages] = useState13([]);
16434
16724
  const [sectionsByPath, setSectionsByPath] = useState13({});
16435
16725
  const sectionsPrefetchGenRef = useRef10(0);
@@ -16438,7 +16728,18 @@ function OhhwellsBridge() {
16438
16728
  const linkPopoverOpenRef = useRef10(false);
16439
16729
  const linkPopoverGraceUntilRef = useRef10(0);
16440
16730
  setLinkPopoverRef.current = setLinkPopover;
16731
+ setFloatingPanelRef.current = setFloatingPanel;
16441
16732
  linkPopoverSessionRef.current = linkPopover;
16733
+ floatingPanelOpenRef.current = Boolean(floatingPanel);
16734
+ useEffect13(() => {
16735
+ const syncViewport = () => {
16736
+ const next = window.innerWidth <= 480 ? "mobile" : "desktop";
16737
+ setEditorViewport((prev) => prev === next ? prev : next);
16738
+ };
16739
+ syncViewport();
16740
+ window.addEventListener("resize", syncViewport);
16741
+ return () => window.removeEventListener("resize", syncViewport);
16742
+ }, []);
16442
16743
  const {
16443
16744
  navDragRef,
16444
16745
  navDropSlots,
@@ -17766,14 +18067,15 @@ function OhhwellsBridge() {
17766
18067
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
17767
18068
  }
17768
18069
  applyBrandChrome(content);
18070
+ initSectionInstancesFromContent(content, window.location.pathname);
17769
18071
  for (const [key, val] of Object.entries(content)) {
17770
18072
  if (key === "__ohw_sections") continue;
17771
18073
  if (key === AI_SECTIONS_KEY) continue;
18074
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
18075
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
17772
18076
  if (key === BRAND_KIT_KEY) continue;
17773
18077
  if (key === STYLE_STORE_KEY) continue;
17774
18078
  if (BRAND_CHROME_KEYS.has(key)) continue;
17775
- if (key === LOGO_PLACEHOLDER_KEY) continue;
17776
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
17777
18079
  if (applyVideoSettingNode(key, val)) continue;
17778
18080
  if (applyCarouselNode(key, val)) continue;
17779
18081
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -17821,7 +18123,6 @@ function OhhwellsBridge() {
17821
18123
  if (isEditModeRef.current) requestMissingSocialIconsRef.current();
17822
18124
  enforceLinkHrefs();
17823
18125
  initSectionsFromContent(content, true);
17824
- initSectionInstancesFromContent(content, window.location.pathname);
17825
18126
  sectionsLoadedRef.current = true;
17826
18127
  pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
17827
18128
  if (imageLoads.length === 0) return Promise.resolve();
@@ -17833,16 +18134,22 @@ function OhhwellsBridge() {
17833
18134
  };
17834
18135
  const cached = contentCache.get(subdomain);
17835
18136
  if (cached) {
18137
+ setShowBranding(brandingCache.get(subdomain) ?? false);
17836
18138
  applyContent(cached).finally(() => setFetchState("done"));
17837
18139
  return;
17838
18140
  }
17839
18141
  let cancelled = false;
17840
18142
  setFetchState("loading");
17841
18143
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17842
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
18144
+ const initialPath = pathname;
18145
+ fetchedContentPaths.add(`${subdomain}::${initialPath}`);
18146
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
17843
18147
  if (cancelled) return;
17844
18148
  const content = data?.content ?? {};
18149
+ const branding = Boolean(data?.showBranding);
17845
18150
  contentCache.set(subdomain, content);
18151
+ brandingCache.set(subdomain, branding);
18152
+ setShowBranding(branding);
17846
18153
  return applyContent(content);
17847
18154
  }).catch(() => {
17848
18155
  }).finally(() => {
@@ -17975,11 +18282,11 @@ function OhhwellsBridge() {
17975
18282
  for (const [key, val] of Object.entries(content)) {
17976
18283
  if (key === "__ohw_sections") continue;
17977
18284
  if (key === AI_SECTIONS_KEY) continue;
18285
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
18286
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
17978
18287
  if (key === BRAND_KIT_KEY) continue;
17979
18288
  if (key === STYLE_STORE_KEY) continue;
17980
18289
  if (BRAND_CHROME_KEYS.has(key)) continue;
17981
- if (key === LOGO_PLACEHOLDER_KEY) continue;
17982
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
17983
18290
  if (applyVideoSettingNode(key, val)) continue;
17984
18291
  if (applyCarouselNode(key, val)) continue;
17985
18292
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -18025,6 +18332,17 @@ function OhhwellsBridge() {
18025
18332
  debounceTimer = setTimeout(applyFromCache, 150);
18026
18333
  };
18027
18334
  applyFromCache();
18335
+ const pathCacheKey = `${subdomain}::${pathname}`;
18336
+ if (!fetchedContentPaths.has(pathCacheKey)) {
18337
+ fetchedContentPaths.add(pathCacheKey);
18338
+ const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
18339
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
18340
+ if (!data?.content) return;
18341
+ contentCache.set(subdomain, data.content);
18342
+ applyFromCache();
18343
+ }).catch(() => {
18344
+ });
18345
+ }
18028
18346
  observer = new MutationObserver(scheduleApply);
18029
18347
  observer.observe(document.body, { childList: true, subtree: true });
18030
18348
  return () => {
@@ -18138,25 +18456,13 @@ function OhhwellsBridge() {
18138
18456
  };
18139
18457
  const t1 = setTimeout(measure, 50);
18140
18458
  const t2 = setTimeout(measure, 500);
18141
- let lastWidth = window.innerWidth;
18142
- let resizeTimers = [];
18143
- const clearResizeTimers = () => {
18144
- resizeTimers.forEach(clearTimeout);
18145
- resizeTimers = [];
18146
- };
18147
- const handleResize = () => {
18148
- if (window.innerWidth === lastWidth) return;
18149
- lastWidth = window.innerWidth;
18150
- clearResizeTimers();
18151
- resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
18152
- };
18153
- window.addEventListener("resize", handleResize);
18459
+ const ro = new ResizeObserver(schedule);
18460
+ ro.observe(document.body);
18154
18461
  return () => {
18155
18462
  clearTimeout(t1);
18156
18463
  clearTimeout(t2);
18157
18464
  if (raf != null) cancelAnimationFrame(raf);
18158
- clearResizeTimers();
18159
- window.removeEventListener("resize", handleResize);
18465
+ ro.disconnect();
18160
18466
  };
18161
18467
  }, [pathname, isEditMode, postToParent2]);
18162
18468
  useEffect13(() => {
@@ -18402,9 +18708,6 @@ function OhhwellsBridge() {
18402
18708
  if (target.closest("[data-ohw-state-toggle]")) return;
18403
18709
  if (target.closest("[data-ohw-max-badge]")) return;
18404
18710
  if (isInsideLinkEditor(target)) return;
18405
- if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
18406
- clearMediaSelectionRef.current();
18407
- }
18408
18711
  if (isInsideFloatingPanel(target)) return;
18409
18712
  if (target.closest("[data-ohw-form-toolbar]")) return;
18410
18713
  if (target.closest(
@@ -18412,6 +18715,9 @@ function OhhwellsBridge() {
18412
18715
  )) {
18413
18716
  return;
18414
18717
  }
18718
+ if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
18719
+ clearMediaSelectionRef.current();
18720
+ }
18415
18721
  {
18416
18722
  const formEl = getFormElement(target);
18417
18723
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -18563,14 +18869,6 @@ function OhhwellsBridge() {
18563
18869
  }
18564
18870
  const clickedButton = findClosestButtonLike(target);
18565
18871
  const buttonOnMedia = Boolean(clickedButton && isMediaEditable(editable) && editable.contains(clickedButton));
18566
- console.log("[click-debug]", {
18567
- editableType: editable.dataset.ohwEditable,
18568
- editableTag: editable.tagName,
18569
- targetTag: target.tagName,
18570
- clickedButtonTag: clickedButton?.tagName ?? null,
18571
- buttonOnMedia,
18572
- isMediaEditableEditable: isMediaEditable(editable)
18573
- });
18574
18872
  if (isMediaEditable(editable) && !buttonOnMedia) {
18575
18873
  e.preventDefault();
18576
18874
  e.stopPropagation();
@@ -18597,11 +18895,6 @@ function OhhwellsBridge() {
18597
18895
  const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
18598
18896
  const hrefCtx = getHrefKeyFromElement(hrefLookupTarget);
18599
18897
  const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
18600
- console.log("[click-debug 2]", {
18601
- hrefLookupTargetTag: hrefLookupTarget.tagName,
18602
- hrefCtx: hrefCtx ? { key: hrefCtx.key } : null,
18603
- navAnchorTag: navAnchor?.tagName ?? null
18604
- });
18605
18898
  if (navAnchor) {
18606
18899
  e.preventDefault();
18607
18900
  e.stopPropagation();
@@ -18771,6 +19064,9 @@ function OhhwellsBridge() {
18771
19064
  setHoveredItemRect(null);
18772
19065
  hoveredNavContainerRef.current = null;
18773
19066
  setHoveredNavContainerRect(null);
19067
+ siblingHintElRef.current = null;
19068
+ setSiblingHintRect(null);
19069
+ setSiblingHintRects([]);
18774
19070
  return;
18775
19071
  }
18776
19072
  {
@@ -18889,7 +19185,6 @@ function OhhwellsBridge() {
18889
19185
  hoveredNavContainerRef.current = null;
18890
19186
  setHoveredNavContainerRect(null);
18891
19187
  hoveredItemElRef.current = editable;
18892
- setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
18893
19188
  }
18894
19189
  }
18895
19190
  }
@@ -19186,7 +19481,7 @@ function OhhwellsBridge() {
19186
19481
  }
19187
19482
  };
19188
19483
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
19189
- if (linkPopoverOpenRef.current) {
19484
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
19190
19485
  if (hoveredImageRef.current) {
19191
19486
  hoveredImageRef.current = null;
19192
19487
  hoveredImageHasTextOverlapRef.current = false;
@@ -19551,8 +19846,7 @@ function OhhwellsBridge() {
19551
19846
  };
19552
19847
  const handleMouseMove = (e) => {
19553
19848
  const { clientX, clientY } = e;
19554
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
19555
- if (isOverEditorChrome(clientX, clientY)) {
19849
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
19556
19850
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
19557
19851
  formHoverElRef.current = null;
19558
19852
  setFormHoverRect(null);
@@ -19560,6 +19854,12 @@ function OhhwellsBridge() {
19560
19854
  setHoveredItemRect(null);
19561
19855
  hoveredNavContainerRef.current = null;
19562
19856
  setHoveredNavContainerRect(null);
19857
+ siblingHintElRef.current = null;
19858
+ setSiblingHintRect(null);
19859
+ setSiblingHintRects([]);
19860
+ dismissImageHover();
19861
+ clearImageHover();
19862
+ setSectionGap(null);
19563
19863
  return;
19564
19864
  }
19565
19865
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -19571,7 +19871,11 @@ function OhhwellsBridge() {
19571
19871
  if (e.data?.type !== "ow:pointer-sync") return;
19572
19872
  const { clientX, clientY } = e.data;
19573
19873
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
19574
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
19874
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
19875
+ dismissImageHover();
19876
+ clearImageHover();
19877
+ return;
19878
+ }
19575
19879
  if (probeSocialsRowAt(clientX, clientY)) return;
19576
19880
  probeSectionGapAt(clientX, clientY);
19577
19881
  probeImageAt(clientX, clientY);
@@ -19871,6 +20175,7 @@ function OhhwellsBridge() {
19871
20175
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
19872
20176
  }
19873
20177
  applyBrandChrome(content);
20178
+ initSectionInstancesFromContent(content, window.location.pathname);
19874
20179
  let sectionsJson = null;
19875
20180
  for (const [key, val] of Object.entries(content)) {
19876
20181
  if (key === "__ohw_sections") {
@@ -19878,11 +20183,11 @@ function OhhwellsBridge() {
19878
20183
  continue;
19879
20184
  }
19880
20185
  if (key === AI_SECTIONS_KEY) continue;
20186
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
20187
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
19881
20188
  if (key === BRAND_KIT_KEY) continue;
19882
20189
  if (key === STYLE_STORE_KEY) continue;
19883
20190
  if (BRAND_CHROME_KEYS.has(key)) continue;
19884
- if (key === LOGO_PLACEHOLDER_KEY) continue;
19885
- if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
19886
20191
  if (applyVideoSettingNode(key, val)) continue;
19887
20192
  if (applyCarouselNode(key, val)) continue;
19888
20193
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -19912,7 +20217,6 @@ function OhhwellsBridge() {
19912
20217
  sectionsLoadedRef.current = true;
19913
20218
  pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
19914
20219
  }
19915
- initSectionInstancesFromContent(content, window.location.pathname);
19916
20220
  editContentRef.current = { ...editContentRef.current, ...content };
19917
20221
  reconcileNavbarItemsFromContent(editContentRef.current);
19918
20222
  reconcileFooterOrderFromContent(editContentRef.current);
@@ -20057,12 +20361,35 @@ function OhhwellsBridge() {
20057
20361
  window.addEventListener("message", handleAiSetBrand);
20058
20362
  const handleAiSetStyles = (e) => {
20059
20363
  if (e.data?.type !== "ow:ai-set-styles") return;
20060
- const value = typeof e.data.value === "string" ? e.data.value : "";
20364
+ let value = typeof e.data.value === "string" ? e.data.value : "";
20061
20365
  const previous = stylesRef.current;
20366
+ let previousSections;
20367
+ const store = parseStyleStore(value);
20368
+ if (store) {
20369
+ const folded = foldAlignIntoTrees(parseAiSectionsState(aiSectionsRef.current), store);
20370
+ if (folded.changed) {
20371
+ const nextSections = serializeAiSectionsState(folded.state);
20372
+ if (nextSections !== aiSectionsRef.current) {
20373
+ previousSections = aiSectionsRef.current;
20374
+ aiSectionsRef.current = nextSections;
20375
+ applyAiSectionsToDom(folded.state);
20376
+ postToParentRef.current({
20377
+ type: "ow:change",
20378
+ nodes: [{ key: AI_SECTIONS_KEY, text: nextSections }]
20379
+ });
20380
+ }
20381
+ value = JSON.stringify(folded.store);
20382
+ }
20383
+ }
20062
20384
  stylesRef.current = value;
20063
20385
  applyStylesToDom(parseStyleStore(value));
20064
20386
  postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
20065
- postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
20387
+ postToParentRef.current({
20388
+ type: "ow:ai-styles-applied",
20389
+ previous,
20390
+ value,
20391
+ ...previousSections !== void 0 ? { previousSections } : {}
20392
+ });
20066
20393
  };
20067
20394
  window.addEventListener("message", handleAiSetStyles);
20068
20395
  const handleGetBrand = (e) => {
@@ -20134,6 +20461,34 @@ function OhhwellsBridge() {
20134
20461
  });
20135
20462
  };
20136
20463
  window.addEventListener("message", handleDeleteSection);
20464
+ const handleDuplicateSection = (e) => {
20465
+ if (e.data?.type !== "ow:duplicate-section") return;
20466
+ const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
20467
+ if (!instanceId) return;
20468
+ const newId = newInstanceId();
20469
+ const currentEntries = getPageSectionOrderEntries(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname);
20470
+ const result = duplicateSectionInstance(instanceId, newId, window.location.pathname, currentEntries);
20471
+ if (!result) return;
20472
+ const { entries, keyRekeys } = result;
20473
+ const orderJson = JSON.stringify(entries);
20474
+ const nodes = [{ key: SECTION_ORDER_KEY, text: orderJson }];
20475
+ for (const { from, to } of keyRekeys) {
20476
+ const inherited = editContentRef.current[from];
20477
+ if (inherited !== void 0) nodes.push({ key: to, text: inherited });
20478
+ }
20479
+ editContentRef.current = {
20480
+ ...editContentRef.current,
20481
+ ...Object.fromEntries(nodes.map(({ key, text }) => [key, text]))
20482
+ };
20483
+ setAiSectionOrder(orderJson, window.location.pathname);
20484
+ postToParentRef.current({ type: "ow:change", nodes });
20485
+ window.dispatchEvent(new Event("resize"));
20486
+ const duplicateHeight = document.body.scrollHeight;
20487
+ if (duplicateHeight > 50) postToParentRef.current({ type: "ow:height", height: duplicateHeight });
20488
+ const clone = document.querySelector(`[data-ohw-instance="${CSS.escape(newId)}"]`);
20489
+ if (clone) aiSectionApiRef.current?.selectFromElement(clone);
20490
+ };
20491
+ window.addEventListener("message", handleDuplicateSection);
20137
20492
  const handleDeactivate = (e) => {
20138
20493
  if (e.data?.type !== "ow:deactivate") return;
20139
20494
  if (document.documentElement.hasAttribute("data-ohw-panel-dragging")) return;
@@ -20143,6 +20498,12 @@ function OhhwellsBridge() {
20143
20498
  closeLinkPopoverRef.current();
20144
20499
  return;
20145
20500
  }
20501
+ if (floatingPanelOpenRef.current) {
20502
+ setFloatingPanelRef.current(null);
20503
+ deselectRef.current();
20504
+ deactivateRef.current();
20505
+ return;
20506
+ }
20146
20507
  deselectRef.current();
20147
20508
  deactivateRef.current();
20148
20509
  clearMediaSelectionRef.current();
@@ -20417,8 +20778,12 @@ function OhhwellsBridge() {
20417
20778
  if (inserted) {
20418
20779
  const tracker = getSectionsTracker();
20419
20780
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
20420
- const h = document.body.scrollHeight;
20421
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
20781
+ const reportHeight = () => {
20782
+ const h = document.body.scrollHeight;
20783
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
20784
+ };
20785
+ reportHeight();
20786
+ setTimeout(reportHeight, 500);
20422
20787
  }
20423
20788
  };
20424
20789
  const handleSwitchSchedule = (e) => {
@@ -20820,11 +21185,12 @@ function OhhwellsBridge() {
20820
21185
  window.removeEventListener("message", handleMoveSection);
20821
21186
  window.removeEventListener("message", handlePanelDragging);
20822
21187
  window.removeEventListener("message", handleDeleteSection);
21188
+ window.removeEventListener("message", handleDuplicateSection);
20823
21189
  window.removeEventListener("message", handleDeactivate);
20824
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
20825
21190
  window.removeEventListener("message", handleToastAction);
20826
21191
  window.removeEventListener("message", handleFormCount);
20827
21192
  window.removeEventListener("message", handleUiEscape);
21193
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
20828
21194
  autoSaveTimers.current.forEach(clearTimeout);
20829
21195
  autoSaveTimers.current.clear();
20830
21196
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
@@ -21027,7 +21393,7 @@ function OhhwellsBridge() {
21027
21393
  postToParent2({
21028
21394
  type: "ow:ready",
21029
21395
  version: "1",
21030
- bridgeVersion: "0.1.82",
21396
+ bridgeVersion: "0.1.84",
21031
21397
  path: pathname,
21032
21398
  nodes: collectEditableNodes(editContentRef.current),
21033
21399
  sections
@@ -21490,6 +21856,7 @@ function OhhwellsBridge() {
21490
21856
  return /* @__PURE__ */ jsxs20(Fragment8, { children: [
21491
21857
  /* @__PURE__ */ jsx33("div", { id: "ohw-loader", suppressHydrationWarning: true, style: { ...OHW_LOADER_STYLE, display: "none" }, children: /* @__PURE__ */ jsx33(OhwLoaderSpinner, {}) }),
21492
21858
  /* @__PURE__ */ jsx33("script", { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: OHW_LOADER_PREHYDRATE_SCRIPT } }),
21859
+ subdomain && !isEditMode && showBranding && /* @__PURE__ */ jsx33(MadeWithOhhWells, {}),
21493
21860
  bridgeRoot ? createPortal2(
21494
21861
  /* @__PURE__ */ jsxs20(Fragment8, { children: [
21495
21862
  /* @__PURE__ */ jsx33("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
@@ -21945,6 +22312,59 @@ function OhhwellsBridge() {
21945
22312
  ) : null
21946
22313
  ] });
21947
22314
  }
22315
+
22316
+ // src/ui/EmptySection.tsx
22317
+ import Link3 from "next/link";
22318
+ import { Fragment as Fragment9, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
22319
+ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
22320
+ return /* @__PURE__ */ jsxs21(Fragment9, { children: [
22321
+ /* @__PURE__ */ jsx34(
22322
+ "p",
22323
+ {
22324
+ style: {
22325
+ fontFamily: "var(--brand-font-body)",
22326
+ fontSize: "0.75rem",
22327
+ fontWeight: 500,
22328
+ letterSpacing: "0.15em",
22329
+ textTransform: "uppercase",
22330
+ color: "var(--brand-accent)",
22331
+ marginBottom: "1.5rem"
22332
+ },
22333
+ children: /* @__PURE__ */ jsx34(Link3, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx34("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
22334
+ }
22335
+ ),
22336
+ /* @__PURE__ */ jsx34(
22337
+ "h1",
22338
+ {
22339
+ style: {
22340
+ fontFamily: "var(--brand-font-heading)",
22341
+ fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
22342
+ lineHeight: 1.1,
22343
+ letterSpacing: "-0.025em",
22344
+ color: "var(--brand-text)",
22345
+ marginBottom: "1rem"
22346
+ },
22347
+ ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
22348
+ children: title
22349
+ }
22350
+ ),
22351
+ /* @__PURE__ */ jsx34(
22352
+ "p",
22353
+ {
22354
+ style: {
22355
+ fontFamily: "var(--brand-font-body)",
22356
+ fontSize: "1rem",
22357
+ lineHeight: 1.7,
22358
+ fontWeight: 300,
22359
+ color: "var(--brand-text-muted)",
22360
+ maxWidth: "340px"
22361
+ },
22362
+ ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
22363
+ children: "This page doesn't have any content yet."
22364
+ }
22365
+ )
22366
+ ] });
22367
+ }
21948
22368
  export {
21949
22369
  AI_DEFAULT_BRAND,
21950
22370
  AI_TREE_SCHEMA_VERSIONS,
@@ -21961,6 +22381,7 @@ export {
21961
22381
  DropdownMenuItem,
21962
22382
  DropdownMenuSeparator,
21963
22383
  DropdownMenuTrigger,
22384
+ EmptySection,
21964
22385
  ItemActionToolbar,
21965
22386
  ItemInteractionLayer,
21966
22387
  LinkEditorPanel,