@ohhwells/bridge 0.1.84 → 0.1.85-next.254

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,
@@ -142,6 +143,7 @@ function isRenderableTree(value) {
142
143
 
143
144
  // src/lib/ai-sections-store.ts
144
145
  var AI_SECTIONS_KEY = "__ohw_ai_sections";
146
+ var AI_SLOT_KEY_PREFIX = "ai.";
145
147
  var EMPTY_AI_SECTIONS = { v: 1, sections: [] };
146
148
  function parseAiSectionsState(raw) {
147
149
  if (!raw) return EMPTY_AI_SECTIONS;
@@ -185,6 +187,63 @@ function applyTreeToState(state, payload) {
185
187
  const others = state.sections.filter((existing) => existing.id !== entry.id);
186
188
  return { ...state, v: 1, sections: [...others, entry] };
187
189
  }
190
+ function foldAlignIntoTrees(state, store) {
191
+ const byId = new Map(state.sections.map((entry) => [entry.id, entry]));
192
+ const nextTrees = /* @__PURE__ */ new Map();
193
+ const treeFor = (id) => {
194
+ const cloned = nextTrees.get(id);
195
+ if (cloned) return cloned;
196
+ const entry = byId.get(id);
197
+ if (!entry) return void 0;
198
+ const fresh = {
199
+ ...entry.tree,
200
+ rows: entry.tree.rows.map((row) => ({ ...row, blocks: row.blocks.map((block) => ({ ...block })) }))
201
+ };
202
+ nextTrees.set(id, fresh);
203
+ return fresh;
204
+ };
205
+ const nodes = {};
206
+ for (const [key, override] of Object.entries(store.nodes)) {
207
+ const match = override.align !== void 0 && key.startsWith(AI_SLOT_KEY_PREFIX) ? key.match(/^ai\.(.+?)\.r(\d+)\.b(\d+)(?:\.|$)/) : null;
208
+ const tree = match ? treeFor(match[1]) : void 0;
209
+ const block = match && tree ? tree.rows[Number(match[2])]?.blocks[Number(match[3])] : void 0;
210
+ if (!block) {
211
+ nodes[key] = override;
212
+ continue;
213
+ }
214
+ block.align = override.align;
215
+ const rest = { ...override };
216
+ delete rest.align;
217
+ if (Object.keys(rest).length > 0) nodes[key] = rest;
218
+ }
219
+ const sections = {};
220
+ for (const [sectionId, override] of Object.entries(store.sections)) {
221
+ const tree = override.align !== void 0 ? treeFor(sectionId) : void 0;
222
+ if (!tree) {
223
+ sections[sectionId] = override;
224
+ continue;
225
+ }
226
+ for (const row of tree.rows) {
227
+ for (const block of row.blocks) block.align = override.align;
228
+ }
229
+ const rest = { ...override };
230
+ delete rest.align;
231
+ if (Object.keys(rest).length > 0) sections[sectionId] = rest;
232
+ }
233
+ if (nextTrees.size === 0) return { state, store, changed: false };
234
+ return {
235
+ state: {
236
+ ...state,
237
+ v: 1,
238
+ sections: state.sections.map((entry) => {
239
+ const tree = nextTrees.get(entry.id);
240
+ return tree ? { ...entry, tree } : entry;
241
+ })
242
+ },
243
+ store: { v: 1, sections, nodes },
244
+ changed: true
245
+ };
246
+ }
188
247
  function removeFromState(state, id) {
189
248
  return { ...state, v: 1, sections: state.sections.filter((entry) => entry.id !== id) };
190
249
  }
@@ -403,6 +462,12 @@ function styleSheetCss() {
403
462
  `[data-ohw-style-spacing="${spacing}"] { padding-top: ${px}px !important; padding-bottom: ${px}px !important; }`
404
463
  );
405
464
  }
465
+ rules.push(
466
+ `[data-ohw-style-corners="sharp"] :is(.card, [data-ohw-card]) { border-radius: 0 !important; }`
467
+ );
468
+ for (const align of ["left", "center", "right"]) {
469
+ rules.push(`[data-ohw-style-align="${align}"] { text-align: ${align} !important; }`);
470
+ }
406
471
  return rules.join("\n");
407
472
  }
408
473
  var STYLE_FONT_LINK_ID = "ohw-style-fonts";
@@ -429,10 +494,25 @@ var SECTION_ATTRS = {
429
494
  textDistribution: "data-ohw-style-distribution",
430
495
  headlineScale: "data-ohw-style-headline",
431
496
  imageAspect: "data-ohw-style-aspect",
432
- spacing: "data-ohw-style-spacing"
497
+ spacing: "data-ohw-style-spacing",
498
+ cornerStyle: "data-ohw-style-corners",
499
+ align: "data-ohw-style-align"
433
500
  };
434
501
  var NODE_WROTE_ATTR = "data-ohw-style-node";
435
- var NODE_PROPS = ["color", "font-family", "font-size", "background"];
502
+ var NODE_PROPS = [
503
+ "color",
504
+ "font-family",
505
+ "font-size",
506
+ "background",
507
+ "text-align",
508
+ "justify-content",
509
+ "align-items"
510
+ ];
511
+ var ALIGN_JUSTIFY = {
512
+ left: "flex-start",
513
+ center: "center",
514
+ right: "flex-end"
515
+ };
436
516
  function saveInline(el, prop) {
437
517
  const attr = `data-ohw-style-prev-${prop}`;
438
518
  if (el.hasAttribute(attr)) return;
@@ -476,6 +556,10 @@ function clearNodeProps(root) {
476
556
  function buttonSurfaceOf(el) {
477
557
  return el.closest("a, button") ?? el;
478
558
  }
559
+ function alignSubjectOf(el) {
560
+ const button = el.closest('[data-ohw-role="button"]');
561
+ return button?.parentElement ?? el;
562
+ }
479
563
  function applyStylesToDom(store) {
480
564
  ensureStyleSheet();
481
565
  clearSectionAttrs(document);
@@ -521,6 +605,18 @@ function applyStylesToDom(store) {
521
605
  el.style.setProperty("font-size", `${override.fontSize}px`, "important");
522
606
  el.setAttribute(NODE_WROTE_ATTR, "");
523
607
  }
608
+ if (override.align !== void 0) {
609
+ const subject = alignSubjectOf(el);
610
+ saveInline(subject, "text-align");
611
+ saveInline(subject, "justify-content");
612
+ subject.style.setProperty("text-align", override.align, "important");
613
+ subject.style.setProperty(
614
+ "justify-content",
615
+ ALIGN_JUSTIFY[override.align] ?? "flex-start",
616
+ "important"
617
+ );
618
+ subject.setAttribute(NODE_WROTE_ATTR, "");
619
+ }
524
620
  if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
525
621
  const surface = buttonSurfaceOf(el);
526
622
  if (override.buttonBackground !== void 0) {
@@ -544,6 +640,284 @@ var import_client = require("react-dom/client");
544
640
  // src/ui/ai-tree/AiTreeRenderer.tsx
545
641
  var import_react = __toESM(require("react"), 1);
546
642
  var import_lucide_react = require("lucide-react");
643
+
644
+ // src/lib/placeholder-imagery.ts
645
+ var U = (id) => `https://images.unsplash.com/photo-${id}?auto=format&fit=crop&w=1600&q=80`;
646
+ var GENERIC = [
647
+ U("1441986300917-64674bd600d8"),
648
+ U("1486406146926-c627a92ad1ab"),
649
+ U("1497032628192-86f99bcd76bc"),
650
+ U("1521737604893-d14cc237f11d"),
651
+ U("1522071820081-009f0129c71c"),
652
+ U("1519389950473-47ba0277781c"),
653
+ U("1460925895917-afdab827c52f"),
654
+ U("1504384308090-c894fdcc538d")
655
+ ];
656
+ var PEOPLE = [
657
+ U("1500648767791-00dcc994a43e"),
658
+ U("1494790108377-be9c29b29330"),
659
+ U("1507003211169-0a1dd7228f2d"),
660
+ U("1438761681033-6461ffad8d80"),
661
+ U("1544005313-94ddf0286df2"),
662
+ U("1472099645785-5658abf4ff4e"),
663
+ U("1519085360753-af0119f7cbe7"),
664
+ U("1534528741775-53994a69daeb")
665
+ ];
666
+ var THEMED = [
667
+ {
668
+ keywords: ["portrait", "headshot", "person", "people", "team", "staff", "avatar", "founder", "face"],
669
+ pool: PEOPLE
670
+ },
671
+ {
672
+ keywords: ["pet", "dog", "cat", "puppy", "kitten", "vet", "animal"],
673
+ pool: [
674
+ U("1548199973-03cce0bbc87b"),
675
+ U("1450778869180-41d0601e046e"),
676
+ U("1583511655857-d19b40a7a54e"),
677
+ U("1587300003388-59208cc962cb"),
678
+ U("1517849845537-4d257902454a"),
679
+ U("1601758228041-f3b2795255f1")
680
+ ]
681
+ },
682
+ {
683
+ keywords: [
684
+ "baker",
685
+ "bakery",
686
+ "cafe",
687
+ "coffee",
688
+ "latte",
689
+ "restaurant",
690
+ "pastr",
691
+ "bread",
692
+ "cake",
693
+ "cater",
694
+ "chef",
695
+ "kitchen",
696
+ "food",
697
+ "pizza",
698
+ "dessert",
699
+ "brunch",
700
+ "bistro",
701
+ "deli",
702
+ "dish",
703
+ "menu"
704
+ ],
705
+ pool: [
706
+ U("1509440159596-0249088772ff"),
707
+ U("1555507036-ab1f4038808a"),
708
+ U("1517433670267-08bbd4be890f"),
709
+ U("1486427944299-d1955d23e34d"),
710
+ U("1504754524776-8f4f37790ca0"),
711
+ U("1495474472287-4d71bcdd2085"),
712
+ U("1521017432531-fbd92d768814"),
713
+ U("1556909114-f6e7ad7d3136")
714
+ ]
715
+ },
716
+ {
717
+ keywords: [
718
+ "shop",
719
+ "store",
720
+ "boutique",
721
+ "retail",
722
+ "clothing",
723
+ "fashion",
724
+ "jewel",
725
+ "gift",
726
+ "florist",
727
+ "market",
728
+ "grocer",
729
+ "product",
730
+ "storefront"
731
+ ],
732
+ pool: [
733
+ U("1441984904996-e0b6ba687e04"),
734
+ U("1472851294608-062f824d29cc"),
735
+ U("1523381210434-271e8be1f52b"),
736
+ U("1534452203293-494d7ddbf7e0"),
737
+ U("1445205170230-053b83016050"),
738
+ U("1560243563-062bfc001d68")
739
+ ]
740
+ },
741
+ {
742
+ keywords: [
743
+ "yoga",
744
+ "pilates",
745
+ "fitness",
746
+ "gym",
747
+ "workout",
748
+ "trainer",
749
+ "wellness",
750
+ "meditat",
751
+ "massage",
752
+ "therap",
753
+ "physio",
754
+ "chiro",
755
+ "nutrition",
756
+ "spa",
757
+ "studio"
758
+ ],
759
+ pool: [
760
+ U("1544367567-0f2fcb009e0b"),
761
+ U("1506126613408-eca07ce68773"),
762
+ U("1545205597-3d9d02c29597"),
763
+ U("1552196563-55cd4e45efb3"),
764
+ U("1518611012118-696072aa579a"),
765
+ U("1571019613454-1cb2f99b2d8b"),
766
+ U("1540555700478-4be289fbecef"),
767
+ U("1519824145371-296894a0daa9")
768
+ ]
769
+ },
770
+ {
771
+ keywords: [
772
+ "salon",
773
+ "hairdress",
774
+ "haircut",
775
+ "barber",
776
+ "manicure",
777
+ "pedicure",
778
+ "nails",
779
+ "beauty",
780
+ "makeup",
781
+ "cosmetic",
782
+ "eyelash",
783
+ "eyebrow",
784
+ "skincare",
785
+ "esthetic",
786
+ "waxing",
787
+ "hair"
788
+ ],
789
+ pool: [
790
+ U("1560066984-138dadb4c035"),
791
+ U("1522337660859-02fbefca4702"),
792
+ U("1562322140-8baeececf3df"),
793
+ U("1521590832167-7bcbfaa6381f"),
794
+ U("1487412947147-5cebf100ffc2"),
795
+ U("1526045478516-99145907023c")
796
+ ]
797
+ },
798
+ {
799
+ keywords: [
800
+ "cleaning",
801
+ "plumb",
802
+ "electric",
803
+ "landscap",
804
+ "contractor",
805
+ "handyman",
806
+ "renov",
807
+ "hvac",
808
+ "roofing",
809
+ "painting",
810
+ "carpentry",
811
+ "flooring",
812
+ "movers",
813
+ "construction",
814
+ "tools"
815
+ ],
816
+ pool: [
817
+ U("1581578731548-c64695cc6952"),
818
+ U("1504307651254-35680f356dfd"),
819
+ U("1581092160562-40aa08e78837"),
820
+ U("1621905251189-08b45d6a269e"),
821
+ U("1558618666-fcd25c85cd64"),
822
+ U("1585128792020-803d29415281")
823
+ ]
824
+ },
825
+ {
826
+ keywords: [
827
+ "legal",
828
+ "attorney",
829
+ "lawyer",
830
+ "account",
831
+ "bookkeep",
832
+ "consult",
833
+ "coaching",
834
+ "financ",
835
+ "insurance",
836
+ "realtor",
837
+ "estate",
838
+ "marketing",
839
+ "agency",
840
+ "office",
841
+ "business",
842
+ "desk"
843
+ ],
844
+ pool: [
845
+ U("1497366216548-37526070297c"),
846
+ U("1497366811353-6870744d04b2"),
847
+ U("1454165804606-c3d57bc86b40"),
848
+ U("1521791136064-7986c2920216"),
849
+ U("1556761175-b413da4baf72"),
850
+ U("1542744173-8e7e53415bb0")
851
+ ]
852
+ },
853
+ {
854
+ keywords: [
855
+ "wedding",
856
+ "event",
857
+ "party",
858
+ "celebrat",
859
+ "venue",
860
+ "community",
861
+ "nonprofit",
862
+ "charity",
863
+ "workshop",
864
+ "photograph",
865
+ "concert"
866
+ ],
867
+ pool: [
868
+ U("1511578314322-379afb476865"),
869
+ U("1501281668745-f7f57925c3b4"),
870
+ U("1523580494863-6f3031224c94"),
871
+ U("1540575467063-178a50c2df87"),
872
+ U("1505236858219-8359eb29e329"),
873
+ U("1528605248644-14dd04022da1")
874
+ ]
875
+ }
876
+ ];
877
+ function poolForSubject(subject) {
878
+ for (const theme of THEMED) {
879
+ if (theme.keywords.some((k) => subject.includes(k))) {
880
+ return theme.pool;
881
+ }
882
+ }
883
+ return GENERIC;
884
+ }
885
+ function mixedHash(text) {
886
+ let hash = 2166136261;
887
+ for (let i = 0; i < text.length; i++) {
888
+ hash ^= text.charCodeAt(i);
889
+ hash = Math.imul(hash, 16777619);
890
+ }
891
+ return hash >>> 16 & 65535;
892
+ }
893
+ function resolvePlaceholderRef(ref) {
894
+ const match = /^placeholder:([a-z0-9-]+)$/.exec(ref);
895
+ if (!match) return null;
896
+ const subject = match[1];
897
+ const pool = poolForSubject(subject.replace(/-\d+$/, ""));
898
+ return pool[mixedHash(ref) % pool.length];
899
+ }
900
+ function collectPlaceholderRefs(tree) {
901
+ const seen = /* @__PURE__ */ new Set();
902
+ for (const match of JSON.stringify(tree ?? null).matchAll(/"(placeholder:[a-z0-9-]+)"/gu)) {
903
+ seen.add(match[1]);
904
+ }
905
+ return [...seen];
906
+ }
907
+ function buildPlaceholderMap(tree) {
908
+ const map = {};
909
+ const cursor = /* @__PURE__ */ new Map();
910
+ for (const ref of collectPlaceholderRefs(tree)) {
911
+ const subject = ref.slice("placeholder:".length).replace(/-\d+$/, "");
912
+ const pool = poolForSubject(subject);
913
+ const start = cursor.get(pool) ?? mixedHash(ref) % pool.length;
914
+ map[ref] = pool[start % pool.length];
915
+ cursor.set(pool, start + 1);
916
+ }
917
+ return map;
918
+ }
919
+
920
+ // src/ui/ai-tree/AiTreeRenderer.tsx
547
921
  var import_jsx_runtime = require("react/jsx-runtime");
548
922
  function lucideByName(name) {
549
923
  const pascal = name.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
@@ -557,6 +931,7 @@ var typeStyle = (spec, font) => ({
557
931
  fontWeight: spec.weight
558
932
  });
559
933
  var str = (value) => typeof value === "string" ? value : "";
934
+ var cardRadius = (slots) => slots.cornerStyle === "sharp" ? 0 : AI_TREE_TOKENS.radiusCard;
560
935
  var featureLines = (value) => value.split(/\n|<br\s*\/?>/i).map((line) => line.trim()).filter(Boolean);
561
936
  var CHECK_MASK = `data:image/svg+xml,${encodeURIComponent(
562
937
  '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>'
@@ -616,6 +991,22 @@ function accentBandContext(brand) {
616
991
  function textAttrs(ctx, path) {
617
992
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
618
993
  }
994
+ var AI_RESPONSIVE_CSS = [
995
+ "@media (max-width: 960px) {",
996
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
997
+ ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
998
+ "}",
999
+ "@media (max-width: 640px) {",
1000
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
1001
+ " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
1002
+ // Group containers flatten to a column on phones; span placements come along for free.
1003
+ " [data-ai-responsive] [data-ai-group] { display: flex !important; flex-direction: column !important; }",
1004
+ " [data-ai-responsive] [data-ai-group] > * { grid-column: auto !important; }",
1005
+ " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
1006
+ " [data-ai-responsive] { overflow-x: hidden; }",
1007
+ " [data-ai-responsive] img { max-width: 100%; }",
1008
+ "}"
1009
+ ].join("\n");
619
1010
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
620
1011
  function MediaBox({
621
1012
  refValue,
@@ -628,13 +1019,17 @@ function MediaBox({
628
1019
  const url = refValue ? ctx.resolveMedia(refValue) : null;
629
1020
  const isIcon = /^(lucide|simple):/.test(refValue);
630
1021
  const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
631
- const editAttrs = ctx.keyFor && editPath && !isIcon ? { "data-ohw-key": ctx.keyFor(editPath), "data-ohw-editable": "image" } : {};
1022
+ const editAttrs = ctx.keyFor && editPath ? {
1023
+ "data-ohw-key": ctx.keyFor(editPath),
1024
+ "data-ohw-editable": isIcon ? "icon" : "image"
1025
+ } : {};
632
1026
  if (isIcon) {
633
1027
  const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
634
1028
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
635
1029
  "span",
636
1030
  {
637
1031
  "data-ai-icon": refValue,
1032
+ ...editAttrs,
638
1033
  style: {
639
1034
  display: "inline-flex",
640
1035
  width: 48,
@@ -729,7 +1124,7 @@ function TextBlock({ slots, ctx, path }) {
729
1124
  }
730
1125
  function SectionHeaderBlock({ node, ctx, path }) {
731
1126
  const slots = node.slots ?? {};
732
- const align = slots.alignment === "center" ? "center" : "left";
1127
+ const align = node.align ?? (slots.alignment === "center" ? "center" : "left");
733
1128
  const children = node.children ?? [];
734
1129
  const buttonRowIdx = children.findIndex((c) => c.type === "button-row");
735
1130
  const buttonRow = buttonRowIdx >= 0 ? children[buttonRowIdx] : void 0;
@@ -773,7 +1168,7 @@ function SectionHeaderBlock({ node, ctx, path }) {
773
1168
  display: "flex",
774
1169
  gap: AI_TREE_TOKENS.spacing6,
775
1170
  marginTop: AI_TREE_TOKENS.spacing8,
776
- justifyContent: align === "center" ? "center" : "flex-start"
1171
+ justifyContent: align === "center" ? "center" : align === "right" ? "flex-end" : "flex-start"
777
1172
  },
778
1173
  children: (buttonRow.children ?? []).map((button, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
779
1174
  ButtonEl,
@@ -879,10 +1274,11 @@ function PricingCard({ node, ctx, path }) {
879
1274
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
880
1275
  "div",
881
1276
  {
1277
+ "data-ohw-card": "",
882
1278
  style: {
883
1279
  background: hasBg ? ctx.brand.palette.light : "transparent",
884
1280
  border: `1px solid ${dark}`,
885
- borderRadius: AI_TREE_TOKENS.radiusCard,
1281
+ borderRadius: cardRadius(slots),
886
1282
  padding: AI_TREE_TOKENS.paddingBlock,
887
1283
  display: "flex",
888
1284
  flexDirection: "column",
@@ -987,10 +1383,11 @@ function TestimonialCard({ node, ctx, path }) {
987
1383
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
988
1384
  "div",
989
1385
  {
1386
+ "data-ohw-card": "",
990
1387
  "data-ai-avatar-pos": avatarPos ?? void 0,
991
1388
  style: {
992
1389
  background: hasBg ? ctx.cardSurface : "transparent",
993
- borderRadius: hasBg ? AI_TREE_TOKENS.radiusCard : 0,
1390
+ borderRadius: hasBg ? cardRadius(slots) : 0,
994
1391
  overflow: "hidden",
995
1392
  outline: slots.featured === true ? `2px solid ${ctx.brand.palette.primary}` : void 0,
996
1393
  minWidth: 0
@@ -1025,10 +1422,11 @@ function TeamCard({ node, ctx, path }) {
1025
1422
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1026
1423
  "div",
1027
1424
  {
1425
+ "data-ohw-card": "",
1028
1426
  "data-ai-avatar-pos": avatarPos ?? void 0,
1029
1427
  style: {
1030
1428
  background: hasBg ? ctx.cardSurface : "transparent",
1031
- borderRadius: hasBg ? AI_TREE_TOKENS.radiusCard : 0,
1429
+ borderRadius: hasBg ? cardRadius(slots) : 0,
1032
1430
  overflow: "hidden",
1033
1431
  outline: slots.featured === true ? `2px solid ${ctx.brand.palette.primary}` : void 0,
1034
1432
  minWidth: 0,
@@ -1106,7 +1504,7 @@ function CardBlock({ node, ctx, path }) {
1106
1504
  editPath: `${path}.media`
1107
1505
  }
1108
1506
  ) : null;
1109
- const centered = slots.alignment === "center";
1507
+ const centered = (node.align ?? slots.alignment) === "center";
1110
1508
  const content = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1111
1509
  "div",
1112
1510
  {
@@ -1199,9 +1597,10 @@ function CardBlock({ node, ctx, path }) {
1199
1597
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1200
1598
  "div",
1201
1599
  {
1600
+ "data-ohw-card": "",
1202
1601
  style: {
1203
1602
  background: hasBg ? ctx.cardSurface : "transparent",
1204
- borderRadius: hasBg ? AI_TREE_TOKENS.radiusCard : 0,
1603
+ borderRadius: hasBg ? cardRadius(slots) : 0,
1205
1604
  overflow: "hidden",
1206
1605
  outline: slots.featured === true ? `2px solid ${ctx.brand.palette.primary}` : void 0,
1207
1606
  display: horizontal ? "flex" : "block",
@@ -1229,7 +1628,7 @@ function CardBlock({ node, ctx, path }) {
1229
1628
  ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1230
1629
  "div",
1231
1630
  {
1232
- style: /^(lucide|simple):/.test(mediaRef) ? { padding: `${AI_TREE_TOKENS.paddingBlock}px ${AI_TREE_TOKENS.paddingBlock}px 0` } : hasBg ? void 0 : { borderRadius: AI_TREE_TOKENS.radiusCard, overflow: "hidden" },
1631
+ style: /^(lucide|simple):/.test(mediaRef) ? { padding: `${AI_TREE_TOKENS.paddingBlock}px ${AI_TREE_TOKENS.paddingBlock}px 0` } : hasBg ? void 0 : { borderRadius: cardRadius(slots), overflow: "hidden" },
1233
1632
  children: media
1234
1633
  }
1235
1634
  )),
@@ -1520,7 +1919,7 @@ function CollectionBlock({ node, ctx, path }) {
1520
1919
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1521
1920
  "div",
1522
1921
  {
1523
- "data-ai-grid": "",
1922
+ "data-ai-grid": String(itemsPerRow),
1524
1923
  style: {
1525
1924
  display: "grid",
1526
1925
  gridTemplateColumns: `repeat(${itemsPerRow}, 1fr)`,
@@ -1640,6 +2039,32 @@ function renderNode(node, ctx, path) {
1640
2039
  if (child) {
1641
2040
  return renderNode(child, ctx, `${path}.c0`);
1642
2041
  }
2042
+ if (str(slots.provider) === "map" && str(slots.query)) {
2043
+ const query = str(slots.query);
2044
+ const mapAttrs = ctx.keyFor ? {
2045
+ "data-ohw-key": ctx.keyFor(`${path}.query`),
2046
+ "data-ohw-editable": "map",
2047
+ "data-ohw-map-query": query
2048
+ } : {};
2049
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2050
+ "iframe",
2051
+ {
2052
+ ...mapAttrs,
2053
+ "data-ai-embed": "map",
2054
+ title: str(slots.title) || "Map",
2055
+ src: `https://www.google.com/maps?q=${encodeURIComponent(query)}&output=embed`,
2056
+ loading: "lazy",
2057
+ referrerPolicy: "no-referrer-when-downgrade",
2058
+ style: {
2059
+ width: "100%",
2060
+ minHeight: 320,
2061
+ border: 0,
2062
+ borderRadius: AI_TREE_TOKENS.radiusCard,
2063
+ display: "block"
2064
+ }
2065
+ }
2066
+ );
2067
+ }
1643
2068
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1644
2069
  "div",
1645
2070
  {
@@ -1796,9 +2221,14 @@ function AiTreeRenderer({
1796
2221
  const resolvedBrand = brand ?? AI_DEFAULT_BRAND;
1797
2222
  const band = (tree.settings ?? {}).sectionBackground === "accent" ? accentBandContext(resolvedBrand) : null;
1798
2223
  const blockBrand = band?.brand ?? resolvedBrand;
2224
+ const placeholderMap = buildPlaceholderMap(tree);
1799
2225
  const ctx = {
1800
2226
  brand: blockBrand,
1801
- resolveMedia: resolveMedia ?? (() => null),
2227
+ // An owner/library ref resolves through the host resolver; a `placeholder:<subject>` ref the
2228
+ // host cannot resolve falls back to real stock photography (the per-section map first, then a
2229
+ // standalone resolve), so generated galleries, image rows, and overlay backgrounds arrive with
2230
+ // photos instead of grey boxes.
2231
+ resolveMedia: (ref) => resolveMedia?.(ref) ?? placeholderMap[ref] ?? resolvePlaceholderRef(ref),
1802
2232
  cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
1803
2233
  keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
1804
2234
  sectionAlignment: (tree.settings ?? {}).alignment === "center" ? "center" : "left",
@@ -1825,11 +2255,25 @@ function AiTreeRenderer({
1825
2255
  }
1826
2256
  })();
1827
2257
  const distributed = !isOverlay && settings.textDistribution;
2258
+ const rowAlignItems = (rowAlign) => {
2259
+ if (rowAlign === "top") return "start";
2260
+ if (rowAlign === "bottom") return "end";
2261
+ if (rowAlign === "center" || rowAlign === "stretch") return rowAlign;
2262
+ if (distributed === "space-between") return "stretch";
2263
+ return (distributed ?? settings.verticalPosition) === "top" ? "start" : "center";
2264
+ };
2265
+ const cellAlignStyle = (blockAlign) => blockAlign ? {
2266
+ display: "flex",
2267
+ flexDirection: "column",
2268
+ alignItems: blockAlign === "left" ? "flex-start" : blockAlign === "right" ? "flex-end" : "center",
2269
+ textAlign: blockAlign
2270
+ } : {};
1828
2271
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1829
2272
  "section",
1830
2273
  {
1831
2274
  "data-ai-section": tree.tag ?? "",
1832
2275
  ...bgAttrs,
2276
+ "data-ai-responsive": "",
1833
2277
  style: {
1834
2278
  position: "relative",
1835
2279
  padding: `${pad}px 0`,
@@ -1840,12 +2284,13 @@ function AiTreeRenderer({
1840
2284
  color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
1841
2285
  },
1842
2286
  children: [
1843
- isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
2287
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
1844
2288
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_MOBILE_CSS }),
2289
+ isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
1845
2290
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1846
2291
  "div",
1847
2292
  {
1848
- "data-ai-container": "",
2293
+ "data-ai-section-inner": "",
1849
2294
  style: {
1850
2295
  position: "relative",
1851
2296
  maxWidth: AI_TREE_TOKENS.sectionMaxWidth,
@@ -1856,12 +2301,12 @@ function AiTreeRenderer({
1856
2301
  children: tree.rows.map((row, r2) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1857
2302
  "div",
1858
2303
  {
1859
- "data-ai-row": "",
2304
+ "data-ai-columns": "",
1860
2305
  style: {
1861
2306
  display: "grid",
1862
2307
  gridTemplateColumns: "repeat(12, 1fr)",
1863
2308
  gap: AI_TREE_TOKENS.spacing6,
1864
- alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
2309
+ alignItems: rowAlignItems(row.align),
1865
2310
  marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
1866
2311
  },
1867
2312
  children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -1871,6 +2316,8 @@ function AiTreeRenderer({
1871
2316
  style: {
1872
2317
  gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
1873
2318
  minWidth: 0,
2319
+ // Horizontal placement of the block's content within its column.
2320
+ ...cellAlignStyle(block.align),
1874
2321
  // space-between: each column becomes a flex column whose content spreads over
1875
2322
  // the full row height instead of clumping at the top.
1876
2323
  ...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
@@ -7882,6 +8329,7 @@ function MediaOverlay({
7882
8329
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
7883
8330
  );
7884
8331
  }, [isVideo]);
8332
+ const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
7885
8333
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
7886
8334
  const box = {
7887
8335
  position: "fixed",
@@ -8011,17 +8459,17 @@ function MediaOverlay({
8011
8459
  },
8012
8460
  children: [
8013
8461
  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 }),
8014
- isVideo ? "Replace video" : "Replace image"
8462
+ replaceLabel
8015
8463
  ]
8016
8464
  }
8017
8465
  ),
8018
- replaceMode === "none" ? null : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
8466
+ showChrome && replaceMode !== "none" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
8019
8467
  Button,
8020
8468
  {
8021
8469
  "data-ohw-media-overlay": "",
8022
8470
  variant: "outline",
8023
8471
  size: "sm",
8024
- "aria-label": isVideo ? "Replace video" : "Replace image",
8472
+ "aria-label": replaceLabel,
8025
8473
  className: "gap-1.5 cursor-pointer hover:bg-background",
8026
8474
  style: {
8027
8475
  ...OVERLAY_BUTTON_STYLE,
@@ -8044,7 +8492,7 @@ function MediaOverlay({
8044
8492
  },
8045
8493
  children: [
8046
8494
  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 }),
8047
- replaceMode === "full" ? isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image" : null
8495
+ replaceMode === "full" ? replaceLabel : null
8048
8496
  ]
8049
8497
  }
8050
8498
  )
@@ -8250,6 +8698,27 @@ function deleteSectionInstance(instanceId, currentPath, existingEntries) {
8250
8698
  function restoreSectionInstance(instanceId, currentPath, existingEntries) {
8251
8699
  return setSectionRemoved(instanceId, currentPath, existingEntries, false);
8252
8700
  }
8701
+ function duplicateSectionInstance(instanceId, newId, currentPath, existingEntries) {
8702
+ const original = findByInstanceId(instanceId);
8703
+ if (!original) return null;
8704
+ const clone = original.cloneNode(true);
8705
+ clone.setAttribute("data-ohw-instance", newId);
8706
+ const keyRekeys = rekeySectionSubtree(clone, newId);
8707
+ original.insertAdjacentElement("afterend", clone);
8708
+ const byId = new Map(existingEntries.map((e) => [e.instanceId, e]));
8709
+ const entries = topLevelSections().map((el, order) => {
8710
+ const id = instanceIdOf(el);
8711
+ return {
8712
+ instanceId: id,
8713
+ type: el.getAttribute("data-ohw-section") ?? "",
8714
+ order,
8715
+ pagePath: currentPath,
8716
+ ...byId.get(id)?.removed ? { removed: true } : {}
8717
+ };
8718
+ });
8719
+ applyPersistedOrder(entries);
8720
+ return { entries, keyRekeys };
8721
+ }
8253
8722
  function newInstanceId() {
8254
8723
  return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `instance-${Date.now()}-${Math.random().toString(36).slice(2)}`;
8255
8724
  }
@@ -8264,14 +8733,20 @@ function getPageSectionOrderEntries(raw, currentPath) {
8264
8733
  }
8265
8734
  function rekeySectionSubtree(root, instanceId) {
8266
8735
  const suffix = `::${instanceId}`;
8736
+ const pairs = [];
8267
8737
  const rekey = (el, attr) => {
8268
8738
  const current = el.getAttribute(attr);
8269
- if (current) el.setAttribute(attr, `${current}${suffix}`);
8739
+ if (!current) return;
8740
+ const base = current.includes("::") ? current.slice(0, current.indexOf("::")) : current;
8741
+ const next = `${base}${suffix}`;
8742
+ el.setAttribute(attr, next);
8743
+ pairs.push({ from: current, to: next });
8270
8744
  };
8271
8745
  if (root.hasAttribute("data-ohw-key")) rekey(root, "data-ohw-key");
8272
8746
  if (root.hasAttribute("data-ohw-href-key")) rekey(root, "data-ohw-href-key");
8273
8747
  root.querySelectorAll("[data-ohw-key]").forEach((el) => rekey(el, "data-ohw-key"));
8274
8748
  root.querySelectorAll("[data-ohw-href-key]").forEach((el) => rekey(el, "data-ohw-href-key"));
8749
+ return pairs;
8275
8750
  }
8276
8751
  function initSectionInstancesFromContent(content, currentPath) {
8277
8752
  document.querySelectorAll("[data-ohw-section]:not([data-ohw-instance])").forEach((el) => {
@@ -13009,6 +13484,7 @@ function readLogoSizeState(content, placement) {
13009
13484
  function getLogoElement(el) {
13010
13485
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
13011
13486
  if (marked) return marked;
13487
+ if (el.closest('[data-ohw-editable="icon"]')) return null;
13012
13488
  const root = el.closest("nav, [data-ohw-nav-root], footer");
13013
13489
  if (!root) return null;
13014
13490
  const anchor = el.closest("a");
@@ -14384,6 +14860,9 @@ function collectEditableNodes(extraContent, root = document) {
14384
14860
  if (el.dataset.ohwEditable === "link") {
14385
14861
  return { key: el.dataset.ohwKey ?? "", type: "link", text: getLinkHref3(el) };
14386
14862
  }
14863
+ if (el.dataset.ohwEditable === "map") {
14864
+ return { key: el.dataset.ohwKey ?? "", type: "map", text: el.dataset.ohwMapQuery ?? "" };
14865
+ }
14387
14866
  return {
14388
14867
  key: el.dataset.ohwKey ?? "",
14389
14868
  type: el.dataset.ohwEditable ?? "text",
@@ -14937,21 +15416,10 @@ function parseSchedulingInsertAfter(insertAfter) {
14937
15416
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
14938
15417
  };
14939
15418
  }
14940
- function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
14941
- const parsed = parseSchedulingInsertAfter(insertAfter);
14942
- const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
14943
- const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
14944
- return { effectiveInsertAfter, insertBefore };
14945
- }
14946
- function getSchedulingMountPoint(insertAfter) {
14947
- const { anchor } = parseSchedulingInsertAfter(insertAfter);
14948
- let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
14949
- if (!anchorEl && anchor === "scheduling") {
14950
- const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
14951
- anchorEl = widgets.at(-1) ?? null;
14952
- }
14953
- if (!anchorEl) return null;
14954
- return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
15419
+ function resolveEntryAnchor(entry) {
15420
+ if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
15421
+ const parsed = parseSchedulingInsertAfter(entry.insertAfter);
15422
+ return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
14955
15423
  }
14956
15424
  function schedulingMountDepth(insertAfter) {
14957
15425
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -14968,8 +15436,7 @@ function getPageSchedulingEntries(raw) {
14968
15436
  }
14969
15437
  }
14970
15438
  function isSchedulingWidgetMissing(entry) {
14971
- const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
14972
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
15439
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
14973
15440
  }
14974
15441
  function hasMissingSchedulingWidgets(entries) {
14975
15442
  return entries.some(isSchedulingWidgetMissing);
@@ -14999,16 +15466,17 @@ function initSectionsFromContent(content, removeExisting = false) {
14999
15466
  } catch {
15000
15467
  }
15001
15468
  }
15002
- function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
15003
- const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
15004
- const sectionId = schedulingSectionId(effectiveInsertAfter);
15469
+ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
15470
+ const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
15471
+ const sectionId = schedulingSectionId(widgetId);
15005
15472
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
15006
- const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
15007
- if (!mountPoint) return false;
15473
+ const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
15474
+ if (!anchorEl) return false;
15475
+ const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
15008
15476
  const container = document.createElement("div");
15009
15477
  container.dataset.ohwSectionContainer = "scheduling";
15010
- if (insertBefore) {
15011
- const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
15478
+ if (beforeId) {
15479
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
15012
15480
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
15013
15481
  if (!beforePoint) return false;
15014
15482
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -15019,19 +15487,25 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
15019
15487
  }
15020
15488
  tail.insertAdjacentElement("afterend", container);
15021
15489
  }
15022
- const root = (0, import_client2.createRoot)(container);
15023
- (0, import_react_dom3.flushSync)(() => {
15024
- root.render(
15025
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
15026
- SchedulingWidget,
15027
- {
15028
- notifyOnConnect,
15029
- initialScheduleId: scheduleId,
15030
- insertAfter: effectiveInsertAfter
15031
- }
15032
- )
15033
- );
15034
- });
15490
+ try {
15491
+ const root = (0, import_client2.createRoot)(container);
15492
+ (0, import_react_dom3.flushSync)(() => {
15493
+ root.render(
15494
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
15495
+ SchedulingWidget,
15496
+ {
15497
+ notifyOnConnect,
15498
+ initialScheduleId: scheduleId,
15499
+ insertAfter: widgetId
15500
+ }
15501
+ )
15502
+ );
15503
+ });
15504
+ } catch (err) {
15505
+ console.error("[ow:scheduling] render threw", err);
15506
+ container.remove();
15507
+ return false;
15508
+ }
15035
15509
  const tracker = getSectionsTracker();
15036
15510
  let sections = [];
15037
15511
  try {
@@ -15039,10 +15513,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
15039
15513
  } catch {
15040
15514
  }
15041
15515
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
15042
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
15516
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
15043
15517
  sections.push({
15044
15518
  type: "scheduling",
15045
- insertAfter: effectiveInsertAfter,
15519
+ insertAfter: widgetId,
15520
+ anchorId,
15521
+ beforeId: beforeId ?? null,
15046
15522
  pagePath: window.location.pathname,
15047
15523
  ...scheduleId ? { scheduleId } : {}
15048
15524
  });
@@ -15056,7 +15532,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
15056
15532
  for (let i = pending.length - 1; i >= 0; i--) {
15057
15533
  const entry = pending[i];
15058
15534
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
15059
- if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId ?? null)) {
15535
+ const { anchorId, beforeId } = resolveEntryAnchor(entry);
15536
+ if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
15060
15537
  pending.splice(i, 1);
15061
15538
  }
15062
15539
  }
@@ -15148,7 +15625,7 @@ var EDITOR_CHROME_SELECTOR = '[data-ohw-toolbar], [data-ohw-form-toolbar], [data
15148
15625
  function isOverEditorChrome(x, y) {
15149
15626
  return document.elementsFromPoint(x, y).some((el) => el instanceof HTMLElement && el.matches(EDITOR_CHROME_SELECTOR));
15150
15627
  }
15151
- var NON_MEDIA_SELECTOR = '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]):not([data-ohw-editable="icon"]):not([data-ohw-editable="form"])';
15628
+ var NON_MEDIA_SELECTOR = '[data-ohw-editable]:not([data-ohw-editable="image"]):not([data-ohw-editable="bg-image"]):not([data-ohw-editable="video"]):not([data-ohw-editable="icon"]):not([data-ohw-editable="form"]):not([data-ohw-editable="map"])';
15152
15629
  function getVideoEl2(el) {
15153
15630
  return el instanceof HTMLVideoElement ? el : el.querySelector("video");
15154
15631
  }
@@ -15204,6 +15681,12 @@ function applyVideoSettingNode(key, val) {
15204
15681
  });
15205
15682
  return true;
15206
15683
  }
15684
+ function applyMapQuery(el, val) {
15685
+ if (!(el instanceof HTMLIFrameElement)) return;
15686
+ const nextSrc = `https://www.google.com/maps?q=${encodeURIComponent(val)}&output=embed`;
15687
+ if (el.src !== nextSrc) el.src = nextSrc;
15688
+ el.setAttribute("data-ohw-map-query", val);
15689
+ }
15207
15690
  function applyLinkByKey(key, val) {
15208
15691
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
15209
15692
  if (el.dataset.ohwEditable === "link") applyLinkHref(el, val);
@@ -15214,6 +15697,11 @@ function applyLinkByKey(key, val) {
15214
15697
  hrefAnchors.forEach((el) => applyLinkHref(el, val));
15215
15698
  }
15216
15699
  }
15700
+ function isInsideLinkEditor(target) {
15701
+ return Boolean(
15702
+ 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"]')
15703
+ );
15704
+ }
15217
15705
  function isInsideFloatingPanel(target) {
15218
15706
  return Boolean(target.closest("[data-ohw-floating-panel]"));
15219
15707
  }
@@ -15221,11 +15709,6 @@ function isPointOverFloatingPanel(clientX, clientY) {
15221
15709
  const el = document.elementFromPoint(clientX, clientY);
15222
15710
  return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
15223
15711
  }
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
- }
15229
15712
  function getHrefKeyFromElement(el) {
15230
15713
  if (!el) return null;
15231
15714
  const anchor = el.closest("[data-ohw-href-key]");
@@ -15484,7 +15967,7 @@ function getNavigationSelectionParent(el) {
15484
15967
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
15485
15968
  return getFooterLinksContainer();
15486
15969
  }
15487
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
15970
+ 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)) {
15488
15971
  return getNavigationRoot(el);
15489
15972
  }
15490
15973
  return null;
@@ -15699,7 +16182,6 @@ var ICONS = {
15699
16182
  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"/>',
15700
16183
  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"/>'
15701
16184
  };
15702
- var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
15703
16185
  var SELECTION_CHROME_GAP2 = 4;
15704
16186
  var TOOLBAR_STROKE_GAP2 = 4;
15705
16187
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -16079,6 +16561,8 @@ function StateToggle({
16079
16561
  );
16080
16562
  }
16081
16563
  var contentCache = /* @__PURE__ */ new Map();
16564
+ var fetchedContentPaths = /* @__PURE__ */ new Set();
16565
+ var brandingCache = /* @__PURE__ */ new Map();
16082
16566
  var OHW_LOADER_STYLE = {
16083
16567
  position: "fixed",
16084
16568
  inset: 0,
@@ -16116,6 +16600,89 @@ function OhwLoaderSpinner() {
16116
16600
  )
16117
16601
  ] });
16118
16602
  }
16603
+ function OhwBrandMark() {
16604
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
16605
+ "svg",
16606
+ {
16607
+ width: "16",
16608
+ height: "16",
16609
+ viewBox: "0 0 48 48",
16610
+ fill: "none",
16611
+ "aria-hidden": true,
16612
+ style: { display: "block", flexShrink: 0 },
16613
+ xmlns: "http://www.w3.org/2000/svg",
16614
+ children: [
16615
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
16616
+ "mask",
16617
+ {
16618
+ id: "ohw-badge-mark",
16619
+ style: { maskType: "luminance" },
16620
+ maskUnits: "userSpaceOnUse",
16621
+ x: "0",
16622
+ y: "0",
16623
+ width: "48",
16624
+ height: "48",
16625
+ 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" })
16626
+ }
16627
+ ),
16628
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("g", { mask: "url(#ohw-badge-mark)", children: [
16629
+ /* @__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" }),
16630
+ /* @__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" }),
16631
+ /* @__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" }),
16632
+ /* @__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" }),
16633
+ /* @__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" })
16634
+ ] })
16635
+ ]
16636
+ }
16637
+ );
16638
+ }
16639
+ var OHW_BADGE_STYLE = {
16640
+ position: "fixed",
16641
+ left: 20,
16642
+ bottom: 20,
16643
+ zIndex: 2147483e3,
16644
+ boxSizing: "border-box",
16645
+ display: "inline-flex",
16646
+ alignItems: "center",
16647
+ gap: 0,
16648
+ padding: "6px 8px",
16649
+ margin: 0,
16650
+ background: "#ffffff",
16651
+ border: "1px solid #e7e5e4",
16652
+ borderRadius: 9999,
16653
+ boxShadow: "0 1px 3px rgba(0, 0, 0, 0.1)",
16654
+ color: "#0c0a09",
16655
+ textDecoration: "none",
16656
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif"
16657
+ };
16658
+ var OHW_BADGE_LABEL_STYLE = {
16659
+ padding: "0 4px",
16660
+ fontSize: 14,
16661
+ lineHeight: "24px",
16662
+ fontWeight: 500,
16663
+ fontStyle: "normal",
16664
+ letterSpacing: "normal",
16665
+ textTransform: "none",
16666
+ color: "#0c0a09",
16667
+ whiteSpace: "nowrap"
16668
+ };
16669
+ function MadeWithOhhWells() {
16670
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
16671
+ "a",
16672
+ {
16673
+ href: "https://ohhwells.com",
16674
+ target: "_blank",
16675
+ rel: "noopener noreferrer",
16676
+ "aria-label": "Made with OhhWells",
16677
+ "data-ohw-badge": "",
16678
+ style: OHW_BADGE_STYLE,
16679
+ children: [
16680
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(OhwBrandMark, {}),
16681
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { style: OHW_BADGE_LABEL_STYLE, children: "Made with OhhWells" })
16682
+ ]
16683
+ }
16684
+ );
16685
+ }
16119
16686
  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){}})();`;
16120
16687
  function resolveSubdomain(subdomainFromQuery) {
16121
16688
  if (subdomainFromQuery) return subdomainFromQuery;
@@ -16175,6 +16742,7 @@ function OhhwellsBridge() {
16175
16742
  }
16176
16743
  }, []);
16177
16744
  const [fetchState, setFetchState] = (0, import_react17.useState)("idle");
16745
+ const [showBranding, setShowBranding] = (0, import_react17.useState)(false);
16178
16746
  const autoSaveTimers = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
16179
16747
  const activeElRef = (0, import_react17.useRef)(null);
16180
16748
  const pointerHeldRef = (0, import_react17.useRef)(false);
@@ -16523,13 +17091,6 @@ function OhhwellsBridge() {
16523
17091
  const [isItemDragging, setIsItemDragging] = (0, import_react17.useState)(false);
16524
17092
  const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react17.useState)(false);
16525
17093
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
16526
- const [floatingPanel, setFloatingPanel] = (0, import_react17.useState)(null);
16527
- const floatingPanelOpenRef = (0, import_react17.useRef)(false);
16528
- floatingPanelOpenRef.current = floatingPanel !== null;
16529
- const [floatingPanelPos, setFloatingPanelPos] = (0, import_react17.useState)(null);
16530
- const [logoSizeDraft, setLogoSizeDraft] = (0, import_react17.useState)(null);
16531
- const [editorViewport, setEditorViewport] = (0, import_react17.useState)("desktop");
16532
- const [parentScrollSnap, setParentScrollSnap] = (0, import_react17.useState)(null);
16533
17094
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react17.useState)(null);
16534
17095
  const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react17.useState)(null);
16535
17096
  const footerDragRef = (0, import_react17.useRef)(null);
@@ -16547,6 +17108,13 @@ function OhhwellsBridge() {
16547
17108
  const brandKitRef = (0, import_react17.useRef)("");
16548
17109
  const stylesRef = (0, import_react17.useRef)("");
16549
17110
  const pendingDeleteUndoRef = (0, import_react17.useRef)(null);
17111
+ const [floatingPanel, setFloatingPanel] = (0, import_react17.useState)(null);
17112
+ const floatingPanelOpenRef = (0, import_react17.useRef)(false);
17113
+ const setFloatingPanelRef = (0, import_react17.useRef)(setFloatingPanel);
17114
+ const [floatingPanelPos, setFloatingPanelPos] = (0, import_react17.useState)(null);
17115
+ const [logoSizeDraft, setLogoSizeDraft] = (0, import_react17.useState)(null);
17116
+ const [editorViewport, setEditorViewport] = (0, import_react17.useState)("desktop");
17117
+ const [parentScrollSnap, setParentScrollSnap] = (0, import_react17.useState)(null);
16550
17118
  const [sitePages, setSitePages] = (0, import_react17.useState)([]);
16551
17119
  const [sectionsByPath, setSectionsByPath] = (0, import_react17.useState)({});
16552
17120
  const sectionsPrefetchGenRef = (0, import_react17.useRef)(0);
@@ -16555,7 +17123,18 @@ function OhhwellsBridge() {
16555
17123
  const linkPopoverOpenRef = (0, import_react17.useRef)(false);
16556
17124
  const linkPopoverGraceUntilRef = (0, import_react17.useRef)(0);
16557
17125
  setLinkPopoverRef.current = setLinkPopover;
17126
+ setFloatingPanelRef.current = setFloatingPanel;
16558
17127
  linkPopoverSessionRef.current = linkPopover;
17128
+ floatingPanelOpenRef.current = Boolean(floatingPanel);
17129
+ (0, import_react17.useEffect)(() => {
17130
+ const syncViewport = () => {
17131
+ const next = window.innerWidth <= 480 ? "mobile" : "desktop";
17132
+ setEditorViewport((prev) => prev === next ? prev : next);
17133
+ };
17134
+ syncViewport();
17135
+ window.addEventListener("resize", syncViewport);
17136
+ return () => window.removeEventListener("resize", syncViewport);
17137
+ }, []);
16559
17138
  const {
16560
17139
  navDragRef,
16561
17140
  navDropSlots,
@@ -17880,17 +18459,19 @@ function OhhwellsBridge() {
17880
18459
  }
17881
18460
  if (typeof content[STYLE_STORE_KEY] === "string") {
17882
18461
  stylesRef.current = content[STYLE_STORE_KEY];
18462
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
17883
18463
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
17884
18464
  }
17885
18465
  applyBrandChrome(content);
18466
+ initSectionInstancesFromContent(content, window.location.pathname);
17886
18467
  for (const [key, val] of Object.entries(content)) {
17887
18468
  if (key === "__ohw_sections") continue;
17888
18469
  if (key === AI_SECTIONS_KEY) continue;
18470
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
18471
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
17889
18472
  if (key === BRAND_KIT_KEY) continue;
17890
18473
  if (key === STYLE_STORE_KEY) continue;
17891
18474
  if (BRAND_CHROME_KEYS.has(key)) continue;
17892
- if (key === LOGO_PLACEHOLDER_KEY) continue;
17893
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
17894
18475
  if (applyVideoSettingNode(key, val)) continue;
17895
18476
  if (applyCarouselNode(key, val)) continue;
17896
18477
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -17918,6 +18499,8 @@ function OhhwellsBridge() {
17918
18499
  }
17919
18500
  } else if (el.dataset.ohwEditable === "link") {
17920
18501
  applyLinkHref(el, val);
18502
+ } else if (el.dataset.ohwEditable === "map") {
18503
+ applyMapQuery(el, val);
17921
18504
  } else if (el.dataset.ohwEditable === "icon") {
17922
18505
  applyIconMarkup(el, val);
17923
18506
  } else if (el.dataset.ohwEditable === "form") {
@@ -17938,7 +18521,6 @@ function OhhwellsBridge() {
17938
18521
  if (isEditModeRef.current) requestMissingSocialIconsRef.current();
17939
18522
  enforceLinkHrefs();
17940
18523
  initSectionsFromContent(content, true);
17941
- initSectionInstancesFromContent(content, window.location.pathname);
17942
18524
  sectionsLoadedRef.current = true;
17943
18525
  pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
17944
18526
  if (imageLoads.length === 0) return Promise.resolve();
@@ -17950,16 +18532,22 @@ function OhhwellsBridge() {
17950
18532
  };
17951
18533
  const cached = contentCache.get(subdomain);
17952
18534
  if (cached) {
18535
+ setShowBranding(brandingCache.get(subdomain) ?? false);
17953
18536
  applyContent(cached).finally(() => setFetchState("done"));
17954
18537
  return;
17955
18538
  }
17956
18539
  let cancelled = false;
17957
18540
  setFetchState("loading");
17958
18541
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
17959
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
18542
+ const initialPath = pathname;
18543
+ fetchedContentPaths.add(`${subdomain}::${initialPath}`);
18544
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
17960
18545
  if (cancelled) return;
17961
18546
  const content = data?.content ?? {};
18547
+ const branding = Boolean(data?.showBranding);
17962
18548
  contentCache.set(subdomain, content);
18549
+ brandingCache.set(subdomain, branding);
18550
+ setShowBranding(branding);
17963
18551
  return applyContent(content);
17964
18552
  }).catch(() => {
17965
18553
  }).finally(() => {
@@ -18087,16 +18675,17 @@ function OhhwellsBridge() {
18087
18675
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
18088
18676
  }
18089
18677
  if (typeof content[STYLE_STORE_KEY] === "string") {
18678
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
18090
18679
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
18091
18680
  }
18092
18681
  for (const [key, val] of Object.entries(content)) {
18093
18682
  if (key === "__ohw_sections") continue;
18094
18683
  if (key === AI_SECTIONS_KEY) continue;
18684
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
18685
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
18095
18686
  if (key === BRAND_KIT_KEY) continue;
18096
18687
  if (key === STYLE_STORE_KEY) continue;
18097
18688
  if (BRAND_CHROME_KEYS.has(key)) continue;
18098
- if (key === LOGO_PLACEHOLDER_KEY) continue;
18099
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
18100
18689
  if (applyVideoSettingNode(key, val)) continue;
18101
18690
  if (applyCarouselNode(key, val)) continue;
18102
18691
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -18111,6 +18700,8 @@ function OhhwellsBridge() {
18111
18700
  if (video && video.src !== val) applyVideoSrc(video, val);
18112
18701
  } else if (el.dataset.ohwEditable === "link") {
18113
18702
  applyLinkHref(el, val);
18703
+ } else if (el.dataset.ohwEditable === "map") {
18704
+ applyMapQuery(el, val);
18114
18705
  } else if (el.dataset.ohwEditable === "form") {
18115
18706
  } else if (isIconMarkupValue(val)) {
18116
18707
  } else if (el.innerHTML !== val) {
@@ -18142,6 +18733,17 @@ function OhhwellsBridge() {
18142
18733
  debounceTimer = setTimeout(applyFromCache, 150);
18143
18734
  };
18144
18735
  applyFromCache();
18736
+ const pathCacheKey = `${subdomain}::${pathname}`;
18737
+ if (!fetchedContentPaths.has(pathCacheKey)) {
18738
+ fetchedContentPaths.add(pathCacheKey);
18739
+ const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
18740
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
18741
+ if (!data?.content) return;
18742
+ contentCache.set(subdomain, data.content);
18743
+ applyFromCache();
18744
+ }).catch(() => {
18745
+ });
18746
+ }
18145
18747
  observer = new MutationObserver(scheduleApply);
18146
18748
  observer.observe(document.body, { childList: true, subtree: true });
18147
18749
  return () => {
@@ -18257,26 +18859,11 @@ function OhhwellsBridge() {
18257
18859
  const t2 = setTimeout(measure, 500);
18258
18860
  const ro = new ResizeObserver(schedule);
18259
18861
  ro.observe(document.body);
18260
- let lastWidth = window.innerWidth;
18261
- let resizeTimers = [];
18262
- const clearResizeTimers = () => {
18263
- resizeTimers.forEach(clearTimeout);
18264
- resizeTimers = [];
18265
- };
18266
- const handleResize = () => {
18267
- if (window.innerWidth === lastWidth) return;
18268
- lastWidth = window.innerWidth;
18269
- clearResizeTimers();
18270
- resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
18271
- };
18272
- window.addEventListener("resize", handleResize);
18273
18862
  return () => {
18274
18863
  clearTimeout(t1);
18275
18864
  clearTimeout(t2);
18276
18865
  if (raf != null) cancelAnimationFrame(raf);
18277
18866
  ro.disconnect();
18278
- clearResizeTimers();
18279
- window.removeEventListener("resize", handleResize);
18280
18867
  };
18281
18868
  }, [pathname, isEditMode, postToParent2]);
18282
18869
  (0, import_react17.useEffect)(() => {
@@ -18522,9 +19109,6 @@ function OhhwellsBridge() {
18522
19109
  if (target.closest("[data-ohw-state-toggle]")) return;
18523
19110
  if (target.closest("[data-ohw-max-badge]")) return;
18524
19111
  if (isInsideLinkEditor(target)) return;
18525
- if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
18526
- clearMediaSelectionRef.current();
18527
- }
18528
19112
  if (isInsideFloatingPanel(target)) return;
18529
19113
  if (target.closest("[data-ohw-form-toolbar]")) return;
18530
19114
  if (target.closest(
@@ -18532,6 +19116,9 @@ function OhhwellsBridge() {
18532
19116
  )) {
18533
19117
  return;
18534
19118
  }
19119
+ if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
19120
+ clearMediaSelectionRef.current();
19121
+ }
18535
19122
  {
18536
19123
  const formEl = getFormElement(target);
18537
19124
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -18683,14 +19270,6 @@ function OhhwellsBridge() {
18683
19270
  }
18684
19271
  const clickedButton = findClosestButtonLike(target);
18685
19272
  const buttonOnMedia = Boolean(clickedButton && isMediaEditable(editable) && editable.contains(clickedButton));
18686
- console.log("[click-debug]", {
18687
- editableType: editable.dataset.ohwEditable,
18688
- editableTag: editable.tagName,
18689
- targetTag: target.tagName,
18690
- clickedButtonTag: clickedButton?.tagName ?? null,
18691
- buttonOnMedia,
18692
- isMediaEditableEditable: isMediaEditable(editable)
18693
- });
18694
19273
  if (isMediaEditable(editable) && !buttonOnMedia) {
18695
19274
  e.preventDefault();
18696
19275
  e.stopPropagation();
@@ -18717,11 +19296,6 @@ function OhhwellsBridge() {
18717
19296
  const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
18718
19297
  const hrefCtx = getHrefKeyFromElement(hrefLookupTarget);
18719
19298
  const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
18720
- console.log("[click-debug 2]", {
18721
- hrefLookupTargetTag: hrefLookupTarget.tagName,
18722
- hrefCtx: hrefCtx ? { key: hrefCtx.key } : null,
18723
- navAnchorTag: navAnchor?.tagName ?? null
18724
- });
18725
19299
  if (navAnchor) {
18726
19300
  e.preventDefault();
18727
19301
  e.stopPropagation();
@@ -18891,6 +19465,9 @@ function OhhwellsBridge() {
18891
19465
  setHoveredItemRect(null);
18892
19466
  hoveredNavContainerRef.current = null;
18893
19467
  setHoveredNavContainerRect(null);
19468
+ siblingHintElRef.current = null;
19469
+ setSiblingHintRect(null);
19470
+ setSiblingHintRects([]);
18894
19471
  return;
18895
19472
  }
18896
19473
  {
@@ -19009,7 +19586,6 @@ function OhhwellsBridge() {
19009
19586
  hoveredNavContainerRef.current = null;
19010
19587
  setHoveredNavContainerRect(null);
19011
19588
  hoveredItemElRef.current = editable;
19012
- setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
19013
19589
  }
19014
19590
  }
19015
19591
  }
@@ -19306,7 +19882,7 @@ function OhhwellsBridge() {
19306
19882
  }
19307
19883
  };
19308
19884
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
19309
- if (linkPopoverOpenRef.current) {
19885
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
19310
19886
  if (hoveredImageRef.current) {
19311
19887
  hoveredImageRef.current = null;
19312
19888
  hoveredImageHasTextOverlapRef.current = false;
@@ -19671,8 +20247,7 @@ function OhhwellsBridge() {
19671
20247
  };
19672
20248
  const handleMouseMove = (e) => {
19673
20249
  const { clientX, clientY } = e;
19674
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
19675
- if (isOverEditorChrome(clientX, clientY)) {
20250
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
19676
20251
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
19677
20252
  formHoverElRef.current = null;
19678
20253
  setFormHoverRect(null);
@@ -19680,6 +20255,12 @@ function OhhwellsBridge() {
19680
20255
  setHoveredItemRect(null);
19681
20256
  hoveredNavContainerRef.current = null;
19682
20257
  setHoveredNavContainerRect(null);
20258
+ siblingHintElRef.current = null;
20259
+ setSiblingHintRect(null);
20260
+ setSiblingHintRects([]);
20261
+ dismissImageHover();
20262
+ clearImageHover();
20263
+ setSectionGap(null);
19683
20264
  return;
19684
20265
  }
19685
20266
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -19691,7 +20272,11 @@ function OhhwellsBridge() {
19691
20272
  if (e.data?.type !== "ow:pointer-sync") return;
19692
20273
  const { clientX, clientY } = e.data;
19693
20274
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
19694
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
20275
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
20276
+ dismissImageHover();
20277
+ clearImageHover();
20278
+ return;
20279
+ }
19695
20280
  if (probeSocialsRowAt(clientX, clientY)) return;
19696
20281
  probeSectionGapAt(clientX, clientY);
19697
20282
  probeImageAt(clientX, clientY);
@@ -19988,9 +20573,11 @@ function OhhwellsBridge() {
19988
20573
  }
19989
20574
  if (typeof content[STYLE_STORE_KEY] === "string") {
19990
20575
  stylesRef.current = content[STYLE_STORE_KEY];
20576
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
19991
20577
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
19992
20578
  }
19993
20579
  applyBrandChrome(content);
20580
+ initSectionInstancesFromContent(content, window.location.pathname);
19994
20581
  let sectionsJson = null;
19995
20582
  for (const [key, val] of Object.entries(content)) {
19996
20583
  if (key === "__ohw_sections") {
@@ -19998,11 +20585,11 @@ function OhhwellsBridge() {
19998
20585
  continue;
19999
20586
  }
20000
20587
  if (key === AI_SECTIONS_KEY) continue;
20588
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
20589
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
20001
20590
  if (key === BRAND_KIT_KEY) continue;
20002
20591
  if (key === STYLE_STORE_KEY) continue;
20003
20592
  if (BRAND_CHROME_KEYS.has(key)) continue;
20004
- if (key === LOGO_PLACEHOLDER_KEY) continue;
20005
- if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
20006
20593
  if (applyVideoSettingNode(key, val)) continue;
20007
20594
  if (applyCarouselNode(key, val)) continue;
20008
20595
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -20016,6 +20603,8 @@ function OhhwellsBridge() {
20016
20603
  if (video && video.src !== val) applyVideoSrc(video, val);
20017
20604
  } else if (el.dataset.ohwEditable === "link") {
20018
20605
  applyLinkHref(el, val);
20606
+ } else if (el.dataset.ohwEditable === "map") {
20607
+ applyMapQuery(el, val);
20019
20608
  } else if (el.dataset.ohwEditable === "icon") {
20020
20609
  applyIconMarkup(el, val);
20021
20610
  } else if (isIconMarkupValue(val)) {
@@ -20032,7 +20621,6 @@ function OhhwellsBridge() {
20032
20621
  sectionsLoadedRef.current = true;
20033
20622
  pendingScheduleConfigRequests.current.splice(0).forEach(processConfigRequest);
20034
20623
  }
20035
- initSectionInstancesFromContent(content, window.location.pathname);
20036
20624
  editContentRef.current = { ...editContentRef.current, ...content };
20037
20625
  reconcileNavbarItemsFromContent(editContentRef.current);
20038
20626
  reconcileFooterOrderFromContent(editContentRef.current);
@@ -20177,12 +20765,35 @@ function OhhwellsBridge() {
20177
20765
  window.addEventListener("message", handleAiSetBrand);
20178
20766
  const handleAiSetStyles = (e) => {
20179
20767
  if (e.data?.type !== "ow:ai-set-styles") return;
20180
- const value = typeof e.data.value === "string" ? e.data.value : "";
20768
+ let value = typeof e.data.value === "string" ? e.data.value : "";
20181
20769
  const previous = stylesRef.current;
20770
+ let previousSections;
20771
+ const store = parseStyleStore(value);
20772
+ if (store) {
20773
+ const folded = foldAlignIntoTrees(parseAiSectionsState(aiSectionsRef.current), store);
20774
+ if (folded.changed) {
20775
+ const nextSections = serializeAiSectionsState(folded.state);
20776
+ if (nextSections !== aiSectionsRef.current) {
20777
+ previousSections = aiSectionsRef.current;
20778
+ aiSectionsRef.current = nextSections;
20779
+ applyAiSectionsToDom(folded.state);
20780
+ postToParentRef.current({
20781
+ type: "ow:change",
20782
+ nodes: [{ key: AI_SECTIONS_KEY, text: nextSections }]
20783
+ });
20784
+ }
20785
+ value = JSON.stringify(folded.store);
20786
+ }
20787
+ }
20182
20788
  stylesRef.current = value;
20183
20789
  applyStylesToDom(parseStyleStore(value));
20184
20790
  postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
20185
- postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
20791
+ postToParentRef.current({
20792
+ type: "ow:ai-styles-applied",
20793
+ previous,
20794
+ value,
20795
+ ...previousSections !== void 0 ? { previousSections } : {}
20796
+ });
20186
20797
  };
20187
20798
  window.addEventListener("message", handleAiSetStyles);
20188
20799
  const handleGetBrand = (e) => {
@@ -20254,6 +20865,34 @@ function OhhwellsBridge() {
20254
20865
  });
20255
20866
  };
20256
20867
  window.addEventListener("message", handleDeleteSection);
20868
+ const handleDuplicateSection = (e) => {
20869
+ if (e.data?.type !== "ow:duplicate-section") return;
20870
+ const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
20871
+ if (!instanceId) return;
20872
+ const newId = newInstanceId();
20873
+ const currentEntries = getPageSectionOrderEntries(editContentRef.current[SECTION_ORDER_KEY], window.location.pathname);
20874
+ const result = duplicateSectionInstance(instanceId, newId, window.location.pathname, currentEntries);
20875
+ if (!result) return;
20876
+ const { entries, keyRekeys } = result;
20877
+ const orderJson = JSON.stringify(entries);
20878
+ const nodes = [{ key: SECTION_ORDER_KEY, text: orderJson }];
20879
+ for (const { from, to } of keyRekeys) {
20880
+ const inherited = editContentRef.current[from];
20881
+ if (inherited !== void 0) nodes.push({ key: to, text: inherited });
20882
+ }
20883
+ editContentRef.current = {
20884
+ ...editContentRef.current,
20885
+ ...Object.fromEntries(nodes.map(({ key, text }) => [key, text]))
20886
+ };
20887
+ setAiSectionOrder(orderJson, window.location.pathname);
20888
+ postToParentRef.current({ type: "ow:change", nodes });
20889
+ window.dispatchEvent(new Event("resize"));
20890
+ const duplicateHeight = document.body.scrollHeight;
20891
+ if (duplicateHeight > 50) postToParentRef.current({ type: "ow:height", height: duplicateHeight });
20892
+ const clone = document.querySelector(`[data-ohw-instance="${CSS.escape(newId)}"]`);
20893
+ if (clone) aiSectionApiRef.current?.selectFromElement(clone);
20894
+ };
20895
+ window.addEventListener("message", handleDuplicateSection);
20257
20896
  const handleDeactivate = (e) => {
20258
20897
  if (e.data?.type !== "ow:deactivate") return;
20259
20898
  if (document.documentElement.hasAttribute("data-ohw-panel-dragging")) return;
@@ -20263,6 +20902,12 @@ function OhhwellsBridge() {
20263
20902
  closeLinkPopoverRef.current();
20264
20903
  return;
20265
20904
  }
20905
+ if (floatingPanelOpenRef.current) {
20906
+ setFloatingPanelRef.current(null);
20907
+ deselectRef.current();
20908
+ deactivateRef.current();
20909
+ return;
20910
+ }
20266
20911
  deselectRef.current();
20267
20912
  deactivateRef.current();
20268
20913
  clearMediaSelectionRef.current();
@@ -20537,8 +21182,12 @@ function OhhwellsBridge() {
20537
21182
  if (inserted) {
20538
21183
  const tracker = getSectionsTracker();
20539
21184
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
20540
- const h = document.body.scrollHeight;
20541
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
21185
+ const reportHeight = () => {
21186
+ const h = document.body.scrollHeight;
21187
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
21188
+ };
21189
+ reportHeight();
21190
+ setTimeout(reportHeight, 500);
20542
21191
  }
20543
21192
  };
20544
21193
  const handleSwitchSchedule = (e) => {
@@ -20940,11 +21589,12 @@ function OhhwellsBridge() {
20940
21589
  window.removeEventListener("message", handleMoveSection);
20941
21590
  window.removeEventListener("message", handlePanelDragging);
20942
21591
  window.removeEventListener("message", handleDeleteSection);
21592
+ window.removeEventListener("message", handleDuplicateSection);
20943
21593
  window.removeEventListener("message", handleDeactivate);
20944
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
20945
21594
  window.removeEventListener("message", handleToastAction);
20946
21595
  window.removeEventListener("message", handleFormCount);
20947
21596
  window.removeEventListener("message", handleUiEscape);
21597
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
20948
21598
  autoSaveTimers.current.forEach(clearTimeout);
20949
21599
  autoSaveTimers.current.clear();
20950
21600
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
@@ -21147,7 +21797,7 @@ function OhhwellsBridge() {
21147
21797
  postToParent2({
21148
21798
  type: "ow:ready",
21149
21799
  version: "1",
21150
- bridgeVersion: "0.1.83",
21800
+ bridgeVersion: "0.1.85",
21151
21801
  path: pathname,
21152
21802
  nodes: collectEditableNodes(editContentRef.current),
21153
21803
  sections
@@ -21610,6 +22260,7 @@ function OhhwellsBridge() {
21610
22260
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
21611
22261
  /* @__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, {}) }),
21612
22262
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("script", { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: OHW_LOADER_PREHYDRATE_SCRIPT } }),
22263
+ subdomain && !isEditMode && showBranding && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(MadeWithOhhWells, {}),
21613
22264
  bridgeRoot ? (0, import_react_dom4.createPortal)(
21614
22265
  /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
21615
22266
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
@@ -22065,6 +22716,59 @@ function OhhwellsBridge() {
22065
22716
  ) : null
22066
22717
  ] });
22067
22718
  }
22719
+
22720
+ // src/ui/EmptySection.tsx
22721
+ var import_link = __toESM(require("next/link"), 1);
22722
+ var import_jsx_runtime34 = require("react/jsx-runtime");
22723
+ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
22724
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
22725
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
22726
+ "p",
22727
+ {
22728
+ style: {
22729
+ fontFamily: "var(--brand-font-body)",
22730
+ fontSize: "0.75rem",
22731
+ fontWeight: 500,
22732
+ letterSpacing: "0.15em",
22733
+ textTransform: "uppercase",
22734
+ color: "var(--brand-accent)",
22735
+ marginBottom: "1.5rem"
22736
+ },
22737
+ 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" }) })
22738
+ }
22739
+ ),
22740
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
22741
+ "h1",
22742
+ {
22743
+ style: {
22744
+ fontFamily: "var(--brand-font-heading)",
22745
+ fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
22746
+ lineHeight: 1.1,
22747
+ letterSpacing: "-0.025em",
22748
+ color: "var(--brand-text)",
22749
+ marginBottom: "1rem"
22750
+ },
22751
+ ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
22752
+ children: title
22753
+ }
22754
+ ),
22755
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
22756
+ "p",
22757
+ {
22758
+ style: {
22759
+ fontFamily: "var(--brand-font-body)",
22760
+ fontSize: "1rem",
22761
+ lineHeight: 1.7,
22762
+ fontWeight: 300,
22763
+ color: "var(--brand-text-muted)",
22764
+ maxWidth: "340px"
22765
+ },
22766
+ ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
22767
+ children: "This page doesn't have any content yet."
22768
+ }
22769
+ )
22770
+ ] });
22771
+ }
22068
22772
  // Annotate the CommonJS export names for ESM import in node:
22069
22773
  0 && (module.exports = {
22070
22774
  AI_DEFAULT_BRAND,
@@ -22082,6 +22786,7 @@ function OhhwellsBridge() {
22082
22786
  DropdownMenuItem,
22083
22787
  DropdownMenuSeparator,
22084
22788
  DropdownMenuTrigger,
22789
+ EmptySection,
22085
22790
  ItemActionToolbar,
22086
22791
  ItemInteractionLayer,
22087
22792
  LinkEditorPanel,