@ohhwells/bridge 0.1.139-next.376 → 0.1.140

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
@@ -72,7 +72,6 @@ __export(index_exports, {
72
72
  dropIndicatorVariants: () => dropIndicatorVariants,
73
73
  ensureFooterTree: () => ensureFooterTree,
74
74
  filterAvailablePages: () => filterAvailablePages,
75
- foldAlignIntoTrees: () => foldAlignIntoTrees,
76
75
  getEditModeInitialState: () => getEditModeInitialState,
77
76
  insertFooterHours: () => insertFooterHours,
78
77
  isEditSessionActive: () => isEditSessionActive,
@@ -81,7 +80,6 @@ __export(index_exports, {
81
80
  isValidUrl: () => isValidUrl,
82
81
  nextViableSlide: () => nextViableSlide,
83
82
  parseAiFooterState: () => parseAiFooterState,
84
- parseAiSectionsState: () => parseAiSectionsState,
85
83
  parseTarget: () => parseTarget,
86
84
  removeFooterBlock: () => removeFooterBlock,
87
85
  resolveFooterCopyright: () => resolveFooterCopyright,
@@ -5298,21 +5296,19 @@ function findIndexSegment(keys) {
5298
5296
  if (len < 2 || tokenLists.some((t) => t.length !== len)) return null;
5299
5297
  let position = -1;
5300
5298
  for (let p = 0; p < len; p++) {
5301
- const values2 = tokenLists.map((t) => t[p]);
5302
- const allSame = values2.every((v) => v === values2[0]);
5299
+ const values = tokenLists.map((t) => t[p]);
5300
+ const allSame = values.every((v) => v === values[0]);
5303
5301
  if (allSame) continue;
5304
- if (position !== -1) return null;
5302
+ const allNumeric = values.every((v) => /^\d+$/.test(v));
5303
+ if (!allNumeric || position !== -1) return null;
5305
5304
  position = p;
5306
5305
  }
5307
5306
  if (position === -1) return null;
5308
- const values = tokenLists.map((t) => t[position]);
5309
- const numericCount = values.filter((v) => /^\d+$/.test(v)).length;
5310
- if (numericCount !== 0 && numericCount !== values.length) return null;
5311
- if (numericCount === 0 && position === len - 1) return null;
5312
5307
  const group = tokenLists[0].slice(0, position).join("-");
5313
5308
  if (!group) return null;
5314
- if (new Set(values).size !== values.length) return null;
5315
- return numericCount === values.length ? { position, group } : { position, group, slug: true };
5309
+ const indices = tokenLists.map((t) => parseInt(t[position], 10));
5310
+ if (new Set(indices).size !== indices.length) return null;
5311
+ return { position, group };
5316
5312
  }
5317
5313
  function singleIndexSegment(key) {
5318
5314
  const tokens = baseKey(key).split("-");
@@ -5358,19 +5354,7 @@ function detectItemGroups(sectionEl) {
5358
5354
  list.push(m);
5359
5355
  byShape.set(shape, list);
5360
5356
  }
5361
- const shaped = Array.from(byShape.values());
5362
- clusters.push(...shaped.filter((c) => c.length >= 2));
5363
- const singles = shaped.filter((c) => c.length === 1).map((c) => c[0]);
5364
- const bySlugShape = /* @__PURE__ */ new Map();
5365
- for (const m of singles) {
5366
- const primary = primaryKey(keyedDescendants(m));
5367
- const tokens = baseKey(primary ?? "").split("-");
5368
- const slugShape = `${tokens.length}|${tokens[0]}|${tokens[tokens.length - 1]}`;
5369
- const list = bySlugShape.get(slugShape) ?? [];
5370
- list.push(m);
5371
- bySlugShape.set(slugShape, list);
5372
- }
5373
- clusters.push(...Array.from(bySlugShape.values()));
5357
+ clusters.push(...Array.from(byShape.values()));
5374
5358
  }
5375
5359
  for (const members of clusters) {
5376
5360
  const keyedPerMember = members.map((m) => keyedDescendants(m));
@@ -5381,28 +5365,11 @@ function detectItemGroups(sectionEl) {
5381
5365
  const dedupeKey = `${segment.group}`;
5382
5366
  if (seenParents.has(dedupeKey)) continue;
5383
5367
  seenParents.add(dedupeKey);
5384
- let items;
5385
- if (segment.slug) {
5386
- const tokens = members.map((_, i) => baseKey(primaries[i]).split("-")[segment.position]);
5387
- let nextOriginal = 1;
5388
- const indices = tokens.map((t) => {
5389
- const cloneMatch = t.match(/^x(\d+)$/);
5390
- return cloneMatch ? parseInt(cloneMatch[1], 10) : nextOriginal++;
5391
- });
5392
- if (new Set(indices).size !== indices.length) continue;
5393
- items = members.map((el, i) => ({
5394
- el,
5395
- index: indices[i],
5396
- keyed: keyedPerMember[i],
5397
- slugToken: tokens[i]
5398
- }));
5399
- } else {
5400
- items = members.map((el, i) => ({
5401
- el,
5402
- index: indexFromKey(primaries[i], segment.position),
5403
- keyed: keyedPerMember[i]
5404
- }));
5405
- }
5368
+ const items = members.map((el, i) => ({
5369
+ el,
5370
+ index: indexFromKey(primaries[i], segment.position),
5371
+ keyed: keyedPerMember[i]
5372
+ }));
5406
5373
  groups.push({ group: segment.group, parent, nested: false, items });
5407
5374
  }
5408
5375
  }
@@ -5526,18 +5493,6 @@ function rekeyItemClone(clone, source, sibling, group, newIndex) {
5526
5493
  const delta = newIndex - source.index;
5527
5494
  const cloneKeyed = keyedDescendants(clone);
5528
5495
  const pairs = [];
5529
- if (source.slugToken) {
5530
- source.keyed.forEach((src, i) => {
5531
- const target = cloneKeyed[i];
5532
- if (!target || target.attr !== src.attr) return;
5533
- const tokens = baseKey(src.key).split("-");
5534
- if (!tokens.includes(source.slugToken)) return;
5535
- const next = tokens.map((t) => t === source.slugToken ? `x${newIndex}` : t).join("-") + keySuffix(src.key);
5536
- target.el.setAttribute(src.attr, next);
5537
- pairs.push({ from: src.key, to: next });
5538
- });
5539
- return pairs;
5540
- }
5541
5496
  source.keyed.forEach((src, i) => {
5542
5497
  const target = cloneKeyed[i];
5543
5498
  if (!target || target.attr !== src.attr) return;
@@ -5581,12 +5536,7 @@ function syncGroup(detected, state) {
5581
5536
  clone.setAttribute(ITEM_ATTR, `${detected.group}:${entry.index}`);
5582
5537
  rekeyItemClone(clone, source, sibling, detected.group, entry.index);
5583
5538
  source.el.insertAdjacentElement("afterend", clone);
5584
- byIndex.set(entry.index, {
5585
- el: clone,
5586
- index: entry.index,
5587
- keyed: keyedDescendants(clone),
5588
- ...source.slugToken ? { slugToken: `x${entry.index}` } : {}
5589
- });
5539
+ byIndex.set(entry.index, { el: clone, index: entry.index, keyed: keyedDescendants(clone) });
5590
5540
  }
5591
5541
  const listed = state.items.map((e) => byIndex.get(e.index)?.el).filter((el) => !!el);
5592
5542
  let prev = null;
@@ -5688,16 +5638,9 @@ function applyItemOps(store, ops, currentContent) {
5688
5638
  const nodes = [];
5689
5639
  const added = [];
5690
5640
  let elements = syncGroup(detected, state);
5691
- const sluggy = detected.items.some((item) => item.slugToken);
5692
- const slugTokenOf = (index) => detected.items.find((item) => item.index === index)?.slugToken ?? (sluggy ? `x${index}` : void 0);
5693
5641
  for (const add of ops.add ?? []) {
5694
5642
  const sourceEl = elements.get(add.cloneOf);
5695
- const source = sourceEl ? {
5696
- el: sourceEl,
5697
- index: add.cloneOf,
5698
- keyed: keyedDescendants(sourceEl),
5699
- ...slugTokenOf(add.cloneOf) ? { slugToken: slugTokenOf(add.cloneOf) } : {}
5700
- } : void 0;
5643
+ const source = sourceEl ? { el: sourceEl, index: add.cloneOf, keyed: keyedDescendants(sourceEl) } : void 0;
5701
5644
  if (!source) continue;
5702
5645
  const newIndex = Math.max(...state.items.map((e) => e.index), ...detected.items.map((i) => i.index)) + 1;
5703
5646
  const sibling = detected.items.find((item) => item.index !== add.cloneOf && !item.el.hasAttribute(ITEM_CLONE_ATTR)) ?? null;
@@ -5724,12 +5667,7 @@ function applyItemOps(store, ops, currentContent) {
5724
5667
  if (anchor) anchor.insertAdjacentElement("afterend", clone);
5725
5668
  else source.el.insertAdjacentElement("afterend", clone);
5726
5669
  elements.set(newIndex, clone);
5727
- detected.items.push({
5728
- el: clone,
5729
- index: newIndex,
5730
- keyed: cloneKeyed,
5731
- ...sluggy ? { slugToken: `x${newIndex}` } : {}
5732
- });
5670
+ detected.items.push({ el: clone, index: newIndex, keyed: cloneKeyed });
5733
5671
  added.push(newIndex);
5734
5672
  elements = syncGroup(detected, state);
5735
5673
  }
@@ -6172,25 +6110,16 @@ function foldAlignIntoTrees(state, store) {
6172
6110
  nextTrees.set(id, fresh);
6173
6111
  return fresh;
6174
6112
  };
6175
- const stripChildAligns = (node) => node.children ? {
6176
- ...node,
6177
- children: node.children.map((child) => {
6178
- const { align: _dropped, ...rest } = child;
6179
- return stripChildAligns(rest);
6180
- })
6181
- } : node;
6182
6113
  const nodes = {};
6183
6114
  for (const [key, override] of Object.entries(store.nodes)) {
6184
6115
  const match = override.align !== void 0 && key.startsWith(AI_SLOT_KEY_PREFIX) ? key.match(/^ai\.(.+?)\.r(\d+)\.b(\d+)(?:\.|$)/) : null;
6185
6116
  const tree = match ? treeFor(match[1]) : void 0;
6186
- const row = match && tree ? tree.rows[Number(match[2])] : void 0;
6187
- const blockIdx = match ? Number(match[3]) : -1;
6188
- const block = row?.blocks[blockIdx];
6189
- if (!row || !block) {
6117
+ const block = match && tree ? tree.rows[Number(match[2])]?.blocks[Number(match[3])] : void 0;
6118
+ if (!block) {
6190
6119
  nodes[key] = override;
6191
6120
  continue;
6192
6121
  }
6193
- row.blocks[blockIdx] = { ...stripChildAligns(block), align: override.align };
6122
+ block.align = override.align;
6194
6123
  const rest = { ...override };
6195
6124
  delete rest.align;
6196
6125
  if (Object.keys(rest).length > 0) nodes[key] = rest;
@@ -6203,7 +6132,7 @@ function foldAlignIntoTrees(state, store) {
6203
6132
  continue;
6204
6133
  }
6205
6134
  for (const row of tree.rows) {
6206
- row.blocks = row.blocks.map((block) => ({ ...stripChildAligns(block), align: override.align }));
6135
+ for (const block of row.blocks) block.align = override.align;
6207
6136
  }
6208
6137
  const rest = { ...override };
6209
6138
  delete rest.align;
@@ -7364,7 +7293,6 @@ var blockAlignStyle = (blockAlign) => blockAlign ? {
7364
7293
  alignItems: blockAlign === "left" ? "flex-start" : blockAlign === "right" ? "flex-end" : "center",
7365
7294
  textAlign: blockAlign
7366
7295
  } : {};
7367
- var fullBleedBlock = (block) => block.type === "carousel" || block.type === "group" && str(block.slots?.layout) === "overlap";
7368
7296
  var featureLines = (value) => value.split(/\n|<br\s*\/?>/i).map((line) => line.trim()).filter(Boolean);
7369
7297
  var AI_MOBILE_CSS = [
7370
7298
  // Unscoped (applies at every viewport, not just mobile): re-enables pointer-events for the
@@ -7409,7 +7337,7 @@ var AI_MOBILE_CSS = [
7409
7337
  // page's first section. Top only (bottom needs no nav clearance); ~96px approximates the
7410
7338
  // template nav's own height (measured ~94px) — there's no shared `--ohw-header-h` var to read
7411
7339
  // an exact figure from (no live measurement exists), so this is a safe approximation, not a
7412
- // pixel-exact fit. Horizontal spacing already comes from `data-ai-section-inner`.
7340
+ // pixel-exact fit. Horizontal spacing already comes from `data-ai-container`.
7413
7341
  '[data-ai-overlay-clearance] [data-ai-overlap-layer="overlay"]{padding-top:96px}',
7414
7342
  // A full-bleed carousel's height is pure arithmetic: it spans the whole window, so 16/9 turns
7415
7343
  // window width straight into height. Measured at 16/9 it came out ~90% of the viewport at EVERY
@@ -7425,10 +7353,8 @@ var AI_MOBILE_CSS = [
7425
7353
  "[data-ai-section]{overflow-x:hidden}",
7426
7354
  // `:not([data-ai-full-bleed])`: this `!important` gutter beat the inline `padding: 0` that a
7427
7355
  // full-bleed section's container sets, so a carousel/media hero that is edge-to-edge on desktop
7428
- // still got a 20px band of section background down both sides on a phone. The footer's own
7429
- // container keeps the unguarded rule below — `data-ai-full-bleed` can never appear on it.
7430
- "[data-ai-section-inner]:not([data-ai-full-bleed]){padding:0 20px !important}",
7431
- "[data-ai-container]{padding:0 20px !important}",
7356
+ // still got a 20px band of section background down both sides on a phone.
7357
+ "[data-ai-container]:not([data-ai-full-bleed]){padding:0 20px !important}",
7432
7358
  "[data-ai-row]{display:flex !important;flex-direction:column !important;align-items:stretch !important}",
7433
7359
  "[data-ai-cell]{width:100%;min-width:0}",
7434
7360
  // The rule above turns every row into a COLUMN flex container on phones, which silently breaks
@@ -7633,31 +7559,6 @@ function pricingCardEmphasis(featured, ctx, hasBg) {
7633
7559
  function textAttrs(ctx, path) {
7634
7560
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
7635
7561
  }
7636
- var AI_RESPONSIVE_CSS = [
7637
- "@media (max-width: 960px) {",
7638
- " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
7639
- ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
7640
- "}",
7641
- "@media (max-width: 640px) {",
7642
- " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
7643
- " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
7644
- // Group containers flatten to a column on phones; span placements come along for free.
7645
- // `:not([data-ai-group="overlap"])` for the same reason as the twin pair in AI_MOBILE_CSS — see
7646
- // the comment there. This sheet's copy is the narrower selector, so without the exemption it
7647
- // flattens an `overlap` hero below 640px even though the other rule stands down: measured a 390px
7648
- // viewport with the heading/CTA in flow *under* the image, 219px of section background where the
7649
- // layered text used to be.
7650
- ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) { display: flex !important; flex-direction: column !important; }',
7651
- ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) > * { grid-column: auto !important; }',
7652
- // `:not([data-ai-full-bleed])` for the same reason as the twin rule in AI_MOBILE_CSS: this one
7653
- // is narrower (0,2,0 vs 0,1,0) and also `!important`, so without the guard it wins outright and
7654
- // a full-bleed hero gets its 20px side band back below 640px only — the one width where the
7655
- // other rule had already been taught to stand down.
7656
- " [data-ai-responsive] [data-ai-section-inner]:not([data-ai-full-bleed]) { padding-left: 20px !important; padding-right: 20px !important; }",
7657
- " [data-ai-responsive] { overflow-x: hidden; }",
7658
- " [data-ai-responsive] img { max-width: 100%; }",
7659
- "}"
7660
- ].join("\n");
7661
7562
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
7662
7563
  function MediaBox({
7663
7564
  refValue,
@@ -7678,9 +7579,9 @@ function MediaBox({
7678
7579
  const url = refValue ? ctx.resolveMedia(refValue) : null;
7679
7580
  const isIcon = /^(lucide|simple):/.test(refValue);
7680
7581
  const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
7681
- const editAttrs = ctx.keyFor && editPath ? {
7582
+ const editAttrs = ctx.keyFor && editPath && !isIcon ? {
7682
7583
  "data-ohw-key": ctx.keyFor(editPath),
7683
- ...isIcon ? { "data-ohw-editable": "icon" } : genericImageEdit ? { "data-ohw-editable": "image" } : {}
7584
+ ...genericImageEdit ? { "data-ohw-editable": "image" } : {}
7684
7585
  } : {};
7685
7586
  if (isIcon) {
7686
7587
  const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
@@ -7688,7 +7589,6 @@ function MediaBox({
7688
7589
  "span",
7689
7590
  {
7690
7591
  "data-ai-icon": refValue,
7691
- ...editAttrs,
7692
7592
  style: {
7693
7593
  display: "inline-flex",
7694
7594
  width: 48,
@@ -9194,7 +9094,7 @@ function CollectionBlock({ node, ctx, path }) {
9194
9094
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
9195
9095
  "div",
9196
9096
  {
9197
- "data-ai-grid": String(itemsPerRow),
9097
+ "data-ai-grid": "",
9198
9098
  "data-ohw-pricing-collection": isPricing ? "" : void 0,
9199
9099
  style: {
9200
9100
  display: "grid",
@@ -9454,12 +9354,12 @@ function renderNode(node, ctx, path, ambientAlign) {
9454
9354
  },
9455
9355
  children: [
9456
9356
  i > 0 && ctx.heroOverlapFullBleed ? (
9457
- // The section's `data-ai-section-inner` gave up its max-width/gutter so the base
9357
+ // The section's `data-ai-container` gave up its max-width/gutter so the base
9458
9358
  // layer (this group's i===0 sibling) can span edge-to-edge — reintroduced here,
9459
9359
  // scoped to just the overlay text, so a heading/copy/CTA stack doesn't stretch
9460
9360
  // the full section width instead of reading at the usual content width.
9461
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { maxWidth: AI_TREE_TOKENS.sectionMaxWidth, margin: "0 auto", padding: "0 40px", width: "100%", boxSizing: "border-box" }, children: renderNode(child, ctx, `${path}.c${i}`, child.align ?? node.align ?? "center") })
9462
- ) : renderNode(child, ctx, `${path}.c${i}`, i > 0 ? child.align ?? node.align ?? "center" : void 0),
9361
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { maxWidth: AI_TREE_TOKENS.sectionMaxWidth, margin: "0 auto", padding: "0 40px", width: "100%", boxSizing: "border-box" }, children: renderNode(child, ctx, `${path}.c${i}`, child.align ?? "center") })
9362
+ ) : renderNode(child, ctx, `${path}.c${i}`, i > 0 ? child.align ?? "center" : void 0),
9463
9363
  i === 0 && needsBaseScrim && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
9464
9364
  "div",
9465
9365
  {
@@ -9492,7 +9392,7 @@ function renderNode(node, ctx, path, ambientAlign) {
9492
9392
  // child otherwise stretches full-width (unchanged pre-existing behaviour).
9493
9393
  ...blockAlignStyle(child.align ?? ambientAlign)
9494
9394
  },
9495
- children: renderNode(child, ctx, `${path}.c${i}`, node.align ?? ambientAlign)
9395
+ children: renderNode(child, ctx, `${path}.c${i}`, ambientAlign)
9496
9396
  },
9497
9397
  i
9498
9398
  ));
@@ -9522,20 +9422,8 @@ function renderNode(node, ctx, path, ambientAlign) {
9522
9422
  }
9523
9423
  case "button":
9524
9424
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ButtonEl, { slots, ctx, path });
9525
- case "button-row": {
9526
- const rowAlign = node.align ?? ambientAlign;
9527
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
9528
- "div",
9529
- {
9530
- style: {
9531
- display: "flex",
9532
- gap: AI_TREE_TOKENS.spacing6,
9533
- justifyContent: rowAlign === "left" ? "flex-start" : rowAlign === "right" ? "flex-end" : "center"
9534
- },
9535
- children: (node.children ?? []).map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ButtonEl, { slots: child.slots ?? {}, ctx, path: `${path}.c${i}` }, i))
9536
- }
9537
- );
9538
- }
9425
+ case "button-row":
9426
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", gap: AI_TREE_TOKENS.spacing6, justifyContent: "center" }, children: (node.children ?? []).map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ButtonEl, { slots: child.slots ?? {}, ctx, path: `${path}.c${i}` }, i)) });
9539
9427
  case "media":
9540
9428
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
9541
9429
  MediaBox,
@@ -9859,7 +9747,6 @@ function AiTreeRenderer({
9859
9747
  ...fillHeight ? { "data-ai-fill-height": "" } : {},
9860
9748
  ...needsOverlayClearance ? { "data-ai-overlay-clearance": "" } : {},
9861
9749
  ...bgAttrs,
9862
- "data-ai-responsive": "",
9863
9750
  style: {
9864
9751
  position: "relative",
9865
9752
  padding: `${padCss} 0`,
@@ -9873,13 +9760,12 @@ function AiTreeRenderer({
9873
9760
  ...fillHeight ? { display: "grid" } : {}
9874
9761
  },
9875
9762
  children: [
9876
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
9877
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_MOBILE_CSS }),
9878
9763
  isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
9764
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_MOBILE_CSS }),
9879
9765
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
9880
9766
  "div",
9881
9767
  {
9882
- "data-ai-section-inner": "",
9768
+ "data-ai-container": "",
9883
9769
  ...fullBleedMedia ? { "data-ai-full-bleed": "" } : {},
9884
9770
  style: {
9885
9771
  position: "relative",
@@ -9896,7 +9782,7 @@ function AiTreeRenderer({
9896
9782
  children: tree.rows.map((row, r2) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
9897
9783
  "div",
9898
9784
  {
9899
- "data-ai-columns": "",
9785
+ "data-ai-row": "",
9900
9786
  style: {
9901
9787
  display: "grid",
9902
9788
  gridTemplateColumns: "repeat(12, 1fr)",
@@ -9916,7 +9802,7 @@ function AiTreeRenderer({
9916
9802
  // its own horizontal `align`, which takes priority for that block.
9917
9803
  ...fillHeight ? { display: "grid" } : {},
9918
9804
  // Horizontal placement of the block's content within its column.
9919
- ...fullBleedBlock(block) ? {} : cellAlignStyle(block.align),
9805
+ ...cellAlignStyle(block.align),
9920
9806
  // space-between: each column becomes a flex column whose content spreads over
9921
9807
  // the full row height instead of clumping at the top.
9922
9808
  ...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
@@ -10552,10 +10438,9 @@ function applyAiSectionsToDom(state, options) {
10552
10438
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
10553
10439
  continue;
10554
10440
  }
10441
+ const isNewMount = !existing || !existing.container.isConnected;
10555
10442
  let container = existing?.container;
10556
- let isNewMount = false;
10557
10443
  if (!container || !container.isConnected) {
10558
- isNewMount = true;
10559
10444
  existing?.root.unmount();
10560
10445
  container?.remove();
10561
10446
  container = document.createElement("div");
@@ -16997,7 +16882,6 @@ function MediaOverlay({
16997
16882
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
16998
16883
  );
16999
16884
  }, [isVideo]);
17000
- const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
17001
16885
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
17002
16886
  const box = {
17003
16887
  position: "fixed",
@@ -17096,10 +16980,11 @@ function MediaOverlay({
17096
16980
  className: "flex items-center justify-center cursor-pointer",
17097
16981
  style: {
17098
16982
  ...box,
17099
- // Visual only. `pointer-events: auto` on this full-bleed rect covered the navbar
17100
- // (hero bg includes the absolute TopNav) and swallowed CTA press-drag. Text overlay
17101
- // and cueOnly also need clicks to fall through — Replace / video controls opt back in.
17102
- pointerEvents: "none",
16983
+ // When text overlays this media, let clicks fall THROUGH to the text underneath. In the
16984
+ // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
16985
+ // in-document, pointer-events does it natively. The button below opts back in, so
16986
+ // Replace still works.
16987
+ pointerEvents: hover.hasTextOverlap || cueOnly ? "none" : "auto",
17103
16988
  // Selected: a firm component ring with no wash, so the image reads as chosen rather
17104
16989
  // than hovered. Hover keeps the existing tinted preview.
17105
16990
  boxShadow: selected ? "inset 0 0 0 2px var(--color-bridge-primary)" : "inset 0 0 0 1.5px var(--color-bridge-primary)",
@@ -17126,17 +17011,17 @@ function MediaOverlay({
17126
17011
  },
17127
17012
  children: [
17128
17013
  isVideo ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
17129
- replaceLabel
17014
+ isVideo ? "Replace video" : "Replace image"
17130
17015
  ]
17131
17016
  }
17132
17017
  ),
17133
- showChrome && replaceMode !== "none" && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
17018
+ replaceMode === "none" || cueOnly ? null : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
17134
17019
  Button,
17135
17020
  {
17136
17021
  "data-ohw-media-overlay": "",
17137
17022
  variant: "outline",
17138
17023
  size: "sm",
17139
- "aria-label": replaceLabel,
17024
+ "aria-label": isVideo ? "Replace video" : "Replace image",
17140
17025
  className: "gap-1.5 cursor-pointer hover:bg-background",
17141
17026
  style: {
17142
17027
  ...OVERLAY_BUTTON_STYLE,
@@ -17159,7 +17044,7 @@ function MediaOverlay({
17159
17044
  },
17160
17045
  children: [
17161
17046
  isVideo ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
17162
- replaceMode === "full" ? replaceLabel : null
17047
+ replaceMode === "full" ? isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image" : null
17163
17048
  ]
17164
17049
  }
17165
17050
  )
@@ -18551,23 +18436,10 @@ function setPricingCardStyle(tree, index, style) {
18551
18436
  });
18552
18437
  }
18553
18438
  function setPricingCardFeaturedTag(tree, index, on) {
18554
- return withPricingCollection(tree, (collection) => {
18555
- const children = collection.children;
18556
- if (index < 0 || index >= children.length) return false;
18557
- if (on) {
18558
- children.forEach((card, i) => {
18559
- if (i === index) return;
18560
- const prev2 = featuredOf(card);
18561
- if (prev2.enabled !== true || badgeOf(prev2).length === 0) return;
18562
- card.slots = card.slots ?? {};
18563
- card.slots.featured = composeFeatured(pricingCardStyleOf(prev2), false, badgeOf(prev2));
18564
- });
18565
- }
18566
- const target = children[index];
18567
- target.slots = target.slots ?? {};
18439
+ return withPricingCard(tree, index, (target) => {
18568
18440
  const prev = featuredOf(target);
18569
- target.slots.featured = composeFeatured(pricingCardStyleOf(prev), on, badgeOf(prev));
18570
- return true;
18441
+ const style = pricingCardStyleOf(prev);
18442
+ target.slots.featured = composeFeatured(style, on, badgeOf(prev));
18571
18443
  });
18572
18444
  }
18573
18445
  function pricingCardStyleOf(featured) {
@@ -18817,40 +18689,25 @@ function ToolbarSeparator() {
18817
18689
  }
18818
18690
  function CardStyleMenu({
18819
18691
  value,
18820
- onChange,
18821
- disabled = false
18692
+ onChange
18822
18693
  }) {
18823
18694
  const current = PRICING_CARD_STYLES.find((s) => s.value === value) ?? PRICING_CARD_STYLES[0];
18824
- const trigger = /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18825
- "button",
18826
- {
18827
- type: "button",
18828
- "aria-label": "Card style",
18829
- disabled,
18830
- className: cn(
18831
- "inline-flex h-8 min-w-[110px] items-center gap-[7px] rounded-[6px] bg-transparent px-2 text-[13px] font-medium text-foreground transition-colors hover:bg-muted data-[state=open]:bg-muted",
18832
- disabled && "cursor-not-allowed opacity-40 hover:bg-transparent"
18833
- ),
18834
- children: [
18835
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(StyleSwatch, { style: current.value }),
18836
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "flex-1 text-left", children: current.label }),
18837
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
18838
- ]
18839
- }
18840
- );
18841
- if (disabled) {
18842
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
18843
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-xs text-muted-foreground opacity-50", children: "Card style" }),
18844
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Tooltip, { children: [
18845
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "inline-flex", children: trigger }) }),
18846
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: "Mark as featured to edit card style" })
18847
- ] })
18848
- ] });
18849
- }
18850
18695
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
18851
18696
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-xs text-muted-foreground", children: "Card style" }),
18852
18697
  /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(DropdownMenu, { modal: false, children: [
18853
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DropdownMenuTrigger, { asChild: true, children: trigger }),
18698
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18699
+ "button",
18700
+ {
18701
+ type: "button",
18702
+ "aria-label": "Card style",
18703
+ className: "inline-flex h-8 min-w-[110px] items-center gap-[7px] rounded-[6px] bg-transparent px-2 text-[13px] font-medium text-foreground transition-colors hover:bg-muted data-[state=open]:bg-muted",
18704
+ children: [
18705
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(StyleSwatch, { style: current.value }),
18706
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "flex-1 text-left", children: current.label }),
18707
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
18708
+ ]
18709
+ }
18710
+ ) }),
18854
18711
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[152px] min-w-[152px] p-1", children: PRICING_CARD_STYLES.map((style) => {
18855
18712
  const selected = style.value === value;
18856
18713
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
@@ -18914,35 +18771,23 @@ function PricingCardToolbar({
18914
18771
  mobile ? "flex-wrap justify-center" : "flex-nowrap"
18915
18772
  ),
18916
18773
  children: [
18774
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CardStyleMenu, { value: style, onChange: onSetStyle }),
18917
18775
  /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Tooltip, { children: [
18918
18776
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18919
18777
  CustomToolbarButton,
18920
18778
  {
18921
18779
  type: "button",
18922
- "aria-label": featuredTag ? "Unmark as featured" : "Mark as featured",
18780
+ "aria-label": featuredTag ? "Remove featured tag" : "Add featured tag",
18923
18781
  "aria-pressed": featuredTag,
18782
+ active: featuredTag,
18924
18783
  onClick: () => onSetFeaturedTag(!featuredTag),
18925
- className: "group size-8",
18926
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18927
- import_lucide_react9.Star,
18928
- {
18929
- size: 16,
18930
- fill: "currentColor",
18931
- strokeWidth: 0,
18932
- className: cn(
18933
- "transition-colors",
18934
- featuredTag ? "text-[#f59e0b] group-hover:text-[#d97706]" : "text-[#d6d3d1] group-hover:text-[#fbbf24]"
18935
- ),
18936
- style: { pointerEvents: "none" }
18937
- }
18938
- )
18784
+ className: "size-8",
18785
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Star, { size: 16, fill: featuredTag ? "currentColor" : "none", style: { pointerEvents: "none" } })
18939
18786
  }
18940
18787
  ) }),
18941
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Unmark featured" : "Mark as featured" })
18788
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Remove featured tag" : "Add featured tag" })
18942
18789
  ] }),
18943
18790
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarSeparator, {}),
18944
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CardStyleMenu, { value: style, onChange: onSetStyle, disabled: !featuredTag }),
18945
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarSeparator, {}),
18946
18791
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Add button", onClick: onAddButton, disabled: buttonCount >= 2, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ButtonGlyph, {}) }),
18947
18792
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Remove button", onClick: onRemoveButton, disabled: buttonCount === 0, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "relative inline-flex", children: [
18948
18793
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ButtonGlyph, {}),
@@ -19085,12 +18930,7 @@ function AiPricingCardOverlay({
19085
18930
  index: active.index,
19086
18931
  pending,
19087
18932
  error,
19088
- onAddTier: () => {
19089
- const newIndex = pricingCardCount(activeEntry.tree);
19090
- void runMutation(active.sectionId, addPricingTier).then((applied) => {
19091
- if (applied) setActive({ sectionId: active.sectionId, index: newIndex });
19092
- });
19093
- },
18933
+ onAddTier: () => runMutation(active.sectionId, addPricingTier),
19094
18934
  onRemoveTier: () => {
19095
18935
  void runMutation(active.sectionId, (tree) => removePricingTier(tree, active.index)).then(
19096
18936
  (applied) => {
@@ -21639,7 +21479,6 @@ function shouldUseDevFixtures() {
21639
21479
  function getLogoElement(el) {
21640
21480
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
21641
21481
  if (marked) return marked;
21642
- if (el.closest('[data-ohw-editable="icon"]')) return null;
21643
21482
  const root = el.closest("nav, [data-ohw-nav-root], footer");
21644
21483
  if (!root) return null;
21645
21484
  const anchor = el.closest("a");
@@ -24203,7 +24042,7 @@ function getNavigationSelectionParent(el) {
24203
24042
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
24204
24043
  return resolveFooterLinksContainerFrom(el);
24205
24044
  }
24206
- 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)) {
24045
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
24207
24046
  return getNavigationRoot(el);
24208
24047
  }
24209
24048
  return null;
@@ -24425,6 +24264,7 @@ var ICONS = {
24425
24264
  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"/>',
24426
24265
  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"/>'
24427
24266
  };
24267
+ var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
24428
24268
  var SELECTION_CHROME_GAP2 = 4;
24429
24269
  var TOOLBAR_STROKE_GAP2 = 4;
24430
24270
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -25404,6 +25244,21 @@ function OhhwellsBridge() {
25404
25244
  const [isItemDragging, setIsItemDragging] = (0, import_react21.useState)(false);
25405
25245
  const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react21.useState)(false);
25406
25246
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
25247
+ const [floatingPanel, setFloatingPanel] = (0, import_react21.useState)(null);
25248
+ const floatingPanelOpenRef = (0, import_react21.useRef)(false);
25249
+ floatingPanelOpenRef.current = floatingPanel !== null;
25250
+ const floatingPanelRef = (0, import_react21.useRef)(null);
25251
+ floatingPanelRef.current = floatingPanel;
25252
+ const [floatingPanelPos, setFloatingPanelPos] = (0, import_react21.useState)(null);
25253
+ const buttonCollectionDragRef = (0, import_react21.useRef)(null);
25254
+ const buttonDropInsertIndexRef = (0, import_react21.useRef)(null);
25255
+ const [buttonDropSlots, setButtonDropSlots] = (0, import_react21.useState)([]);
25256
+ const [activeButtonDropIndex, setActiveButtonDropIndex] = (0, import_react21.useState)(null);
25257
+ const buttonPointerDragRef = (0, import_react21.useRef)(null);
25258
+ const [buttonStyleTick, setButtonStyleTick] = (0, import_react21.useState)(0);
25259
+ const [logoSizeDraft, setLogoSizeDraft] = (0, import_react21.useState)(null);
25260
+ const [editorViewport, setEditorViewport] = (0, import_react21.useState)("desktop");
25261
+ const [parentScrollSnap, setParentScrollSnap] = (0, import_react21.useState)(null);
25407
25262
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react21.useState)(null);
25408
25263
  const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react21.useState)(null);
25409
25264
  const footerDragRef = (0, import_react21.useRef)(null);
@@ -25429,21 +25284,6 @@ function OhhwellsBridge() {
25429
25284
  const stylesRef = (0, import_react21.useRef)("");
25430
25285
  const appliedDrawerRef = (0, import_react21.useRef)(null);
25431
25286
  const pendingDeleteUndoRef = (0, import_react21.useRef)(null);
25432
- const [floatingPanel, setFloatingPanel] = (0, import_react21.useState)(null);
25433
- const floatingPanelOpenRef = (0, import_react21.useRef)(false);
25434
- const floatingPanelRef = (0, import_react21.useRef)(null);
25435
- floatingPanelRef.current = floatingPanel;
25436
- const setFloatingPanelRef = (0, import_react21.useRef)(setFloatingPanel);
25437
- const [floatingPanelPos, setFloatingPanelPos] = (0, import_react21.useState)(null);
25438
- const buttonCollectionDragRef = (0, import_react21.useRef)(null);
25439
- const buttonDropInsertIndexRef = (0, import_react21.useRef)(null);
25440
- const [buttonDropSlots, setButtonDropSlots] = (0, import_react21.useState)([]);
25441
- const [activeButtonDropIndex, setActiveButtonDropIndex] = (0, import_react21.useState)(null);
25442
- const buttonPointerDragRef = (0, import_react21.useRef)(null);
25443
- const [buttonStyleTick, setButtonStyleTick] = (0, import_react21.useState)(0);
25444
- const [logoSizeDraft, setLogoSizeDraft] = (0, import_react21.useState)(null);
25445
- const [editorViewport, setEditorViewport] = (0, import_react21.useState)("desktop");
25446
- const [parentScrollSnap, setParentScrollSnap] = (0, import_react21.useState)(null);
25447
25287
  const [sitePages, setSitePages] = (0, import_react21.useState)([]);
25448
25288
  const [sectionsByPath, setSectionsByPath] = (0, import_react21.useState)({});
25449
25289
  const sectionsPrefetchGenRef = (0, import_react21.useRef)(0);
@@ -25452,18 +25292,7 @@ function OhhwellsBridge() {
25452
25292
  const linkPopoverOpenRef = (0, import_react21.useRef)(false);
25453
25293
  const linkPopoverGraceUntilRef = (0, import_react21.useRef)(0);
25454
25294
  setLinkPopoverRef.current = setLinkPopover;
25455
- setFloatingPanelRef.current = setFloatingPanel;
25456
25295
  linkPopoverSessionRef.current = linkPopover;
25457
- floatingPanelOpenRef.current = Boolean(floatingPanel);
25458
- (0, import_react21.useEffect)(() => {
25459
- const syncViewport = () => {
25460
- const next = window.innerWidth <= 480 ? "mobile" : "desktop";
25461
- setEditorViewport((prev) => prev === next ? prev : next);
25462
- };
25463
- syncViewport();
25464
- window.addEventListener("resize", syncViewport);
25465
- return () => window.removeEventListener("resize", syncViewport);
25466
- }, []);
25467
25296
  (0, import_react21.useEffect)(() => {
25468
25297
  setSectionOrderPersistHandler((entries) => {
25469
25298
  const currentPath = window.location.pathname;
@@ -27142,7 +26971,6 @@ function OhhwellsBridge() {
27142
26971
  applyAiFooterFromContent(content);
27143
26972
  if (typeof content[STYLE_STORE_KEY] === "string") {
27144
26973
  stylesRef.current = content[STYLE_STORE_KEY];
27145
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
27146
26974
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
27147
26975
  }
27148
26976
  applyBrandChrome(content);
@@ -27153,12 +26981,12 @@ function OhhwellsBridge() {
27153
26981
  if (key === "__ohw_sections") continue;
27154
26982
  if (key === AI_SECTIONS_KEY) continue;
27155
26983
  if (key === AI_FOOTER_KEY) continue;
27156
- if (key === LOGO_PLACEHOLDER_KEY) continue;
27157
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
27158
26984
  if (key === BRAND_KIT_KEY) continue;
27159
26985
  if (key === STYLE_STORE_KEY) continue;
27160
26986
  if (BRAND_CHROME_KEYS.has(key)) continue;
26987
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
27161
26988
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
26989
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
27162
26990
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
27163
26991
  continue;
27164
26992
  }
@@ -27373,18 +27201,17 @@ function OhhwellsBridge() {
27373
27201
  }
27374
27202
  applyAiFooterFromContent(content);
27375
27203
  if (typeof content[STYLE_STORE_KEY] === "string") {
27376
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
27377
27204
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
27378
27205
  }
27379
27206
  for (const [key, val] of Object.entries(content)) {
27380
27207
  if (key === "__ohw_sections") continue;
27381
27208
  if (key === AI_SECTIONS_KEY) continue;
27382
27209
  if (key === AI_FOOTER_KEY) continue;
27383
- if (key === LOGO_PLACEHOLDER_KEY) continue;
27384
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
27385
27210
  if (key === BRAND_KIT_KEY) continue;
27386
27211
  if (key === STYLE_STORE_KEY) continue;
27387
27212
  if (BRAND_CHROME_KEYS.has(key)) continue;
27213
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
27214
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
27388
27215
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
27389
27216
  continue;
27390
27217
  }
@@ -27641,11 +27468,26 @@ function OhhwellsBridge() {
27641
27468
  const t2 = setTimeout(measure, 500);
27642
27469
  const ro = new ResizeObserver(schedule);
27643
27470
  ro.observe(document.body);
27471
+ let lastWidth = window.innerWidth;
27472
+ let resizeTimers = [];
27473
+ const clearResizeTimers = () => {
27474
+ resizeTimers.forEach(clearTimeout);
27475
+ resizeTimers = [];
27476
+ };
27477
+ const handleResize = () => {
27478
+ if (window.innerWidth === lastWidth) return;
27479
+ lastWidth = window.innerWidth;
27480
+ clearResizeTimers();
27481
+ resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
27482
+ };
27483
+ window.addEventListener("resize", handleResize);
27644
27484
  return () => {
27645
27485
  clearTimeout(t1);
27646
27486
  clearTimeout(t2);
27647
27487
  if (raf != null) cancelAnimationFrame(raf);
27648
27488
  ro.disconnect();
27489
+ clearResizeTimers();
27490
+ window.removeEventListener("resize", handleResize);
27649
27491
  };
27650
27492
  }, [pathname, isEditMode, postToParent2]);
27651
27493
  (0, import_react21.useEffect)(() => {
@@ -27940,13 +27782,6 @@ function OhhwellsBridge() {
27940
27782
  if (target.closest("[data-ohw-state-toggle]")) return;
27941
27783
  if (target.closest("[data-ohw-max-badge]")) return;
27942
27784
  if (isInsideLinkEditor(target)) return;
27943
- if (isInsideFloatingPanel(target)) return;
27944
- if (target.closest("[data-ohw-form-toolbar]")) return;
27945
- if (target.closest(
27946
- '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
27947
- )) {
27948
- return;
27949
- }
27950
27785
  if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
27951
27786
  clearMediaSelectionRef.current();
27952
27787
  }
@@ -27967,6 +27802,13 @@ function OhhwellsBridge() {
27967
27802
  return;
27968
27803
  }
27969
27804
  }
27805
+ if (isInsideFloatingPanel(target)) return;
27806
+ if (target.closest("[data-ohw-form-toolbar]")) return;
27807
+ if (target.closest(
27808
+ '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
27809
+ )) {
27810
+ return;
27811
+ }
27970
27812
  {
27971
27813
  const formEl = getFormElement(target);
27972
27814
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -28346,9 +28188,6 @@ function OhhwellsBridge() {
28346
28188
  setHoveredItemRect(null);
28347
28189
  hoveredNavContainerRef.current = null;
28348
28190
  setHoveredNavContainerRect(null);
28349
- siblingHintElRef.current = null;
28350
- setSiblingHintRect(null);
28351
- setSiblingHintRects([]);
28352
28191
  return;
28353
28192
  }
28354
28193
  {
@@ -28469,6 +28308,7 @@ function OhhwellsBridge() {
28469
28308
  hoveredNavContainerRef.current = null;
28470
28309
  setHoveredNavContainerRect(null);
28471
28310
  hoveredItemElRef.current = editable;
28311
+ setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
28472
28312
  }
28473
28313
  }
28474
28314
  }
@@ -29178,11 +29018,7 @@ function OhhwellsBridge() {
29178
29018
  };
29179
29019
  const handleMouseMove = (e) => {
29180
29020
  const { clientX, clientY } = e;
29181
- if (pointOwnedByFloatingPanel(clientX, clientY)) {
29182
- dismissImageHover();
29183
- clearImageHover();
29184
- return;
29185
- }
29021
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
29186
29022
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) {
29187
29023
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
29188
29024
  formHoverElRef.current = null;
@@ -29211,12 +29047,7 @@ function OhhwellsBridge() {
29211
29047
  setHoveredItemRect(null);
29212
29048
  hoveredNavContainerRef.current = null;
29213
29049
  setHoveredNavContainerRect(null);
29214
- siblingHintElRef.current = null;
29215
- setSiblingHintRect(null);
29216
- setSiblingHintRects([]);
29217
29050
  dismissImageHover();
29218
- clearImageHover();
29219
- setSectionGap(null);
29220
29051
  return;
29221
29052
  }
29222
29053
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -29228,11 +29059,7 @@ function OhhwellsBridge() {
29228
29059
  if (e.data?.type !== "ow:pointer-sync") return;
29229
29060
  const { clientX, clientY } = e.data;
29230
29061
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
29231
- if (pointOwnedByFloatingPanel(clientX, clientY)) {
29232
- dismissImageHover();
29233
- clearImageHover();
29234
- return;
29235
- }
29062
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
29236
29063
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) return;
29237
29064
  if (probeSocialsRowAt(clientX, clientY)) return;
29238
29065
  probeSectionGapAt(clientX, clientY);
@@ -29584,7 +29411,6 @@ function OhhwellsBridge() {
29584
29411
  applyAiFooterFromContent(content);
29585
29412
  if (typeof content[STYLE_STORE_KEY] === "string") {
29586
29413
  stylesRef.current = content[STYLE_STORE_KEY];
29587
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
29588
29414
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
29589
29415
  }
29590
29416
  applyBrandChrome(content);
@@ -29599,12 +29425,12 @@ function OhhwellsBridge() {
29599
29425
  }
29600
29426
  if (key === AI_SECTIONS_KEY) continue;
29601
29427
  if (key === AI_FOOTER_KEY) continue;
29602
- if (key === LOGO_PLACEHOLDER_KEY) continue;
29603
- if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
29604
29428
  if (key === BRAND_KIT_KEY) continue;
29605
29429
  if (key === STYLE_STORE_KEY) continue;
29606
29430
  if (BRAND_CHROME_KEYS.has(key)) continue;
29431
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
29607
29432
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
29433
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
29608
29434
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
29609
29435
  continue;
29610
29436
  }
@@ -30142,12 +29968,6 @@ function OhhwellsBridge() {
30142
29968
  closeLinkPopoverRef.current();
30143
29969
  return;
30144
29970
  }
30145
- if (floatingPanelOpenRef.current) {
30146
- setFloatingPanelRef.current(null);
30147
- deselectRef.current();
30148
- deactivateRef.current();
30149
- return;
30150
- }
30151
29971
  deselectRef.current();
30152
29972
  deactivateRef.current();
30153
29973
  clearMediaSelectionRef.current();
@@ -30517,10 +30337,9 @@ function OhhwellsBridge() {
30517
30337
  if (e.data?.type !== "ow:collect-section") return;
30518
30338
  const sectionId = e.data.sectionId;
30519
30339
  if (typeof sectionId !== "string" || !sectionId) return;
30520
- const foundEl = document.querySelector(
30340
+ const sectionEl = document.querySelector(
30521
30341
  `[data-ohw-section="${CSS.escape(sectionId)}"]`
30522
30342
  );
30523
- const sectionEl = foundEl && !foundEl.hasAttribute("data-ohw-ai-template-hidden") && !foundEl.hasAttribute("data-ohw-ai-removed") && !foundEl.hasAttribute("data-ohw-ai-replaced-by") ? foundEl : null;
30524
30343
  const nodes = sectionEl ? collectEditableNodes(void 0, sectionEl).filter(
30525
30344
  (n) => n.key && !n.key.startsWith("__ohw_") && !["meta", "sections", "video-setting"].includes(n.type)
30526
30345
  ).map((n) => ({ ...n, text: n.text ?? "", type: normalizeAiNodeType(n.type) })) : [];
@@ -30962,10 +30781,10 @@ function OhhwellsBridge() {
30962
30781
  window.removeEventListener("message", handleDeleteSection);
30963
30782
  window.removeEventListener("message", handleDuplicateSection);
30964
30783
  window.removeEventListener("message", handleDeactivate);
30784
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
30965
30785
  window.removeEventListener("message", handleToastAction);
30966
30786
  window.removeEventListener("message", handleFormCount);
30967
30787
  window.removeEventListener("message", handleUiEscape);
30968
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
30969
30788
  autoSaveTimers.current.forEach(clearTimeout);
30970
30789
  autoSaveTimers.current.clear();
30971
30790
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
@@ -32475,7 +32294,6 @@ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey
32475
32294
  dropIndicatorVariants,
32476
32295
  ensureFooterTree,
32477
32296
  filterAvailablePages,
32478
- foldAlignIntoTrees,
32479
32297
  getEditModeInitialState,
32480
32298
  insertFooterHours,
32481
32299
  isEditSessionActive,
@@ -32484,7 +32302,6 @@ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey
32484
32302
  isValidUrl,
32485
32303
  nextViableSlide,
32486
32304
  parseAiFooterState,
32487
- parseAiSectionsState,
32488
32305
  parseTarget,
32489
32306
  removeFooterBlock,
32490
32307
  resolveFooterCopyright,