@ohhwells/bridge 0.1.157 → 0.1.158-next.422

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
@@ -64,6 +64,7 @@ __export(index_exports, {
64
64
  OhwLoaderSurface: () => OhwLoaderSurface,
65
65
  SITE_OG_IMAGE_KEY: () => SITE_OG_IMAGE_KEY,
66
66
  SchedulingWidget: () => SchedulingWidget,
67
+ TEMPLATE_LOGO_ATTR: () => TEMPLATE_LOGO_ATTR,
67
68
  Toggle: () => Toggle,
68
69
  ToggleGroup: () => ToggleGroup,
69
70
  ToggleGroupItem: () => ToggleGroupItem,
@@ -97,6 +98,7 @@ __export(index_exports, {
97
98
  parseAiSectionsState: () => parseAiSectionsState,
98
99
  parseTarget: () => parseTarget,
99
100
  recordOverriddenPaths: () => recordOverriddenPaths,
101
+ releaseTemplateLogoIfReplaced: () => releaseTemplateLogoIfReplaced,
100
102
  removeFooterBlock: () => removeFooterBlock,
101
103
  resetOverriddenPathsCache: () => resetOverriddenPathsCache,
102
104
  resolveFooterCopyright: () => resolveFooterCopyright,
@@ -518,11 +520,37 @@ function logoTextElement(root) {
518
520
  );
519
521
  return hasOwnText ? root : null;
520
522
  }
523
+ var TEMPLATE_LOGO_ATTR = "data-ohw-template-logo";
524
+ function releaseTemplateLogoIfReplaced(img, url) {
525
+ if (!img.hasAttribute(TEMPLATE_LOGO_ATTR)) return;
526
+ let incoming = url;
527
+ try {
528
+ incoming = new URL(url, document.baseURI).href;
529
+ } catch {
530
+ }
531
+ if (incoming !== img.src) img.removeAttribute(TEMPLATE_LOGO_ATTR);
532
+ }
533
+ function logoImage(root, selector) {
534
+ if (root instanceof HTMLImageElement && root.matches(selector)) return root;
535
+ return root.querySelector(selector);
536
+ }
521
537
  function logoRootImage(root) {
522
- const img = root.querySelector("img");
538
+ const img = logoImage(root, `img:not([${TEMPLATE_LOGO_ATTR}])`);
523
539
  const src = img?.getAttribute("src")?.trim() ?? "";
524
540
  return img && src && img.style.display !== "none" ? img : null;
525
541
  }
542
+ function templateLogoImage(root) {
543
+ const img = logoImage(root, `img[${TEMPLATE_LOGO_ATTR}]`);
544
+ return img && img.style.display !== "none" ? img : null;
545
+ }
546
+ function createWordmark(root) {
547
+ const el = document.createElement("span");
548
+ el.setAttribute("data-ohw-editable", "plain");
549
+ el.setAttribute("data-ohw-key", textKeyForRoot(root));
550
+ if (root instanceof HTMLImageElement) root.parentNode?.insertBefore(el, root.nextSibling);
551
+ else root.appendChild(el);
552
+ return el;
553
+ }
526
554
  function applyLogoIdentity(text, isPlaceholder) {
527
555
  const display = resolveLogoDisplayText(text);
528
556
  const placeholder = isPlaceholder || !text.trim() || display === PLACEHOLDER_BUSINESS_NAME;
@@ -541,7 +569,9 @@ function applyLogoIdentity(text, isPlaceholder) {
541
569
  if (placeholder) el.setAttribute("data-ohw-placeholder", "");
542
570
  else el.removeAttribute("data-ohw-placeholder");
543
571
  if (logoRootImage(el)) return;
544
- const textEl = logoTextElement(el);
572
+ const templateMark = placeholder ? null : templateLogoImage(el);
573
+ if (templateMark) templateMark.style.display = "none";
574
+ const textEl = logoTextElement(el) ?? (templateMark ? createWordmark(el) : null);
545
575
  if (textEl && textEl.textContent !== display) textEl.textContent = display;
546
576
  });
547
577
  return display;
@@ -573,6 +603,7 @@ function applyLogoImage(url, alt, placement) {
573
603
  }
574
604
  img.removeAttribute("srcset");
575
605
  img.removeAttribute("sizes");
606
+ img.removeAttribute(TEMPLATE_LOGO_ATTR);
576
607
  img.src = url;
577
608
  img.alt = displayAlt;
578
609
  img.style.display = "block";
@@ -592,10 +623,7 @@ function applyLogoImage(url, alt, placement) {
592
623
  img.style.display = "none";
593
624
  }
594
625
  if (!textEl) {
595
- textEl = document.createElement("span");
596
- textEl.setAttribute("data-ohw-editable", "plain");
597
- textEl.setAttribute("data-ohw-key", textKey);
598
- root.appendChild(textEl);
626
+ textEl = createWordmark(root);
599
627
  }
600
628
  textEl.style.display = "";
601
629
  if (textEl.textContent !== displayAlt) textEl.textContent = displayAlt;
@@ -6654,6 +6682,21 @@ function pricingCardFeatureCount(tree, index) {
6654
6682
  const target = findPricingCollection(tree)?.children?.[index];
6655
6683
  return target ? pricingFeatureList(target).length : 0;
6656
6684
  }
6685
+ function prevailingFeatureIcon(features) {
6686
+ const counts = /* @__PURE__ */ new Map();
6687
+ for (const row of features) {
6688
+ const key = `${row.icon ?? ""}|${row.iconColor ?? ""}`;
6689
+ const entry = counts.get(key) ?? { count: 0, ...row.icon ? { icon: row.icon } : {}, ...row.iconColor ? { iconColor: row.iconColor } : {} };
6690
+ entry.count += 1;
6691
+ counts.set(key, entry);
6692
+ }
6693
+ let best = null;
6694
+ for (const entry of counts.values()) {
6695
+ if (!best || entry.count > best.count) best = entry;
6696
+ }
6697
+ if (!best) return {};
6698
+ return { ...best.icon ? { icon: best.icon } : {}, ...best.iconColor ? { iconColor: best.iconColor } : {} };
6699
+ }
6657
6700
  function addPricingCardFeature(tree, index, text) {
6658
6701
  return withPricingCollection(tree, (collection) => {
6659
6702
  const children = collection.children;
@@ -6662,7 +6705,7 @@ function addPricingCardFeature(tree, index, text) {
6662
6705
  const features = pricingFeatureList(target);
6663
6706
  if (features.length >= PRICING_FEATURES_MAX) return false;
6664
6707
  target.slots = target.slots ?? {};
6665
- target.slots.features = [...features, { text }];
6708
+ target.slots.features = [...features, { text, ...prevailingFeatureIcon(features) }];
6666
6709
  return true;
6667
6710
  });
6668
6711
  }
@@ -6849,6 +6892,7 @@ function applyBrandChrome(content) {
6849
6892
  if (el.textContent !== name) el.textContent = name;
6850
6893
  if (el.getAttribute("title") !== name) el.setAttribute("title", name);
6851
6894
  });
6895
+ applyLogoIdentity(name, false);
6852
6896
  }
6853
6897
  const title = content[BRAND_TITLE_KEY];
6854
6898
  if (typeof title === "string" && title.length > 0) {
@@ -7861,7 +7905,7 @@ var AI_MOBILE_CSS = [
7861
7905
  // page's first section. Top only (bottom needs no nav clearance); ~96px approximates the
7862
7906
  // template nav's own height (measured ~94px) — there's no shared `--ohw-header-h` var to read
7863
7907
  // an exact figure from (no live measurement exists), so this is a safe approximation, not a
7864
- // pixel-exact fit. Horizontal spacing already comes from `data-ai-container`.
7908
+ // pixel-exact fit. Horizontal spacing already comes from `data-ai-section-inner`.
7865
7909
  '[data-ai-overlay-clearance] [data-ai-overlap-layer="overlay"]{padding-top:96px}',
7866
7910
  // A full-bleed carousel's height is pure arithmetic: it spans the whole window, so 16/9 turns
7867
7911
  // window width straight into height. Measured at 16/9 it came out ~90% of the viewport at EVERY
@@ -7877,8 +7921,10 @@ var AI_MOBILE_CSS = [
7877
7921
  "[data-ai-section]{overflow-x:hidden}",
7878
7922
  // `:not([data-ai-full-bleed])`: this `!important` gutter beat the inline `padding: 0` that a
7879
7923
  // full-bleed section's container sets, so a carousel/media hero that is edge-to-edge on desktop
7880
- // still got a 20px band of section background down both sides on a phone.
7881
- "[data-ai-container]:not([data-ai-full-bleed]){padding:0 20px !important}",
7924
+ // still got a 20px band of section background down both sides on a phone. The footer's own
7925
+ // container keeps the unguarded rule below — `data-ai-full-bleed` can never appear on it.
7926
+ "[data-ai-section-inner]:not([data-ai-full-bleed]){padding:0 20px !important}",
7927
+ "[data-ai-container]{padding:0 20px !important}",
7882
7928
  "[data-ai-row]{display:flex !important;flex-direction:column !important;align-items:stretch !important}",
7883
7929
  "[data-ai-cell]{width:100%;min-width:0}",
7884
7930
  // The rule above turns every row into a COLUMN flex container on phones, which silently breaks
@@ -8083,6 +8129,31 @@ function pricingCardEmphasis(featured, ctx, hasBg) {
8083
8129
  function textAttrs(ctx, path) {
8084
8130
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
8085
8131
  }
8132
+ var AI_RESPONSIVE_CSS = [
8133
+ "@media (max-width: 960px) {",
8134
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
8135
+ ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
8136
+ "}",
8137
+ "@media (max-width: 640px) {",
8138
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
8139
+ " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
8140
+ // Group containers flatten to a column on phones; span placements come along for free.
8141
+ // `:not([data-ai-group="overlap"])` for the same reason as the twin pair in AI_MOBILE_CSS — see
8142
+ // the comment there. This sheet's copy is the narrower selector, so without the exemption it
8143
+ // flattens an `overlap` hero below 640px even though the other rule stands down: measured a 390px
8144
+ // viewport with the heading/CTA in flow *under* the image, 219px of section background where the
8145
+ // layered text used to be.
8146
+ ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) { display: flex !important; flex-direction: column !important; }',
8147
+ ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) > * { grid-column: auto !important; }',
8148
+ // `:not([data-ai-full-bleed])` for the same reason as the twin rule in AI_MOBILE_CSS: this one
8149
+ // is narrower (0,2,0 vs 0,1,0) and also `!important`, so without the guard it wins outright and
8150
+ // a full-bleed hero gets its 20px side band back below 640px only — the one width where the
8151
+ // other rule had already been taught to stand down.
8152
+ " [data-ai-responsive] [data-ai-section-inner]:not([data-ai-full-bleed]) { padding-left: 20px !important; padding-right: 20px !important; }",
8153
+ " [data-ai-responsive] { overflow-x: hidden; }",
8154
+ " [data-ai-responsive] img { max-width: 100%; }",
8155
+ "}"
8156
+ ].join("\n");
8086
8157
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
8087
8158
  function MediaBox({
8088
8159
  refValue,
@@ -8137,7 +8208,7 @@ function MediaBox({
8137
8208
  position: fill ? "absolute" : void 0,
8138
8209
  inset: fill ? 0 : void 0,
8139
8210
  width: fill ? "100%" : fixedSize ? "auto" : "100%",
8140
- height: fill ? "100%" : void 0,
8211
+ height: fill ? "100%" : fixedSize ? height : void 0,
8141
8212
  aspectRatio: fill ? void 0 : aspectRatio,
8142
8213
  minHeight: fill || fixedSize ? void 0 : height ?? (aspectRatio ? void 0 : 180),
8143
8214
  borderRadius: radius,
@@ -9633,7 +9704,7 @@ function CollectionBlock({ node, ctx, path }) {
9633
9704
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
9634
9705
  "div",
9635
9706
  {
9636
- "data-ai-grid": "",
9707
+ "data-ai-grid": String(itemsPerRow),
9637
9708
  "data-ohw-pricing-collection": isPricing ? "" : void 0,
9638
9709
  style: {
9639
9710
  display: "grid",
@@ -9935,7 +10006,7 @@ function renderNode(node, ctx, path, ambientAlign) {
9935
10006
  },
9936
10007
  children: [
9937
10008
  i > 0 && ctx.heroOverlapFullBleed ? (
9938
- // The section's `data-ai-container` gave up its max-width/gutter so the base
10009
+ // The section's `data-ai-section-inner` gave up its max-width/gutter so the base
9939
10010
  // layer (this group's i===0 sibling) can span edge-to-edge — reintroduced here,
9940
10011
  // scoped to just the overlay text, so a heading/copy/CTA stack doesn't stretch
9941
10012
  // the full section width instead of reading at the usual content width.
@@ -10354,6 +10425,7 @@ function AiTreeRenderer({
10354
10425
  ...fillHeight ? { "data-ai-fill-height": "" } : {},
10355
10426
  ...needsOverlayClearance ? { "data-ai-overlay-clearance": "" } : {},
10356
10427
  ...bgAttrs,
10428
+ "data-ai-responsive": "",
10357
10429
  style: {
10358
10430
  position: "relative",
10359
10431
  padding: `${padCss} 0`,
@@ -10367,12 +10439,13 @@ function AiTreeRenderer({
10367
10439
  ...fillHeight ? { display: "grid" } : {}
10368
10440
  },
10369
10441
  children: [
10370
- isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
10442
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
10371
10443
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_MOBILE_CSS }),
10444
+ isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
10372
10445
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
10373
10446
  "div",
10374
10447
  {
10375
- "data-ai-container": "",
10448
+ "data-ai-section-inner": "",
10376
10449
  ...fullBleedMedia ? { "data-ai-full-bleed": "" } : {},
10377
10450
  style: {
10378
10451
  position: "relative",
@@ -10389,7 +10462,7 @@ function AiTreeRenderer({
10389
10462
  children: tree.rows.map((row, r2) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
10390
10463
  "div",
10391
10464
  {
10392
- "data-ai-row": "",
10465
+ "data-ai-columns": "",
10393
10466
  style: {
10394
10467
  display: "grid",
10395
10468
  gridTemplateColumns: "repeat(12, 1fr)",
@@ -11051,9 +11124,10 @@ function applyAiSectionsToDom(state, options) {
11051
11124
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
11052
11125
  continue;
11053
11126
  }
11054
- const isNewMount = !existing || !existing.container.isConnected;
11055
11127
  let container = existing?.container;
11128
+ let isNewMount = false;
11056
11129
  if (!container || !container.isConnected) {
11130
+ isNewMount = true;
11057
11131
  existing?.root.unmount();
11058
11132
  container?.remove();
11059
11133
  container = document.createElement("div");
@@ -17212,7 +17286,7 @@ function apiBase() {
17212
17286
  function bridgeVersion() {
17213
17287
  try {
17214
17288
  if (true) {
17215
- return "0.1.157";
17289
+ return "0.1.158-next.422";
17216
17290
  }
17217
17291
  } catch {
17218
17292
  }
@@ -17226,7 +17300,7 @@ function stampBridgeVersionOnDocument() {
17226
17300
  return;
17227
17301
  }
17228
17302
  try {
17229
- const v = true ? "0.1.157" : "";
17303
+ const v = true ? "0.1.158-next.422" : "";
17230
17304
  if (v) {
17231
17305
  document.documentElement.setAttribute("data-ohw-bridge-version", v);
17232
17306
  window.__OHW_BRIDGE_VERSION__ = v;
@@ -17904,6 +17978,7 @@ function MediaOverlay({
17904
17978
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
17905
17979
  );
17906
17980
  }, [isVideo]);
17981
+ const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
17907
17982
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
17908
17983
  const box = {
17909
17984
  position: "fixed",
@@ -18002,11 +18077,10 @@ function MediaOverlay({
18002
18077
  className: "flex items-center justify-center cursor-pointer",
18003
18078
  style: {
18004
18079
  ...box,
18005
- // When text overlays this media, let clicks fall THROUGH to the text underneath. In the
18006
- // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
18007
- // in-document, pointer-events does it natively. The button below opts back in, so
18008
- // Replace still works.
18009
- pointerEvents: hover.hasTextOverlap || cueOnly ? "none" : "auto",
18080
+ // Visual only. `pointer-events: auto` on this full-bleed rect covered the navbar
18081
+ // (hero bg includes the absolute TopNav) and swallowed CTA press-drag. Text overlay
18082
+ // and cueOnly also need clicks to fall through — Replace / video controls opt back in.
18083
+ pointerEvents: "none",
18010
18084
  // Selected: a firm component ring with no wash, so the image reads as chosen rather
18011
18085
  // than hovered. Hover keeps the existing tinted preview.
18012
18086
  boxShadow: selected ? "inset 0 0 0 2px var(--color-bridge-primary)" : "inset 0 0 0 1.5px var(--color-bridge-primary)",
@@ -18033,17 +18107,17 @@ function MediaOverlay({
18033
18107
  },
18034
18108
  children: [
18035
18109
  isVideo ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
18036
- isVideo ? "Replace video" : "Replace image"
18110
+ replaceLabel
18037
18111
  ]
18038
18112
  }
18039
18113
  ),
18040
- replaceMode === "none" || cueOnly ? null : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
18114
+ showChrome && replaceMode !== "none" && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
18041
18115
  Button,
18042
18116
  {
18043
18117
  "data-ohw-media-overlay": "",
18044
18118
  variant: "outline",
18045
18119
  size: "sm",
18046
- "aria-label": isVideo ? "Replace video" : "Replace image",
18120
+ "aria-label": replaceLabel,
18047
18121
  className: "gap-1.5 cursor-pointer hover:bg-background",
18048
18122
  style: {
18049
18123
  ...OVERLAY_BUTTON_STYLE,
@@ -18066,7 +18140,7 @@ function MediaOverlay({
18066
18140
  },
18067
18141
  children: [
18068
18142
  isVideo ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
18069
- replaceMode === "full" ? isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image" : null
18143
+ replaceMode === "full" ? replaceLabel : null
18070
18144
  ]
18071
18145
  }
18072
18146
  )
@@ -18576,7 +18650,7 @@ function AiSectionOverlay({
18576
18650
 
18577
18651
  // src/ui/ai-section/AiMediaStripOverlay.tsx
18578
18652
  var import_react10 = require("react");
18579
- var import_lucide_react8 = require("lucide-react");
18653
+ var import_lucide_react9 = require("lucide-react");
18580
18654
 
18581
18655
  // src/lib/ai-media-strip-tree.ts
18582
18656
  var MEDIA_STRIP_ITEM_MIN = 1;
@@ -18690,14 +18764,6 @@ function setMediaStripItemAsset(tree, index, assetId) {
18690
18764
  return true;
18691
18765
  });
18692
18766
  }
18693
- function setMediaStripItemAlt(tree, index, alt) {
18694
- return withMediaStrip(tree, (block) => {
18695
- const items = block.items;
18696
- if (index < 0 || index >= items.length) return false;
18697
- items[index] = { ...items[index], alt };
18698
- return true;
18699
- });
18700
- }
18701
18767
  function setMediaStripItemLink(tree, index, link) {
18702
18768
  return withMediaStrip(tree, (block) => {
18703
18769
  const items = block.items;
@@ -18715,11 +18781,117 @@ function setMediaStripItemLink(tree, index, link) {
18715
18781
  });
18716
18782
  }
18717
18783
 
18718
- // src/ui/ai-section/AiMediaStripOverlay.tsx
18784
+ // src/ui/dialog.tsx
18785
+ var React9 = __toESM(require("react"), 1);
18786
+ var import_radix_ui6 = require("radix-ui");
18787
+ var import_lucide_react8 = require("lucide-react");
18719
18788
  var import_jsx_runtime20 = require("react/jsx-runtime");
18720
- var PRIMARY3 = "#0885FE";
18721
- var OHW_BLUE = `var(--ohw-primary, ${PRIMARY3})`;
18722
- var OHW_BLUE_DARK = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY3}) 60%, #000000)`;
18789
+ function Dialog2({
18790
+ ...props
18791
+ }) {
18792
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_radix_ui6.Dialog.Root, { "data-slot": "dialog", ...props });
18793
+ }
18794
+ function DialogPortal({
18795
+ ...props
18796
+ }) {
18797
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_radix_ui6.Dialog.Portal, { ...props });
18798
+ }
18799
+ function DialogOverlay({
18800
+ className,
18801
+ ...props
18802
+ }) {
18803
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18804
+ import_radix_ui6.Dialog.Overlay,
18805
+ {
18806
+ "data-slot": "dialog-overlay",
18807
+ "data-ohw-link-modal-root": "",
18808
+ className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
18809
+ ...props
18810
+ }
18811
+ );
18812
+ }
18813
+ var DialogContent = React9.forwardRef(
18814
+ ({ className, children, showCloseButton = true, container, ...props }, ref) => {
18815
+ const positionMode = container ? "absolute" : "fixed";
18816
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(DialogPortal, { container: container ?? void 0, children: [
18817
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
18818
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18819
+ import_radix_ui6.Dialog.Content,
18820
+ {
18821
+ ref,
18822
+ "data-slot": "dialog-content",
18823
+ className: cn(
18824
+ positionMode,
18825
+ "left-1/2 top-1/2 z-[2147483647] flex w-full max-w-[448px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-visible",
18826
+ "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
18827
+ container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
18828
+ className
18829
+ ),
18830
+ onMouseDown: (e) => e.stopPropagation(),
18831
+ ...props,
18832
+ children: [
18833
+ children,
18834
+ showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18835
+ import_radix_ui6.Dialog.Close,
18836
+ {
18837
+ type: "button",
18838
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
18839
+ "aria-label": "Close",
18840
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react8.X, { size: 16, "aria-hidden": true })
18841
+ }
18842
+ ) : null
18843
+ ]
18844
+ }
18845
+ )
18846
+ ] });
18847
+ }
18848
+ );
18849
+ DialogContent.displayName = import_radix_ui6.Dialog.Content.displayName;
18850
+ function DialogHeader({
18851
+ className,
18852
+ ...props
18853
+ }) {
18854
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
18855
+ }
18856
+ function DialogFooter({
18857
+ className,
18858
+ ...props
18859
+ }) {
18860
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18861
+ "div",
18862
+ {
18863
+ className: cn("flex items-center justify-end gap-2", className),
18864
+ ...props
18865
+ }
18866
+ );
18867
+ }
18868
+ var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18869
+ import_radix_ui6.Dialog.Title,
18870
+ {
18871
+ ref,
18872
+ className: cn(
18873
+ "text-lg font-semibold leading-none tracking-tight text-card-foreground",
18874
+ className
18875
+ ),
18876
+ ...props
18877
+ }
18878
+ ));
18879
+ DialogTitle.displayName = import_radix_ui6.Dialog.Title.displayName;
18880
+ var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18881
+ import_radix_ui6.Dialog.Description,
18882
+ {
18883
+ ref,
18884
+ className: cn("text-sm text-muted-foreground", className),
18885
+ ...props
18886
+ }
18887
+ ));
18888
+ DialogDescription.displayName = import_radix_ui6.Dialog.Description.displayName;
18889
+ var DialogClose = import_radix_ui6.Dialog.Close;
18890
+
18891
+ // src/ui/ai-section/AiMediaStripOverlay.tsx
18892
+ var import_jsx_runtime21 = require("react/jsx-runtime");
18893
+ var PRIMARY3 = "var(--ohw-primary, #0885FE)";
18894
+ var CHROME_SELECTOR = "[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-link-modal-root], [data-ohw-section-picker], [data-ohw-ai-review], [data-ohw-more-menu]";
18723
18895
  function findMediaStripItemElement(sectionId, index) {
18724
18896
  const sectionEl = findByInstanceId(sectionId);
18725
18897
  if (!sectionEl) return null;
@@ -18729,14 +18901,35 @@ function findMediaStripItemElement(sectionId, index) {
18729
18901
  }
18730
18902
  return null;
18731
18903
  }
18732
- function readRect2(ref) {
18733
- const el = ref && findMediaStripItemElement(ref.sectionId, ref.index);
18904
+ function itemRefFromTarget(target) {
18905
+ if (!(target instanceof Element)) return null;
18906
+ if (target.closest(CHROME_SELECTOR)) return null;
18907
+ const itemEl = target.closest("[data-ohw-media-strip-item]");
18908
+ if (!itemEl) return null;
18909
+ const sectionEl = itemEl.closest("[data-ohw-section]");
18910
+ const unit = sectionEl ? movableUnit(sectionEl) : null;
18911
+ const sectionId = unit ? instanceIdOf(unit) || null : null;
18912
+ const index = Number(itemEl.dataset.ohwMediaStripIndex);
18913
+ if (!sectionId || Number.isNaN(index)) return null;
18914
+ return { sectionId, index };
18915
+ }
18916
+ function sameRef(a, b) {
18917
+ return a?.sectionId === b?.sectionId && a?.index === b?.index;
18918
+ }
18919
+ function rectOf(el) {
18734
18920
  if (!el) return null;
18735
18921
  const r2 = el.getBoundingClientRect();
18736
18922
  if (r2.width <= 0 || r2.height <= 0) return null;
18737
18923
  return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
18738
18924
  }
18739
- function useLiveItemRect(ref) {
18925
+ function readRect2(ref) {
18926
+ return rectOf(ref && findMediaStripItemElement(ref.sectionId, ref.index));
18927
+ }
18928
+ function readStripRect(ref) {
18929
+ const item = ref && findMediaStripItemElement(ref.sectionId, ref.index);
18930
+ return rectOf(item?.closest("[data-ohw-media-strip]") ?? item);
18931
+ }
18932
+ function useLiveItemRect(ref, strip = false) {
18740
18933
  const [rect, setRect] = (0, import_react10.useState)(null);
18741
18934
  (0, import_react10.useEffect)(() => {
18742
18935
  if (!ref) {
@@ -18744,7 +18937,7 @@ function useLiveItemRect(ref) {
18744
18937
  return;
18745
18938
  }
18746
18939
  const update = () => {
18747
- const next = readRect2(ref);
18940
+ const next = strip ? readStripRect(ref) : readRect2(ref);
18748
18941
  setRect(
18749
18942
  (prev) => prev && next && prev.top === next.top && prev.left === next.left && prev.width === next.width && prev.height === next.height ? prev : next
18750
18943
  );
@@ -18759,9 +18952,49 @@ function useLiveItemRect(ref) {
18759
18952
  window.removeEventListener("resize", update);
18760
18953
  clearInterval(interval);
18761
18954
  };
18762
- }, [ref?.sectionId, ref?.index]);
18955
+ }, [ref?.sectionId, ref?.index, strip]);
18763
18956
  return rect;
18764
18957
  }
18958
+ var TOOLBAR_STRIP_GAP = 52;
18959
+ var TOOLBAR_STRIP_GAP_MOBILE = 42;
18960
+ var TOOLBAR_EDGE_MARGIN2 = 4;
18961
+ function StripToolbarSlot({ stripRect, children }) {
18962
+ const slotRef = (0, import_react10.useRef)(null);
18963
+ const [shift, setShift] = (0, import_react10.useState)(0);
18964
+ const centerX = stripRect.left + stripRect.width / 2;
18965
+ (0, import_react10.useLayoutEffect)(() => {
18966
+ const slot = slotRef.current;
18967
+ if (!slot) return;
18968
+ const clamp = () => {
18969
+ const w = slot.offsetWidth;
18970
+ if (w <= 0) return;
18971
+ const half = w / 2;
18972
+ const clamped = Math.max(TOOLBAR_EDGE_MARGIN2 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half));
18973
+ setShift((prev) => Math.abs(prev - (clamped - centerX)) < 0.5 ? prev : clamped - centerX);
18974
+ };
18975
+ clamp();
18976
+ const ro = new ResizeObserver(clamp);
18977
+ ro.observe(slot);
18978
+ return () => ro.disconnect();
18979
+ }, [centerX, children]);
18980
+ const mobile = window.innerWidth <= 480;
18981
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
18982
+ "div",
18983
+ {
18984
+ ref: slotRef,
18985
+ "data-ohw-bridge": "",
18986
+ "data-ohw-item-interaction": "",
18987
+ className: "pointer-events-auto fixed",
18988
+ style: {
18989
+ top: stripRect.top - (mobile ? TOOLBAR_STRIP_GAP_MOBILE : TOOLBAR_STRIP_GAP),
18990
+ left: centerX + shift,
18991
+ transform: "translate(-50%, -100%)",
18992
+ zIndex: 2147483645
18993
+ },
18994
+ children
18995
+ }
18996
+ );
18997
+ }
18765
18998
  function requestValidateTree(postToParent2, tree) {
18766
18999
  return new Promise((resolve) => {
18767
19000
  const id = `ai-validate-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
@@ -18798,116 +19031,72 @@ function requestAssetPick(postToParent2) {
18798
19031
  postToParent2({ type: "ow:asset-pick", id });
18799
19032
  });
18800
19033
  }
18801
- function Pill({
19034
+ function ToolbarAction({
18802
19035
  label,
18803
19036
  onClick,
18804
- disabled,
18805
- active
19037
+ disabled = false,
19038
+ active = false,
19039
+ children
18806
19040
  }) {
18807
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18808
- "button",
18809
- {
18810
- type: "button",
18811
- onClick,
18812
- disabled,
18813
- style: {
18814
- height: 28,
18815
- padding: "0 10px",
18816
- display: "inline-flex",
18817
- alignItems: "center",
18818
- gap: 4,
18819
- borderRadius: 999,
18820
- border: "none",
18821
- cursor: disabled ? "default" : "pointer",
18822
- background: active ? OHW_BLUE_DARK : "#ffffff",
18823
- color: active ? "#ffffff" : OHW_BLUE_DARK,
18824
- opacity: disabled ? 0.4 : 1,
18825
- fontSize: 12,
18826
- lineHeight: "16px",
18827
- fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
18828
- whiteSpace: "nowrap",
18829
- flex: "0 0 auto"
18830
- },
18831
- children: label
18832
- }
18833
- );
19041
+ const button = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, active, onClick, className: "size-8", children });
19042
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19043
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "inline-flex", children: button }) : button }),
19044
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
19045
+ ] });
18834
19046
  }
18835
- function AltInput({ value, onCommit }) {
18836
- const [draft, setDraft] = (0, import_react10.useState)(value);
18837
- (0, import_react10.useEffect)(() => setDraft(value), [value]);
18838
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18839
- "div",
18840
- {
18841
- style: {
18842
- display: "flex",
18843
- alignItems: "center",
18844
- gap: 6,
18845
- height: 28,
18846
- padding: "0 12px",
18847
- borderRadius: 999,
18848
- background: "#ffffff",
18849
- width: "100%",
18850
- maxWidth: 260,
18851
- boxSizing: "border-box"
18852
- },
18853
- children: [
18854
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18855
- "span",
18856
- {
18857
- style: {
18858
- flex: "0 0 auto",
18859
- color: OHW_BLUE_DARK,
18860
- fontSize: 10,
18861
- fontWeight: 700,
18862
- textTransform: "uppercase",
18863
- letterSpacing: 0.4
18864
- },
18865
- children: "Alt"
18866
- }
18867
- ),
18868
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18869
- "input",
19047
+ function ToolbarSeparator() {
19048
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
19049
+ }
19050
+ function OptionMenu({
19051
+ label,
19052
+ value,
19053
+ options,
19054
+ onChange
19055
+ }) {
19056
+ const current = options.find((o) => o.value === value) ?? options[0];
19057
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19058
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-xs text-muted-foreground", children: label }),
19059
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DropdownMenu, { modal: false, children: [
19060
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19061
+ "button",
19062
+ {
19063
+ type: "button",
19064
+ "aria-label": label,
19065
+ className: "inline-flex h-8 min-w-[76px] 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",
19066
+ children: [
19067
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "flex-1 text-left", children: current.label }),
19068
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
19069
+ ]
19070
+ }
19071
+ ) }),
19072
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[132px] min-w-[132px] p-1", children: options.map((option) => {
19073
+ const selected = option.value === value;
19074
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19075
+ DropdownMenuItem,
18870
19076
  {
18871
- type: "text",
18872
- value: draft,
18873
- onChange: (e) => setDraft(e.target.value),
18874
- onBlur: () => {
18875
- if (draft !== value) onCommit(draft);
18876
- },
18877
- onKeyDown: (e) => {
18878
- if (e.key === "Enter") e.currentTarget.blur();
18879
- if (e.key === "Escape") {
18880
- setDraft(value);
18881
- e.currentTarget.blur();
18882
- }
18883
- },
18884
- placeholder: "Describe this image",
18885
- "aria-label": "Image alt text",
18886
- style: {
18887
- flex: 1,
18888
- minWidth: 0,
18889
- border: "none",
18890
- outline: "none",
18891
- background: "transparent",
18892
- fontSize: 12,
18893
- lineHeight: "16px",
18894
- fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
18895
- color: "#1f2937"
18896
- }
18897
- }
18898
- )
18899
- ]
18900
- }
18901
- );
19077
+ role: "menuitemradio",
19078
+ "aria-checked": selected,
19079
+ onSelect: () => onChange(option.value),
19080
+ className: cn("grid grid-cols-[1fr_16px] gap-2 py-2 pl-2 pr-2 text-[13px]", selected && "bg-muted"),
19081
+ children: [
19082
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: option.label }),
19083
+ selected && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Check, { size: 16, className: "text-bridge-primary" })
19084
+ ]
19085
+ },
19086
+ option.value
19087
+ );
19088
+ }) })
19089
+ ] })
19090
+ ] });
18902
19091
  }
18903
19092
  var VARIANTS = [
18904
19093
  { value: "logos", label: "Logos" },
18905
19094
  { value: "reel", label: "Reel" }
18906
19095
  ];
18907
19096
  var ALIGNS = [
18908
- { value: "left", label: "Left" },
18909
- { value: "center", label: "Center" },
18910
- { value: "right", label: "Right" }
19097
+ { value: "left", label: "Align left", Icon: import_lucide_react9.AlignLeft },
19098
+ { value: "center", label: "Align center", Icon: import_lucide_react9.AlignCenter },
19099
+ { value: "right", label: "Align right", Icon: import_lucide_react9.AlignRight }
18911
19100
  ];
18912
19101
  var HOVERS = [
18913
19102
  { value: "none", label: "None" },
@@ -18915,104 +19104,104 @@ var HOVERS = [
18915
19104
  { value: "zoom", label: "Zoom" },
18916
19105
  { value: "fade", label: "Fade" }
18917
19106
  ];
18918
- var TREATMENTS = [
18919
- { value: "color", label: "Color" },
18920
- { value: "grayscale", label: "Grayscale" }
18921
- ];
18922
19107
  var HTTP_URL_RE = /^https?:\/\/\S+$/i;
18923
- function LinkInput({ value, onCommit }) {
18924
- const [draft, setDraft] = (0, import_react10.useState)(value);
19108
+ function LinkModal({
19109
+ href,
19110
+ linkTarget,
19111
+ onClose,
19112
+ onSave
19113
+ }) {
19114
+ const [draft, setDraft] = (0, import_react10.useState)(href);
19115
+ const [newTab, setNewTab] = (0, import_react10.useState)(linkTarget !== "same-tab");
18925
19116
  const [invalid, setInvalid] = (0, import_react10.useState)(false);
18926
- (0, import_react10.useEffect)(() => {
18927
- setDraft(value);
18928
- setInvalid(false);
18929
- }, [value]);
18930
- const commit = () => {
19117
+ const save = () => {
18931
19118
  const next = draft.trim();
18932
- if (next === value) return setInvalid(false);
18933
- if (next && !HTTP_URL_RE.test(next)) return setInvalid(true);
18934
- setInvalid(false);
18935
- onCommit(next);
18936
- };
18937
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18938
- "div",
18939
- {
18940
- style: {
18941
- display: "flex",
18942
- alignItems: "center",
18943
- gap: 6,
18944
- height: 28,
18945
- padding: "0 12px",
18946
- borderRadius: 999,
18947
- background: "#ffffff",
18948
- width: "100%",
18949
- maxWidth: 260,
18950
- boxSizing: "border-box",
18951
- boxShadow: invalid ? "inset 0 0 0 1.5px #EF4444" : "none"
18952
- },
18953
- children: [
18954
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18955
- "span",
18956
- {
18957
- style: {
18958
- flex: "0 0 auto",
18959
- color: OHW_BLUE_DARK,
18960
- fontSize: 10,
18961
- fontWeight: 700,
18962
- textTransform: "uppercase",
18963
- letterSpacing: 0.4
18964
- },
18965
- children: "Link"
18966
- }
18967
- ),
18968
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18969
- "input",
18970
- {
18971
- type: "text",
18972
- inputMode: "url",
18973
- value: draft,
18974
- onChange: (e) => setDraft(e.target.value),
18975
- onBlur: commit,
18976
- onKeyDown: (e) => {
18977
- if (e.key === "Enter") e.currentTarget.blur();
18978
- if (e.key === "Escape") {
18979
- setDraft(value);
18980
- setInvalid(false);
18981
- e.currentTarget.blur();
18982
- }
18983
- },
18984
- placeholder: "https://example.com",
18985
- "aria-label": "Image link URL",
18986
- "aria-invalid": invalid,
18987
- style: {
18988
- flex: 1,
18989
- minWidth: 0,
18990
- border: "none",
18991
- outline: "none",
18992
- background: "transparent",
18993
- fontSize: 12,
18994
- lineHeight: "16px",
18995
- fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
18996
- color: "#1f2937"
18997
- }
18998
- }
18999
- )
19000
- ]
19119
+ if (next && !HTTP_URL_RE.test(next)) {
19120
+ setInvalid(true);
19121
+ return;
19001
19122
  }
19002
- );
19123
+ onSave(next, newTab ? "new-tab" : "same-tab");
19124
+ onClose();
19125
+ };
19126
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Dialog2, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DialogContent, { "data-ohw-link-modal-root": "", className: "max-w-[400px] gap-4 p-5", children: [
19127
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogTitle, { className: "text-base", children: "Image link" }) }),
19128
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col gap-3", children: [
19129
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19130
+ "input",
19131
+ {
19132
+ autoFocus: true,
19133
+ type: "text",
19134
+ inputMode: "url",
19135
+ value: draft,
19136
+ onChange: (e) => {
19137
+ setDraft(e.target.value);
19138
+ setInvalid(false);
19139
+ },
19140
+ onKeyDown: (e) => {
19141
+ if (e.key === "Enter") save();
19142
+ },
19143
+ placeholder: "https://example.com",
19144
+ "aria-label": "Image link URL",
19145
+ "aria-invalid": invalid,
19146
+ className: cn(
19147
+ "h-9 w-full rounded-[6px] border border-border bg-background px-3 text-[13px] text-foreground outline-none placeholder:text-muted-foreground/70 focus:border-bridge-primary",
19148
+ invalid && "border-destructive focus:border-destructive"
19149
+ )
19150
+ }
19151
+ ),
19152
+ invalid && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-xs text-destructive", children: "Enter a full URL starting with http:// or https://" }),
19153
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { className: "flex items-center gap-2 text-[13px] text-foreground", children: [
19154
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("input", { type: "checkbox", checked: newTab, onChange: (e) => setNewTab(e.target.checked) }),
19155
+ "Open in new tab"
19156
+ ] })
19157
+ ] }),
19158
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DialogFooter, { children: [
19159
+ href && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19160
+ "button",
19161
+ {
19162
+ type: "button",
19163
+ onClick: () => {
19164
+ onSave("", "new-tab");
19165
+ onClose();
19166
+ },
19167
+ className: "mr-auto rounded-[6px] px-3 py-2 text-[13px] font-medium text-destructive transition-colors hover:bg-muted",
19168
+ children: "Remove link"
19169
+ }
19170
+ ),
19171
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19172
+ "button",
19173
+ {
19174
+ type: "button",
19175
+ onClick: onClose,
19176
+ className: "rounded-[6px] px-3 py-2 text-[13px] font-medium text-foreground transition-colors hover:bg-muted",
19177
+ children: "Cancel"
19178
+ }
19179
+ ),
19180
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19181
+ "button",
19182
+ {
19183
+ type: "button",
19184
+ onClick: save,
19185
+ className: "rounded-[6px] bg-bridge-primary px-3 py-2 text-[13px] font-medium text-primary-foreground transition-opacity hover:opacity-90",
19186
+ children: "Save"
19187
+ }
19188
+ )
19189
+ ] })
19190
+ ] }) });
19003
19191
  }
19004
19192
  function MediaStripToolbar({
19005
19193
  entry,
19006
19194
  index,
19007
19195
  pending,
19008
19196
  error,
19197
+ itemEditOn,
19198
+ onToggleItemEdit,
19009
19199
  onSetVariant,
19010
19200
  onSetAlign,
19011
19201
  onSetHover,
19012
19202
  onSetTreatment,
19013
19203
  onResize,
19014
- onSetAlt,
19015
- onSetLink,
19204
+ onOpenLink,
19016
19205
  onAddItem,
19017
19206
  onRemoveItem,
19018
19207
  onChangeImage,
@@ -19021,81 +19210,102 @@ function MediaStripToolbar({
19021
19210
  }) {
19022
19211
  const items = mediaStripItems(entry.tree);
19023
19212
  const itemCount = items.length;
19024
- const currentAlt = items[index]?.alt ?? "";
19025
- const currentHref = items[index]?.href ?? "";
19026
- const currentTarget = items[index]?.linkTarget === "same-tab" ? "same-tab" : "new-tab";
19027
19213
  const slots = findMediaStripSlots(entry.tree);
19028
19214
  const variant = slots?.variant === "reel" ? "reel" : "logos";
19029
19215
  const align = slots?.align === "left" || slots?.align === "right" ? slots.align : "center";
19030
19216
  const hover = slots?.hover === "none" || slots?.hover === "lift" || slots?.hover === "zoom" || slots?.hover === "fade" ? slots.hover : "lift";
19031
19217
  const treatment = slots?.treatment === "color" || slots?.treatment === "grayscale" ? slots.treatment : variant === "logos" ? "grayscale" : "color";
19032
19218
  const sizeValue = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
19033
- const isMobile = window.innerWidth <= 480;
19034
- const rowStyle = isMobile ? {
19035
- display: "flex",
19036
- gap: 6,
19037
- flexWrap: "nowrap",
19038
- justifyContent: "center",
19039
- overflowX: "auto",
19040
- WebkitOverflowScrolling: "touch",
19041
- width: "100%",
19042
- scrollbarWidth: "none"
19043
- } : { display: "flex", gap: 6, flexWrap: "nowrap", justifyContent: "center" };
19044
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
19219
+ const mobile = window.innerWidth <= 480;
19220
+ const gatedLabel = (label) => itemEditOn ? label : "Turn on the star to edit this image";
19221
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19045
19222
  "div",
19046
19223
  {
19047
19224
  "data-ohw-bridge": "",
19048
19225
  "data-ohw-toolbar": "",
19049
19226
  onMouseDown: (e) => e.stopPropagation(),
19050
- style: {
19051
- display: "flex",
19052
- flexDirection: "column",
19053
- gap: 6,
19054
- alignItems: "center",
19055
- background: OHW_BLUE,
19056
- padding: 8,
19057
- borderRadius: 12,
19058
- boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
19059
- pointerEvents: pending ? "none" : "auto",
19060
- opacity: pending ? 0.6 : 1,
19061
- maxWidth: isMobile ? "calc(100vw - 24px)" : "none"
19062
- },
19227
+ className: cn("flex flex-col items-center gap-1.5 font-sans", pending && "pointer-events-none opacity-60"),
19228
+ style: { maxWidth: mobile ? "calc(100vw - 24px)" : void 0 },
19063
19229
  children: [
19064
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: rowStyle, children: [
19065
- VARIANTS.map((v) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: v.label, active: variant === v.value, onClick: () => onSetVariant(v.value) }, v.value)),
19066
- ALIGNS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: a.label, active: align === a.value, onClick: () => onSetAlign(a.value) }, a.value))
19067
- ] }),
19068
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: rowStyle, children: [
19069
- HOVERS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: h.label, active: hover === h.value, onClick: () => onSetHover(h.value) }, h.value)),
19070
- TREATMENTS.map((t) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: t.label, active: treatment === t.value, onClick: () => onSetTreatment(t.value) }, t.value))
19071
- ] }),
19072
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
19073
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "\u2212", onClick: () => onResize(-8) }),
19074
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { style: { color: "#ffffff", fontSize: 12, minWidth: 34, textAlign: "center" }, children: [
19075
- sizeValue,
19076
- "px"
19077
- ] }),
19078
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "+", onClick: () => onResize(8) })
19079
- ] }),
19080
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { display: "flex", justifyContent: "center", width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(AltInput, { value: currentAlt, onCommit: onSetAlt }) }),
19081
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { display: "flex", justifyContent: "center", width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LinkInput, { value: currentHref, onCommit: (href) => onSetLink(href, currentTarget) }) }),
19082
- currentHref && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: rowStyle, children: [
19083
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "New tab", active: currentTarget === "new-tab", onClick: () => onSetLink(currentHref, "new-tab") }),
19084
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "Same tab", active: currentTarget === "same-tab", onClick: () => onSetLink(currentHref, "same-tab") })
19085
- ] }),
19086
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: rowStyle, children: [
19087
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "Change Image", onClick: onChangeImage }),
19088
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "+ Item", onClick: onAddItem }),
19089
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "\u2212 Item", onClick: onRemoveItem, disabled: itemCount <= 1 })
19090
- ] }),
19091
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: rowStyle, children: [
19092
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "\u2190 Move", onClick: onMoveLeft, disabled: index <= 0 }),
19093
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "\u2192 Move", onClick: onMoveRight, disabled: index >= itemCount - 1 })
19094
- ] }),
19095
- error && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
19230
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19231
+ "div",
19232
+ {
19233
+ className: cn(
19234
+ "flex items-center gap-1 rounded-[10px] border border-border bg-background p-1 whitespace-nowrap",
19235
+ "shadow-[0px_10px_15px_-3px_rgba(0,0,0,0.1),0px_4px_6px_-4px_rgba(0,0,0,0.1)]",
19236
+ mobile ? "flex-wrap justify-center" : "flex-nowrap"
19237
+ ),
19238
+ children: [
19239
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19240
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19241
+ CustomToolbarButton,
19242
+ {
19243
+ type: "button",
19244
+ "aria-label": itemEditOn ? "Done editing this image" : "Edit this image",
19245
+ "aria-pressed": itemEditOn,
19246
+ onClick: onToggleItemEdit,
19247
+ className: "group size-8",
19248
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19249
+ import_lucide_react9.Star,
19250
+ {
19251
+ size: 16,
19252
+ fill: "currentColor",
19253
+ strokeWidth: 0,
19254
+ className: cn(
19255
+ "transition-colors",
19256
+ itemEditOn ? "text-[#f59e0b] group-hover:text-[#d97706]" : "text-[#d6d3d1] group-hover:text-[#fbbf24]"
19257
+ ),
19258
+ style: { pointerEvents: "none" }
19259
+ }
19260
+ )
19261
+ }
19262
+ ) }),
19263
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: itemEditOn ? "Done editing this image" : "Edit this image" })
19264
+ ] }),
19265
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19266
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: gatedLabel("Change image"), onClick: onChangeImage, disabled: !itemEditOn, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Image, { size: 16, style: { pointerEvents: "none" } }) }),
19267
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: gatedLabel("Link"), onClick: onOpenLink, disabled: !itemEditOn, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Link, { size: 16, style: { pointerEvents: "none" } }) }),
19268
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: gatedLabel("Move left"), onClick: onMoveLeft, disabled: !itemEditOn || index <= 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ArrowLeft, { size: 16, style: { pointerEvents: "none" } }) }),
19269
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19270
+ ToolbarAction,
19271
+ {
19272
+ label: gatedLabel("Move right"),
19273
+ onClick: onMoveRight,
19274
+ disabled: !itemEditOn || index >= itemCount - 1,
19275
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ArrowRight, { size: 16, style: { pointerEvents: "none" } })
19276
+ }
19277
+ ),
19278
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: gatedLabel("Remove image"), onClick: onRemoveItem, disabled: !itemEditOn || itemCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ImageMinus, { size: 16, style: { pointerEvents: "none" } }) }),
19279
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19280
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(OptionMenu, { label: "Layout", value: variant, options: VARIANTS, onChange: onSetVariant }),
19281
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(OptionMenu, { label: "Hover", value: hover, options: HOVERS, onChange: onSetHover }),
19282
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19283
+ ToolbarAction,
19284
+ {
19285
+ label: treatment === "grayscale" ? "Show in color" : "Show grayscale",
19286
+ active: treatment === "grayscale",
19287
+ onClick: () => onSetTreatment(treatment === "grayscale" ? "color" : "grayscale"),
19288
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Palette, { size: 16, style: { pointerEvents: "none" } })
19289
+ }
19290
+ ),
19291
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19292
+ ALIGNS.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label, active: align === value, onClick: () => onSetAlign(value), children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Icon, { size: 16, style: { pointerEvents: "none" } }) }, value)),
19293
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19294
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Smaller", onClick: () => onResize(-8), children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Minus, { size: 16, style: { pointerEvents: "none" } }) }),
19295
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "min-w-[40px] text-center text-xs tabular-nums text-muted-foreground", children: [
19296
+ sizeValue,
19297
+ "px"
19298
+ ] }),
19299
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Larger", onClick: () => onResize(8), children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Plus, { size: 16, style: { pointerEvents: "none" } }) }),
19300
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19301
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Add image", onClick: onAddItem, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ImagePlus, { size: 16, style: { pointerEvents: "none" } }) })
19302
+ ]
19303
+ }
19304
+ ),
19305
+ error && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
19096
19306
  ]
19097
19307
  }
19098
- );
19308
+ ) });
19099
19309
  }
19100
19310
  function findMediaStripSlots(tree) {
19101
19311
  const stack = tree.rows.flatMap((row) => row.blocks);
@@ -19114,31 +19324,23 @@ function AiMediaStripOverlay({
19114
19324
  }) {
19115
19325
  const [hover, setHover] = (0, import_react10.useState)(null);
19116
19326
  const [active, setActive] = (0, import_react10.useState)(null);
19327
+ const [itemEditOn, setItemEditOn] = (0, import_react10.useState)(false);
19328
+ const [linkOpen, setLinkOpen] = (0, import_react10.useState)(false);
19117
19329
  const [pending, setPending] = (0, import_react10.useState)(false);
19118
19330
  const [error, setError] = (0, import_react10.useState)(null);
19119
19331
  const activeRef = (0, import_react10.useRef)(null);
19120
19332
  activeRef.current = active;
19333
+ const linkOpenRef = (0, import_react10.useRef)(false);
19334
+ linkOpenRef.current = linkOpen;
19121
19335
  (0, import_react10.useEffect)(() => {
19122
19336
  const onMove = (e) => {
19337
+ const ref = itemRefFromTarget(e.target);
19123
19338
  const t = e.target;
19124
- if (!(t instanceof HTMLElement)) return;
19125
- if (t.closest("[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-link-modal-root], [data-ohw-section-picker], [data-ohw-ai-review]")) {
19126
- return;
19127
- }
19128
- const itemEl = t.closest("[data-ohw-media-strip-item]");
19129
- if (!itemEl) {
19130
- setHover(null);
19131
- return;
19132
- }
19133
- const sectionEl = itemEl.closest("[data-ohw-section]");
19134
- const unit = sectionEl ? movableUnit(sectionEl) : null;
19135
- const sectionId = unit ? instanceIdOf(unit) || null : null;
19136
- const index = Number(itemEl.dataset.ohwMediaStripIndex);
19137
- if (!sectionId || Number.isNaN(index)) {
19138
- setHover(null);
19139
- return;
19140
- }
19141
- setHover({ sectionId, index });
19339
+ const overEditable = Boolean(t?.closest?.("[data-ohw-editable]"));
19340
+ setHover((prev) => {
19341
+ const next = ref && !overEditable ? ref : null;
19342
+ return sameRef(prev, next) ? prev : next;
19343
+ });
19142
19344
  };
19143
19345
  const onLeave = () => setHover(null);
19144
19346
  document.addEventListener("mousemove", onMove, { passive: true });
@@ -19150,23 +19352,25 @@ function AiMediaStripOverlay({
19150
19352
  }, []);
19151
19353
  (0, import_react10.useEffect)(() => {
19152
19354
  const onClick = (e) => {
19153
- const current = activeRef.current;
19154
- if (!current) return;
19155
19355
  const t = e.target;
19156
- if (!(t instanceof HTMLElement)) return;
19157
- if (t.closest("[data-ohw-item-interaction]")) return;
19158
- const itemEl = findMediaStripItemElement(current.sectionId, current.index);
19159
- if (itemEl && itemEl.contains(t)) return;
19160
- setActive(null);
19356
+ if (!(t instanceof Element)) return;
19357
+ if (t.closest(CHROME_SELECTOR)) return;
19358
+ const ref = itemRefFromTarget(t);
19359
+ if (sameRef(activeRef.current, ref)) return;
19360
+ if (ref) clearInteractionChromeRef.current();
19361
+ setItemEditOn(false);
19161
19362
  setError(null);
19363
+ setActive(ref);
19162
19364
  };
19163
19365
  document.addEventListener("click", onClick, true);
19164
19366
  return () => document.removeEventListener("click", onClick, true);
19165
- }, []);
19367
+ }, [clearInteractionChromeRef]);
19166
19368
  (0, import_react10.useEffect)(() => {
19167
19369
  const onBlur = () => {
19168
19370
  if (!activeRef.current) return;
19169
19371
  setActive(null);
19372
+ setItemEditOn(false);
19373
+ setLinkOpen(false);
19170
19374
  setError(null);
19171
19375
  };
19172
19376
  window.addEventListener("blur", onBlur);
@@ -19176,7 +19380,10 @@ function AiMediaStripOverlay({
19176
19380
  const onKeyDown = (e) => {
19177
19381
  if (e.key !== "Escape") return;
19178
19382
  if (!activeRef.current) return;
19383
+ if (linkOpenRef.current) return;
19384
+ if (document.querySelector('[data-ohw-more-menu][data-state="open"]')) return;
19179
19385
  setActive(null);
19386
+ setItemEditOn(false);
19180
19387
  setError(null);
19181
19388
  };
19182
19389
  document.addEventListener("keydown", onKeyDown);
@@ -19210,137 +19417,133 @@ function AiMediaStripOverlay({
19210
19417
  tree: nextTree,
19211
19418
  label: entry.label,
19212
19419
  media: extraMedia ? { ...entry.media, ...extraMedia } : entry.media,
19213
- ...entry.brand ? { brand: entry.brand } : {}
19420
+ ...entry.brand ? { brand: entry.brand } : {},
19421
+ // The user is already looking at the strip they just edited — recentering the viewport
19422
+ // on every edit reads as the section jumping around.
19423
+ skipScrollIntoView: true
19214
19424
  });
19215
19425
  return true;
19216
19426
  },
19217
19427
  [findEntry, postToParent2, applyTreeLocalRef]
19218
19428
  );
19219
19429
  const activeEntry = active ? findEntry(active.sectionId) : null;
19220
- const hoverHandleRect = useLiveItemRect(!active && hover ? hover : null);
19221
19430
  const activeItemRect = useLiveItemRect(active);
19222
- const TOOLBAR_APPROX_H = 170;
19223
- const toolbarState = activeItemRect && activeItemRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
19224
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
19225
- hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19226
- "button",
19227
- {
19228
- type: "button",
19229
- "data-ohw-toolbar": "",
19230
- onClick: () => {
19231
- clearInteractionChromeRef.current();
19232
- setActive(hover);
19233
- },
19234
- "aria-label": "Configure media item",
19235
- style: {
19236
- position: "fixed",
19237
- top: hoverHandleRect.top + 8,
19238
- left: hoverHandleRect.left + hoverHandleRect.width - 36,
19239
- width: 28,
19240
- height: 28,
19241
- borderRadius: 8,
19242
- border: `1.5px dashed ${PRIMARY3}`,
19243
- background: "#ffffff",
19244
- color: PRIMARY3,
19245
- display: "flex",
19246
- alignItems: "center",
19247
- justifyContent: "center",
19248
- cursor: "pointer",
19249
- zIndex: 2147483643,
19250
- fontSize: 14,
19251
- fontWeight: 700
19252
- },
19253
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react8.Star, { size: 14, style: { pointerEvents: "none" } })
19254
- }
19255
- ),
19256
- activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19257
- ItemInteractionLayer,
19258
- {
19259
- rect: activeItemRect,
19260
- state: toolbarState,
19261
- toolbar: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19262
- MediaStripToolbar,
19263
- {
19264
- entry: activeEntry,
19265
- index: active.index,
19266
- pending,
19267
- error,
19268
- onSetVariant: (variant) => runMutation(active.sectionId, (tree) => setMediaStripVariant(tree, variant)),
19269
- onSetAlign: (align) => runMutation(active.sectionId, (tree) => setMediaStripAlign(tree, align)),
19270
- onSetHover: (hoverPreset) => runMutation(active.sectionId, (tree) => setMediaStripHover(tree, hoverPreset)),
19271
- onSetTreatment: (treatment) => runMutation(active.sectionId, (tree) => setMediaStripTreatment(tree, treatment)),
19272
- onResize: (delta) => {
19273
- const slots = findMediaStripSlots(activeEntry.tree);
19274
- const variant = slots?.variant === "reel" ? "reel" : "logos";
19275
- const current = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
19276
- void runMutation(active.sectionId, (tree) => setMediaStripSizing(tree, current + delta));
19277
- },
19278
- onSetAlt: (alt) => {
19279
- void runMutation(active.sectionId, (tree) => setMediaStripItemAlt(tree, active.index, alt));
19280
- },
19281
- onSetLink: (href, linkTarget) => {
19431
+ const activeStripRect = useLiveItemRect(active, true);
19432
+ const hoverRect = useLiveItemRect(hover && !sameRef(hover, active) ? hover : null);
19433
+ const activeItems = activeEntry ? mediaStripItems(activeEntry.tree) : [];
19434
+ const activeItem = active ? activeItems[active.index] : void 0;
19435
+ const activeHref = activeItem?.href ?? "";
19436
+ const activeTarget = activeItem?.linkTarget === "same-tab" ? "same-tab" : "new-tab";
19437
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19438
+ hoverRect && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
19439
+ activeItemRect && activeStripRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19440
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: activeItemRect, state: "active-top" }),
19441
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StripToolbarSlot, { stripRect: activeStripRect, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19442
+ MediaStripToolbar,
19443
+ {
19444
+ entry: activeEntry,
19445
+ index: active.index,
19446
+ pending,
19447
+ error,
19448
+ itemEditOn,
19449
+ onToggleItemEdit: () => setItemEditOn((on) => !on),
19450
+ onSetVariant: (variant) => runMutation(active.sectionId, (tree) => setMediaStripVariant(tree, variant)),
19451
+ onSetAlign: (align) => runMutation(active.sectionId, (tree) => setMediaStripAlign(tree, align)),
19452
+ onSetHover: (hoverPreset) => runMutation(active.sectionId, (tree) => setMediaStripHover(tree, hoverPreset)),
19453
+ onSetTreatment: (treatment) => runMutation(active.sectionId, (tree) => setMediaStripTreatment(tree, treatment)),
19454
+ onResize: (delta) => {
19455
+ const slots = findMediaStripSlots(activeEntry.tree);
19456
+ const variant = slots?.variant === "reel" ? "reel" : "logos";
19457
+ const current = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
19458
+ void runMutation(active.sectionId, (tree) => setMediaStripSizing(tree, current + delta));
19459
+ },
19460
+ onOpenLink: () => setLinkOpen(true),
19461
+ onAddItem: () => {
19462
+ void requestAssetPick(postToParent2).then((picked) => {
19463
+ if (!picked) return;
19464
+ const item = { assetId: picked.assetId, alt: "Logo" };
19282
19465
  void runMutation(
19283
19466
  active.sectionId,
19284
- (tree) => setMediaStripItemLink(tree, active.index, href ? { href, linkTarget } : {})
19285
- );
19286
- },
19287
- onAddItem: () => {
19288
- void requestAssetPick(postToParent2).then((picked) => {
19289
- if (!picked) return;
19290
- const item = { assetId: picked.assetId, alt: "Logo" };
19291
- void runMutation(
19292
- active.sectionId,
19293
- (tree) => addMediaStripItem(tree, item, active.index),
19294
- { [picked.assetId]: picked.url }
19295
- ).then((applied) => {
19296
- if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19297
- });
19298
- });
19299
- },
19300
- onChangeImage: () => {
19301
- void requestAssetPick(postToParent2).then((picked) => {
19302
- if (!picked) return;
19303
- void runMutation(
19304
- active.sectionId,
19305
- (tree) => setMediaStripItemAsset(tree, active.index, picked.assetId),
19306
- { [picked.assetId]: picked.url }
19307
- );
19467
+ (tree) => addMediaStripItem(tree, item, active.index),
19468
+ { [picked.assetId]: picked.url }
19469
+ ).then((applied) => {
19470
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19308
19471
  });
19309
- },
19310
- onRemoveItem: () => {
19311
- void runMutation(active.sectionId, (tree) => removeMediaStripItem(tree, active.index)).then(
19312
- (applied) => {
19313
- if (applied) setActive(null);
19314
- }
19315
- );
19316
- },
19317
- onMoveLeft: () => {
19318
- void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, -1)).then(
19319
- (applied) => {
19320
- if (applied) setActive({ sectionId: active.sectionId, index: active.index - 1 });
19321
- }
19322
- );
19323
- },
19324
- onMoveRight: () => {
19325
- void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, 1)).then(
19326
- (applied) => {
19327
- if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19328
- }
19472
+ });
19473
+ },
19474
+ onChangeImage: () => {
19475
+ void requestAssetPick(postToParent2).then((picked) => {
19476
+ if (!picked) return;
19477
+ void runMutation(
19478
+ active.sectionId,
19479
+ (tree) => setMediaStripItemAsset(tree, active.index, picked.assetId),
19480
+ { [picked.assetId]: picked.url }
19329
19481
  );
19330
- }
19482
+ });
19483
+ },
19484
+ onRemoveItem: () => {
19485
+ void runMutation(active.sectionId, (tree) => removeMediaStripItem(tree, active.index)).then(
19486
+ (applied) => {
19487
+ if (applied) setActive(null);
19488
+ }
19489
+ );
19490
+ },
19491
+ onMoveLeft: () => {
19492
+ void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, -1)).then(
19493
+ (applied) => {
19494
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index - 1 });
19495
+ }
19496
+ );
19497
+ },
19498
+ onMoveRight: () => {
19499
+ void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, 1)).then(
19500
+ (applied) => {
19501
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19502
+ }
19503
+ );
19331
19504
  }
19332
- )
19333
- }
19334
- )
19505
+ }
19506
+ ) }),
19507
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19508
+ "div",
19509
+ {
19510
+ "data-ohw-bridge": "",
19511
+ "data-ohw-item-interaction": "",
19512
+ "aria-hidden": true,
19513
+ className: "pointer-events-none fixed rounded-[4px] px-[7px] py-[3px] font-sans text-[11px] font-medium leading-none text-primary-foreground whitespace-nowrap",
19514
+ style: {
19515
+ top: activeItemRect.top + activeItemRect.height + 10,
19516
+ left: activeItemRect.left - 5,
19517
+ background: PRIMARY3,
19518
+ zIndex: 2147483644
19519
+ },
19520
+ children: "Media item"
19521
+ }
19522
+ ),
19523
+ linkOpen && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19524
+ LinkModal,
19525
+ {
19526
+ href: activeHref,
19527
+ linkTarget: activeTarget,
19528
+ onClose: () => setLinkOpen(false),
19529
+ onSave: (href, linkTarget) => {
19530
+ void runMutation(
19531
+ active.sectionId,
19532
+ (tree) => setMediaStripItemLink(tree, active.index, href ? { href, linkTarget } : {})
19533
+ );
19534
+ }
19535
+ }
19536
+ )
19537
+ ] })
19335
19538
  ] });
19336
19539
  }
19337
19540
 
19338
19541
  // src/ui/ai-section/AiPricingCardOverlay.tsx
19339
19542
  var import_react11 = require("react");
19340
- var import_lucide_react9 = require("lucide-react");
19341
- var import_jsx_runtime21 = require("react/jsx-runtime");
19543
+ var import_lucide_react10 = require("lucide-react");
19544
+ var import_jsx_runtime22 = require("react/jsx-runtime");
19342
19545
  var PRIMARY4 = "var(--ohw-primary, #0885FE)";
19343
- var CHROME_SELECTOR = "[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-link-modal-root], [data-ohw-section-picker], [data-ohw-ai-review], [data-ohw-more-menu]";
19546
+ var CHROME_SELECTOR2 = "[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-link-modal-root], [data-ohw-section-picker], [data-ohw-ai-review], [data-ohw-more-menu]";
19344
19547
  function findPricingCardElement(sectionId, index) {
19345
19548
  const sectionEl = findByInstanceId(sectionId);
19346
19549
  if (!sectionEl) return null;
@@ -19352,7 +19555,7 @@ function findPricingCardElement(sectionId, index) {
19352
19555
  }
19353
19556
  function cardRefFromTarget(target) {
19354
19557
  if (!(target instanceof Element)) return null;
19355
- if (target.closest(CHROME_SELECTOR)) return null;
19558
+ if (target.closest(CHROME_SELECTOR2)) return null;
19356
19559
  const cardEl = target.closest("[data-ohw-pricing-card]");
19357
19560
  if (!cardEl) return null;
19358
19561
  const sectionEl = cardEl.closest("[data-ohw-section]");
@@ -19362,21 +19565,21 @@ function cardRefFromTarget(target) {
19362
19565
  if (!sectionId || Number.isNaN(index)) return null;
19363
19566
  return { sectionId, index };
19364
19567
  }
19365
- function sameRef(a, b) {
19568
+ function sameRef2(a, b) {
19366
19569
  return a?.sectionId === b?.sectionId && a?.index === b?.index;
19367
19570
  }
19368
- function rectOf(el) {
19571
+ function rectOf2(el) {
19369
19572
  if (!el) return null;
19370
19573
  const r2 = el.getBoundingClientRect();
19371
19574
  if (r2.width <= 0 || r2.height <= 0) return null;
19372
19575
  return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
19373
19576
  }
19374
19577
  function readRect3(ref) {
19375
- return rectOf(ref && findPricingCardElement(ref.sectionId, ref.index));
19578
+ return rectOf2(ref && findPricingCardElement(ref.sectionId, ref.index));
19376
19579
  }
19377
19580
  function readGridRect(ref) {
19378
19581
  const card = ref && findPricingCardElement(ref.sectionId, ref.index);
19379
- return rectOf(card?.closest("[data-ohw-pricing-collection]") ?? card);
19582
+ return rectOf2(card?.closest("[data-ohw-pricing-collection]") ?? card);
19380
19583
  }
19381
19584
  function useLiveCardRect(ref, grid = false) {
19382
19585
  const [rect, setRect] = (0, import_react11.useState)(null);
@@ -19406,7 +19609,7 @@ function useLiveCardRect(ref, grid = false) {
19406
19609
  }
19407
19610
  var TOOLBAR_GRID_GAP = 52;
19408
19611
  var TOOLBAR_GRID_GAP_MOBILE = 42;
19409
- var TOOLBAR_EDGE_MARGIN2 = 4;
19612
+ var TOOLBAR_EDGE_MARGIN3 = 4;
19410
19613
  function GridToolbarSlot({ gridRect, children }) {
19411
19614
  const slotRef = (0, import_react11.useRef)(null);
19412
19615
  const [shift, setShift] = (0, import_react11.useState)(0);
@@ -19418,7 +19621,7 @@ function GridToolbarSlot({ gridRect, children }) {
19418
19621
  const w = slot.offsetWidth;
19419
19622
  if (w <= 0) return;
19420
19623
  const half = w / 2;
19421
- const clamped = Math.max(TOOLBAR_EDGE_MARGIN2 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half));
19624
+ const clamped = Math.max(TOOLBAR_EDGE_MARGIN3 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN3 - half));
19422
19625
  setShift((prev) => Math.abs(prev - (clamped - centerX)) < 0.5 ? prev : clamped - centerX);
19423
19626
  };
19424
19627
  clamp();
@@ -19427,7 +19630,7 @@ function GridToolbarSlot({ gridRect, children }) {
19427
19630
  return () => ro.disconnect();
19428
19631
  }, [centerX, children]);
19429
19632
  const mobile = window.innerWidth <= 480;
19430
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19633
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19431
19634
  "div",
19432
19635
  {
19433
19636
  ref: slotRef,
@@ -19462,7 +19665,7 @@ function requestValidateTree2(postToParent2, tree) {
19462
19665
  });
19463
19666
  }
19464
19667
  function StyleSwatch({ style }) {
19465
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19668
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19466
19669
  "span",
19467
19670
  {
19468
19671
  "aria-hidden": true,
@@ -19476,35 +19679,35 @@ function StyleSwatch({ style }) {
19476
19679
  );
19477
19680
  }
19478
19681
  function ButtonGlyph() {
19479
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("svg", { width: "18", height: "12", viewBox: "0 0 18 12", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
19480
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("rect", { x: "0.75", y: "0.75", width: "16.5", height: "10.5", rx: "3", stroke: "currentColor", strokeWidth: "1.5" }),
19481
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M5.5 6H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
19682
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("svg", { width: "18", height: "12", viewBox: "0 0 18 12", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
19683
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("rect", { x: "0.75", y: "0.75", width: "16.5", height: "10.5", rx: "3", stroke: "currentColor", strokeWidth: "1.5" }),
19684
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M5.5 6H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
19482
19685
  ] });
19483
19686
  }
19484
19687
  function FeatureGlyph({ mode }) {
19485
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
19486
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("circle", { cx: "8", cy: "8", r: "7.25", stroke: "currentColor", strokeWidth: "1.5" }),
19487
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M5 8.2L7.2 10.3L11 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
19488
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("circle", { cx: "13.5", cy: "13.5", r: "4.25", fill: "var(--ohw-background, #ffffff)", stroke: "var(--ohw-background, #ffffff)", strokeWidth: "1" }),
19489
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("circle", { cx: "13.5", cy: "13.5", r: "3.75", fill: "var(--ohw-background, #ffffff)", stroke: PRIMARY4, strokeWidth: "1" }),
19490
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M11.5 13.5H15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" }),
19491
- mode === "add" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M13.5 11.5V15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" })
19688
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
19689
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "8", cy: "8", r: "7.25", stroke: "currentColor", strokeWidth: "1.5" }),
19690
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M5 8.2L7.2 10.3L11 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
19691
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "13.5", cy: "13.5", r: "4.25", fill: "var(--ohw-background, #ffffff)", stroke: "var(--ohw-background, #ffffff)", strokeWidth: "1" }),
19692
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "13.5", cy: "13.5", r: "3.75", fill: "var(--ohw-background, #ffffff)", stroke: PRIMARY4, strokeWidth: "1" }),
19693
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M11.5 13.5H15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" }),
19694
+ mode === "add" && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M13.5 11.5V15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" })
19492
19695
  ] });
19493
19696
  }
19494
- function ToolbarAction({
19697
+ function ToolbarAction2({
19495
19698
  label,
19496
19699
  onClick,
19497
19700
  disabled = false,
19498
19701
  children
19499
19702
  }) {
19500
- const button = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, onClick, className: "size-8", children });
19501
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19502
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "inline-flex", children: button }) : button }),
19503
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
19703
+ const button = /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, onClick, className: "size-8", children });
19704
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Tooltip, { children: [
19705
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "inline-flex", children: button }) : button }),
19706
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
19504
19707
  ] });
19505
19708
  }
19506
- function ToolbarSeparator() {
19507
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
19709
+ function ToolbarSeparator2() {
19710
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
19508
19711
  }
19509
19712
  function CardStyleMenu({
19510
19713
  value,
@@ -19512,7 +19715,7 @@ function CardStyleMenu({
19512
19715
  disabled = false
19513
19716
  }) {
19514
19717
  const current = PRICING_CARD_STYLES.find((s) => s.value === value) ?? PRICING_CARD_STYLES[0];
19515
- const trigger = /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19718
+ const trigger = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19516
19719
  "button",
19517
19720
  {
19518
19721
  type: "button",
@@ -19523,28 +19726,28 @@ function CardStyleMenu({
19523
19726
  disabled && "cursor-not-allowed opacity-40 hover:bg-transparent"
19524
19727
  ),
19525
19728
  children: [
19526
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StyleSwatch, { style: current.value }),
19527
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "flex-1 text-left", children: current.label }),
19528
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
19729
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(StyleSwatch, { style: current.value }),
19730
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "flex-1 text-left", children: current.label }),
19731
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
19529
19732
  ]
19530
19733
  }
19531
19734
  );
19532
19735
  if (disabled) {
19533
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19534
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-xs text-muted-foreground opacity-50", children: "Card style" }),
19535
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19536
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "inline-flex", children: trigger }) }),
19537
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: "Mark as featured to edit card style" })
19736
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19737
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-xs text-muted-foreground opacity-50", children: "Card style" }),
19738
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Tooltip, { children: [
19739
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "inline-flex", children: trigger }) }),
19740
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: "Mark as featured to edit card style" })
19538
19741
  ] })
19539
19742
  ] });
19540
19743
  }
19541
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19542
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-xs text-muted-foreground", children: "Card style" }),
19543
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DropdownMenu, { modal: false, children: [
19544
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuTrigger, { asChild: true, children: trigger }),
19545
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[152px] min-w-[152px] p-1", children: PRICING_CARD_STYLES.map((style) => {
19744
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19745
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-xs text-muted-foreground", children: "Card style" }),
19746
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(DropdownMenu, { modal: false, children: [
19747
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DropdownMenuTrigger, { asChild: true, children: trigger }),
19748
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[152px] min-w-[152px] p-1", children: PRICING_CARD_STYLES.map((style) => {
19546
19749
  const selected = style.value === value;
19547
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19750
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19548
19751
  DropdownMenuItem,
19549
19752
  {
19550
19753
  role: "menuitemradio",
@@ -19552,9 +19755,9 @@ function CardStyleMenu({
19552
19755
  onSelect: () => onChange(style.value),
19553
19756
  className: cn("grid grid-cols-[18px_1fr_16px] gap-2 py-2 pl-2 pr-2 text-[13px]", selected && "bg-muted"),
19554
19757
  children: [
19555
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StyleSwatch, { style: style.value }),
19556
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: style.label }),
19557
- selected && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Check, { size: 16, className: "text-bridge-primary" })
19758
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(StyleSwatch, { style: style.value }),
19759
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: style.label }),
19760
+ selected && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Check, { size: 16, className: "text-bridge-primary" })
19558
19761
  ]
19559
19762
  },
19560
19763
  style.value
@@ -19584,7 +19787,7 @@ function PricingCardToolbar({
19584
19787
  const style = pricingCardStyle(entry.tree, index);
19585
19788
  const featuredTag = pricingCardFeaturedTag(entry.tree, index);
19586
19789
  const mobile = window.innerWidth <= 480;
19587
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19790
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19588
19791
  "div",
19589
19792
  {
19590
19793
  "data-ohw-bridge": "",
@@ -19596,7 +19799,7 @@ function PricingCardToolbar({
19596
19799
  ),
19597
19800
  style: { maxWidth: mobile ? "calc(100vw - 24px)" : void 0 },
19598
19801
  children: [
19599
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19802
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19600
19803
  "div",
19601
19804
  {
19602
19805
  className: cn(
@@ -19605,8 +19808,8 @@ function PricingCardToolbar({
19605
19808
  mobile ? "flex-wrap justify-center" : "flex-nowrap"
19606
19809
  ),
19607
19810
  children: [
19608
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19609
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19811
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Tooltip, { children: [
19812
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19610
19813
  CustomToolbarButton,
19611
19814
  {
19612
19815
  type: "button",
@@ -19614,8 +19817,8 @@ function PricingCardToolbar({
19614
19817
  "aria-pressed": featuredTag,
19615
19818
  onClick: () => onSetFeaturedTag(!featuredTag),
19616
19819
  className: "group size-8",
19617
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19618
- import_lucide_react9.Star,
19820
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19821
+ import_lucide_react10.Star,
19619
19822
  {
19620
19823
  size: 16,
19621
19824
  fill: "currentColor",
@@ -19629,25 +19832,25 @@ function PricingCardToolbar({
19629
19832
  )
19630
19833
  }
19631
19834
  ) }),
19632
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Unmark featured" : "Mark as featured" })
19835
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Unmark featured" : "Mark as featured" })
19633
19836
  ] }),
19634
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19635
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CardStyleMenu, { value: style, onChange: onSetStyle, disabled: !featuredTag }),
19636
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19637
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Add button", onClick: onAddButton, disabled: buttonCount >= 2, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ButtonGlyph, {}) }),
19638
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Remove button", onClick: onRemoveButton, disabled: buttonCount === 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "relative inline-flex", children: [
19639
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ButtonGlyph, {}),
19640
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Minus, { size: 10, strokeWidth: 2.5, className: "absolute -right-1.5 -top-1.5 rounded-full bg-background text-bridge-primary" })
19837
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarSeparator2, {}),
19838
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CardStyleMenu, { value: style, onChange: onSetStyle, disabled: !featuredTag }),
19839
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarSeparator2, {}),
19840
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Add button", onClick: onAddButton, disabled: buttonCount >= 2, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ButtonGlyph, {}) }),
19841
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Remove button", onClick: onRemoveButton, disabled: buttonCount === 0, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: "relative inline-flex", children: [
19842
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ButtonGlyph, {}),
19843
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Minus, { size: 10, strokeWidth: 2.5, className: "absolute -right-1.5 -top-1.5 rounded-full bg-background text-bridge-primary" })
19641
19844
  ] }) }),
19642
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Add feature", onClick: onAddFeature, disabled: featureCount >= 10, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(FeatureGlyph, { mode: "add" }) }),
19643
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Remove feature", onClick: onRemoveFeature, disabled: featureCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(FeatureGlyph, { mode: "remove" }) }),
19644
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19645
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Add tier", onClick: onAddTier, disabled: collectionCount >= 4, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Plus, { size: 16, style: { pointerEvents: "none" } }) }),
19646
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Remove tier", onClick: onRemoveTier, disabled: collectionCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Minus, { size: 16, style: { pointerEvents: "none" } }) })
19845
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Add feature", onClick: onAddFeature, disabled: featureCount >= 10, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(FeatureGlyph, { mode: "add" }) }),
19846
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Remove feature", onClick: onRemoveFeature, disabled: featureCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(FeatureGlyph, { mode: "remove" }) }),
19847
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarSeparator2, {}),
19848
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Add tier", onClick: onAddTier, disabled: collectionCount >= 4, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Plus, { size: 16, style: { pointerEvents: "none" } }) }),
19849
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Remove tier", onClick: onRemoveTier, disabled: collectionCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Minus, { size: 16, style: { pointerEvents: "none" } }) })
19647
19850
  ]
19648
19851
  }
19649
19852
  ),
19650
- error && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
19853
+ error && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
19651
19854
  ]
19652
19855
  }
19653
19856
  ) });
@@ -19681,7 +19884,7 @@ function AiPricingCardOverlay({
19681
19884
  const overEditable = Boolean(t?.closest?.("[data-ohw-editable], [data-ohw-role], a, button"));
19682
19885
  setHover((prev) => {
19683
19886
  const next = ref && !overEditable ? ref : null;
19684
- return sameRef(prev, next) ? prev : next;
19887
+ return sameRef2(prev, next) ? prev : next;
19685
19888
  });
19686
19889
  };
19687
19890
  const onLeave = () => setHover(null);
@@ -19696,10 +19899,10 @@ function AiPricingCardOverlay({
19696
19899
  const onClick = (e) => {
19697
19900
  const t = e.target;
19698
19901
  if (!(t instanceof Element)) return;
19699
- if (t.closest(CHROME_SELECTOR)) return;
19902
+ if (t.closest(CHROME_SELECTOR2)) return;
19700
19903
  const ref = cardRefFromTarget(t);
19701
- setActive((prev) => sameRef(prev, ref) ? prev : ref);
19702
- if (!ref || !sameRef(activeRef.current, ref)) setError(null);
19904
+ setActive((prev) => sameRef2(prev, ref) ? prev : ref);
19905
+ if (!ref || !sameRef2(activeRef.current, ref)) setError(null);
19703
19906
  };
19704
19907
  document.addEventListener("click", onClick, true);
19705
19908
  return () => document.removeEventListener("click", onClick, true);
@@ -19764,12 +19967,12 @@ function AiPricingCardOverlay({
19764
19967
  const activeEntry = active ? findEntry(active.sectionId) : null;
19765
19968
  const activeCardRect = useLiveCardRect(active);
19766
19969
  const activeGridRect = useLiveCardRect(active, true);
19767
- const hoverRect = useLiveCardRect(hover && !sameRef(hover, active) ? hover : null);
19768
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19769
- hoverRect && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
19770
- activeCardRect && activeGridRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19771
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: activeCardRect, state: "active-top" }),
19772
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(GridToolbarSlot, { gridRect: activeGridRect, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19970
+ const hoverRect = useLiveCardRect(hover && !sameRef2(hover, active) ? hover : null);
19971
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
19972
+ hoverRect && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
19973
+ activeCardRect && activeGridRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
19974
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ItemInteractionLayer, { rect: activeCardRect, state: "active-top" }),
19975
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GridToolbarSlot, { gridRect: activeGridRect, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19773
19976
  PricingCardToolbar,
19774
19977
  {
19775
19978
  entry: activeEntry,
@@ -19797,7 +20000,7 @@ function AiPricingCardOverlay({
19797
20000
  onRemoveFeature: () => runMutation(active.sectionId, (tree) => removePricingCardFeature(tree, active.index))
19798
20001
  }
19799
20002
  ) }),
19800
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
20003
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19801
20004
  "div",
19802
20005
  {
19803
20006
  "data-ohw-bridge": "",
@@ -19819,7 +20022,7 @@ function AiPricingCardOverlay({
19819
20022
 
19820
20023
  // src/ui/ai-section/AiVideoOverlay.tsx
19821
20024
  var import_react12 = require("react");
19822
- var import_lucide_react10 = require("lucide-react");
20025
+ var import_lucide_react11 = require("lucide-react");
19823
20026
 
19824
20027
  // src/lib/ai-video-tree.ts
19825
20028
  function resolveVideoBlock(tree, path) {
@@ -19890,10 +20093,10 @@ function extractYouTubeId(input) {
19890
20093
  }
19891
20094
 
19892
20095
  // src/ui/ai-section/AiVideoOverlay.tsx
19893
- var import_jsx_runtime22 = require("react/jsx-runtime");
20096
+ var import_jsx_runtime23 = require("react/jsx-runtime");
19894
20097
  var PRIMARY5 = "#0885FE";
19895
- var OHW_BLUE2 = `var(--ohw-primary, ${PRIMARY5})`;
19896
- var OHW_BLUE_DARK2 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY5}) 60%, #000000)`;
20098
+ var OHW_BLUE = `var(--ohw-primary, ${PRIMARY5})`;
20099
+ var OHW_BLUE_DARK = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY5}) 60%, #000000)`;
19897
20100
  function findVideoBlockElement(sectionId, path) {
19898
20101
  const sectionEl = findByInstanceId(sectionId);
19899
20102
  if (!sectionEl) return null;
@@ -19953,12 +20156,12 @@ function requestValidateTree3(postToParent2, tree) {
19953
20156
  postToParent2({ type: "ow:ai-validate-tree-request", id, tree });
19954
20157
  });
19955
20158
  }
19956
- function Pill2({
20159
+ function Pill({
19957
20160
  label,
19958
20161
  onClick,
19959
20162
  disabled
19960
20163
  }) {
19961
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20164
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
19962
20165
  "button",
19963
20166
  {
19964
20167
  type: "button",
@@ -19974,7 +20177,7 @@ function Pill2({
19974
20177
  border: "none",
19975
20178
  cursor: disabled ? "default" : "pointer",
19976
20179
  background: "#ffffff",
19977
- color: OHW_BLUE_DARK2,
20180
+ color: OHW_BLUE_DARK,
19978
20181
  opacity: disabled ? 0.4 : 1,
19979
20182
  fontSize: 12,
19980
20183
  lineHeight: "16px",
@@ -19996,7 +20199,7 @@ function VideoUrlInput({ value, onCommit }) {
19996
20199
  setDraft(value ? watchUrl(value) : "");
19997
20200
  setInvalid(false);
19998
20201
  }, [value]);
19999
- const commit = () => {
20202
+ const commit2 = () => {
20000
20203
  const next = draft.trim();
20001
20204
  if (!next) {
20002
20205
  setInvalid(false);
@@ -20017,8 +20220,8 @@ function VideoUrlInput({ value, onCommit }) {
20017
20220
  onCommit(id);
20018
20221
  }
20019
20222
  };
20020
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 4, width: "100%", maxWidth: 280 }, children: [
20021
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
20223
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 4, width: "100%", maxWidth: 280 }, children: [
20224
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
20022
20225
  "div",
20023
20226
  {
20024
20227
  style: {
@@ -20034,12 +20237,12 @@ function VideoUrlInput({ value, onCommit }) {
20034
20237
  boxShadow: invalid ? "inset 0 0 0 1.5px #EF4444" : "none"
20035
20238
  },
20036
20239
  children: [
20037
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20240
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20038
20241
  "span",
20039
20242
  {
20040
20243
  style: {
20041
20244
  flex: "0 0 auto",
20042
- color: OHW_BLUE_DARK2,
20245
+ color: OHW_BLUE_DARK,
20043
20246
  fontSize: 10,
20044
20247
  lineHeight: "16px",
20045
20248
  fontWeight: 700,
@@ -20049,14 +20252,14 @@ function VideoUrlInput({ value, onCommit }) {
20049
20252
  children: "Video"
20050
20253
  }
20051
20254
  ),
20052
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20255
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20053
20256
  "input",
20054
20257
  {
20055
20258
  type: "text",
20056
20259
  inputMode: "url",
20057
20260
  value: draft,
20058
20261
  onChange: (e) => handleChange(e.target.value),
20059
- onBlur: commit,
20262
+ onBlur: commit2,
20060
20263
  onKeyDown: (e) => {
20061
20264
  if (e.key === "Enter") e.currentTarget.blur();
20062
20265
  if (e.key === "Escape") {
@@ -20086,7 +20289,7 @@ function VideoUrlInput({ value, onCommit }) {
20086
20289
  ]
20087
20290
  }
20088
20291
  ),
20089
- invalid && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, textAlign: "center" }, children: "That doesn't look like a YouTube link \u2014 try pasting the video URL again." })
20292
+ invalid && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, textAlign: "center" }, children: "That doesn't look like a YouTube link \u2014 try pasting the video URL again." })
20090
20293
  ] });
20091
20294
  }
20092
20295
  function VideoToolbar({
@@ -20096,7 +20299,7 @@ function VideoToolbar({
20096
20299
  onSetVideoId,
20097
20300
  onRemove
20098
20301
  }) {
20099
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
20302
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
20100
20303
  "div",
20101
20304
  {
20102
20305
  "data-ohw-bridge": "",
@@ -20108,7 +20311,7 @@ function VideoToolbar({
20108
20311
  gap: 6,
20109
20312
  alignItems: "center",
20110
20313
  width: 280,
20111
- background: OHW_BLUE2,
20314
+ background: OHW_BLUE,
20112
20315
  padding: 8,
20113
20316
  borderRadius: 12,
20114
20317
  boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
@@ -20116,9 +20319,9 @@ function VideoToolbar({
20116
20319
  opacity: pending ? 0.6 : 1
20117
20320
  },
20118
20321
  children: [
20119
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VideoUrlInput, { value: videoId, onCommit: onSetVideoId }),
20120
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Pill2, { label: "Remove video", onClick: onRemove, disabled: !videoId }),
20121
- error && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20322
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(VideoUrlInput, { value: videoId, onCommit: onSetVideoId }),
20323
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Pill, { label: "Remove video", onClick: onRemove, disabled: !videoId }),
20324
+ error && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20122
20325
  ]
20123
20326
  }
20124
20327
  );
@@ -20238,8 +20441,8 @@ function AiVideoOverlay({
20238
20441
  const activeBlockRect = useLiveBlockRect(active);
20239
20442
  const TOOLBAR_APPROX_H = 90;
20240
20443
  const toolbarState = activeBlockRect && activeBlockRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
20241
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
20242
- hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20444
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
20445
+ hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20243
20446
  "button",
20244
20447
  {
20245
20448
  type: "button",
@@ -20267,15 +20470,15 @@ function AiVideoOverlay({
20267
20470
  fontSize: 14,
20268
20471
  fontWeight: 700
20269
20472
  },
20270
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Star, { size: 14, style: { pointerEvents: "none" } })
20473
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Star, { size: 14, style: { pointerEvents: "none" } })
20271
20474
  }
20272
20475
  ),
20273
- activeBlockRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20476
+ activeBlockRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20274
20477
  ItemInteractionLayer,
20275
20478
  {
20276
20479
  rect: activeBlockRect,
20277
20480
  state: toolbarState,
20278
- toolbar: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20481
+ toolbar: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20279
20482
  VideoToolbar,
20280
20483
  {
20281
20484
  videoId: videoIdAt(activeEntry.tree, active.path),
@@ -20299,7 +20502,7 @@ function AiVideoOverlay({
20299
20502
 
20300
20503
  // src/ui/ai-section/AiFadeCarouselOverlay.tsx
20301
20504
  var import_react13 = require("react");
20302
- var import_lucide_react11 = require("lucide-react");
20505
+ var import_lucide_react12 = require("lucide-react");
20303
20506
 
20304
20507
  // src/lib/ai-carousel-tree.ts
20305
20508
  function isCarouselBlock(node) {
@@ -20350,10 +20553,10 @@ function setCarouselAutoplay(tree, on) {
20350
20553
  }
20351
20554
 
20352
20555
  // src/ui/ai-section/AiFadeCarouselOverlay.tsx
20353
- var import_jsx_runtime23 = require("react/jsx-runtime");
20556
+ var import_jsx_runtime24 = require("react/jsx-runtime");
20354
20557
  var PRIMARY6 = "#0885FE";
20355
- var OHW_BLUE3 = `var(--ohw-primary, ${PRIMARY6})`;
20356
- var OHW_BLUE_DARK3 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY6}) 60%, #000000)`;
20558
+ var OHW_BLUE2 = `var(--ohw-primary, ${PRIMARY6})`;
20559
+ var OHW_BLUE_DARK2 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY6}) 60%, #000000)`;
20357
20560
  function findCarouselItemElement(sectionId, index) {
20358
20561
  const sectionEl = findByInstanceId(sectionId);
20359
20562
  if (!sectionEl) return null;
@@ -20432,8 +20635,8 @@ function requestAssetPick2(postToParent2) {
20432
20635
  postToParent2({ type: "ow:asset-pick", id });
20433
20636
  });
20434
20637
  }
20435
- function Pill3({ label, onClick, disabled }) {
20436
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20638
+ function Pill2({ label, onClick, disabled }) {
20639
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20437
20640
  "button",
20438
20641
  {
20439
20642
  type: "button",
@@ -20449,7 +20652,7 @@ function Pill3({ label, onClick, disabled }) {
20449
20652
  border: "none",
20450
20653
  cursor: disabled ? "default" : "pointer",
20451
20654
  background: "#ffffff",
20452
- color: OHW_BLUE_DARK3,
20655
+ color: OHW_BLUE_DARK2,
20453
20656
  opacity: disabled ? 0.4 : 1,
20454
20657
  fontSize: 12,
20455
20658
  lineHeight: "16px",
@@ -20467,7 +20670,7 @@ function TogglePill({
20467
20670
  onToggle,
20468
20671
  disabled
20469
20672
  }) {
20470
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
20673
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
20471
20674
  "button",
20472
20675
  {
20473
20676
  type: "button",
@@ -20486,7 +20689,7 @@ function TogglePill({
20486
20689
  border: "none",
20487
20690
  cursor: disabled ? "default" : "pointer",
20488
20691
  background: "#ffffff",
20489
- color: OHW_BLUE_DARK3,
20692
+ color: OHW_BLUE_DARK2,
20490
20693
  opacity: disabled ? 0.4 : 1,
20491
20694
  fontSize: 12,
20492
20695
  lineHeight: "16px",
@@ -20496,7 +20699,7 @@ function TogglePill({
20496
20699
  },
20497
20700
  children: [
20498
20701
  label,
20499
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20702
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20500
20703
  "span",
20501
20704
  {
20502
20705
  "aria-hidden": true,
@@ -20509,10 +20712,10 @@ function TogglePill({
20509
20712
  // Off is a grey track, not a washed-out blue: the pill's own white background already
20510
20713
  // carries the brand colour in the text, and a pale blue track next to it read as
20511
20714
  // "disabled control" rather than "switch that is off".
20512
- background: on ? OHW_BLUE_DARK3 : "#CBD5E1",
20715
+ background: on ? OHW_BLUE_DARK2 : "#CBD5E1",
20513
20716
  transition: "background 120ms ease"
20514
20717
  },
20515
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20718
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20516
20719
  "span",
20517
20720
  {
20518
20721
  style: {
@@ -20541,7 +20744,7 @@ function CarouselSlideToolbar({
20541
20744
  onChangeImage,
20542
20745
  onToggleAutoplay
20543
20746
  }) {
20544
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
20747
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
20545
20748
  "div",
20546
20749
  {
20547
20750
  "data-ohw-bridge": "",
@@ -20552,7 +20755,7 @@ function CarouselSlideToolbar({
20552
20755
  flexDirection: "column",
20553
20756
  gap: 6,
20554
20757
  alignItems: "center",
20555
- background: OHW_BLUE3,
20758
+ background: OHW_BLUE2,
20556
20759
  padding: 8,
20557
20760
  borderRadius: 12,
20558
20761
  boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
@@ -20564,11 +20767,11 @@ function CarouselSlideToolbar({
20564
20767
  pointerEvents: pending ? "none" : "auto"
20565
20768
  },
20566
20769
  children: [
20567
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
20568
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Pill3, { label: "Change Image", onClick: onChangeImage }),
20569
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(TogglePill, { label: "Autoplay", on: autoplay, onToggle: onToggleAutoplay })
20770
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
20771
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Pill2, { label: "Change Image", onClick: onChangeImage }),
20772
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(TogglePill, { label: "Autoplay", on: autoplay, onToggle: onToggleAutoplay })
20570
20773
  ] }),
20571
- error && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20774
+ error && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20572
20775
  ]
20573
20776
  }
20574
20777
  );
@@ -20708,8 +20911,8 @@ function AiFadeCarouselOverlay({
20708
20911
  const activeItemRect = useLiveItemRect2(active);
20709
20912
  const TOOLBAR_APPROX_H = 90;
20710
20913
  const toolbarState = activeItemRect && activeItemRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
20711
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
20712
- hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20914
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
20915
+ hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20713
20916
  "button",
20714
20917
  {
20715
20918
  type: "button",
@@ -20738,10 +20941,10 @@ function AiFadeCarouselOverlay({
20738
20941
  fontSize: 14,
20739
20942
  fontWeight: 700
20740
20943
  },
20741
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Star, { size: 14, style: { pointerEvents: "none" } })
20944
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.Star, { size: 14, style: { pointerEvents: "none" } })
20742
20945
  }
20743
20946
  ),
20744
- swapping && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20947
+ swapping && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20745
20948
  "div",
20746
20949
  {
20747
20950
  ref: skeletonRef,
@@ -20759,15 +20962,15 @@ function AiFadeCarouselOverlay({
20759
20962
  zIndex: 2147483646,
20760
20963
  pointerEvents: "none"
20761
20964
  },
20762
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("style", { children: MEDIA_SKELETON_CSS })
20965
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("style", { children: MEDIA_SKELETON_CSS })
20763
20966
  }
20764
20967
  ),
20765
- activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20968
+ activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20766
20969
  ItemInteractionLayer,
20767
20970
  {
20768
20971
  rect: activeItemRect,
20769
20972
  state: toolbarState,
20770
- toolbar: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20973
+ toolbar: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20771
20974
  CarouselSlideToolbar,
20772
20975
  {
20773
20976
  pending,
@@ -20985,113 +21188,6 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
20985
21188
  // src/ui/link-modal/LinkPopover.tsx
20986
21189
  var import_react17 = require("react");
20987
21190
 
20988
- // src/ui/dialog.tsx
20989
- var React9 = __toESM(require("react"), 1);
20990
- var import_radix_ui6 = require("radix-ui");
20991
- var import_lucide_react12 = require("lucide-react");
20992
- var import_jsx_runtime24 = require("react/jsx-runtime");
20993
- function Dialog2({
20994
- ...props
20995
- }) {
20996
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_radix_ui6.Dialog.Root, { "data-slot": "dialog", ...props });
20997
- }
20998
- function DialogPortal({
20999
- ...props
21000
- }) {
21001
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_radix_ui6.Dialog.Portal, { ...props });
21002
- }
21003
- function DialogOverlay({
21004
- className,
21005
- ...props
21006
- }) {
21007
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
21008
- import_radix_ui6.Dialog.Overlay,
21009
- {
21010
- "data-slot": "dialog-overlay",
21011
- "data-ohw-link-modal-root": "",
21012
- className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
21013
- ...props
21014
- }
21015
- );
21016
- }
21017
- var DialogContent = React9.forwardRef(
21018
- ({ className, children, showCloseButton = true, container, ...props }, ref) => {
21019
- const positionMode = container ? "absolute" : "fixed";
21020
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(DialogPortal, { container: container ?? void 0, children: [
21021
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
21022
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
21023
- import_radix_ui6.Dialog.Content,
21024
- {
21025
- ref,
21026
- "data-slot": "dialog-content",
21027
- className: cn(
21028
- positionMode,
21029
- "left-1/2 top-1/2 z-[2147483647] flex w-full max-w-[448px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-visible",
21030
- "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
21031
- container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
21032
- className
21033
- ),
21034
- onMouseDown: (e) => e.stopPropagation(),
21035
- ...props,
21036
- children: [
21037
- children,
21038
- showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
21039
- import_radix_ui6.Dialog.Close,
21040
- {
21041
- type: "button",
21042
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
21043
- "aria-label": "Close",
21044
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.X, { size: 16, "aria-hidden": true })
21045
- }
21046
- ) : null
21047
- ]
21048
- }
21049
- )
21050
- ] });
21051
- }
21052
- );
21053
- DialogContent.displayName = import_radix_ui6.Dialog.Content.displayName;
21054
- function DialogHeader({
21055
- className,
21056
- ...props
21057
- }) {
21058
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
21059
- }
21060
- function DialogFooter({
21061
- className,
21062
- ...props
21063
- }) {
21064
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
21065
- "div",
21066
- {
21067
- className: cn("flex items-center justify-end gap-2", className),
21068
- ...props
21069
- }
21070
- );
21071
- }
21072
- var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
21073
- import_radix_ui6.Dialog.Title,
21074
- {
21075
- ref,
21076
- className: cn(
21077
- "text-lg font-semibold leading-none tracking-tight text-card-foreground",
21078
- className
21079
- ),
21080
- ...props
21081
- }
21082
- ));
21083
- DialogTitle.displayName = import_radix_ui6.Dialog.Title.displayName;
21084
- var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
21085
- import_radix_ui6.Dialog.Description,
21086
- {
21087
- ref,
21088
- className: cn("text-sm text-muted-foreground", className),
21089
- ...props
21090
- }
21091
- ));
21092
- DialogDescription.displayName = import_radix_ui6.Dialog.Description.displayName;
21093
- var DialogClose = import_radix_ui6.Dialog.Close;
21094
-
21095
21191
  // src/ui/link-modal/LinkEditorPanel.tsx
21096
21192
  var import_lucide_react16 = require("lucide-react");
21097
21193
 
@@ -22195,6 +22291,7 @@ function shouldUseDevFixtures() {
22195
22291
  function getLogoElement(el) {
22196
22292
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
22197
22293
  if (marked) return marked;
22294
+ if (el.closest('[data-ohw-editable="icon"]')) return null;
22198
22295
  const root = el.closest("nav, [data-ohw-nav-root], footer");
22199
22296
  if (!root) return null;
22200
22297
  const anchor = el.closest("a");
@@ -23889,6 +23986,102 @@ function deleteSelectedNavFooterItem(deps) {
23889
23986
  return false;
23890
23987
  }
23891
23988
 
23989
+ // src/lib/nav-page-sync.ts
23990
+ function labelKeyFor(hrefKey) {
23991
+ return hrefKey.replace(/-href$/, "-label");
23992
+ }
23993
+ function isPageLinkKey(key) {
23994
+ return isNavbarHrefKey(key) || isFooterHrefKey(key);
23995
+ }
23996
+ function currentHref(key, anchor, content) {
23997
+ const stored = content[key];
23998
+ if (typeof stored === "string" && stored.length > 0) return stored;
23999
+ return anchor.getAttribute("href") ?? "";
24000
+ }
24001
+ function normalizePath2(value) {
24002
+ if (value.length > 1 && value.endsWith("/")) return value.slice(0, -1);
24003
+ return value;
24004
+ }
24005
+ function pointsAt(href, path) {
24006
+ const normalized = normalizePath2(href);
24007
+ const target = normalizePath2(path);
24008
+ if (normalized === target) return true;
24009
+ return (normalized.startsWith(`${target}#`) || normalized.startsWith(`${target}?`)) && target !== "/";
24010
+ }
24011
+ function retargetHref(href, oldPath, newPath) {
24012
+ const normalized = normalizePath2(href);
24013
+ const target = normalizePath2(oldPath);
24014
+ if (normalized === target) return newPath;
24015
+ return `${newPath}${normalized.slice(target.length)}`;
24016
+ }
24017
+ function setLabelText(key, text) {
24018
+ document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`).forEach((el) => {
24019
+ el.textContent = text;
24020
+ });
24021
+ }
24022
+ function commit(delta, deps) {
24023
+ if (Object.keys(delta).length === 0) return;
24024
+ deps.setEditContent({ ...deps.getEditContent(), ...delta });
24025
+ deps.postToParent({
24026
+ type: "ow:change",
24027
+ nodes: Object.entries(delta).map(([key, text]) => ({ key, text }))
24028
+ });
24029
+ }
24030
+ function navLinksTo(path, content) {
24031
+ return Array.from(document.querySelectorAll("[data-ohw-href-key]")).filter((anchor) => {
24032
+ const key = anchor.getAttribute("data-ohw-href-key");
24033
+ return isNavbarHrefKey(key) && pointsAt(currentHref(key, anchor, content), path);
24034
+ }).map((anchor) => anchor.getAttribute("data-ohw-href-key"));
24035
+ }
24036
+ function addNavLinkForPage(page, deps) {
24037
+ const content = deps.getEditContent();
24038
+ if (navLinksTo(page.path, content).length > 0) return false;
24039
+ const roots = listNavbarRootItems();
24040
+ const afterAnchor = roots[roots.length - 1] ?? null;
24041
+ let inserted;
24042
+ try {
24043
+ inserted = insertNavbarItem(page.path, page.title, afterAnchor);
24044
+ } catch {
24045
+ return false;
24046
+ }
24047
+ const { hrefKey, labelKey, index } = inserted;
24048
+ deps.applyLinkByKey(hrefKey, page.path);
24049
+ setLabelText(labelKey, page.title);
24050
+ commit(
24051
+ {
24052
+ [hrefKey]: page.path,
24053
+ [labelKey]: page.title,
24054
+ [NAV_COUNT_KEY]: String(index + 1),
24055
+ [NAV_ORDER_KEY]: getNavbarOrderJson()
24056
+ },
24057
+ deps
24058
+ );
24059
+ return true;
24060
+ }
24061
+ function retargetPageLinks(change, deps) {
24062
+ const { oldPath, newPath, oldTitle, newTitle } = change;
24063
+ if (!oldPath || !newPath || normalizePath2(oldPath) === normalizePath2(newPath)) return 0;
24064
+ const content = deps.getEditContent();
24065
+ const delta = {};
24066
+ for (const anchor of Array.from(document.querySelectorAll("[data-ohw-href-key]"))) {
24067
+ const hrefKey = anchor.getAttribute("data-ohw-href-key");
24068
+ if (!isPageLinkKey(hrefKey)) continue;
24069
+ const href = currentHref(hrefKey, anchor, content);
24070
+ if (!pointsAt(href, oldPath)) continue;
24071
+ const nextHref = retargetHref(href, oldPath, newPath);
24072
+ deps.applyLinkByKey(hrefKey, nextHref);
24073
+ delta[hrefKey] = nextHref;
24074
+ if (!oldTitle || !newTitle) continue;
24075
+ const labelKey = labelKeyFor(hrefKey);
24076
+ const label = content[labelKey] ?? anchor.querySelector('[data-ohw-editable="text"]')?.textContent ?? "";
24077
+ if (label.trim() !== oldTitle) continue;
24078
+ setLabelText(labelKey, newTitle);
24079
+ delta[labelKey] = newTitle;
24080
+ }
24081
+ commit(delta, deps);
24082
+ return Object.keys(delta).filter((key) => key.endsWith("-href")).length;
24083
+ }
24084
+
23892
24085
  // src/ui/navbar-container-chrome.tsx
23893
24086
  var import_lucide_react21 = require("lucide-react");
23894
24087
  var import_jsx_runtime37 = require("react/jsx-runtime");
@@ -24031,6 +24224,7 @@ function applyEditableImageSrc(img, url) {
24031
24224
  img.removeAttribute("srcset");
24032
24225
  img.removeAttribute("sizes");
24033
24226
  if (img.loading === "lazy") img.loading = "eager";
24227
+ releaseTemplateLogoIfReplaced(img, url);
24034
24228
  img.src = url;
24035
24229
  }
24036
24230
  function fadeInBgImage(el, url, onReady) {
@@ -24775,7 +24969,7 @@ function getNavigationSelectionParent(el) {
24775
24969
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
24776
24970
  return resolveFooterLinksContainerFrom(el);
24777
24971
  }
24778
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
24972
+ 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)) {
24779
24973
  return getNavigationRoot(el);
24780
24974
  }
24781
24975
  return null;
@@ -24997,7 +25191,6 @@ var ICONS = {
24997
25191
  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"/>',
24998
25192
  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"/>'
24999
25193
  };
25000
- var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
25001
25194
  var SELECTION_CHROME_GAP2 = 4;
25002
25195
  var TOOLBAR_STROKE_GAP2 = 4;
25003
25196
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -25095,7 +25288,7 @@ function applyVisibleViewport(el, parentScroll) {
25095
25288
  el.style.top = `${top}px`;
25096
25289
  el.style.height = `${height}px`;
25097
25290
  }
25098
- var TOOLBAR_EDGE_MARGIN3 = 4;
25291
+ var TOOLBAR_EDGE_MARGIN4 = 4;
25099
25292
  function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
25100
25293
  const isAbove = transform.includes("translateY(-100%)");
25101
25294
  let visualTop = isAbove ? top - approxH : top;
@@ -25122,8 +25315,8 @@ function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
25122
25315
  function clampToolbarLeft(centerX, toolbarWidth) {
25123
25316
  const half = toolbarWidth / 2;
25124
25317
  return Math.max(
25125
- TOOLBAR_EDGE_MARGIN3 + half,
25126
- Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN3 - half)
25318
+ TOOLBAR_EDGE_MARGIN4 + half,
25319
+ Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN4 - half)
25127
25320
  );
25128
25321
  }
25129
25322
  function calcToolbarPos(rect, parentScroll, toolbarW = 306) {
@@ -26003,21 +26196,6 @@ function OhhwellsBridge() {
26003
26196
  const [isItemDragging, setIsItemDragging] = (0, import_react21.useState)(false);
26004
26197
  const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react21.useState)(false);
26005
26198
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
26006
- const [floatingPanel, setFloatingPanel] = (0, import_react21.useState)(null);
26007
- const floatingPanelOpenRef = (0, import_react21.useRef)(false);
26008
- floatingPanelOpenRef.current = floatingPanel !== null;
26009
- const floatingPanelRef = (0, import_react21.useRef)(null);
26010
- floatingPanelRef.current = floatingPanel;
26011
- const [floatingPanelPos, setFloatingPanelPos] = (0, import_react21.useState)(null);
26012
- const buttonCollectionDragRef = (0, import_react21.useRef)(null);
26013
- const buttonDropInsertIndexRef = (0, import_react21.useRef)(null);
26014
- const [buttonDropSlots, setButtonDropSlots] = (0, import_react21.useState)([]);
26015
- const [activeButtonDropIndex, setActiveButtonDropIndex] = (0, import_react21.useState)(null);
26016
- const buttonPointerDragRef = (0, import_react21.useRef)(null);
26017
- const [buttonStyleTick, setButtonStyleTick] = (0, import_react21.useState)(0);
26018
- const [logoSizeDraft, setLogoSizeDraft] = (0, import_react21.useState)(null);
26019
- const [editorViewport, setEditorViewport] = (0, import_react21.useState)("desktop");
26020
- const [parentScrollSnap, setParentScrollSnap] = (0, import_react21.useState)(null);
26021
26199
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react21.useState)(null);
26022
26200
  const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react21.useState)(null);
26023
26201
  const footerDragRef = (0, import_react21.useRef)(null);
@@ -26043,6 +26221,21 @@ function OhhwellsBridge() {
26043
26221
  const stylesRef = (0, import_react21.useRef)("");
26044
26222
  const appliedDrawerRef = (0, import_react21.useRef)(null);
26045
26223
  const pendingDeleteUndoRef = (0, import_react21.useRef)(null);
26224
+ const [floatingPanel, setFloatingPanel] = (0, import_react21.useState)(null);
26225
+ const floatingPanelOpenRef = (0, import_react21.useRef)(false);
26226
+ const floatingPanelRef = (0, import_react21.useRef)(null);
26227
+ floatingPanelRef.current = floatingPanel;
26228
+ const setFloatingPanelRef = (0, import_react21.useRef)(setFloatingPanel);
26229
+ const [floatingPanelPos, setFloatingPanelPos] = (0, import_react21.useState)(null);
26230
+ const buttonCollectionDragRef = (0, import_react21.useRef)(null);
26231
+ const buttonDropInsertIndexRef = (0, import_react21.useRef)(null);
26232
+ const [buttonDropSlots, setButtonDropSlots] = (0, import_react21.useState)([]);
26233
+ const [activeButtonDropIndex, setActiveButtonDropIndex] = (0, import_react21.useState)(null);
26234
+ const buttonPointerDragRef = (0, import_react21.useRef)(null);
26235
+ const [buttonStyleTick, setButtonStyleTick] = (0, import_react21.useState)(0);
26236
+ const [logoSizeDraft, setLogoSizeDraft] = (0, import_react21.useState)(null);
26237
+ const [editorViewport, setEditorViewport] = (0, import_react21.useState)("desktop");
26238
+ const [parentScrollSnap, setParentScrollSnap] = (0, import_react21.useState)(null);
26046
26239
  const [sitePages, setSitePages] = (0, import_react21.useState)([]);
26047
26240
  const collectionItemsRef = (0, import_react21.useRef)(/* @__PURE__ */ new Map());
26048
26241
  const [sectionsByPath, setSectionsByPath] = (0, import_react21.useState)({});
@@ -26052,7 +26245,18 @@ function OhhwellsBridge() {
26052
26245
  const linkPopoverOpenRef = (0, import_react21.useRef)(false);
26053
26246
  const linkPopoverGraceUntilRef = (0, import_react21.useRef)(0);
26054
26247
  setLinkPopoverRef.current = setLinkPopover;
26248
+ setFloatingPanelRef.current = setFloatingPanel;
26055
26249
  linkPopoverSessionRef.current = linkPopover;
26250
+ floatingPanelOpenRef.current = Boolean(floatingPanel);
26251
+ (0, import_react21.useEffect)(() => {
26252
+ const syncViewport = () => {
26253
+ const next = window.innerWidth <= 480 ? "mobile" : "desktop";
26254
+ setEditorViewport((prev) => prev === next ? prev : next);
26255
+ };
26256
+ syncViewport();
26257
+ window.addEventListener("resize", syncViewport);
26258
+ return () => window.removeEventListener("resize", syncViewport);
26259
+ }, []);
26056
26260
  (0, import_react21.useEffect)(() => {
26057
26261
  setSectionOrderPersistHandler((entries) => {
26058
26262
  const currentPath = window.location.pathname;
@@ -26201,6 +26405,43 @@ function OhhwellsBridge() {
26201
26405
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
26202
26406
  return () => window.removeEventListener("message", onSitePages);
26203
26407
  }, [isEditMode, postToParent2]);
26408
+ (0, import_react21.useEffect)(() => {
26409
+ if (!isEditMode) return;
26410
+ const deps = {
26411
+ getEditContent: () => editContentRef.current,
26412
+ setEditContent: (next) => {
26413
+ editContentRef.current = next;
26414
+ },
26415
+ applyLinkByKey,
26416
+ postToParent: postToParent2
26417
+ };
26418
+ const onPageMessage = (e) => {
26419
+ if (e.data?.type === "ow:page-created") {
26420
+ const { path, title } = e.data;
26421
+ if (typeof path !== "string" || !path) return;
26422
+ if (addNavLinkForPage({ path, title: typeof title === "string" && title ? title : path }, deps)) {
26423
+ enforceLinkHrefs();
26424
+ }
26425
+ return;
26426
+ }
26427
+ if (e.data?.type === "ow:page-renamed") {
26428
+ const { oldPath, newPath, oldTitle, newTitle } = e.data;
26429
+ if (typeof oldPath !== "string" || typeof newPath !== "string") return;
26430
+ const moved = retargetPageLinks(
26431
+ {
26432
+ oldPath,
26433
+ newPath,
26434
+ oldTitle: typeof oldTitle === "string" ? oldTitle : void 0,
26435
+ newTitle: typeof newTitle === "string" ? newTitle : void 0
26436
+ },
26437
+ deps
26438
+ );
26439
+ if (moved > 0) enforceLinkHrefs();
26440
+ }
26441
+ };
26442
+ window.addEventListener("message", onPageMessage);
26443
+ return () => window.removeEventListener("message", onPageMessage);
26444
+ }, [isEditMode, postToParent2]);
26204
26445
  (0, import_react21.useEffect)(() => {
26205
26446
  if (!isEditMode || shouldUseDevFixtures()) return;
26206
26447
  void loadAllSectionsManifest().then((manifest) => {
@@ -27749,6 +27990,7 @@ function OhhwellsBridge() {
27749
27990
  applyAiFooterFromContent(content);
27750
27991
  if (typeof content[STYLE_STORE_KEY] === "string") {
27751
27992
  stylesRef.current = content[STYLE_STORE_KEY];
27993
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
27752
27994
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
27753
27995
  }
27754
27996
  applyBrandChrome(content);
@@ -27759,12 +28001,12 @@ function OhhwellsBridge() {
27759
28001
  if (key === "__ohw_sections") continue;
27760
28002
  if (key === AI_SECTIONS_KEY) continue;
27761
28003
  if (key === AI_FOOTER_KEY) continue;
28004
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
28005
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
27762
28006
  if (key === BRAND_KIT_KEY) continue;
27763
28007
  if (key === STYLE_STORE_KEY) continue;
27764
28008
  if (BRAND_CHROME_KEYS.has(key)) continue;
27765
- if (key === LOGO_PLACEHOLDER_KEY) continue;
27766
28009
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
27767
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
27768
28010
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
27769
28011
  continue;
27770
28012
  }
@@ -27995,17 +28237,18 @@ function OhhwellsBridge() {
27995
28237
  }
27996
28238
  applyAiFooterFromContent(content);
27997
28239
  if (typeof content[STYLE_STORE_KEY] === "string") {
28240
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
27998
28241
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
27999
28242
  }
28000
28243
  for (const [key, val] of Object.entries(content)) {
28001
28244
  if (key === "__ohw_sections") continue;
28002
28245
  if (key === AI_SECTIONS_KEY) continue;
28003
28246
  if (key === AI_FOOTER_KEY) continue;
28247
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
28248
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
28004
28249
  if (key === BRAND_KIT_KEY) continue;
28005
28250
  if (key === STYLE_STORE_KEY) continue;
28006
28251
  if (BRAND_CHROME_KEYS.has(key)) continue;
28007
- if (key === LOGO_PLACEHOLDER_KEY) continue;
28008
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
28009
28252
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
28010
28253
  continue;
28011
28254
  }
@@ -28272,26 +28515,11 @@ function OhhwellsBridge() {
28272
28515
  const t2 = setTimeout(measure, 500);
28273
28516
  const ro = new ResizeObserver(schedule);
28274
28517
  ro.observe(document.body);
28275
- let lastWidth = window.innerWidth;
28276
- let resizeTimers = [];
28277
- const clearResizeTimers = () => {
28278
- resizeTimers.forEach(clearTimeout);
28279
- resizeTimers = [];
28280
- };
28281
- const handleResize = () => {
28282
- if (window.innerWidth === lastWidth) return;
28283
- lastWidth = window.innerWidth;
28284
- clearResizeTimers();
28285
- resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
28286
- };
28287
- window.addEventListener("resize", handleResize);
28288
28518
  return () => {
28289
28519
  clearTimeout(t1);
28290
28520
  clearTimeout(t2);
28291
28521
  if (raf != null) cancelAnimationFrame(raf);
28292
28522
  ro.disconnect();
28293
- clearResizeTimers();
28294
- window.removeEventListener("resize", handleResize);
28295
28523
  };
28296
28524
  }, [pathname, isEditMode, postToParent2]);
28297
28525
  (0, import_react21.useEffect)(() => {
@@ -28588,6 +28816,13 @@ function OhhwellsBridge() {
28588
28816
  if (target.closest("[data-ohw-state-toggle]")) return;
28589
28817
  if (target.closest("[data-ohw-max-badge]")) return;
28590
28818
  if (isInsideLinkEditor(target)) return;
28819
+ if (isInsideFloatingPanel(target)) return;
28820
+ if (target.closest("[data-ohw-form-toolbar]")) return;
28821
+ if (target.closest(
28822
+ '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
28823
+ )) {
28824
+ return;
28825
+ }
28591
28826
  if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
28592
28827
  clearMediaSelectionRef.current();
28593
28828
  }
@@ -28611,8 +28846,6 @@ function OhhwellsBridge() {
28611
28846
  return;
28612
28847
  }
28613
28848
  }
28614
- if (isInsideFloatingPanel(target)) return;
28615
- if (target.closest("[data-ohw-form-toolbar]")) return;
28616
28849
  const collectionItem = matchCollectionItemClick(target, collectionItemsRef.current);
28617
28850
  if (collectionItem) {
28618
28851
  e.preventDefault();
@@ -28624,11 +28857,6 @@ function OhhwellsBridge() {
28624
28857
  });
28625
28858
  return;
28626
28859
  }
28627
- if (target.closest(
28628
- '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
28629
- )) {
28630
- return;
28631
- }
28632
28860
  {
28633
28861
  const formEl = getFormElement(target);
28634
28862
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -28782,9 +29010,13 @@ function OhhwellsBridge() {
28782
29010
  if (isIconEditable(editable) && !getSocialItem(editable)) {
28783
29011
  e.preventDefault();
28784
29012
  e.stopPropagation();
28785
- selectIconElementRef.current(editable);
28786
29013
  const aiIcon = editable.hasAttribute("data-ai-icon");
28787
29014
  const featureIcon = aiIcon && /\.features\.\d+\.icon$/.test(editable.dataset.ohwKey ?? "");
29015
+ if (aiIcon) {
29016
+ aiSectionApiRef.current?.selectFromElement(editable);
29017
+ } else {
29018
+ selectIconElementRef.current(editable);
29019
+ }
28788
29020
  postToParentRef.current({
28789
29021
  type: "ow:icon-pick",
28790
29022
  key: editable.dataset.ohwKey ?? "",
@@ -29012,6 +29244,9 @@ function OhhwellsBridge() {
29012
29244
  setHoveredItemRect(null);
29013
29245
  hoveredNavContainerRef.current = null;
29014
29246
  setHoveredNavContainerRect(null);
29247
+ siblingHintElRef.current = null;
29248
+ setSiblingHintRect(null);
29249
+ setSiblingHintRects([]);
29015
29250
  return;
29016
29251
  }
29017
29252
  {
@@ -29132,7 +29367,6 @@ function OhhwellsBridge() {
29132
29367
  hoveredNavContainerRef.current = null;
29133
29368
  setHoveredNavContainerRect(null);
29134
29369
  hoveredItemElRef.current = editable;
29135
- setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
29136
29370
  }
29137
29371
  }
29138
29372
  }
@@ -29842,7 +30076,11 @@ function OhhwellsBridge() {
29842
30076
  };
29843
30077
  const handleMouseMove = (e) => {
29844
30078
  const { clientX, clientY } = e;
29845
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
30079
+ if (pointOwnedByFloatingPanel(clientX, clientY)) {
30080
+ dismissImageHover();
30081
+ clearImageHover();
30082
+ return;
30083
+ }
29846
30084
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) {
29847
30085
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
29848
30086
  formHoverElRef.current = null;
@@ -29871,7 +30109,12 @@ function OhhwellsBridge() {
29871
30109
  setHoveredItemRect(null);
29872
30110
  hoveredNavContainerRef.current = null;
29873
30111
  setHoveredNavContainerRect(null);
30112
+ siblingHintElRef.current = null;
30113
+ setSiblingHintRect(null);
30114
+ setSiblingHintRects([]);
29874
30115
  dismissImageHover();
30116
+ clearImageHover();
30117
+ setSectionGap(null);
29875
30118
  return;
29876
30119
  }
29877
30120
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -29883,7 +30126,11 @@ function OhhwellsBridge() {
29883
30126
  if (e.data?.type !== "ow:pointer-sync") return;
29884
30127
  const { clientX, clientY } = e.data;
29885
30128
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
29886
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
30129
+ if (pointOwnedByFloatingPanel(clientX, clientY)) {
30130
+ dismissImageHover();
30131
+ clearImageHover();
30132
+ return;
30133
+ }
29887
30134
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) return;
29888
30135
  if (probeSocialsRowAt(clientX, clientY)) return;
29889
30136
  probeSectionGapAt(clientX, clientY);
@@ -30263,6 +30510,7 @@ function OhhwellsBridge() {
30263
30510
  applyAiFooterFromContent(content);
30264
30511
  if (typeof content[STYLE_STORE_KEY] === "string") {
30265
30512
  stylesRef.current = content[STYLE_STORE_KEY];
30513
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
30266
30514
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
30267
30515
  }
30268
30516
  applyBrandChrome(content);
@@ -30277,12 +30525,12 @@ function OhhwellsBridge() {
30277
30525
  }
30278
30526
  if (key === AI_SECTIONS_KEY) continue;
30279
30527
  if (key === AI_FOOTER_KEY) continue;
30528
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
30529
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
30280
30530
  if (key === BRAND_KIT_KEY) continue;
30281
30531
  if (key === STYLE_STORE_KEY) continue;
30282
30532
  if (BRAND_CHROME_KEYS.has(key)) continue;
30283
- if (key === LOGO_PLACEHOLDER_KEY) continue;
30284
30533
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
30285
- if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
30286
30534
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
30287
30535
  continue;
30288
30536
  }
@@ -30918,6 +31166,12 @@ function OhhwellsBridge() {
30918
31166
  closeLinkPopoverRef.current();
30919
31167
  return;
30920
31168
  }
31169
+ if (floatingPanelOpenRef.current) {
31170
+ setFloatingPanelRef.current(null);
31171
+ deselectRef.current();
31172
+ deactivateRef.current();
31173
+ return;
31174
+ }
30921
31175
  deselectRef.current();
30922
31176
  deactivateRef.current();
30923
31177
  clearMediaSelectionRef.current();
@@ -31747,10 +32001,10 @@ function OhhwellsBridge() {
31747
32001
  window.removeEventListener("message", handleDuplicateSection);
31748
32002
  window.removeEventListener("message", handleDeactivate);
31749
32003
  window.removeEventListener("message", handleSelectNav);
31750
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
31751
32004
  window.removeEventListener("message", handleToastAction);
31752
32005
  window.removeEventListener("message", handleFormCount);
31753
32006
  window.removeEventListener("message", handleUiEscape);
32007
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
31754
32008
  autoSaveTimers.current.forEach(clearTimeout);
31755
32009
  autoSaveTimers.current.clear();
31756
32010
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
@@ -32044,7 +32298,7 @@ function OhhwellsBridge() {
32044
32298
  postToParent2({
32045
32299
  type: "ow:ready",
32046
32300
  version: "1",
32047
- bridgeVersion: "0.1.157",
32301
+ bridgeVersion: "0.1.158-next.422",
32048
32302
  path: pathname,
32049
32303
  nodes: collectEditableNodes(editContentRef.current),
32050
32304
  sections
@@ -33251,6 +33505,7 @@ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey
33251
33505
  OhwLoaderSurface,
33252
33506
  SITE_OG_IMAGE_KEY,
33253
33507
  SchedulingWidget,
33508
+ TEMPLATE_LOGO_ATTR,
33254
33509
  Toggle,
33255
33510
  ToggleGroup,
33256
33511
  ToggleGroupItem,
@@ -33284,6 +33539,7 @@ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey
33284
33539
  parseAiSectionsState,
33285
33540
  parseTarget,
33286
33541
  recordOverriddenPaths,
33542
+ releaseTemplateLogoIfReplaced,
33287
33543
  removeFooterBlock,
33288
33544
  resetOverriddenPathsCache,
33289
33545
  resolveFooterCopyright,