@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.js CHANGED
@@ -333,10 +333,24 @@ var SECTION_ATTRS = {
333
333
  spacing: "data-ohw-style-spacing"
334
334
  };
335
335
  var NODE_WROTE_ATTR = "data-ohw-style-node";
336
- var NODE_PROPS = ["color", "font-family", "font-size", "background"];
336
+ var NODE_PROPS = [
337
+ "color",
338
+ "font-family",
339
+ "font-size",
340
+ "background",
341
+ "text-align",
342
+ "justify-content"
343
+ ];
344
+ var ALIGN_JUSTIFY = {
345
+ left: "flex-start",
346
+ center: "center",
347
+ right: "flex-end"
348
+ };
337
349
  function saveInline(el, prop) {
338
350
  const attr = `data-ohw-style-prev-${prop}`;
339
- if (!el.hasAttribute(attr)) el.setAttribute(attr, el.style.getPropertyValue(prop));
351
+ if (el.hasAttribute(attr)) return;
352
+ const value = el.style.getPropertyValue(prop) || (prop === "background" ? el.style.getPropertyValue("background-color") : "");
353
+ el.setAttribute(attr, value);
340
354
  }
341
355
  function restoreInline(el, prop) {
342
356
  const attr = `data-ohw-style-prev-${prop}`;
@@ -387,7 +401,8 @@ function applyStylesToDom(store) {
387
401
  const sections = document.querySelectorAll(
388
402
  `[data-ohw-section="${CSS.escape(sectionId)}"]`
389
403
  );
390
- for (const section of Array.from(sections)) {
404
+ for (const marker of Array.from(sections)) {
405
+ const section = marker.querySelector(":scope > [data-ai-section]") ?? marker;
391
406
  for (const [prop, attr] of Object.entries(SECTION_ATTRS)) {
392
407
  const value = override[prop];
393
408
  if (value === void 0) continue;
@@ -419,6 +434,13 @@ function applyStylesToDom(store) {
419
434
  el.style.setProperty("font-size", `${override.fontSize}px`, "important");
420
435
  el.setAttribute(NODE_WROTE_ATTR, "");
421
436
  }
437
+ if (override.align !== void 0) {
438
+ saveInline(el, "text-align");
439
+ saveInline(el, "justify-content");
440
+ el.style.setProperty("text-align", override.align, "important");
441
+ el.style.setProperty("justify-content", ALIGN_JUSTIFY[override.align] ?? "flex-start", "important");
442
+ el.setAttribute(NODE_WROTE_ATTR, "");
443
+ }
422
444
  if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
423
445
  const surface = buttonSurfaceOf(el);
424
446
  if (override.buttonBackground !== void 0) {
@@ -469,8 +491,6 @@ var AI_MOBILE_CSS = [
469
491
  // Group containers flatten to a column on phones; span placements come along for free.
470
492
  "[data-ai-group]{display:flex !important;flex-direction:column !important}",
471
493
  "[data-ai-group] > *{grid-column:auto !important}",
472
- // The 50:50 form collapses to a single stacked column on phones.
473
- "[data-ai-form]{grid-template-columns:1fr !important}",
474
494
  "[data-ai-section] img{max-width:100%}",
475
495
  "}",
476
496
  "@media (min-width: 769px) and (max-width: 1024px){",
@@ -516,6 +536,22 @@ function accentBandContext(brand) {
516
536
  function textAttrs(ctx, path) {
517
537
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
518
538
  }
539
+ var AI_RESPONSIVE_CSS = [
540
+ "@media (max-width: 960px) {",
541
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
542
+ ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
543
+ "}",
544
+ "@media (max-width: 640px) {",
545
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
546
+ " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
547
+ // Group containers flatten to a column on phones; span placements come along for free.
548
+ " [data-ai-responsive] [data-ai-group] { display: flex !important; flex-direction: column !important; }",
549
+ " [data-ai-responsive] [data-ai-group] > * { grid-column: auto !important; }",
550
+ " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
551
+ " [data-ai-responsive] { overflow-x: hidden; }",
552
+ " [data-ai-responsive] img { max-width: 100%; }",
553
+ "}"
554
+ ].join("\n");
519
555
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
520
556
  function MediaBox({
521
557
  refValue,
@@ -528,13 +564,17 @@ function MediaBox({
528
564
  const url = refValue ? ctx.resolveMedia(refValue) : null;
529
565
  const isIcon = /^(lucide|simple):/.test(refValue);
530
566
  const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
531
- const editAttrs = ctx.keyFor && editPath && !isIcon ? { "data-ohw-key": ctx.keyFor(editPath), "data-ohw-editable": "image" } : {};
567
+ const editAttrs = ctx.keyFor && editPath ? {
568
+ "data-ohw-key": ctx.keyFor(editPath),
569
+ "data-ohw-editable": isIcon ? "icon" : "image"
570
+ } : {};
532
571
  if (isIcon) {
533
572
  const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
534
573
  return /* @__PURE__ */ jsx(
535
574
  "span",
536
575
  {
537
576
  "data-ai-icon": refValue,
577
+ ...editAttrs,
538
578
  style: {
539
579
  display: "inline-flex",
540
580
  width: 48,
@@ -1420,7 +1460,7 @@ function CollectionBlock({ node, ctx, path }) {
1420
1460
  return /* @__PURE__ */ jsx(
1421
1461
  "div",
1422
1462
  {
1423
- "data-ai-grid": "",
1463
+ "data-ai-grid": String(itemsPerRow),
1424
1464
  style: {
1425
1465
  display: "grid",
1426
1466
  gridTemplateColumns: `repeat(${itemsPerRow}, 1fr)`,
@@ -1567,9 +1607,9 @@ function renderNode(node, ctx, path) {
1567
1607
  const fieldStyle = {
1568
1608
  width: "100%",
1569
1609
  boxSizing: "border-box",
1570
- border: `1px solid ${ctx.brand.palette.accent}`,
1571
- borderRadius: AI_TREE_TOKENS.radiusButton,
1572
- padding: "12px 14px",
1610
+ border: `1px solid color-mix(in srgb, ${ctx.brand.palette.dark} 45%, #ffffff)`,
1611
+ borderRadius: 0,
1612
+ padding: 12,
1573
1613
  background: "#fff",
1574
1614
  color: ctx.brand.palette.dark,
1575
1615
  outline: "none",
@@ -1577,81 +1617,90 @@ function renderNode(node, ctx, path) {
1577
1617
  };
1578
1618
  const labelStyle = {
1579
1619
  ...typeStyle(AI_TREE_TOKENS.type.bodyMBold, ctx.brand.fonts.body),
1580
- color: ctx.brand.palette.dark
1620
+ color: ctx.brand.palette.dark,
1621
+ textAlign: "left",
1622
+ width: "100%"
1581
1623
  };
1582
- return /* @__PURE__ */ jsx(
1583
- "form",
1584
- {
1585
- ...formAttrs,
1586
- "data-ai-form": "",
1587
- style: {
1588
- display: "grid",
1589
- gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
1590
- columnGap: 64,
1591
- rowGap: AI_TREE_TOKENS.spacing4
1592
- },
1593
- children: (node.children ?? []).map((child, i) => {
1594
- if (child.type === "input") {
1595
- const cs2 = child.slots ?? {};
1596
- const kind = str(cs2.kind);
1597
- const label = str(cs2.label);
1598
- const placeholder = str(cs2.placeholder);
1599
- const name = label.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "") || `field-${i}`;
1600
- const isTextarea = kind === "textarea";
1601
- return /* @__PURE__ */ jsxs(
1602
- "div",
1603
- {
1604
- style: {
1605
- display: "flex",
1606
- flexDirection: "column",
1607
- gap: 8,
1608
- ...isTextarea ? { gridColumn: "1 / -1", maxWidth: 780 } : {}
1624
+ const centered = ctx.sectionAlignment === "center";
1625
+ const submitAlign = centered ? "center" : "flex-start";
1626
+ const children = node.children ?? [];
1627
+ return (
1628
+ // 32px between the field group and the submit. In a stacked (centered) section the form is
1629
+ // capped at 780px and centered — the section's 12-col grid would otherwise leave it hugging
1630
+ // the left edge; a split section lets it fill its own column.
1631
+ /* @__PURE__ */ jsxs(
1632
+ "form",
1633
+ {
1634
+ ...formAttrs,
1635
+ "data-ai-form": "",
1636
+ style: {
1637
+ display: "flex",
1638
+ flexDirection: "column",
1639
+ gap: 32,
1640
+ width: "100%",
1641
+ ...centered ? { maxWidth: 780, marginLeft: "auto", marginRight: "auto" } : {}
1642
+ },
1643
+ children: [
1644
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: 24, width: "100%", alignItems: "flex-start" }, children: children.map((child, i) => {
1645
+ if (child.type !== "input") return null;
1646
+ const cs = child.slots ?? {};
1647
+ const kind = str(cs.kind);
1648
+ const label = str(cs.label);
1649
+ const placeholder = str(cs.placeholder);
1650
+ const required = cs.required === true;
1651
+ const name = label.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "") || `field-${i}`;
1652
+ const isTextarea = kind === "textarea";
1653
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8, width: "100%" }, children: [
1654
+ /* @__PURE__ */ jsx("label", { ...textAttrs(ctx, `${path}.c${i}.label`), style: labelStyle, children: label }),
1655
+ isTextarea ? /* @__PURE__ */ jsx(
1656
+ "textarea",
1657
+ {
1658
+ name,
1659
+ placeholder,
1660
+ required,
1661
+ style: { ...fieldStyle, height: 180, resize: "vertical" }
1662
+ }
1663
+ ) : /* @__PURE__ */ jsx(
1664
+ "input",
1665
+ {
1666
+ name,
1667
+ type: kind === "email" ? "email" : "text",
1668
+ placeholder,
1669
+ required,
1670
+ style: { ...fieldStyle, height: 48 }
1671
+ }
1672
+ )
1673
+ ] }, i);
1674
+ }) }),
1675
+ children.map((child, i) => {
1676
+ if (child.type === "input") return null;
1677
+ const cs = child.slots ?? {};
1678
+ return /* @__PURE__ */ jsx(
1679
+ "button",
1680
+ {
1681
+ type: "submit",
1682
+ style: {
1683
+ alignSelf: submitAlign,
1684
+ border: "none",
1685
+ cursor: "pointer",
1686
+ padding: "12px 24px",
1687
+ // Corner radius follows the host template's own buttons (measured from a template
1688
+ // CTA); 8px only when the page has no template button to match.
1689
+ borderRadius: ctx.buttonRadius ?? 8,
1690
+ // Brand-styled: primary fill, brand-derived label colour (not a fixed token) so it
1691
+ // reads correctly on custom palettes.
1692
+ background: ctx.brand.palette.primary,
1693
+ color: ctx.buttonLabel ?? ctx.brand.palette.light,
1694
+ ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
1695
+ },
1696
+ children: /* @__PURE__ */ jsx("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
1609
1697
  },
1610
- children: [
1611
- /* @__PURE__ */ jsx("label", { ...textAttrs(ctx, `${path}.c${i}.label`), style: labelStyle, children: label }),
1612
- isTextarea ? /* @__PURE__ */ jsx(
1613
- "textarea",
1614
- {
1615
- name,
1616
- placeholder,
1617
- style: { ...fieldStyle, height: 140, resize: "vertical" }
1618
- }
1619
- ) : /* @__PURE__ */ jsx(
1620
- "input",
1621
- {
1622
- name,
1623
- type: kind === "email" ? "email" : "text",
1624
- placeholder,
1625
- style: { ...fieldStyle, height: 48 }
1626
- }
1627
- )
1628
- ]
1629
- },
1630
- i
1631
- );
1632
- }
1633
- const cs = child.slots ?? {};
1634
- return /* @__PURE__ */ jsx(
1635
- "button",
1636
- {
1637
- type: "submit",
1638
- style: {
1639
- gridColumn: "1 / -1",
1640
- justifySelf: "start",
1641
- border: "none",
1642
- cursor: "pointer",
1643
- padding: `${AI_TREE_TOKENS.spacing3}px ${AI_TREE_TOKENS.spacing6}px`,
1644
- borderRadius: AI_TREE_TOKENS.radiusButton,
1645
- background: ctx.brand.palette.primary,
1646
- color: AI_TREE_TOKENS.textPrimaryForeground,
1647
- ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
1648
- },
1649
- children: /* @__PURE__ */ jsx("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
1650
- },
1651
- i
1652
- );
1653
- })
1654
- }
1698
+ i
1699
+ );
1700
+ })
1701
+ ]
1702
+ }
1703
+ )
1655
1704
  );
1656
1705
  }
1657
1706
  case "schedule-widget":
@@ -1674,7 +1723,13 @@ function renderNode(node, ctx, path) {
1674
1723
  return null;
1675
1724
  }
1676
1725
  }
1677
- function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1726
+ function AiTreeRenderer({
1727
+ tree,
1728
+ brand,
1729
+ buttonRadius,
1730
+ resolveMedia,
1731
+ editKeyPrefix
1732
+ }) {
1678
1733
  if (!isRenderableTree(tree)) {
1679
1734
  return null;
1680
1735
  }
@@ -1686,6 +1741,8 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1686
1741
  resolveMedia: resolveMedia ?? (() => null),
1687
1742
  cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
1688
1743
  keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
1744
+ sectionAlignment: (tree.settings ?? {}).alignment === "center" ? "center" : "left",
1745
+ buttonRadius,
1689
1746
  ...band ? { buttonLabel: band.buttonLabel } : {}
1690
1747
  };
1691
1748
  const settings = tree.settings ?? {};
@@ -1708,11 +1765,25 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1708
1765
  }
1709
1766
  })();
1710
1767
  const distributed = !isOverlay && settings.textDistribution;
1768
+ const rowAlignItems = (rowAlign) => {
1769
+ if (rowAlign === "top") return "start";
1770
+ if (rowAlign === "bottom") return "end";
1771
+ if (rowAlign === "center" || rowAlign === "stretch") return rowAlign;
1772
+ if (distributed === "space-between") return "stretch";
1773
+ return (distributed ?? settings.verticalPosition) === "top" ? "start" : "center";
1774
+ };
1775
+ const cellAlignStyle = (blockAlign) => blockAlign ? {
1776
+ display: "flex",
1777
+ flexDirection: "column",
1778
+ alignItems: blockAlign === "left" ? "flex-start" : blockAlign === "right" ? "flex-end" : "center",
1779
+ textAlign: blockAlign
1780
+ } : {};
1711
1781
  return /* @__PURE__ */ jsxs(
1712
1782
  "section",
1713
1783
  {
1714
1784
  "data-ai-section": tree.tag ?? "",
1715
1785
  ...bgAttrs,
1786
+ "data-ai-responsive": "",
1716
1787
  style: {
1717
1788
  position: "relative",
1718
1789
  padding: `${pad}px 0`,
@@ -1723,12 +1794,13 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1723
1794
  color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
1724
1795
  },
1725
1796
  children: [
1726
- isOverlay && backgroundUrl && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
1797
+ /* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
1727
1798
  /* @__PURE__ */ jsx("style", { children: AI_MOBILE_CSS }),
1799
+ isOverlay && backgroundUrl && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
1728
1800
  /* @__PURE__ */ jsx(
1729
1801
  "div",
1730
1802
  {
1731
- "data-ai-container": "",
1803
+ "data-ai-section-inner": "",
1732
1804
  style: {
1733
1805
  position: "relative",
1734
1806
  maxWidth: AI_TREE_TOKENS.sectionMaxWidth,
@@ -1739,12 +1811,12 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1739
1811
  children: tree.rows.map((row, r2) => /* @__PURE__ */ jsx(
1740
1812
  "div",
1741
1813
  {
1742
- "data-ai-row": "",
1814
+ "data-ai-columns": "",
1743
1815
  style: {
1744
1816
  display: "grid",
1745
1817
  gridTemplateColumns: "repeat(12, 1fr)",
1746
1818
  gap: AI_TREE_TOKENS.spacing6,
1747
- alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
1819
+ alignItems: rowAlignItems(row.align),
1748
1820
  marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
1749
1821
  },
1750
1822
  children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ jsx(
@@ -1754,6 +1826,8 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1754
1826
  style: {
1755
1827
  gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
1756
1828
  minWidth: 0,
1829
+ // Horizontal placement of the block's content within its column.
1830
+ ...cellAlignStyle(block.align),
1757
1831
  // space-between: each column becomes a flex column whose content spreads over
1758
1832
  // the full row height instead of clumping at the top.
1759
1833
  ...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
@@ -1815,6 +1889,13 @@ function deriveTemplateBrand() {
1815
1889
  }
1816
1890
  };
1817
1891
  }
1892
+ function deriveTemplateButtonRadius() {
1893
+ if (typeof document === "undefined") return null;
1894
+ const btn = document.querySelector('[data-ohw-role="button"]');
1895
+ if (!btn) return null;
1896
+ const radius = getComputedStyle(btn).borderTopLeftRadius;
1897
+ return radius || null;
1898
+ }
1818
1899
  var mounted = /* @__PURE__ */ new Map();
1819
1900
  function findTemplateSection(id) {
1820
1901
  for (const el of document.querySelectorAll(`[data-ohw-section="${CSS.escape(id)}"]`)) {
@@ -1966,6 +2047,7 @@ function applyAiSectionsToDom(state, options) {
1966
2047
  if (typeof document === "undefined") return;
1967
2048
  const brandOverride = deriveBrandOverride();
1968
2049
  const templateBrand = deriveTemplateBrand();
2050
+ const templateButtonRadius = deriveTemplateButtonRadius();
1969
2051
  const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
1970
2052
  const pagePath = window.location.pathname;
1971
2053
  const pageSections = state.sections.filter((entry) => (entry.path ?? "/") === pagePath);
@@ -2005,6 +2087,7 @@ function applyAiSectionsToDom(state, options) {
2005
2087
  {
2006
2088
  tree: entry.tree,
2007
2089
  brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
2090
+ buttonRadius: templateButtonRadius,
2008
2091
  resolveMedia,
2009
2092
  editKeyPrefix: `ai.${entry.id}`
2010
2093
  }
@@ -2153,6 +2236,7 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
2153
2236
  /* @__PURE__ */ jsx3(
2154
2237
  Dialog.Overlay,
2155
2238
  {
2239
+ "data-ohw-scheduling-modal": "",
2156
2240
  className: "fixed inset-0 z-50",
2157
2241
  style: { background: "rgba(0,0,0,0.45)" }
2158
2242
  }
@@ -2160,6 +2244,7 @@ function EmailCaptureModal({ title, subtitle, onSubmit, onClose }) {
2160
2244
  /* @__PURE__ */ jsxs2(
2161
2245
  Dialog.Content,
2162
2246
  {
2247
+ "data-ohw-scheduling-modal": "",
2163
2248
  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",
2164
2249
  style: { maxWidth: 400 },
2165
2250
  children: [
@@ -2633,7 +2718,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2633
2718
  const autoId = useId();
2634
2719
  const insertAfter = insertAfterProp ?? autoId;
2635
2720
  const [schedule, setSchedule] = useState2(null);
2636
- const [loading, setLoading] = useState2(true);
2721
+ const [loading, setLoading] = useState2(initialScheduleId !== null);
2637
2722
  const [inEditor, setInEditor] = useState2(false);
2638
2723
  const [isHovered, setIsHovered] = useState2(false);
2639
2724
  const [modalState, setModalState] = useState2(null);
@@ -2807,8 +2892,10 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2807
2892
  "*"
2808
2893
  );
2809
2894
  };
2810
- if (!inEditor && !loading && !schedule) return null;
2811
2895
  const sectionId = `scheduling-${insertAfter}`;
2896
+ if (!inEditor && !loading && !schedule) {
2897
+ return /* @__PURE__ */ jsx4("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
2898
+ }
2812
2899
  return /* @__PURE__ */ jsxs3(
2813
2900
  "section",
2814
2901
  {
@@ -7749,6 +7836,7 @@ function MediaOverlay({
7749
7836
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
7750
7837
  );
7751
7838
  }, [isVideo]);
7839
+ const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
7752
7840
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
7753
7841
  const box = {
7754
7842
  position: "fixed",
@@ -7878,17 +7966,17 @@ function MediaOverlay({
7878
7966
  },
7879
7967
  children: [
7880
7968
  isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
7881
- isVideo ? "Replace video" : "Replace image"
7969
+ replaceLabel
7882
7970
  ]
7883
7971
  }
7884
7972
  ),
7885
- replaceMode === "none" ? null : /* @__PURE__ */ jsxs7(
7973
+ showChrome && replaceMode !== "none" && /* @__PURE__ */ jsxs7(
7886
7974
  Button,
7887
7975
  {
7888
7976
  "data-ohw-media-overlay": "",
7889
7977
  variant: "outline",
7890
7978
  size: "sm",
7891
- "aria-label": isVideo ? "Replace video" : "Replace image",
7979
+ "aria-label": replaceLabel,
7892
7980
  className: "gap-1.5 cursor-pointer hover:bg-background",
7893
7981
  style: {
7894
7982
  ...OVERLAY_BUTTON_STYLE,
@@ -7911,7 +7999,7 @@ function MediaOverlay({
7911
7999
  },
7912
8000
  children: [
7913
8001
  isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
7914
- replaceMode === "full" ? isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image" : null
8002
+ replaceMode === "full" ? replaceLabel : null
7915
8003
  ]
7916
8004
  }
7917
8005
  )
@@ -12855,6 +12943,7 @@ function readLogoSizeState(content, placement) {
12855
12943
  function getLogoElement(el) {
12856
12944
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
12857
12945
  if (marked) return marked;
12946
+ if (el.closest('[data-ohw-editable="icon"]')) return null;
12858
12947
  const root = el.closest("nav, [data-ohw-nav-root], footer");
12859
12948
  if (!root) return null;
12860
12949
  const anchor = el.closest("a");
@@ -14789,21 +14878,10 @@ function parseSchedulingInsertAfter(insertAfter) {
14789
14878
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
14790
14879
  };
14791
14880
  }
14792
- function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
14793
- const parsed = parseSchedulingInsertAfter(insertAfter);
14794
- const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
14795
- const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
14796
- return { effectiveInsertAfter, insertBefore };
14797
- }
14798
- function getSchedulingMountPoint(insertAfter) {
14799
- const { anchor } = parseSchedulingInsertAfter(insertAfter);
14800
- let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
14801
- if (!anchorEl && anchor === "scheduling") {
14802
- const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
14803
- anchorEl = widgets.at(-1) ?? null;
14804
- }
14805
- if (!anchorEl) return null;
14806
- return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
14881
+ function resolveEntryAnchor(entry) {
14882
+ if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
14883
+ const parsed = parseSchedulingInsertAfter(entry.insertAfter);
14884
+ return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
14807
14885
  }
14808
14886
  function schedulingMountDepth(insertAfter) {
14809
14887
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -14820,8 +14898,7 @@ function getPageSchedulingEntries(raw) {
14820
14898
  }
14821
14899
  }
14822
14900
  function isSchedulingWidgetMissing(entry) {
14823
- const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
14824
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
14901
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
14825
14902
  }
14826
14903
  function hasMissingSchedulingWidgets(entries) {
14827
14904
  return entries.some(isSchedulingWidgetMissing);
@@ -14851,16 +14928,17 @@ function initSectionsFromContent(content, removeExisting = false) {
14851
14928
  } catch {
14852
14929
  }
14853
14930
  }
14854
- function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
14855
- const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
14856
- const sectionId = schedulingSectionId(effectiveInsertAfter);
14931
+ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
14932
+ const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
14933
+ const sectionId = schedulingSectionId(widgetId);
14857
14934
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
14858
- const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
14859
- if (!mountPoint) return false;
14935
+ const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
14936
+ if (!anchorEl) return false;
14937
+ const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
14860
14938
  const container = document.createElement("div");
14861
14939
  container.dataset.ohwSectionContainer = "scheduling";
14862
- if (insertBefore) {
14863
- const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
14940
+ if (beforeId) {
14941
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
14864
14942
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
14865
14943
  if (!beforePoint) return false;
14866
14944
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -14871,19 +14949,25 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
14871
14949
  }
14872
14950
  tail.insertAdjacentElement("afterend", container);
14873
14951
  }
14874
- const root = createRoot2(container);
14875
- flushSync2(() => {
14876
- root.render(
14877
- /* @__PURE__ */ jsx33(
14878
- SchedulingWidget,
14879
- {
14880
- notifyOnConnect,
14881
- initialScheduleId: scheduleId,
14882
- insertAfter: effectiveInsertAfter
14883
- }
14884
- )
14885
- );
14886
- });
14952
+ try {
14953
+ const root = createRoot2(container);
14954
+ flushSync2(() => {
14955
+ root.render(
14956
+ /* @__PURE__ */ jsx33(
14957
+ SchedulingWidget,
14958
+ {
14959
+ notifyOnConnect,
14960
+ initialScheduleId: scheduleId,
14961
+ insertAfter: widgetId
14962
+ }
14963
+ )
14964
+ );
14965
+ });
14966
+ } catch (err) {
14967
+ console.error("[ow:scheduling] render threw", err);
14968
+ container.remove();
14969
+ return false;
14970
+ }
14887
14971
  const tracker = getSectionsTracker();
14888
14972
  let sections = [];
14889
14973
  try {
@@ -14891,10 +14975,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
14891
14975
  } catch {
14892
14976
  }
14893
14977
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
14894
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
14978
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
14895
14979
  sections.push({
14896
14980
  type: "scheduling",
14897
- insertAfter: effectiveInsertAfter,
14981
+ insertAfter: widgetId,
14982
+ anchorId,
14983
+ beforeId: beforeId ?? null,
14898
14984
  pagePath: window.location.pathname,
14899
14985
  ...scheduleId ? { scheduleId } : {}
14900
14986
  });
@@ -14908,7 +14994,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
14908
14994
  for (let i = pending.length - 1; i >= 0; i--) {
14909
14995
  const entry = pending[i];
14910
14996
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
14911
- if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
14997
+ const { anchorId, beforeId } = resolveEntryAnchor(entry);
14998
+ if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
14912
14999
  pending.splice(i, 1);
14913
15000
  }
14914
15001
  }
@@ -15066,6 +15153,11 @@ function applyLinkByKey(key, val) {
15066
15153
  hrefAnchors.forEach((el) => applyLinkHref(el, val));
15067
15154
  }
15068
15155
  }
15156
+ function isInsideLinkEditor(target) {
15157
+ return Boolean(
15158
+ 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"]')
15159
+ );
15160
+ }
15069
15161
  function isInsideFloatingPanel(target) {
15070
15162
  return Boolean(target.closest("[data-ohw-floating-panel]"));
15071
15163
  }
@@ -15073,11 +15165,6 @@ function isPointOverFloatingPanel(clientX, clientY) {
15073
15165
  const el = document.elementFromPoint(clientX, clientY);
15074
15166
  return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
15075
15167
  }
15076
- function isInsideLinkEditor(target) {
15077
- return Boolean(
15078
- 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"]')
15079
- );
15080
- }
15081
15168
  function getHrefKeyFromElement(el) {
15082
15169
  if (!el) return null;
15083
15170
  const anchor = el.closest("[data-ohw-href-key]");
@@ -15336,7 +15423,7 @@ function getNavigationSelectionParent(el) {
15336
15423
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
15337
15424
  return getFooterLinksContainer();
15338
15425
  }
15339
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
15426
+ 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)) {
15340
15427
  return getNavigationRoot(el);
15341
15428
  }
15342
15429
  return null;
@@ -15551,7 +15638,6 @@ var ICONS = {
15551
15638
  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"/>',
15552
15639
  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"/>'
15553
15640
  };
15554
- var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
15555
15641
  var SELECTION_CHROME_GAP2 = 4;
15556
15642
  var TOOLBAR_STROKE_GAP2 = 4;
15557
15643
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -15931,6 +16017,8 @@ function StateToggle({
15931
16017
  );
15932
16018
  }
15933
16019
  var contentCache = /* @__PURE__ */ new Map();
16020
+ var fetchedContentPaths = /* @__PURE__ */ new Set();
16021
+ var brandingCache = /* @__PURE__ */ new Map();
15934
16022
  var OHW_LOADER_STYLE = {
15935
16023
  position: "fixed",
15936
16024
  inset: 0,
@@ -15968,6 +16056,89 @@ function OhwLoaderSpinner() {
15968
16056
  )
15969
16057
  ] });
15970
16058
  }
16059
+ function OhwBrandMark() {
16060
+ return /* @__PURE__ */ jsxs20(
16061
+ "svg",
16062
+ {
16063
+ width: "16",
16064
+ height: "16",
16065
+ viewBox: "0 0 48 48",
16066
+ fill: "none",
16067
+ "aria-hidden": true,
16068
+ style: { display: "block", flexShrink: 0 },
16069
+ xmlns: "http://www.w3.org/2000/svg",
16070
+ children: [
16071
+ /* @__PURE__ */ jsx33(
16072
+ "mask",
16073
+ {
16074
+ id: "ohw-badge-mark",
16075
+ style: { maskType: "luminance" },
16076
+ maskUnits: "userSpaceOnUse",
16077
+ x: "0",
16078
+ y: "0",
16079
+ width: "48",
16080
+ height: "48",
16081
+ 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" })
16082
+ }
16083
+ ),
16084
+ /* @__PURE__ */ jsxs20("g", { mask: "url(#ohw-badge-mark)", children: [
16085
+ /* @__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" }),
16086
+ /* @__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" }),
16087
+ /* @__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" }),
16088
+ /* @__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" }),
16089
+ /* @__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" })
16090
+ ] })
16091
+ ]
16092
+ }
16093
+ );
16094
+ }
16095
+ var OHW_BADGE_STYLE = {
16096
+ position: "fixed",
16097
+ left: 20,
16098
+ bottom: 20,
16099
+ zIndex: 2147483e3,
16100
+ boxSizing: "border-box",
16101
+ display: "inline-flex",
16102
+ alignItems: "center",
16103
+ gap: 0,
16104
+ padding: "6px 8px",
16105
+ margin: 0,
16106
+ background: "#ffffff",
16107
+ border: "1px solid #e7e5e4",
16108
+ borderRadius: 9999,
16109
+ boxShadow: "0 1px 3px rgba(0, 0, 0, 0.1)",
16110
+ color: "#0c0a09",
16111
+ textDecoration: "none",
16112
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
16113
+ };
16114
+ var OHW_BADGE_LABEL_STYLE = {
16115
+ padding: "0 4px",
16116
+ fontSize: 14,
16117
+ lineHeight: "24px",
16118
+ fontWeight: 500,
16119
+ fontStyle: "normal",
16120
+ letterSpacing: "normal",
16121
+ textTransform: "none",
16122
+ color: "#0c0a09",
16123
+ whiteSpace: "nowrap"
16124
+ };
16125
+ function MadeWithOhhWells() {
16126
+ return /* @__PURE__ */ jsxs20(
16127
+ "a",
16128
+ {
16129
+ href: "https://ohhwells.com",
16130
+ target: "_blank",
16131
+ rel: "noopener noreferrer",
16132
+ "aria-label": "Made with OhhWells",
16133
+ "data-ohw-badge": "",
16134
+ style: OHW_BADGE_STYLE,
16135
+ children: [
16136
+ /* @__PURE__ */ jsx33(OhwBrandMark, {}),
16137
+ /* @__PURE__ */ jsx33("span", { style: OHW_BADGE_LABEL_STYLE, children: "Made with OhhWells" })
16138
+ ]
16139
+ }
16140
+ );
16141
+ }
15971
16142
  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){}})();`;
15972
16143
  function resolveSubdomain(subdomainFromQuery) {
15973
16144
  if (subdomainFromQuery) return subdomainFromQuery;
@@ -16027,6 +16198,7 @@ function OhhwellsBridge() {
16027
16198
  }
16028
16199
  }, []);
16029
16200
  const [fetchState, setFetchState] = useState13("idle");
16201
+ const [showBranding, setShowBranding] = useState13(false);
16030
16202
  const autoSaveTimers = useRef10(/* @__PURE__ */ new Map());
16031
16203
  const activeElRef = useRef10(null);
16032
16204
  const pointerHeldRef = useRef10(false);
@@ -16375,13 +16547,6 @@ function OhhwellsBridge() {
16375
16547
  const [isItemDragging, setIsItemDragging] = useState13(false);
16376
16548
  const [isFooterFrameSelection, setIsFooterFrameSelection] = useState13(false);
16377
16549
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
16378
- const [floatingPanel, setFloatingPanel] = useState13(null);
16379
- const floatingPanelOpenRef = useRef10(false);
16380
- floatingPanelOpenRef.current = floatingPanel !== null;
16381
- const [floatingPanelPos, setFloatingPanelPos] = useState13(null);
16382
- const [logoSizeDraft, setLogoSizeDraft] = useState13(null);
16383
- const [editorViewport, setEditorViewport] = useState13("desktop");
16384
- const [parentScrollSnap, setParentScrollSnap] = useState13(null);
16385
16550
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState13(null);
16386
16551
  const [footerHeadingVisible, setFooterHeadingVisible] = useState13(null);
16387
16552
  const footerDragRef = useRef10(null);
@@ -16399,6 +16564,13 @@ function OhhwellsBridge() {
16399
16564
  const brandKitRef = useRef10("");
16400
16565
  const stylesRef = useRef10("");
16401
16566
  const pendingDeleteUndoRef = useRef10(null);
16567
+ const [floatingPanel, setFloatingPanel] = useState13(null);
16568
+ const floatingPanelOpenRef = useRef10(false);
16569
+ const setFloatingPanelRef = useRef10(setFloatingPanel);
16570
+ const [floatingPanelPos, setFloatingPanelPos] = useState13(null);
16571
+ const [logoSizeDraft, setLogoSizeDraft] = useState13(null);
16572
+ const [editorViewport, setEditorViewport] = useState13("desktop");
16573
+ const [parentScrollSnap, setParentScrollSnap] = useState13(null);
16402
16574
  const [sitePages, setSitePages] = useState13([]);
16403
16575
  const [sectionsByPath, setSectionsByPath] = useState13({});
16404
16576
  const sectionsPrefetchGenRef = useRef10(0);
@@ -16407,7 +16579,18 @@ function OhhwellsBridge() {
16407
16579
  const linkPopoverOpenRef = useRef10(false);
16408
16580
  const linkPopoverGraceUntilRef = useRef10(0);
16409
16581
  setLinkPopoverRef.current = setLinkPopover;
16582
+ setFloatingPanelRef.current = setFloatingPanel;
16410
16583
  linkPopoverSessionRef.current = linkPopover;
16584
+ floatingPanelOpenRef.current = Boolean(floatingPanel);
16585
+ useEffect13(() => {
16586
+ const syncViewport = () => {
16587
+ const next = window.innerWidth <= 480 ? "mobile" : "desktop";
16588
+ setEditorViewport((prev) => prev === next ? prev : next);
16589
+ };
16590
+ syncViewport();
16591
+ window.addEventListener("resize", syncViewport);
16592
+ return () => window.removeEventListener("resize", syncViewport);
16593
+ }, []);
16411
16594
  const {
16412
16595
  navDragRef,
16413
16596
  navDropSlots,
@@ -17738,11 +17921,11 @@ function OhhwellsBridge() {
17738
17921
  for (const [key, val] of Object.entries(content)) {
17739
17922
  if (key === "__ohw_sections") continue;
17740
17923
  if (key === AI_SECTIONS_KEY) continue;
17924
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
17925
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
17741
17926
  if (key === BRAND_KIT_KEY) continue;
17742
17927
  if (key === STYLE_STORE_KEY) continue;
17743
17928
  if (BRAND_CHROME_KEYS.has(key)) continue;
17744
- if (key === LOGO_PLACEHOLDER_KEY) continue;
17745
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
17746
17929
  if (applyVideoSettingNode(key, val)) continue;
17747
17930
  if (applyCarouselNode(key, val)) continue;
17748
17931
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -17802,16 +17985,22 @@ function OhhwellsBridge() {
17802
17985
  };
17803
17986
  const cached = contentCache.get(subdomain);
17804
17987
  if (cached) {
17988
+ setShowBranding(brandingCache.get(subdomain) ?? false);
17805
17989
  applyContent(cached).finally(() => setFetchState("done"));
17806
17990
  return;
17807
17991
  }
17808
17992
  let cancelled = false;
17809
17993
  setFetchState("loading");
17810
17994
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17811
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
17995
+ const initialPath = pathname;
17996
+ fetchedContentPaths.add(`${subdomain}::${initialPath}`);
17997
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
17812
17998
  if (cancelled) return;
17813
17999
  const content = data?.content ?? {};
18000
+ const branding = Boolean(data?.showBranding);
17814
18001
  contentCache.set(subdomain, content);
18002
+ brandingCache.set(subdomain, branding);
18003
+ setShowBranding(branding);
17815
18004
  return applyContent(content);
17816
18005
  }).catch(() => {
17817
18006
  }).finally(() => {
@@ -17944,11 +18133,12 @@ function OhhwellsBridge() {
17944
18133
  for (const [key, val] of Object.entries(content)) {
17945
18134
  if (key === "__ohw_sections") continue;
17946
18135
  if (key === AI_SECTIONS_KEY) continue;
18136
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
18137
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
17947
18138
  if (key === BRAND_KIT_KEY) continue;
17948
18139
  if (key === STYLE_STORE_KEY) continue;
18140
+ if (key === STYLE_STORE_KEY) continue;
17949
18141
  if (BRAND_CHROME_KEYS.has(key)) continue;
17950
- if (key === LOGO_PLACEHOLDER_KEY) continue;
17951
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
17952
18142
  if (applyVideoSettingNode(key, val)) continue;
17953
18143
  if (applyCarouselNode(key, val)) continue;
17954
18144
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -17994,6 +18184,17 @@ function OhhwellsBridge() {
17994
18184
  debounceTimer = setTimeout(applyFromCache, 150);
17995
18185
  };
17996
18186
  applyFromCache();
18187
+ const pathCacheKey = `${subdomain}::${pathname}`;
18188
+ if (!fetchedContentPaths.has(pathCacheKey)) {
18189
+ fetchedContentPaths.add(pathCacheKey);
18190
+ const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
18191
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
18192
+ if (!data?.content) return;
18193
+ contentCache.set(subdomain, data.content);
18194
+ applyFromCache();
18195
+ }).catch(() => {
18196
+ });
18197
+ }
17997
18198
  observer = new MutationObserver(scheduleApply);
17998
18199
  observer.observe(document.body, { childList: true, subtree: true });
17999
18200
  return () => {
@@ -18107,25 +18308,13 @@ function OhhwellsBridge() {
18107
18308
  };
18108
18309
  const t1 = setTimeout(measure, 50);
18109
18310
  const t2 = setTimeout(measure, 500);
18110
- let lastWidth = window.innerWidth;
18111
- let resizeTimers = [];
18112
- const clearResizeTimers = () => {
18113
- resizeTimers.forEach(clearTimeout);
18114
- resizeTimers = [];
18115
- };
18116
- const handleResize = () => {
18117
- if (window.innerWidth === lastWidth) return;
18118
- lastWidth = window.innerWidth;
18119
- clearResizeTimers();
18120
- resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
18121
- };
18122
- window.addEventListener("resize", handleResize);
18311
+ const ro = new ResizeObserver(schedule);
18312
+ ro.observe(document.body);
18123
18313
  return () => {
18124
18314
  clearTimeout(t1);
18125
18315
  clearTimeout(t2);
18126
18316
  if (raf != null) cancelAnimationFrame(raf);
18127
- clearResizeTimers();
18128
- window.removeEventListener("resize", handleResize);
18317
+ ro.disconnect();
18129
18318
  };
18130
18319
  }, [pathname, isEditMode, postToParent2]);
18131
18320
  useEffect13(() => {
@@ -18371,9 +18560,6 @@ function OhhwellsBridge() {
18371
18560
  if (target.closest("[data-ohw-state-toggle]")) return;
18372
18561
  if (target.closest("[data-ohw-max-badge]")) return;
18373
18562
  if (isInsideLinkEditor(target)) return;
18374
- if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
18375
- clearMediaSelectionRef.current();
18376
- }
18377
18563
  if (isInsideFloatingPanel(target)) return;
18378
18564
  if (target.closest("[data-ohw-form-toolbar]")) return;
18379
18565
  if (target.closest(
@@ -18381,6 +18567,9 @@ function OhhwellsBridge() {
18381
18567
  )) {
18382
18568
  return;
18383
18569
  }
18570
+ if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
18571
+ clearMediaSelectionRef.current();
18572
+ }
18384
18573
  {
18385
18574
  const formEl = getFormElement(target);
18386
18575
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -18532,14 +18721,6 @@ function OhhwellsBridge() {
18532
18721
  }
18533
18722
  const clickedButton = findClosestButtonLike(target);
18534
18723
  const buttonOnMedia = Boolean(clickedButton && isMediaEditable(editable) && editable.contains(clickedButton));
18535
- console.log("[click-debug]", {
18536
- editableType: editable.dataset.ohwEditable,
18537
- editableTag: editable.tagName,
18538
- targetTag: target.tagName,
18539
- clickedButtonTag: clickedButton?.tagName ?? null,
18540
- buttonOnMedia,
18541
- isMediaEditableEditable: isMediaEditable(editable)
18542
- });
18543
18724
  if (isMediaEditable(editable) && !buttonOnMedia) {
18544
18725
  e.preventDefault();
18545
18726
  e.stopPropagation();
@@ -18566,11 +18747,6 @@ function OhhwellsBridge() {
18566
18747
  const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
18567
18748
  const hrefCtx = getHrefKeyFromElement(hrefLookupTarget);
18568
18749
  const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
18569
- console.log("[click-debug 2]", {
18570
- hrefLookupTargetTag: hrefLookupTarget.tagName,
18571
- hrefCtx: hrefCtx ? { key: hrefCtx.key } : null,
18572
- navAnchorTag: navAnchor?.tagName ?? null
18573
- });
18574
18750
  if (navAnchor) {
18575
18751
  e.preventDefault();
18576
18752
  e.stopPropagation();
@@ -18740,6 +18916,9 @@ function OhhwellsBridge() {
18740
18916
  setHoveredItemRect(null);
18741
18917
  hoveredNavContainerRef.current = null;
18742
18918
  setHoveredNavContainerRect(null);
18919
+ siblingHintElRef.current = null;
18920
+ setSiblingHintRect(null);
18921
+ setSiblingHintRects([]);
18743
18922
  return;
18744
18923
  }
18745
18924
  {
@@ -18858,7 +19037,6 @@ function OhhwellsBridge() {
18858
19037
  hoveredNavContainerRef.current = null;
18859
19038
  setHoveredNavContainerRect(null);
18860
19039
  hoveredItemElRef.current = editable;
18861
- setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
18862
19040
  }
18863
19041
  }
18864
19042
  }
@@ -19155,7 +19333,7 @@ function OhhwellsBridge() {
19155
19333
  }
19156
19334
  };
19157
19335
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
19158
- if (linkPopoverOpenRef.current) {
19336
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
19159
19337
  if (hoveredImageRef.current) {
19160
19338
  hoveredImageRef.current = null;
19161
19339
  hoveredImageHasTextOverlapRef.current = false;
@@ -19520,8 +19698,7 @@ function OhhwellsBridge() {
19520
19698
  };
19521
19699
  const handleMouseMove = (e) => {
19522
19700
  const { clientX, clientY } = e;
19523
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
19524
- if (isOverEditorChrome(clientX, clientY)) {
19701
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
19525
19702
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
19526
19703
  formHoverElRef.current = null;
19527
19704
  setFormHoverRect(null);
@@ -19529,6 +19706,12 @@ function OhhwellsBridge() {
19529
19706
  setHoveredItemRect(null);
19530
19707
  hoveredNavContainerRef.current = null;
19531
19708
  setHoveredNavContainerRect(null);
19709
+ siblingHintElRef.current = null;
19710
+ setSiblingHintRect(null);
19711
+ setSiblingHintRects([]);
19712
+ dismissImageHover();
19713
+ clearImageHover();
19714
+ setSectionGap(null);
19532
19715
  return;
19533
19716
  }
19534
19717
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -19540,7 +19723,11 @@ function OhhwellsBridge() {
19540
19723
  if (e.data?.type !== "ow:pointer-sync") return;
19541
19724
  const { clientX, clientY } = e.data;
19542
19725
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
19543
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
19726
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
19727
+ dismissImageHover();
19728
+ clearImageHover();
19729
+ return;
19730
+ }
19544
19731
  if (probeSocialsRowAt(clientX, clientY)) return;
19545
19732
  probeSectionGapAt(clientX, clientY);
19546
19733
  probeImageAt(clientX, clientY);
@@ -19847,11 +20034,11 @@ function OhhwellsBridge() {
19847
20034
  continue;
19848
20035
  }
19849
20036
  if (key === AI_SECTIONS_KEY) continue;
20037
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
20038
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
19850
20039
  if (key === BRAND_KIT_KEY) continue;
19851
20040
  if (key === STYLE_STORE_KEY) continue;
19852
20041
  if (BRAND_CHROME_KEYS.has(key)) continue;
19853
- if (key === LOGO_PLACEHOLDER_KEY) continue;
19854
- if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
19855
20042
  if (applyVideoSettingNode(key, val)) continue;
19856
20043
  if (applyCarouselNode(key, val)) continue;
19857
20044
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -20012,6 +20199,20 @@ function OhhwellsBridge() {
20012
20199
  postAiSectionsChanged();
20013
20200
  };
20014
20201
  window.addEventListener("message", handleAiSetSections);
20202
+ const handleMoveSection = (e) => {
20203
+ if (e.data?.type !== "ow:move-section") return;
20204
+ const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
20205
+ const direction = e.data.direction === "up" || e.data.direction === "down" ? e.data.direction : null;
20206
+ if (!instanceId || !direction) return;
20207
+ const entries = moveSectionInstance(instanceId, direction, window.location.pathname);
20208
+ if (!entries) return;
20209
+ const orderJson = JSON.stringify(entries);
20210
+ editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
20211
+ setAiSectionOrder(orderJson, window.location.pathname);
20212
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
20213
+ window.dispatchEvent(new Event("resize"));
20214
+ };
20215
+ window.addEventListener("message", handleMoveSection);
20015
20216
  const handleAiSetBrand = (e) => {
20016
20217
  if (e.data?.type !== "ow:ai-set-brand") return;
20017
20218
  const value = typeof e.data.value === "string" ? e.data.value : "";
@@ -20041,20 +20242,6 @@ function OhhwellsBridge() {
20041
20242
  postToParentRef.current({ type: "ow:brand-value", value });
20042
20243
  };
20043
20244
  window.addEventListener("message", handleGetBrand);
20044
- const handleMoveSection = (e) => {
20045
- if (e.data?.type !== "ow:move-section") return;
20046
- const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
20047
- const direction = e.data.direction === "up" || e.data.direction === "down" ? e.data.direction : null;
20048
- if (!instanceId || !direction) return;
20049
- const entries = moveSectionInstance(instanceId, direction, window.location.pathname);
20050
- if (!entries) return;
20051
- const orderJson = JSON.stringify(entries);
20052
- editContentRef.current = { ...editContentRef.current, [SECTION_ORDER_KEY]: orderJson };
20053
- setAiSectionOrder(orderJson, window.location.pathname);
20054
- postToParentRef.current({ type: "ow:change", nodes: [{ key: SECTION_ORDER_KEY, text: orderJson }] });
20055
- window.dispatchEvent(new Event("resize"));
20056
- };
20057
- window.addEventListener("message", handleMoveSection);
20058
20245
  const handlePanelDragging = (e) => {
20059
20246
  if (e.data?.type !== "ow:panel-dragging") return;
20060
20247
  if (e.data.dragging) document.documentElement.setAttribute("data-ohw-panel-dragging", "");
@@ -20112,6 +20299,12 @@ function OhhwellsBridge() {
20112
20299
  closeLinkPopoverRef.current();
20113
20300
  return;
20114
20301
  }
20302
+ if (floatingPanelOpenRef.current) {
20303
+ setFloatingPanelRef.current(null);
20304
+ deselectRef.current();
20305
+ deactivateRef.current();
20306
+ return;
20307
+ }
20115
20308
  deselectRef.current();
20116
20309
  deactivateRef.current();
20117
20310
  clearMediaSelectionRef.current();
@@ -20386,8 +20579,12 @@ function OhhwellsBridge() {
20386
20579
  if (inserted) {
20387
20580
  const tracker = getSectionsTracker();
20388
20581
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
20389
- const h = document.body.scrollHeight;
20390
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
20582
+ const reportHeight = () => {
20583
+ const h = document.body.scrollHeight;
20584
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
20585
+ };
20586
+ reportHeight();
20587
+ setTimeout(reportHeight, 500);
20391
20588
  }
20392
20589
  };
20393
20590
  const handleSwitchSchedule = (e) => {
@@ -20783,17 +20980,17 @@ function OhhwellsBridge() {
20783
20980
  window.removeEventListener("message", handleAiApplyTree);
20784
20981
  window.removeEventListener("message", handleAiDeleteSection);
20785
20982
  window.removeEventListener("message", handleAiSetSections);
20983
+ window.removeEventListener("message", handleMoveSection);
20786
20984
  window.removeEventListener("message", handleAiSetBrand);
20787
20985
  window.removeEventListener("message", handleAiSetStyles);
20788
20986
  window.removeEventListener("message", handleGetBrand);
20789
- window.removeEventListener("message", handleMoveSection);
20790
20987
  window.removeEventListener("message", handlePanelDragging);
20791
20988
  window.removeEventListener("message", handleDeleteSection);
20792
20989
  window.removeEventListener("message", handleDeactivate);
20793
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
20794
20990
  window.removeEventListener("message", handleToastAction);
20795
20991
  window.removeEventListener("message", handleFormCount);
20796
20992
  window.removeEventListener("message", handleUiEscape);
20993
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
20797
20994
  autoSaveTimers.current.forEach(clearTimeout);
20798
20995
  autoSaveTimers.current.clear();
20799
20996
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
@@ -20996,7 +21193,7 @@ function OhhwellsBridge() {
20996
21193
  postToParent2({
20997
21194
  type: "ow:ready",
20998
21195
  version: "1",
20999
- bridgeVersion: "0.1.78",
21196
+ bridgeVersion: "0.1.80",
21000
21197
  path: pathname,
21001
21198
  nodes: collectEditableNodes(editContentRef.current),
21002
21199
  sections
@@ -21459,6 +21656,7 @@ function OhhwellsBridge() {
21459
21656
  return /* @__PURE__ */ jsxs20(Fragment8, { children: [
21460
21657
  /* @__PURE__ */ jsx33("div", { id: "ohw-loader", suppressHydrationWarning: true, style: { ...OHW_LOADER_STYLE, display: "none" }, children: /* @__PURE__ */ jsx33(OhwLoaderSpinner, {}) }),
21461
21658
  /* @__PURE__ */ jsx33("script", { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: OHW_LOADER_PREHYDRATE_SCRIPT } }),
21659
+ subdomain && !isEditMode && showBranding && /* @__PURE__ */ jsx33(MadeWithOhhWells, {}),
21462
21660
  bridgeRoot ? createPortal2(
21463
21661
  /* @__PURE__ */ jsxs20(Fragment8, { children: [
21464
21662
  /* @__PURE__ */ jsx33("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
@@ -21914,6 +22112,59 @@ function OhhwellsBridge() {
21914
22112
  ) : null
21915
22113
  ] });
21916
22114
  }
22115
+
22116
+ // src/ui/EmptySection.tsx
22117
+ import Link3 from "next/link";
22118
+ import { Fragment as Fragment9, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
22119
+ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
22120
+ return /* @__PURE__ */ jsxs21(Fragment9, { children: [
22121
+ /* @__PURE__ */ jsx34(
22122
+ "p",
22123
+ {
22124
+ style: {
22125
+ fontFamily: "var(--brand-font-body)",
22126
+ fontSize: "0.75rem",
22127
+ fontWeight: 500,
22128
+ letterSpacing: "0.15em",
22129
+ textTransform: "uppercase",
22130
+ color: "var(--brand-accent)",
22131
+ marginBottom: "1.5rem"
22132
+ },
22133
+ children: /* @__PURE__ */ jsx34(Link3, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx34("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
22134
+ }
22135
+ ),
22136
+ /* @__PURE__ */ jsx34(
22137
+ "h1",
22138
+ {
22139
+ style: {
22140
+ fontFamily: "var(--brand-font-heading)",
22141
+ fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
22142
+ lineHeight: 1.1,
22143
+ letterSpacing: "-0.025em",
22144
+ color: "var(--brand-text)",
22145
+ marginBottom: "1rem"
22146
+ },
22147
+ ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
22148
+ children: title
22149
+ }
22150
+ ),
22151
+ /* @__PURE__ */ jsx34(
22152
+ "p",
22153
+ {
22154
+ style: {
22155
+ fontFamily: "var(--brand-font-body)",
22156
+ fontSize: "1rem",
22157
+ lineHeight: 1.7,
22158
+ fontWeight: 300,
22159
+ color: "var(--brand-text-muted)",
22160
+ maxWidth: "340px"
22161
+ },
22162
+ ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
22163
+ children: "This page doesn't have any content yet."
22164
+ }
22165
+ )
22166
+ ] });
22167
+ }
21917
22168
  export {
21918
22169
  AI_DEFAULT_BRAND,
21919
22170
  AI_TREE_SCHEMA_VERSIONS,
@@ -21930,6 +22181,7 @@ export {
21930
22181
  DropdownMenuItem,
21931
22182
  DropdownMenuSeparator,
21932
22183
  DropdownMenuTrigger,
22184
+ EmptySection,
21933
22185
  ItemActionToolbar,
21934
22186
  ItemInteractionLayer,
21935
22187
  LinkEditorPanel,