@ohhwells/bridge 0.1.79 → 0.1.80-next.242

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.cjs CHANGED
@@ -46,6 +46,7 @@ __export(index_exports, {
46
46
  DropdownMenuItem: () => DropdownMenuItem,
47
47
  DropdownMenuSeparator: () => DropdownMenuSeparator,
48
48
  DropdownMenuTrigger: () => DropdownMenuTrigger,
49
+ EmptySection: () => EmptySection,
49
50
  ItemActionToolbar: () => ItemActionToolbar,
50
51
  ItemInteractionLayer: () => ItemInteractionLayer,
51
52
  LinkEditorPanel: () => LinkEditorPanel,
@@ -406,10 +407,24 @@ var SECTION_ATTRS = {
406
407
  spacing: "data-ohw-style-spacing"
407
408
  };
408
409
  var NODE_WROTE_ATTR = "data-ohw-style-node";
409
- var NODE_PROPS = ["color", "font-family", "font-size", "background"];
410
+ var NODE_PROPS = [
411
+ "color",
412
+ "font-family",
413
+ "font-size",
414
+ "background",
415
+ "text-align",
416
+ "justify-content"
417
+ ];
418
+ var ALIGN_JUSTIFY = {
419
+ left: "flex-start",
420
+ center: "center",
421
+ right: "flex-end"
422
+ };
410
423
  function saveInline(el, prop) {
411
424
  const attr = `data-ohw-style-prev-${prop}`;
412
- if (!el.hasAttribute(attr)) el.setAttribute(attr, el.style.getPropertyValue(prop));
425
+ if (el.hasAttribute(attr)) return;
426
+ const value = el.style.getPropertyValue(prop) || (prop === "background" ? el.style.getPropertyValue("background-color") : "");
427
+ el.setAttribute(attr, value);
413
428
  }
414
429
  function restoreInline(el, prop) {
415
430
  const attr = `data-ohw-style-prev-${prop}`;
@@ -460,7 +475,8 @@ function applyStylesToDom(store) {
460
475
  const sections = document.querySelectorAll(
461
476
  `[data-ohw-section="${CSS.escape(sectionId)}"]`
462
477
  );
463
- for (const section of Array.from(sections)) {
478
+ for (const marker of Array.from(sections)) {
479
+ const section = marker.querySelector(":scope > [data-ai-section]") ?? marker;
464
480
  for (const [prop, attr] of Object.entries(SECTION_ATTRS)) {
465
481
  const value = override[prop];
466
482
  if (value === void 0) continue;
@@ -492,6 +508,13 @@ function applyStylesToDom(store) {
492
508
  el.style.setProperty("font-size", `${override.fontSize}px`, "important");
493
509
  el.setAttribute(NODE_WROTE_ATTR, "");
494
510
  }
511
+ if (override.align !== void 0) {
512
+ saveInline(el, "text-align");
513
+ saveInline(el, "justify-content");
514
+ el.style.setProperty("text-align", override.align, "important");
515
+ el.style.setProperty("justify-content", ALIGN_JUSTIFY[override.align] ?? "flex-start", "important");
516
+ el.setAttribute(NODE_WROTE_ATTR, "");
517
+ }
495
518
  if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
496
519
  const surface = buttonSurfaceOf(el);
497
520
  if (override.buttonBackground !== void 0) {
@@ -542,8 +565,6 @@ var AI_MOBILE_CSS = [
542
565
  // Group containers flatten to a column on phones; span placements come along for free.
543
566
  "[data-ai-group]{display:flex !important;flex-direction:column !important}",
544
567
  "[data-ai-group] > *{grid-column:auto !important}",
545
- // The 50:50 form collapses to a single stacked column on phones.
546
- "[data-ai-form]{grid-template-columns:1fr !important}",
547
568
  "[data-ai-section] img{max-width:100%}",
548
569
  "}",
549
570
  "@media (min-width: 769px) and (max-width: 1024px){",
@@ -589,6 +610,22 @@ function accentBandContext(brand) {
589
610
  function textAttrs(ctx, path) {
590
611
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
591
612
  }
613
+ var AI_RESPONSIVE_CSS = [
614
+ "@media (max-width: 960px) {",
615
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
616
+ ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
617
+ "}",
618
+ "@media (max-width: 640px) {",
619
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
620
+ " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
621
+ // Group containers flatten to a column on phones; span placements come along for free.
622
+ " [data-ai-responsive] [data-ai-group] { display: flex !important; flex-direction: column !important; }",
623
+ " [data-ai-responsive] [data-ai-group] > * { grid-column: auto !important; }",
624
+ " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
625
+ " [data-ai-responsive] { overflow-x: hidden; }",
626
+ " [data-ai-responsive] img { max-width: 100%; }",
627
+ "}"
628
+ ].join("\n");
592
629
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
593
630
  function MediaBox({
594
631
  refValue,
@@ -601,13 +638,17 @@ function MediaBox({
601
638
  const url = refValue ? ctx.resolveMedia(refValue) : null;
602
639
  const isIcon = /^(lucide|simple):/.test(refValue);
603
640
  const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
604
- const editAttrs = ctx.keyFor && editPath && !isIcon ? { "data-ohw-key": ctx.keyFor(editPath), "data-ohw-editable": "image" } : {};
641
+ const editAttrs = ctx.keyFor && editPath ? {
642
+ "data-ohw-key": ctx.keyFor(editPath),
643
+ "data-ohw-editable": isIcon ? "icon" : "image"
644
+ } : {};
605
645
  if (isIcon) {
606
646
  const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
607
647
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
608
648
  "span",
609
649
  {
610
650
  "data-ai-icon": refValue,
651
+ ...editAttrs,
611
652
  style: {
612
653
  display: "inline-flex",
613
654
  width: 48,
@@ -1493,7 +1534,7 @@ function CollectionBlock({ node, ctx, path }) {
1493
1534
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1494
1535
  "div",
1495
1536
  {
1496
- "data-ai-grid": "",
1537
+ "data-ai-grid": String(itemsPerRow),
1497
1538
  style: {
1498
1539
  display: "grid",
1499
1540
  gridTemplateColumns: `repeat(${itemsPerRow}, 1fr)`,
@@ -1640,9 +1681,9 @@ function renderNode(node, ctx, path) {
1640
1681
  const fieldStyle = {
1641
1682
  width: "100%",
1642
1683
  boxSizing: "border-box",
1643
- border: `1px solid ${ctx.brand.palette.accent}`,
1644
- borderRadius: AI_TREE_TOKENS.radiusButton,
1645
- padding: "12px 14px",
1684
+ border: `1px solid color-mix(in srgb, ${ctx.brand.palette.dark} 45%, #ffffff)`,
1685
+ borderRadius: 0,
1686
+ padding: 12,
1646
1687
  background: "#fff",
1647
1688
  color: ctx.brand.palette.dark,
1648
1689
  outline: "none",
@@ -1650,81 +1691,90 @@ function renderNode(node, ctx, path) {
1650
1691
  };
1651
1692
  const labelStyle = {
1652
1693
  ...typeStyle(AI_TREE_TOKENS.type.bodyMBold, ctx.brand.fonts.body),
1653
- color: ctx.brand.palette.dark
1694
+ color: ctx.brand.palette.dark,
1695
+ textAlign: "left",
1696
+ width: "100%"
1654
1697
  };
1655
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1656
- "form",
1657
- {
1658
- ...formAttrs,
1659
- "data-ai-form": "",
1660
- style: {
1661
- display: "grid",
1662
- gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
1663
- columnGap: 64,
1664
- rowGap: AI_TREE_TOKENS.spacing4
1665
- },
1666
- children: (node.children ?? []).map((child, i) => {
1667
- if (child.type === "input") {
1668
- const cs2 = child.slots ?? {};
1669
- const kind = str(cs2.kind);
1670
- const label = str(cs2.label);
1671
- const placeholder = str(cs2.placeholder);
1672
- const name = label.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "") || `field-${i}`;
1673
- const isTextarea = kind === "textarea";
1674
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1675
- "div",
1676
- {
1677
- style: {
1678
- display: "flex",
1679
- flexDirection: "column",
1680
- gap: 8,
1681
- ...isTextarea ? { gridColumn: "1 / -1", maxWidth: 780 } : {}
1698
+ const centered = ctx.sectionAlignment === "center";
1699
+ const submitAlign = centered ? "center" : "flex-start";
1700
+ const children = node.children ?? [];
1701
+ return (
1702
+ // 32px between the field group and the submit. In a stacked (centered) section the form is
1703
+ // capped at 780px and centered — the section's 12-col grid would otherwise leave it hugging
1704
+ // the left edge; a split section lets it fill its own column.
1705
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1706
+ "form",
1707
+ {
1708
+ ...formAttrs,
1709
+ "data-ai-form": "",
1710
+ style: {
1711
+ display: "flex",
1712
+ flexDirection: "column",
1713
+ gap: 32,
1714
+ width: "100%",
1715
+ ...centered ? { maxWidth: 780, marginLeft: "auto", marginRight: "auto" } : {}
1716
+ },
1717
+ children: [
1718
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 24, width: "100%", alignItems: "flex-start" }, children: children.map((child, i) => {
1719
+ if (child.type !== "input") return null;
1720
+ const cs = child.slots ?? {};
1721
+ const kind = str(cs.kind);
1722
+ const label = str(cs.label);
1723
+ const placeholder = str(cs.placeholder);
1724
+ const required = cs.required === true;
1725
+ const name = label.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "") || `field-${i}`;
1726
+ const isTextarea = kind === "textarea";
1727
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 8, width: "100%" }, children: [
1728
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { ...textAttrs(ctx, `${path}.c${i}.label`), style: labelStyle, children: label }),
1729
+ isTextarea ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1730
+ "textarea",
1731
+ {
1732
+ name,
1733
+ placeholder,
1734
+ required,
1735
+ style: { ...fieldStyle, height: 180, resize: "vertical" }
1736
+ }
1737
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1738
+ "input",
1739
+ {
1740
+ name,
1741
+ type: kind === "email" ? "email" : "text",
1742
+ placeholder,
1743
+ required,
1744
+ style: { ...fieldStyle, height: 48 }
1745
+ }
1746
+ )
1747
+ ] }, i);
1748
+ }) }),
1749
+ children.map((child, i) => {
1750
+ if (child.type === "input") return null;
1751
+ const cs = child.slots ?? {};
1752
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1753
+ "button",
1754
+ {
1755
+ type: "submit",
1756
+ style: {
1757
+ alignSelf: submitAlign,
1758
+ border: "none",
1759
+ cursor: "pointer",
1760
+ padding: "12px 24px",
1761
+ // Corner radius follows the host template's own buttons (measured from a template
1762
+ // CTA); 8px only when the page has no template button to match.
1763
+ borderRadius: ctx.buttonRadius ?? 8,
1764
+ // Brand-styled: primary fill, brand-derived label colour (not a fixed token) so it
1765
+ // reads correctly on custom palettes.
1766
+ background: ctx.brand.palette.primary,
1767
+ color: ctx.buttonLabel ?? ctx.brand.palette.light,
1768
+ ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
1769
+ },
1770
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
1682
1771
  },
1683
- children: [
1684
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { ...textAttrs(ctx, `${path}.c${i}.label`), style: labelStyle, children: label }),
1685
- isTextarea ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1686
- "textarea",
1687
- {
1688
- name,
1689
- placeholder,
1690
- style: { ...fieldStyle, height: 140, resize: "vertical" }
1691
- }
1692
- ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1693
- "input",
1694
- {
1695
- name,
1696
- type: kind === "email" ? "email" : "text",
1697
- placeholder,
1698
- style: { ...fieldStyle, height: 48 }
1699
- }
1700
- )
1701
- ]
1702
- },
1703
- i
1704
- );
1705
- }
1706
- const cs = child.slots ?? {};
1707
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1708
- "button",
1709
- {
1710
- type: "submit",
1711
- style: {
1712
- gridColumn: "1 / -1",
1713
- justifySelf: "start",
1714
- border: "none",
1715
- cursor: "pointer",
1716
- padding: `${AI_TREE_TOKENS.spacing3}px ${AI_TREE_TOKENS.spacing6}px`,
1717
- borderRadius: AI_TREE_TOKENS.radiusButton,
1718
- background: ctx.brand.palette.primary,
1719
- color: AI_TREE_TOKENS.textPrimaryForeground,
1720
- ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
1721
- },
1722
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
1723
- },
1724
- i
1725
- );
1726
- })
1727
- }
1772
+ i
1773
+ );
1774
+ })
1775
+ ]
1776
+ }
1777
+ )
1728
1778
  );
1729
1779
  }
1730
1780
  case "schedule-widget":
@@ -1747,7 +1797,13 @@ function renderNode(node, ctx, path) {
1747
1797
  return null;
1748
1798
  }
1749
1799
  }
1750
- function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1800
+ function AiTreeRenderer({
1801
+ tree,
1802
+ brand,
1803
+ buttonRadius,
1804
+ resolveMedia,
1805
+ editKeyPrefix
1806
+ }) {
1751
1807
  if (!isRenderableTree(tree)) {
1752
1808
  return null;
1753
1809
  }
@@ -1759,6 +1815,8 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1759
1815
  resolveMedia: resolveMedia ?? (() => null),
1760
1816
  cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
1761
1817
  keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
1818
+ sectionAlignment: (tree.settings ?? {}).alignment === "center" ? "center" : "left",
1819
+ buttonRadius,
1762
1820
  ...band ? { buttonLabel: band.buttonLabel } : {}
1763
1821
  };
1764
1822
  const settings = tree.settings ?? {};
@@ -1781,11 +1839,25 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1781
1839
  }
1782
1840
  })();
1783
1841
  const distributed = !isOverlay && settings.textDistribution;
1842
+ const rowAlignItems = (rowAlign) => {
1843
+ if (rowAlign === "top") return "start";
1844
+ if (rowAlign === "bottom") return "end";
1845
+ if (rowAlign === "center" || rowAlign === "stretch") return rowAlign;
1846
+ if (distributed === "space-between") return "stretch";
1847
+ return (distributed ?? settings.verticalPosition) === "top" ? "start" : "center";
1848
+ };
1849
+ const cellAlignStyle = (blockAlign) => blockAlign ? {
1850
+ display: "flex",
1851
+ flexDirection: "column",
1852
+ alignItems: blockAlign === "left" ? "flex-start" : blockAlign === "right" ? "flex-end" : "center",
1853
+ textAlign: blockAlign
1854
+ } : {};
1784
1855
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1785
1856
  "section",
1786
1857
  {
1787
1858
  "data-ai-section": tree.tag ?? "",
1788
1859
  ...bgAttrs,
1860
+ "data-ai-responsive": "",
1789
1861
  style: {
1790
1862
  position: "relative",
1791
1863
  padding: `${pad}px 0`,
@@ -1796,12 +1868,13 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1796
1868
  color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
1797
1869
  },
1798
1870
  children: [
1799
- isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
1871
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
1800
1872
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_MOBILE_CSS }),
1873
+ isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
1801
1874
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1802
1875
  "div",
1803
1876
  {
1804
- "data-ai-container": "",
1877
+ "data-ai-section-inner": "",
1805
1878
  style: {
1806
1879
  position: "relative",
1807
1880
  maxWidth: AI_TREE_TOKENS.sectionMaxWidth,
@@ -1812,12 +1885,12 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1812
1885
  children: tree.rows.map((row, r2) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1813
1886
  "div",
1814
1887
  {
1815
- "data-ai-row": "",
1888
+ "data-ai-columns": "",
1816
1889
  style: {
1817
1890
  display: "grid",
1818
1891
  gridTemplateColumns: "repeat(12, 1fr)",
1819
1892
  gap: AI_TREE_TOKENS.spacing6,
1820
- alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
1893
+ alignItems: rowAlignItems(row.align),
1821
1894
  marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
1822
1895
  },
1823
1896
  children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1827,6 +1900,8 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1827
1900
  style: {
1828
1901
  gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
1829
1902
  minWidth: 0,
1903
+ // Horizontal placement of the block's content within its column.
1904
+ ...cellAlignStyle(block.align),
1830
1905
  // space-between: each column becomes a flex column whose content spreads over
1831
1906
  // the full row height instead of clumping at the top.
1832
1907
  ...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
@@ -1888,6 +1963,13 @@ function deriveTemplateBrand() {
1888
1963
  }
1889
1964
  };
1890
1965
  }
1966
+ function deriveTemplateButtonRadius() {
1967
+ if (typeof document === "undefined") return null;
1968
+ const btn = document.querySelector('[data-ohw-role="button"]');
1969
+ if (!btn) return null;
1970
+ const radius = getComputedStyle(btn).borderTopLeftRadius;
1971
+ return radius || null;
1972
+ }
1891
1973
  var mounted = /* @__PURE__ */ new Map();
1892
1974
  function findTemplateSection(id) {
1893
1975
  for (const el of document.querySelectorAll(`[data-ohw-section="${CSS.escape(id)}"]`)) {
@@ -2039,6 +2121,7 @@ function applyAiSectionsToDom(state, options) {
2039
2121
  if (typeof document === "undefined") return;
2040
2122
  const brandOverride = deriveBrandOverride();
2041
2123
  const templateBrand = deriveTemplateBrand();
2124
+ const templateButtonRadius = deriveTemplateButtonRadius();
2042
2125
  const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
2043
2126
  const pagePath = window.location.pathname;
2044
2127
  const pageSections = state.sections.filter((entry) => (entry.path ?? "/") === pagePath);
@@ -2078,6 +2161,7 @@ function applyAiSectionsToDom(state, options) {
2078
2161
  {
2079
2162
  tree: entry.tree,
2080
2163
  brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
2164
+ buttonRadius: templateButtonRadius,
2081
2165
  resolveMedia,
2082
2166
  editKeyPrefix: `ai.${entry.id}`
2083
2167
  }
@@ -2226,6 +2310,7 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
2226
2310
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2227
2311
  import_radix_ui.Dialog.Overlay,
2228
2312
  {
2313
+ "data-ohw-scheduling-modal": "",
2229
2314
  className: "fixed inset-0 z-50",
2230
2315
  style: { background: "rgba(0,0,0,0.45)" }
2231
2316
  }
@@ -2233,6 +2318,7 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
2233
2318
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2234
2319
  import_radix_ui.Dialog.Content,
2235
2320
  {
2321
+ "data-ohw-scheduling-modal": "",
2236
2322
  className: "fixed left-1/2 top-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 bg-white rounded-xl shadow-xl outline-none font-body box-border overflow-hidden",
2237
2323
  style: { maxWidth: 400 },
2238
2324
  children: [
@@ -2706,7 +2792,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2706
2792
  const autoId = (0, import_react5.useId)();
2707
2793
  const insertAfter = insertAfterProp ?? autoId;
2708
2794
  const [schedule, setSchedule] = (0, import_react5.useState)(null);
2709
- const [loading, setLoading] = (0, import_react5.useState)(true);
2795
+ const [loading, setLoading] = (0, import_react5.useState)(initialScheduleId !== null);
2710
2796
  const [inEditor, setInEditor] = (0, import_react5.useState)(false);
2711
2797
  const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
2712
2798
  const [modalState, setModalState] = (0, import_react5.useState)(null);
@@ -2880,8 +2966,10 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2880
2966
  "*"
2881
2967
  );
2882
2968
  };
2883
- if (!inEditor && !loading && !schedule) return null;
2884
2969
  const sectionId = `scheduling-${insertAfter}`;
2970
+ if (!inEditor && !loading && !schedule) {
2971
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
2972
+ }
2885
2973
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2886
2974
  "section",
2887
2975
  {
@@ -7822,6 +7910,7 @@ function MediaOverlay({
7822
7910
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
7823
7911
  );
7824
7912
  }, [isVideo]);
7913
+ const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
7825
7914
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
7826
7915
  const box = {
7827
7916
  position: "fixed",
@@ -7951,17 +8040,17 @@ function MediaOverlay({
7951
8040
  },
7952
8041
  children: [
7953
8042
  isVideo ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
7954
- isVideo ? "Replace video" : "Replace image"
8043
+ replaceLabel
7955
8044
  ]
7956
8045
  }
7957
8046
  ),
7958
- replaceMode === "none" ? null : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
8047
+ showChrome && replaceMode !== "none" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7959
8048
  Button,
7960
8049
  {
7961
8050
  "data-ohw-media-overlay": "",
7962
8051
  variant: "outline",
7963
8052
  size: "sm",
7964
- "aria-label": isVideo ? "Replace video" : "Replace image",
8053
+ "aria-label": replaceLabel,
7965
8054
  className: "gap-1.5 cursor-pointer hover:bg-background",
7966
8055
  style: {
7967
8056
  ...OVERLAY_BUTTON_STYLE,
@@ -7984,7 +8073,7 @@ function MediaOverlay({
7984
8073
  },
7985
8074
  children: [
7986
8075
  isVideo ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
7987
- replaceMode === "full" ? isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image" : null
8076
+ replaceMode === "full" ? replaceLabel : null
7988
8077
  ]
7989
8078
  }
7990
8079
  )
@@ -12928,6 +13017,7 @@ function readLogoSizeState(content, placement) {
12928
13017
  function getLogoElement(el) {
12929
13018
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
12930
13019
  if (marked) return marked;
13020
+ if (el.closest('[data-ohw-editable="icon"]')) return null;
12931
13021
  const root = el.closest("nav, [data-ohw-nav-root], footer");
12932
13022
  if (!root) return null;
12933
13023
  const anchor = el.closest("a");
@@ -14856,21 +14946,10 @@ function parseSchedulingInsertAfter(insertAfter) {
14856
14946
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
14857
14947
  };
14858
14948
  }
14859
- function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
14860
- const parsed = parseSchedulingInsertAfter(insertAfter);
14861
- const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
14862
- const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
14863
- return { effectiveInsertAfter, insertBefore };
14864
- }
14865
- function getSchedulingMountPoint(insertAfter) {
14866
- const { anchor } = parseSchedulingInsertAfter(insertAfter);
14867
- let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
14868
- if (!anchorEl && anchor === "scheduling") {
14869
- const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
14870
- anchorEl = widgets.at(-1) ?? null;
14871
- }
14872
- if (!anchorEl) return null;
14873
- return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
14949
+ function resolveEntryAnchor(entry) {
14950
+ if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
14951
+ const parsed = parseSchedulingInsertAfter(entry.insertAfter);
14952
+ return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
14874
14953
  }
14875
14954
  function schedulingMountDepth(insertAfter) {
14876
14955
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -14887,8 +14966,7 @@ function getPageSchedulingEntries(raw) {
14887
14966
  }
14888
14967
  }
14889
14968
  function isSchedulingWidgetMissing(entry) {
14890
- const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
14891
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
14969
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
14892
14970
  }
14893
14971
  function hasMissingSchedulingWidgets(entries) {
14894
14972
  return entries.some(isSchedulingWidgetMissing);
@@ -14918,16 +14996,17 @@ function initSectionsFromContent(content, removeExisting = false) {
14918
14996
  } catch {
14919
14997
  }
14920
14998
  }
14921
- function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
14922
- const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
14923
- const sectionId = schedulingSectionId(effectiveInsertAfter);
14999
+ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
15000
+ const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
15001
+ const sectionId = schedulingSectionId(widgetId);
14924
15002
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
14925
- const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
14926
- if (!mountPoint) return false;
15003
+ const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
15004
+ if (!anchorEl) return false;
15005
+ const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
14927
15006
  const container = document.createElement("div");
14928
15007
  container.dataset.ohwSectionContainer = "scheduling";
14929
- if (insertBefore) {
14930
- const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
15008
+ if (beforeId) {
15009
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
14931
15010
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
14932
15011
  if (!beforePoint) return false;
14933
15012
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -14938,19 +15017,25 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
14938
15017
  }
14939
15018
  tail.insertAdjacentElement("afterend", container);
14940
15019
  }
14941
- const root = (0, import_client2.createRoot)(container);
14942
- (0, import_react_dom3.flushSync)(() => {
14943
- root.render(
14944
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
14945
- SchedulingWidget,
14946
- {
14947
- notifyOnConnect,
14948
- initialScheduleId: scheduleId,
14949
- insertAfter: effectiveInsertAfter
14950
- }
14951
- )
14952
- );
14953
- });
15020
+ try {
15021
+ const root = (0, import_client2.createRoot)(container);
15022
+ (0, import_react_dom3.flushSync)(() => {
15023
+ root.render(
15024
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
15025
+ SchedulingWidget,
15026
+ {
15027
+ notifyOnConnect,
15028
+ initialScheduleId: scheduleId,
15029
+ insertAfter: widgetId
15030
+ }
15031
+ )
15032
+ );
15033
+ });
15034
+ } catch (err) {
15035
+ console.error("[ow:scheduling] render threw", err);
15036
+ container.remove();
15037
+ return false;
15038
+ }
14954
15039
  const tracker = getSectionsTracker();
14955
15040
  let sections = [];
14956
15041
  try {
@@ -14958,10 +15043,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
14958
15043
  } catch {
14959
15044
  }
14960
15045
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
14961
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
15046
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
14962
15047
  sections.push({
14963
15048
  type: "scheduling",
14964
- insertAfter: effectiveInsertAfter,
15049
+ insertAfter: widgetId,
15050
+ anchorId,
15051
+ beforeId: beforeId ?? null,
14965
15052
  pagePath: window.location.pathname,
14966
15053
  ...scheduleId ? { scheduleId } : {}
14967
15054
  });
@@ -14975,7 +15062,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
14975
15062
  for (let i = pending.length - 1; i >= 0; i--) {
14976
15063
  const entry = pending[i];
14977
15064
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
14978
- if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
15065
+ const { anchorId, beforeId } = resolveEntryAnchor(entry);
15066
+ if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
14979
15067
  pending.splice(i, 1);
14980
15068
  }
14981
15069
  }
@@ -15133,6 +15221,11 @@ function applyLinkByKey(key, val) {
15133
15221
  hrefAnchors.forEach((el) => applyLinkHref(el, val));
15134
15222
  }
15135
15223
  }
15224
+ function isInsideLinkEditor(target) {
15225
+ return Boolean(
15226
+ 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"]')
15227
+ );
15228
+ }
15136
15229
  function isInsideFloatingPanel(target) {
15137
15230
  return Boolean(target.closest("[data-ohw-floating-panel]"));
15138
15231
  }
@@ -15140,11 +15233,6 @@ function isPointOverFloatingPanel(clientX, clientY) {
15140
15233
  const el = document.elementFromPoint(clientX, clientY);
15141
15234
  return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
15142
15235
  }
15143
- function isInsideLinkEditor(target) {
15144
- return Boolean(
15145
- 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"]')
15146
- );
15147
- }
15148
15236
  function getHrefKeyFromElement(el) {
15149
15237
  if (!el) return null;
15150
15238
  const anchor = el.closest("[data-ohw-href-key]");
@@ -15403,7 +15491,7 @@ function getNavigationSelectionParent(el) {
15403
15491
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
15404
15492
  return getFooterLinksContainer();
15405
15493
  }
15406
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
15494
+ 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)) {
15407
15495
  return getNavigationRoot(el);
15408
15496
  }
15409
15497
  return null;
@@ -15618,7 +15706,6 @@ var ICONS = {
15618
15706
  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"/>',
15619
15707
  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"/>'
15620
15708
  };
15621
- var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
15622
15709
  var SELECTION_CHROME_GAP2 = 4;
15623
15710
  var TOOLBAR_STROKE_GAP2 = 4;
15624
15711
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -15998,6 +16085,8 @@ function StateToggle({
15998
16085
  );
15999
16086
  }
16000
16087
  var contentCache = /* @__PURE__ */ new Map();
16088
+ var fetchedContentPaths = /* @__PURE__ */ new Set();
16089
+ var brandingCache = /* @__PURE__ */ new Map();
16001
16090
  var OHW_LOADER_STYLE = {
16002
16091
  position: "fixed",
16003
16092
  inset: 0,
@@ -16035,6 +16124,89 @@ function OhwLoaderSpinner() {
16035
16124
  )
16036
16125
  ] });
16037
16126
  }
16127
+ function OhwBrandMark() {
16128
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
16129
+ "svg",
16130
+ {
16131
+ width: "16",
16132
+ height: "16",
16133
+ viewBox: "0 0 48 48",
16134
+ fill: "none",
16135
+ "aria-hidden": true,
16136
+ style: { display: "block", flexShrink: 0 },
16137
+ xmlns: "http://www.w3.org/2000/svg",
16138
+ children: [
16139
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
16140
+ "mask",
16141
+ {
16142
+ id: "ohw-badge-mark",
16143
+ style: { maskType: "luminance" },
16144
+ maskUnits: "userSpaceOnUse",
16145
+ x: "0",
16146
+ y: "0",
16147
+ width: "48",
16148
+ height: "48",
16149
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("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" })
16150
+ }
16151
+ ),
16152
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("g", { mask: "url(#ohw-badge-mark)", children: [
16153
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("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" }),
16154
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("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" }),
16155
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("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" }),
16156
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("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" }),
16157
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("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" })
16158
+ ] })
16159
+ ]
16160
+ }
16161
+ );
16162
+ }
16163
+ var OHW_BADGE_STYLE = {
16164
+ position: "fixed",
16165
+ left: 20,
16166
+ bottom: 20,
16167
+ zIndex: 2147483e3,
16168
+ boxSizing: "border-box",
16169
+ display: "inline-flex",
16170
+ alignItems: "center",
16171
+ gap: 0,
16172
+ padding: "6px 8px",
16173
+ margin: 0,
16174
+ background: "#ffffff",
16175
+ border: "1px solid #e7e5e4",
16176
+ borderRadius: 9999,
16177
+ boxShadow: "0 1px 3px rgba(0, 0, 0, 0.1)",
16178
+ color: "#0c0a09",
16179
+ textDecoration: "none",
16180
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
16181
+ };
16182
+ var OHW_BADGE_LABEL_STYLE = {
16183
+ padding: "0 4px",
16184
+ fontSize: 14,
16185
+ lineHeight: "24px",
16186
+ fontWeight: 500,
16187
+ fontStyle: "normal",
16188
+ letterSpacing: "normal",
16189
+ textTransform: "none",
16190
+ color: "#0c0a09",
16191
+ whiteSpace: "nowrap"
16192
+ };
16193
+ function MadeWithOhhWells() {
16194
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
16195
+ "a",
16196
+ {
16197
+ href: "https://ohhwells.com",
16198
+ target: "_blank",
16199
+ rel: "noopener noreferrer",
16200
+ "aria-label": "Made with OhhWells",
16201
+ "data-ohw-badge": "",
16202
+ style: OHW_BADGE_STYLE,
16203
+ children: [
16204
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(OhwBrandMark, {}),
16205
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { style: OHW_BADGE_LABEL_STYLE, children: "Made with OhhWells" })
16206
+ ]
16207
+ }
16208
+ );
16209
+ }
16038
16210
  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){}})();`;
16039
16211
  function resolveSubdomain(subdomainFromQuery) {
16040
16212
  if (subdomainFromQuery) return subdomainFromQuery;
@@ -16094,6 +16266,7 @@ function OhhwellsBridge() {
16094
16266
  }
16095
16267
  }, []);
16096
16268
  const [fetchState, setFetchState] = (0, import_react17.useState)("idle");
16269
+ const [showBranding, setShowBranding] = (0, import_react17.useState)(false);
16097
16270
  const autoSaveTimers = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
16098
16271
  const activeElRef = (0, import_react17.useRef)(null);
16099
16272
  const pointerHeldRef = (0, import_react17.useRef)(false);
@@ -16442,13 +16615,6 @@ function OhhwellsBridge() {
16442
16615
  const [isItemDragging, setIsItemDragging] = (0, import_react17.useState)(false);
16443
16616
  const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react17.useState)(false);
16444
16617
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
16445
- const [floatingPanel, setFloatingPanel] = (0, import_react17.useState)(null);
16446
- const floatingPanelOpenRef = (0, import_react17.useRef)(false);
16447
- floatingPanelOpenRef.current = floatingPanel !== null;
16448
- const [floatingPanelPos, setFloatingPanelPos] = (0, import_react17.useState)(null);
16449
- const [logoSizeDraft, setLogoSizeDraft] = (0, import_react17.useState)(null);
16450
- const [editorViewport, setEditorViewport] = (0, import_react17.useState)("desktop");
16451
- const [parentScrollSnap, setParentScrollSnap] = (0, import_react17.useState)(null);
16452
16618
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react17.useState)(null);
16453
16619
  const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react17.useState)(null);
16454
16620
  const footerDragRef = (0, import_react17.useRef)(null);
@@ -16466,6 +16632,13 @@ function OhhwellsBridge() {
16466
16632
  const brandKitRef = (0, import_react17.useRef)("");
16467
16633
  const stylesRef = (0, import_react17.useRef)("");
16468
16634
  const pendingDeleteUndoRef = (0, import_react17.useRef)(null);
16635
+ const [floatingPanel, setFloatingPanel] = (0, import_react17.useState)(null);
16636
+ const floatingPanelOpenRef = (0, import_react17.useRef)(false);
16637
+ const setFloatingPanelRef = (0, import_react17.useRef)(setFloatingPanel);
16638
+ const [floatingPanelPos, setFloatingPanelPos] = (0, import_react17.useState)(null);
16639
+ const [logoSizeDraft, setLogoSizeDraft] = (0, import_react17.useState)(null);
16640
+ const [editorViewport, setEditorViewport] = (0, import_react17.useState)("desktop");
16641
+ const [parentScrollSnap, setParentScrollSnap] = (0, import_react17.useState)(null);
16469
16642
  const [sitePages, setSitePages] = (0, import_react17.useState)([]);
16470
16643
  const [sectionsByPath, setSectionsByPath] = (0, import_react17.useState)({});
16471
16644
  const sectionsPrefetchGenRef = (0, import_react17.useRef)(0);
@@ -16474,7 +16647,18 @@ function OhhwellsBridge() {
16474
16647
  const linkPopoverOpenRef = (0, import_react17.useRef)(false);
16475
16648
  const linkPopoverGraceUntilRef = (0, import_react17.useRef)(0);
16476
16649
  setLinkPopoverRef.current = setLinkPopover;
16650
+ setFloatingPanelRef.current = setFloatingPanel;
16477
16651
  linkPopoverSessionRef.current = linkPopover;
16652
+ floatingPanelOpenRef.current = Boolean(floatingPanel);
16653
+ (0, import_react17.useEffect)(() => {
16654
+ const syncViewport = () => {
16655
+ const next = window.innerWidth <= 480 ? "mobile" : "desktop";
16656
+ setEditorViewport((prev) => prev === next ? prev : next);
16657
+ };
16658
+ syncViewport();
16659
+ window.addEventListener("resize", syncViewport);
16660
+ return () => window.removeEventListener("resize", syncViewport);
16661
+ }, []);
16478
16662
  const {
16479
16663
  navDragRef,
16480
16664
  navDropSlots,
@@ -17805,11 +17989,11 @@ function OhhwellsBridge() {
17805
17989
  for (const [key, val] of Object.entries(content)) {
17806
17990
  if (key === "__ohw_sections") continue;
17807
17991
  if (key === AI_SECTIONS_KEY) continue;
17992
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
17993
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
17808
17994
  if (key === BRAND_KIT_KEY) continue;
17809
17995
  if (key === STYLE_STORE_KEY) continue;
17810
17996
  if (BRAND_CHROME_KEYS.has(key)) continue;
17811
- if (key === LOGO_PLACEHOLDER_KEY) continue;
17812
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
17813
17997
  if (applyVideoSettingNode(key, val)) continue;
17814
17998
  if (applyCarouselNode(key, val)) continue;
17815
17999
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -17869,16 +18053,22 @@ function OhhwellsBridge() {
17869
18053
  };
17870
18054
  const cached = contentCache.get(subdomain);
17871
18055
  if (cached) {
18056
+ setShowBranding(brandingCache.get(subdomain) ?? false);
17872
18057
  applyContent(cached).finally(() => setFetchState("done"));
17873
18058
  return;
17874
18059
  }
17875
18060
  let cancelled = false;
17876
18061
  setFetchState("loading");
17877
18062
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17878
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
18063
+ const initialPath = pathname;
18064
+ fetchedContentPaths.add(`${subdomain}::${initialPath}`);
18065
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
17879
18066
  if (cancelled) return;
17880
18067
  const content = data?.content ?? {};
18068
+ const branding = Boolean(data?.showBranding);
17881
18069
  contentCache.set(subdomain, content);
18070
+ brandingCache.set(subdomain, branding);
18071
+ setShowBranding(branding);
17882
18072
  return applyContent(content);
17883
18073
  }).catch(() => {
17884
18074
  }).finally(() => {
@@ -18011,11 +18201,12 @@ function OhhwellsBridge() {
18011
18201
  for (const [key, val] of Object.entries(content)) {
18012
18202
  if (key === "__ohw_sections") continue;
18013
18203
  if (key === AI_SECTIONS_KEY) continue;
18204
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
18205
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
18014
18206
  if (key === BRAND_KIT_KEY) continue;
18015
18207
  if (key === STYLE_STORE_KEY) continue;
18208
+ if (key === STYLE_STORE_KEY) continue;
18016
18209
  if (BRAND_CHROME_KEYS.has(key)) continue;
18017
- if (key === LOGO_PLACEHOLDER_KEY) continue;
18018
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
18019
18210
  if (applyVideoSettingNode(key, val)) continue;
18020
18211
  if (applyCarouselNode(key, val)) continue;
18021
18212
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -18061,6 +18252,17 @@ function OhhwellsBridge() {
18061
18252
  debounceTimer = setTimeout(applyFromCache, 150);
18062
18253
  };
18063
18254
  applyFromCache();
18255
+ const pathCacheKey = `${subdomain}::${pathname}`;
18256
+ if (!fetchedContentPaths.has(pathCacheKey)) {
18257
+ fetchedContentPaths.add(pathCacheKey);
18258
+ const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
18259
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
18260
+ if (!data?.content) return;
18261
+ contentCache.set(subdomain, data.content);
18262
+ applyFromCache();
18263
+ }).catch(() => {
18264
+ });
18265
+ }
18064
18266
  observer = new MutationObserver(scheduleApply);
18065
18267
  observer.observe(document.body, { childList: true, subtree: true });
18066
18268
  return () => {
@@ -18174,25 +18376,13 @@ function OhhwellsBridge() {
18174
18376
  };
18175
18377
  const t1 = setTimeout(measure, 50);
18176
18378
  const t2 = setTimeout(measure, 500);
18177
- let lastWidth = window.innerWidth;
18178
- let resizeTimers = [];
18179
- const clearResizeTimers = () => {
18180
- resizeTimers.forEach(clearTimeout);
18181
- resizeTimers = [];
18182
- };
18183
- const handleResize = () => {
18184
- if (window.innerWidth === lastWidth) return;
18185
- lastWidth = window.innerWidth;
18186
- clearResizeTimers();
18187
- resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
18188
- };
18189
- window.addEventListener("resize", handleResize);
18379
+ const ro = new ResizeObserver(schedule);
18380
+ ro.observe(document.body);
18190
18381
  return () => {
18191
18382
  clearTimeout(t1);
18192
18383
  clearTimeout(t2);
18193
18384
  if (raf != null) cancelAnimationFrame(raf);
18194
- clearResizeTimers();
18195
- window.removeEventListener("resize", handleResize);
18385
+ ro.disconnect();
18196
18386
  };
18197
18387
  }, [pathname, isEditMode, postToParent2]);
18198
18388
  (0, import_react17.useEffect)(() => {
@@ -18438,9 +18628,6 @@ function OhhwellsBridge() {
18438
18628
  if (target.closest("[data-ohw-state-toggle]")) return;
18439
18629
  if (target.closest("[data-ohw-max-badge]")) return;
18440
18630
  if (isInsideLinkEditor(target)) return;
18441
- if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
18442
- clearMediaSelectionRef.current();
18443
- }
18444
18631
  if (isInsideFloatingPanel(target)) return;
18445
18632
  if (target.closest("[data-ohw-form-toolbar]")) return;
18446
18633
  if (target.closest(
@@ -18448,6 +18635,9 @@ function OhhwellsBridge() {
18448
18635
  )) {
18449
18636
  return;
18450
18637
  }
18638
+ if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
18639
+ clearMediaSelectionRef.current();
18640
+ }
18451
18641
  {
18452
18642
  const formEl = getFormElement(target);
18453
18643
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -18599,14 +18789,6 @@ function OhhwellsBridge() {
18599
18789
  }
18600
18790
  const clickedButton = findClosestButtonLike(target);
18601
18791
  const buttonOnMedia = Boolean(clickedButton && isMediaEditable(editable) && editable.contains(clickedButton));
18602
- console.log("[click-debug]", {
18603
- editableType: editable.dataset.ohwEditable,
18604
- editableTag: editable.tagName,
18605
- targetTag: target.tagName,
18606
- clickedButtonTag: clickedButton?.tagName ?? null,
18607
- buttonOnMedia,
18608
- isMediaEditableEditable: isMediaEditable(editable)
18609
- });
18610
18792
  if (isMediaEditable(editable) && !buttonOnMedia) {
18611
18793
  e.preventDefault();
18612
18794
  e.stopPropagation();
@@ -18633,11 +18815,6 @@ function OhhwellsBridge() {
18633
18815
  const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
18634
18816
  const hrefCtx = getHrefKeyFromElement(hrefLookupTarget);
18635
18817
  const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
18636
- console.log("[click-debug 2]", {
18637
- hrefLookupTargetTag: hrefLookupTarget.tagName,
18638
- hrefCtx: hrefCtx ? { key: hrefCtx.key } : null,
18639
- navAnchorTag: navAnchor?.tagName ?? null
18640
- });
18641
18818
  if (navAnchor) {
18642
18819
  e.preventDefault();
18643
18820
  e.stopPropagation();
@@ -18807,6 +18984,9 @@ function OhhwellsBridge() {
18807
18984
  setHoveredItemRect(null);
18808
18985
  hoveredNavContainerRef.current = null;
18809
18986
  setHoveredNavContainerRect(null);
18987
+ siblingHintElRef.current = null;
18988
+ setSiblingHintRect(null);
18989
+ setSiblingHintRects([]);
18810
18990
  return;
18811
18991
  }
18812
18992
  {
@@ -18925,7 +19105,6 @@ function OhhwellsBridge() {
18925
19105
  hoveredNavContainerRef.current = null;
18926
19106
  setHoveredNavContainerRect(null);
18927
19107
  hoveredItemElRef.current = editable;
18928
- setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
18929
19108
  }
18930
19109
  }
18931
19110
  }
@@ -19222,7 +19401,7 @@ function OhhwellsBridge() {
19222
19401
  }
19223
19402
  };
19224
19403
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
19225
- if (linkPopoverOpenRef.current) {
19404
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
19226
19405
  if (hoveredImageRef.current) {
19227
19406
  hoveredImageRef.current = null;
19228
19407
  hoveredImageHasTextOverlapRef.current = false;
@@ -19587,8 +19766,7 @@ function OhhwellsBridge() {
19587
19766
  };
19588
19767
  const handleMouseMove = (e) => {
19589
19768
  const { clientX, clientY } = e;
19590
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
19591
- if (isOverEditorChrome(clientX, clientY)) {
19769
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
19592
19770
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
19593
19771
  formHoverElRef.current = null;
19594
19772
  setFormHoverRect(null);
@@ -19596,6 +19774,12 @@ function OhhwellsBridge() {
19596
19774
  setHoveredItemRect(null);
19597
19775
  hoveredNavContainerRef.current = null;
19598
19776
  setHoveredNavContainerRect(null);
19777
+ siblingHintElRef.current = null;
19778
+ setSiblingHintRect(null);
19779
+ setSiblingHintRects([]);
19780
+ dismissImageHover();
19781
+ clearImageHover();
19782
+ setSectionGap(null);
19599
19783
  return;
19600
19784
  }
19601
19785
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -19607,7 +19791,11 @@ function OhhwellsBridge() {
19607
19791
  if (e.data?.type !== "ow:pointer-sync") return;
19608
19792
  const { clientX, clientY } = e.data;
19609
19793
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
19610
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
19794
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
19795
+ dismissImageHover();
19796
+ clearImageHover();
19797
+ return;
19798
+ }
19611
19799
  if (probeSocialsRowAt(clientX, clientY)) return;
19612
19800
  probeSectionGapAt(clientX, clientY);
19613
19801
  probeImageAt(clientX, clientY);
@@ -19914,11 +20102,11 @@ function OhhwellsBridge() {
19914
20102
  continue;
19915
20103
  }
19916
20104
  if (key === AI_SECTIONS_KEY) continue;
20105
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
20106
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
19917
20107
  if (key === BRAND_KIT_KEY) continue;
19918
20108
  if (key === STYLE_STORE_KEY) continue;
19919
20109
  if (BRAND_CHROME_KEYS.has(key)) continue;
19920
- if (key === LOGO_PLACEHOLDER_KEY) continue;
19921
- if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
19922
20110
  if (applyVideoSettingNode(key, val)) continue;
19923
20111
  if (applyCarouselNode(key, val)) continue;
19924
20112
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -20079,6 +20267,20 @@ function OhhwellsBridge() {
20079
20267
  postAiSectionsChanged();
20080
20268
  };
20081
20269
  window.addEventListener("message", handleAiSetSections);
20270
+ const handleMoveSection = (e) => {
20271
+ if (e.data?.type !== "ow:move-section") return;
20272
+ const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
20273
+ const direction = e.data.direction === "up" || e.data.direction === "down" ? e.data.direction : null;
20274
+ if (!instanceId || !direction) return;
20275
+ const entries = moveSectionInstance(instanceId, direction, window.location.pathname);
20276
+ if (!entries) return;
20277
+ const orderJson = JSON.stringify(entries);
20278
+ editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
20279
+ setAiSectionOrder(orderJson, window.location.pathname);
20280
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
20281
+ window.dispatchEvent(new Event("resize"));
20282
+ };
20283
+ window.addEventListener("message", handleMoveSection);
20082
20284
  const handleAiSetBrand = (e) => {
20083
20285
  if (e.data?.type !== "ow:ai-set-brand") return;
20084
20286
  const value = typeof e.data.value === "string" ? e.data.value : "";
@@ -20108,20 +20310,6 @@ function OhhwellsBridge() {
20108
20310
  postToParentRef.current({ type: "ow:brand-value", value });
20109
20311
  };
20110
20312
  window.addEventListener("message", handleGetBrand);
20111
- const handleMoveSection = (e) => {
20112
- if (e.data?.type !== "ow:move-section") return;
20113
- const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
20114
- const direction = e.data.direction === "up" || e.data.direction === "down" ? e.data.direction : null;
20115
- if (!instanceId || !direction) return;
20116
- const entries = moveSectionInstance(instanceId, direction, window.location.pathname);
20117
- if (!entries) return;
20118
- const orderJson = JSON.stringify(entries);
20119
- editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
20120
- setAiSectionOrder(orderJson, window.location.pathname);
20121
- postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
20122
- window.dispatchEvent(new Event("resize"));
20123
- };
20124
- window.addEventListener("message", handleMoveSection);
20125
20313
  const handlePanelDragging = (e) => {
20126
20314
  if (e.data?.type !== "ow:panel-dragging") return;
20127
20315
  if (e.data.dragging) document.documentElement.setAttribute("data-ohw-panel-dragging", "");
@@ -20179,6 +20367,12 @@ function OhhwellsBridge() {
20179
20367
  closeLinkPopoverRef.current();
20180
20368
  return;
20181
20369
  }
20370
+ if (floatingPanelOpenRef.current) {
20371
+ setFloatingPanelRef.current(null);
20372
+ deselectRef.current();
20373
+ deactivateRef.current();
20374
+ return;
20375
+ }
20182
20376
  deselectRef.current();
20183
20377
  deactivateRef.current();
20184
20378
  clearMediaSelectionRef.current();
@@ -20453,8 +20647,12 @@ function OhhwellsBridge() {
20453
20647
  if (inserted) {
20454
20648
  const tracker = getSectionsTracker();
20455
20649
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
20456
- const h = document.body.scrollHeight;
20457
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
20650
+ const reportHeight = () => {
20651
+ const h = document.body.scrollHeight;
20652
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
20653
+ };
20654
+ reportHeight();
20655
+ setTimeout(reportHeight, 500);
20458
20656
  }
20459
20657
  };
20460
20658
  const handleSwitchSchedule = (e) => {
@@ -20850,17 +21048,17 @@ function OhhwellsBridge() {
20850
21048
  window.removeEventListener("message", handleAiApplyTree);
20851
21049
  window.removeEventListener("message", handleAiDeleteSection);
20852
21050
  window.removeEventListener("message", handleAiSetSections);
21051
+ window.removeEventListener("message", handleMoveSection);
20853
21052
  window.removeEventListener("message", handleAiSetBrand);
20854
21053
  window.removeEventListener("message", handleAiSetStyles);
20855
21054
  window.removeEventListener("message", handleGetBrand);
20856
- window.removeEventListener("message", handleMoveSection);
20857
21055
  window.removeEventListener("message", handlePanelDragging);
20858
21056
  window.removeEventListener("message", handleDeleteSection);
20859
21057
  window.removeEventListener("message", handleDeactivate);
20860
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
20861
21058
  window.removeEventListener("message", handleToastAction);
20862
21059
  window.removeEventListener("message", handleFormCount);
20863
21060
  window.removeEventListener("message", handleUiEscape);
21061
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
20864
21062
  autoSaveTimers.current.forEach(clearTimeout);
20865
21063
  autoSaveTimers.current.clear();
20866
21064
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
@@ -21063,7 +21261,7 @@ function OhhwellsBridge() {
21063
21261
  postToParent2({
21064
21262
  type: "ow:ready",
21065
21263
  version: "1",
21066
- bridgeVersion: "0.1.78",
21264
+ bridgeVersion: "0.1.80",
21067
21265
  path: pathname,
21068
21266
  nodes: collectEditableNodes(editContentRef.current),
21069
21267
  sections
@@ -21526,6 +21724,7 @@ function OhhwellsBridge() {
21526
21724
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
21527
21725
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { id: "ohw-loader", suppressHydrationWarning: true, style: { ...OHW_LOADER_STYLE, display: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(OhwLoaderSpinner, {}) }),
21528
21726
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("script", { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: OHW_LOADER_PREHYDRATE_SCRIPT } }),
21727
+ subdomain && !isEditMode && showBranding && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(MadeWithOhhWells, {}),
21529
21728
  bridgeRoot ? (0, import_react_dom4.createPortal)(
21530
21729
  /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
21531
21730
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
@@ -21981,6 +22180,59 @@ function OhhwellsBridge() {
21981
22180
  ) : null
21982
22181
  ] });
21983
22182
  }
22183
+
22184
+ // src/ui/EmptySection.tsx
22185
+ var import_link = __toESM(require("next/link"), 1);
22186
+ var import_jsx_runtime34 = require("react/jsx-runtime");
22187
+ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
22188
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
22189
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
22190
+ "p",
22191
+ {
22192
+ style: {
22193
+ fontFamily: "var(--brand-font-body)",
22194
+ fontSize: "0.75rem",
22195
+ fontWeight: 500,
22196
+ letterSpacing: "0.15em",
22197
+ textTransform: "uppercase",
22198
+ color: "var(--brand-accent)",
22199
+ marginBottom: "1.5rem"
22200
+ },
22201
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_link.default, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
22202
+ }
22203
+ ),
22204
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
22205
+ "h1",
22206
+ {
22207
+ style: {
22208
+ fontFamily: "var(--brand-font-heading)",
22209
+ fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
22210
+ lineHeight: 1.1,
22211
+ letterSpacing: "-0.025em",
22212
+ color: "var(--brand-text)",
22213
+ marginBottom: "1rem"
22214
+ },
22215
+ ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
22216
+ children: title
22217
+ }
22218
+ ),
22219
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
22220
+ "p",
22221
+ {
22222
+ style: {
22223
+ fontFamily: "var(--brand-font-body)",
22224
+ fontSize: "1rem",
22225
+ lineHeight: 1.7,
22226
+ fontWeight: 300,
22227
+ color: "var(--brand-text-muted)",
22228
+ maxWidth: "340px"
22229
+ },
22230
+ ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
22231
+ children: "This page doesn't have any content yet."
22232
+ }
22233
+ )
22234
+ ] });
22235
+ }
21984
22236
  // Annotate the CommonJS export names for ESM import in node:
21985
22237
  0 && (module.exports = {
21986
22238
  AI_DEFAULT_BRAND,
@@ -21998,6 +22250,7 @@ function OhhwellsBridge() {
21998
22250
  DropdownMenuItem,
21999
22251
  DropdownMenuSeparator,
22000
22252
  DropdownMenuTrigger,
22253
+ EmptySection,
22001
22254
  ItemActionToolbar,
22002
22255
  ItemInteractionLayer,
22003
22256
  LinkEditorPanel,