@ohhwells/bridge 0.1.120 → 0.1.122

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -428,6 +428,40 @@ function applyBrandToDom(kit) {
428
428
  }
429
429
  }
430
430
 
431
+ // src/lib/custom-code.ts
432
+ var ALLOWED_HEAD_TAGS = /* @__PURE__ */ new Set(["style", "meta", "link", "script", "noscript", "base"]);
433
+ var CUSTOM_CODE_AFTER_HEAD_KEY = "__ohw_custom_code_after_head";
434
+ var CUSTOM_CODE_BEFORE_HEAD_KEY = "__ohw_custom_code_before_head";
435
+ function clearExisting(slot) {
436
+ document.querySelectorAll(`[data-ohw-custom-code="${slot}"]`).forEach((el) => el.remove());
437
+ }
438
+ function buildNodes(raw, slot) {
439
+ if (!raw || !raw.trim()) return [];
440
+ const parsed = new DOMParser().parseFromString(raw, "text/html");
441
+ const source = [...parsed.head.childNodes, ...parsed.body.childNodes];
442
+ const nodes = [];
443
+ for (const node of source) {
444
+ if (node.nodeType !== Node.ELEMENT_NODE) continue;
445
+ const el = node;
446
+ const tag = el.tagName.toLowerCase();
447
+ if (!ALLOWED_HEAD_TAGS.has(tag)) continue;
448
+ const built = document.createElement(tag);
449
+ for (const attr of Array.from(el.attributes)) built.setAttribute(attr.name, attr.value);
450
+ if (tag === "script" || tag === "style" || tag === "noscript") built.textContent = el.textContent;
451
+ built.dataset.ohwCustomCode = slot;
452
+ nodes.push(built);
453
+ }
454
+ return nodes;
455
+ }
456
+ function applyCustomCodeToHead(content) {
457
+ clearExisting("after");
458
+ clearExisting("before");
459
+ const afterNodes = buildNodes(content[CUSTOM_CODE_AFTER_HEAD_KEY], "after");
460
+ if (afterNodes.length > 0) document.head.prepend(...afterNodes);
461
+ const beforeNodes = buildNodes(content[CUSTOM_CODE_BEFORE_HEAD_KEY], "before");
462
+ if (beforeNodes.length > 0) document.head.append(...beforeNodes);
463
+ }
464
+
431
465
  // src/lib/section-styles.ts
432
466
  var STYLE_STORE_KEY = "__ohw_styles";
433
467
  var STYLE_SHEET_ID = "ohw-section-styles";
@@ -491,9 +525,9 @@ function styleSheetCss() {
491
525
  );
492
526
  }
493
527
  const pad = { tight: 40, balanced: 64, airy: 96 };
494
- for (const [spacing, px] of Object.entries(pad)) {
528
+ for (const [spacing, px2] of Object.entries(pad)) {
495
529
  rules.push(
496
- `[data-ohw-style-spacing="${spacing}"] { padding-top: ${px}px !important; padding-bottom: ${px}px !important; }`
530
+ `[data-ohw-style-spacing="${spacing}"] { padding-top: ${px2}px !important; padding-bottom: ${px2}px !important; }`
497
531
  );
498
532
  }
499
533
  rules.push(
@@ -3060,12 +3094,12 @@ function deriveTemplateButtonStyle() {
3060
3094
  cs.borderBottomLeftRadius
3061
3095
  ].map((v) => v || "0px");
3062
3096
  const radius = corners.every((v) => v === corners[0]) ? corners[0] : corners.join(" ");
3063
- const px = (v) => parseFloat(v) || 0;
3064
- const lineHeight = px(cs.lineHeight) || px(cs.fontSize) * 1.2;
3065
- const contentH = btn.getBoundingClientRect().height - px(cs.borderTopWidth) - px(cs.borderBottomWidth);
3097
+ const px2 = (v) => parseFloat(v) || 0;
3098
+ const lineHeight = px2(cs.lineHeight) || px2(cs.fontSize) * 1.2;
3099
+ const contentH = btn.getBoundingClientRect().height - px2(cs.borderTopWidth) - px2(cs.borderBottomWidth);
3066
3100
  const impliedY = Math.round(Math.max(0, (contentH - lineHeight) / 2));
3067
- const padY = Math.max(px(cs.paddingTop), px(cs.paddingBottom), impliedY);
3068
- const padX = Math.max(px(cs.paddingLeft), px(cs.paddingRight));
3101
+ const padY = Math.max(px2(cs.paddingTop), px2(cs.paddingBottom), impliedY);
3102
+ const padX = Math.max(px2(cs.paddingLeft), px2(cs.paddingRight));
3069
3103
  return {
3070
3104
  radius: radius || "10px",
3071
3105
  padding: `${padY}px ${padX}px`,
@@ -7982,6 +8016,14 @@ function ItemActionToolbar({
7982
8016
  e.preventDefault();
7983
8017
  e.stopPropagation();
7984
8018
  onStyle?.();
8019
+ },
8020
+ // Same as Edit link: mousedown opens the panel and remounts this toolbar
8021
+ // (styleActive flips). Without stopping click, the gesture lands on whatever
8022
+ // is now under the cursor — navbar CTAs park the toolbar below the button, so
8023
+ // that miss deselects and the panel closes as soon as it opens.
8024
+ onClick: (e) => {
8025
+ e.preventDefault();
8026
+ e.stopPropagation();
7985
8027
  }
7986
8028
  },
7987
8029
  children: /* @__PURE__ */ jsx11(Brush, { className: "size-4 shrink-0", "aria-hidden": true })
@@ -8304,20 +8346,32 @@ function ItemInteractionLayer({
8304
8346
  itemDragSurface = true,
8305
8347
  chromeGap,
8306
8348
  toolbarAlign = "center",
8349
+ hidden = false,
8307
8350
  className
8308
8351
  }) {
8352
+ const localRef = useRef2(null);
8353
+ useLayoutEffect2(() => {
8354
+ const node = localRef.current;
8355
+ if (!node) return;
8356
+ node.style.display = hidden ? "none" : "block";
8357
+ }, [hidden, rect.top, rect.left, rect.width, rect.height, state]);
8309
8358
  if (state === "default") return null;
8310
8359
  const gap = chromeGap ?? (state === "hover" ? HOVER_CHROME_GAP : SELECTION_CHROME_GAP);
8311
8360
  const isActive = state === "active-top" || state === "active-bottom";
8312
8361
  const isDragging = state === "dragging";
8313
8362
  const showToolbar = isActive && toolbar;
8314
8363
  const showDragHandle = (isActive || isDragging) && showHandle;
8315
- const itemDragEnabled = itemDragSurface && isActive && showHandle && !isDragging && !dragDisabled;
8364
+ const itemDragEnabled = itemDragSurface && isActive && !isDragging && !dragDisabled && Boolean(showHandle || onItemPointerDown);
8316
8365
  const useDetachedBelowToolbar = Boolean(showToolbar && state === "active-bottom" && toolbarBelowRect);
8366
+ const setRootRef = (node) => {
8367
+ localRef.current = node;
8368
+ if (typeof elRef === "function") elRef(node);
8369
+ else if (elRef) elRef.current = node;
8370
+ };
8317
8371
  return /* @__PURE__ */ jsxs5(
8318
8372
  "div",
8319
8373
  {
8320
- ref: elRef,
8374
+ ref: setRootRef,
8321
8375
  "data-ohw-item-interaction": "",
8322
8376
  "data-ohw-item-interaction-state": state,
8323
8377
  className: cn("pointer-events-none", className),
@@ -8327,7 +8381,8 @@ function ItemInteractionLayer({
8327
8381
  left: rect.left - gap,
8328
8382
  width: rect.width + gap * 2,
8329
8383
  height: rect.height + gap * 2,
8330
- zIndex: getChromeZIndex(state)
8384
+ zIndex: getChromeZIndex(state),
8385
+ display: hidden ? "none" : "block"
8331
8386
  },
8332
8387
  children: [
8333
8388
  /* @__PURE__ */ jsx12(
@@ -11148,6 +11203,13 @@ function findButtonAtPoint(x, y, media) {
11148
11203
  if (hits.length === 0) return null;
11149
11204
  return hits.reduce((best, el) => best.contains(el) ? el : best);
11150
11205
  }
11206
+ function findNavbarCtaAtPoint(x, y) {
11207
+ const hits = Array.from(
11208
+ document.querySelectorAll('[data-ohw-role="navbar-button"]')
11209
+ ).filter((el) => containsPoint(el, x, y) && !el.closest("[data-ohw-bridge]"));
11210
+ if (hits.length === 0) return null;
11211
+ return hits.reduce((best, el) => best.contains(el) ? el : best);
11212
+ }
11151
11213
 
11152
11214
  // src/lib/session-search.ts
11153
11215
  var STORAGE_KEY = "ohw-preserved-search";
@@ -11883,6 +11945,26 @@ import { useCallback as useCallback5, useEffect as useEffect8, useMemo as useMem
11883
11945
  import { createPortal as createPortal2 } from "react-dom";
11884
11946
  import { ArrowLeft as ArrowLeft2, Check as Check2 } from "lucide-react";
11885
11947
  import { usePathname, useRouter as useRouter2 } from "next/navigation";
11948
+
11949
+ // src/lib/parent-scroll.ts
11950
+ function visibleBand(parentScroll) {
11951
+ const { iframeOffsetTop, headerH: visibleCanvasTop, canvasH } = parentScroll;
11952
+ const top = Math.max(0, visibleCanvasTop - iframeOffsetTop);
11953
+ const bottom = Math.min(window.innerHeight, visibleCanvasTop + canvasH - iframeOffsetTop);
11954
+ return { top, bottom: Math.max(top, bottom) };
11955
+ }
11956
+ function getIframeVisibleClip(parentScroll) {
11957
+ if (!parentScroll) return null;
11958
+ return visibleBand(parentScroll);
11959
+ }
11960
+ function getIframeChromeClip(parentScroll) {
11961
+ if (!parentScroll) return null;
11962
+ const band = visibleBand(parentScroll);
11963
+ const promptBarH = Math.max(0, parentScroll.promptBarH ?? 0);
11964
+ return { top: band.top, bottom: Math.max(band.top, band.bottom - promptBarH) };
11965
+ }
11966
+
11967
+ // src/ui/link-modal/SectionPickerOverlay.tsx
11886
11968
  import { jsx as jsx27, jsxs as jsxs17 } from "react/jsx-runtime";
11887
11969
  var DIM_OVERLAY = "rgba(0, 0, 0, 0.45)";
11888
11970
  function rectsEqual(a, b) {
@@ -12020,20 +12102,22 @@ function SectionPickerOverlay({
12020
12102
  []
12021
12103
  );
12022
12104
  useEffect8(() => {
12023
- const applyClip = (iframeOffsetTop, visibleCanvasTop, canvasH) => {
12024
- const top = Math.max(0, visibleCanvasTop - iframeOffsetTop);
12025
- const bottom = Math.min(
12026
- window.innerHeight,
12027
- visibleCanvasTop + canvasH - iframeOffsetTop
12028
- );
12105
+ const applyClip = (iframeOffsetTop, visibleCanvasTop, canvasH, promptBarH = 0) => {
12106
+ const next = getIframeVisibleClip({
12107
+ iframeOffsetTop,
12108
+ headerH: visibleCanvasTop,
12109
+ canvasH,
12110
+ promptBarH
12111
+ });
12112
+ if (!next) return;
12029
12113
  setChromeClip(
12030
- (prev) => prev.top === top && prev.bottom === bottom ? prev : { top, bottom: Math.max(top, bottom) }
12114
+ (prev) => prev.top === next.top && prev.bottom === next.bottom ? prev : next
12031
12115
  );
12032
12116
  };
12033
12117
  const onParentScroll = (e) => {
12034
12118
  if (e.data?.type !== "ow:parent-scroll") return;
12035
- const { iframeOffsetTop, headerH, canvasH } = e.data;
12036
- applyClip(iframeOffsetTop, headerH, canvasH);
12119
+ const { iframeOffsetTop, headerH, canvasH, promptBarH = 0 } = e.data;
12120
+ applyClip(iframeOffsetTop, headerH, canvasH, promptBarH);
12037
12121
  iframeOffsetTopRef.current = iframeOffsetTop;
12038
12122
  if (!pointerScreenRef.current && pointerRef.current) {
12039
12123
  pointerScreenRef.current = {
@@ -15703,36 +15787,50 @@ import { X as X5 } from "lucide-react";
15703
15787
  import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
15704
15788
  var PANEL_WIDTH = 256;
15705
15789
  var EDGE_MARGIN = 16;
15790
+ var HEADER_MIN = 56;
15706
15791
  function getVisibleClip(parentScroll) {
15707
15792
  const left = 0;
15708
15793
  const right = window.innerWidth;
15709
- if (!parentScroll) {
15794
+ const band = getIframeChromeClip(parentScroll);
15795
+ if (!band) {
15710
15796
  return { top: 0, bottom: window.innerHeight, left, right };
15711
15797
  }
15712
- const { iframeOffsetTop, headerH: visibleCanvasTop, canvasH } = parentScroll;
15713
- const top = Math.max(0, visibleCanvasTop - iframeOffsetTop);
15714
- const bottom = Math.min(window.innerHeight, visibleCanvasTop + canvasH - iframeOffsetTop);
15715
- return { top, bottom: Math.max(top, bottom), left, right };
15798
+ return { ...band, left, right };
15716
15799
  }
15717
- function defaultFloatingPanelPosition(parentScroll, panelHeight = 280) {
15800
+ function defaultFloatingPanelPosition(parentScroll, _panelHeight = 280) {
15718
15801
  const clip = getVisibleClip(parentScroll);
15719
15802
  return {
15720
15803
  x: Math.max(EDGE_MARGIN, clip.right - PANEL_WIDTH - EDGE_MARGIN),
15721
- y: Math.min(
15722
- Math.max(clip.top + EDGE_MARGIN, EDGE_MARGIN),
15723
- Math.max(clip.top + EDGE_MARGIN, clip.bottom - panelHeight - EDGE_MARGIN)
15724
- )
15804
+ y: clip.top + EDGE_MARGIN
15725
15805
  };
15726
15806
  }
15727
- function clampPosition(pos, parentScroll, panelW, panelH) {
15807
+ function clampX(x, panelW) {
15808
+ const left = EDGE_MARGIN;
15809
+ const maxX = Math.max(left, window.innerWidth - panelW - EDGE_MARGIN);
15810
+ return Math.min(Math.max(x, left), maxX);
15811
+ }
15812
+ function clampDragPosition(pos, parentScroll, panelW) {
15728
15813
  const clip = getVisibleClip(parentScroll);
15729
- const maxX = Math.max(clip.left + EDGE_MARGIN, clip.right - panelW - EDGE_MARGIN);
15730
- const maxY = Math.max(clip.top + EDGE_MARGIN, clip.bottom - panelH - EDGE_MARGIN);
15814
+ const minY = clip.top + EDGE_MARGIN;
15815
+ const maxY = Math.max(minY, clip.bottom - EDGE_MARGIN - HEADER_MIN);
15731
15816
  return {
15732
- x: Math.min(Math.max(pos.x, clip.left + EDGE_MARGIN), maxX),
15733
- y: Math.min(Math.max(pos.y, clip.top + EDGE_MARGIN), maxY)
15817
+ x: clampX(pos.x, panelW),
15818
+ y: Math.min(Math.max(pos.y, minY), maxY)
15734
15819
  };
15735
15820
  }
15821
+ function fitPanelInClip(homeY, parentScroll, preferredHeight) {
15822
+ const clip = getVisibleClip(parentScroll);
15823
+ const minY = clip.top + EDGE_MARGIN;
15824
+ const maxBottom = clip.bottom - EDGE_MARGIN;
15825
+ const band = Math.max(HEADER_MIN, maxBottom - minY);
15826
+ let y = Math.max(homeY, minY);
15827
+ const want = Math.min(Math.max(preferredHeight, HEADER_MIN), band);
15828
+ if (y + want > maxBottom) {
15829
+ y = Math.max(minY, maxBottom - want);
15830
+ }
15831
+ const maxHeight = Math.max(HEADER_MIN, Math.floor(maxBottom - y));
15832
+ return { y, maxHeight };
15833
+ }
15736
15834
  function FloatingPanel({
15737
15835
  open,
15738
15836
  title,
@@ -15749,19 +15847,54 @@ function FloatingPanel({
15749
15847
  const panelRef = useRef9(null);
15750
15848
  const [measured, setMeasured] = useState12({ w: PANEL_WIDTH, h: 280 });
15751
15849
  const dragRef = useRef9(null);
15752
- const resolved = position ?? defaultFloatingPanelPosition(parentScroll, measured.h);
15753
- const clamped = clampPosition(resolved, parentScroll, measured.w, measured.h);
15850
+ const homeRef = useRef9(null);
15851
+ if (!open) {
15852
+ homeRef.current = null;
15853
+ } else if (position) {
15854
+ homeRef.current = position;
15855
+ } else if (homeRef.current == null) {
15856
+ homeRef.current = defaultFloatingPanelPosition(parentScroll);
15857
+ }
15858
+ const [liveScroll, setLiveScroll] = useState12(parentScroll);
15859
+ const home = position ?? homeRef.current ?? defaultFloatingPanelPosition(parentScroll);
15860
+ const { y: fittedY, maxHeight } = fitPanelInClip(home.y, liveScroll, measured.h);
15861
+ const clamped = {
15862
+ x: clampX(home.x, measured.w),
15863
+ y: fittedY
15864
+ };
15865
+ useEffect11(() => {
15866
+ setLiveScroll(parentScroll);
15867
+ }, [parentScroll]);
15868
+ useEffect11(() => {
15869
+ if (!open) return;
15870
+ const onParentScroll = (e) => {
15871
+ if (e.data?.type !== "ow:parent-scroll") return;
15872
+ const { iframeOffsetTop, headerH, canvasH, promptBarH = 0 } = e.data;
15873
+ setLiveScroll({ iframeOffsetTop, headerH, canvasH, promptBarH });
15874
+ };
15875
+ window.addEventListener("message", onParentScroll);
15876
+ return () => window.removeEventListener("message", onParentScroll);
15877
+ }, [open]);
15878
+ const positionRef = useRef9(position);
15879
+ useLayoutEffect4(() => {
15880
+ if (!open) {
15881
+ positionRef.current = position;
15882
+ return;
15883
+ }
15884
+ if (position == null && positionRef.current != null) {
15885
+ homeRef.current = defaultFloatingPanelPosition(liveScroll ?? parentScroll);
15886
+ }
15887
+ positionRef.current = position;
15888
+ }, [open, position, liveScroll, parentScroll]);
15754
15889
  useLayoutEffect4(() => {
15755
15890
  if (!open || !panelRef.current) return;
15756
15891
  const el = panelRef.current;
15757
- const next = { w: el.offsetWidth || PANEL_WIDTH, h: el.offsetHeight || 280 };
15892
+ const header = el.querySelector("[data-ohw-floating-panel-header]");
15893
+ const body = el.querySelector("[data-ohw-floating-panel-body]");
15894
+ const contentH = (header?.offsetHeight ?? 0) + (body?.scrollHeight ?? 0) || el.scrollHeight || 280;
15895
+ const next = { w: el.offsetWidth || PANEL_WIDTH, h: contentH };
15758
15896
  setMeasured((prev) => prev.w === next.w && prev.h === next.h ? prev : next);
15759
15897
  }, [open, children, title, context]);
15760
- useEffect11(() => {
15761
- if (!open || !position || !onPositionChange) return;
15762
- const next = clampPosition(position, parentScroll, measured.w, measured.h);
15763
- if (next.x !== position.x || next.y !== position.y) onPositionChange(next);
15764
- }, [open, parentScroll, measured.w, measured.h, position, onPositionChange]);
15765
15898
  const onHeaderPointerDown = useCallback7(
15766
15899
  (e) => {
15767
15900
  if (e.button !== 0) return;
@@ -15795,18 +15928,17 @@ function FloatingPanel({
15795
15928
  return;
15796
15929
  }
15797
15930
  e.preventDefault();
15798
- const next = clampPosition(
15931
+ const next = clampDragPosition(
15799
15932
  {
15800
15933
  x: drag.originX + (e.clientX - drag.startX),
15801
15934
  y: drag.originY + (e.clientY - drag.startY)
15802
15935
  },
15803
- parentScroll,
15804
- measured.w,
15805
- measured.h
15936
+ liveScroll,
15937
+ measured.w
15806
15938
  );
15807
15939
  onPositionChange?.(next);
15808
15940
  },
15809
- [measured.h, measured.w, onPositionChange, parentScroll]
15941
+ [measured.w, onPositionChange, liveScroll]
15810
15942
  );
15811
15943
  const endDrag = useCallback7((e) => {
15812
15944
  const drag = dragRef.current;
@@ -15836,7 +15968,7 @@ function FloatingPanel({
15836
15968
  "fixed z-[2147483647] flex w-64 flex-col overflow-hidden rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
15837
15969
  className
15838
15970
  ),
15839
- style: { left: clamped.x, top: clamped.y },
15971
+ style: { left: clamped.x, top: clamped.y, maxHeight },
15840
15972
  onMouseDown: (e) => e.stopPropagation(),
15841
15973
  onPointerDown: (e) => e.stopPropagation(),
15842
15974
  onClick: (e) => e.stopPropagation(),
@@ -15845,7 +15977,7 @@ function FloatingPanel({
15845
15977
  "div",
15846
15978
  {
15847
15979
  "data-ohw-floating-panel-header": "",
15848
- className: "relative flex cursor-grab items-start gap-2 border-b border-border py-5 pl-5 pr-11 active:cursor-grabbing",
15980
+ className: "relative flex shrink-0 cursor-grab items-start gap-2 border-b border-border py-4! pl-5! pr-11! active:cursor-grabbing",
15849
15981
  onPointerDown: onHeaderPointerDown,
15850
15982
  onPointerMove: onHeaderPointerMove,
15851
15983
  onPointerUp: endDrag,
@@ -15880,7 +16012,11 @@ function FloatingPanel({
15880
16012
  "div",
15881
16013
  {
15882
16014
  "data-ohw-floating-panel-body": "",
15883
- className: cn("flex w-full flex-col gap-4 p-5", bodyClassName),
16015
+ className: cn(
16016
+ "flex min-h-0 w-full flex-1 flex-col gap-4 overflow-y-auto overscroll-contain p-4",
16017
+ bodyClassName
16018
+ ),
16019
+ style: { WebkitOverflowScrolling: "touch" },
15884
16020
  children
15885
16021
  }
15886
16022
  )
@@ -15958,199 +16094,1633 @@ function SocialsDisplayPanel({ display, onChange, className }) {
15958
16094
  ] });
15959
16095
  }
15960
16096
 
15961
- // src/lib/item-drag-interaction.ts
15962
- function disableNativeHrefDrag(el) {
15963
- if (el.draggable) el.draggable = false;
15964
- if (el.getAttribute("draggable") !== "false") {
15965
- el.setAttribute("draggable", "false");
16097
+ // src/ui/button-style-panel.tsx
16098
+ import { Check as Check3 } from "lucide-react";
16099
+
16100
+ // src/lib/button-styles.ts
16101
+ var BUTTON_VARIANTS_KEY = "__ohw_button_variants";
16102
+ var BUTTON_SIZES_KEY = "__ohw_button_sizes";
16103
+ var BUTTON_ASSIGNMENTS_KEY = "__ohw_button_assignments";
16104
+ var BUTTON_VARIANT_IDS = ["primary", "secondary", "ghost"];
16105
+ var BUTTON_SIZE_IDS = ["s", "m", "l"];
16106
+ var TRANSPARENT = "rgba(0, 0, 0, 0)";
16107
+ var REFERENCE_SIZES = {
16108
+ s: { height: 40, paddingTop: 8, paddingRight: 16, paddingBottom: 8, paddingLeft: 16, fontSize: 15, borderRadius: 8, gap: 8 },
16109
+ m: { height: 48, paddingTop: 12, paddingRight: 24, paddingBottom: 12, paddingLeft: 24, fontSize: 15, borderRadius: 8, gap: 8 },
16110
+ l: { height: 56, paddingTop: 16, paddingRight: 32, paddingBottom: 16, paddingLeft: 32, fontSize: 15, borderRadius: 8, gap: 8 }
16111
+ };
16112
+ function buttonStyleContext(_el) {
16113
+ return "Button";
16114
+ }
16115
+ function previewSlotBackground(el) {
16116
+ let node = el.parentElement;
16117
+ while (node && node !== el.ownerDocument.documentElement) {
16118
+ if (node.closest("[data-ohw-bridge], [data-ohw-bridge-root]")) {
16119
+ node = node.parentElement;
16120
+ continue;
16121
+ }
16122
+ const bg = getComputedStyle(node).backgroundColor;
16123
+ const parsed = parseColor(bg);
16124
+ if (parsed && !isTransparent(parsed)) return bg;
16125
+ node = node.parentElement;
15966
16126
  }
16127
+ const root = el.ownerDocument.documentElement;
16128
+ const style = getComputedStyle(root);
16129
+ for (const name of ["--brand-surface", "--brand-background", "--ohw-brand-light", "--color-light"]) {
16130
+ const value = style.getPropertyValue(name).trim();
16131
+ if (value) return value;
16132
+ }
16133
+ return "var(--brand-background, var(--ohw-brand-light, var(--color-light, #ffffff)))";
15967
16134
  }
15968
- function clearTextSelection() {
15969
- const sel = window.getSelection();
15970
- if (sel && !sel.isCollapsed) sel.removeAllRanges();
16135
+ function buttonId(el) {
16136
+ if (!el) return null;
16137
+ const button = el.matches(CTA_BUTTON_SELECTOR) ? el : el.closest(CTA_BUTTON_SELECTOR);
16138
+ const id = button?.getAttribute("data-ohw-href-key")?.trim();
16139
+ return id || null;
15971
16140
  }
15972
- function armItemPressDrag() {
15973
- document.documentElement.setAttribute("data-ohw-footer-press-drag", "");
16141
+ function isButtonVariantId(value) {
16142
+ return BUTTON_VARIANT_IDS.includes(value);
15974
16143
  }
15975
- function lockItemDuringDrag() {
15976
- document.documentElement.removeAttribute("data-ohw-footer-press-drag");
15977
- document.documentElement.setAttribute("data-ohw-item-dragging", "");
15978
- clearTextSelection();
16144
+ function isButtonSizeId(value) {
16145
+ return BUTTON_SIZE_IDS.includes(value);
15979
16146
  }
15980
- function unlockItemDragInteraction() {
15981
- const wasDragging = document.documentElement.hasAttribute("data-ohw-item-dragging");
15982
- document.documentElement.removeAttribute("data-ohw-footer-press-drag");
15983
- document.documentElement.removeAttribute("data-ohw-item-dragging");
15984
- if (wasDragging) clearTextSelection();
16147
+ function px(value) {
16148
+ const rounded = Math.round(value * 100) / 100;
16149
+ return `${rounded}px`;
15985
16150
  }
15986
- var armFooterPressDrag = armItemPressDrag;
15987
- var lockFooterDuringDrag = lockItemDuringDrag;
15988
- var unlockFooterDragInteraction = unlockItemDragInteraction;
15989
-
15990
- // src/lib/nav-dnd.ts
15991
- function listReorderableNavItems(draggedEl) {
15992
- return listActiveNavbarItems(draggedEl).filter((el) => {
15993
- const key = el.getAttribute("data-ohw-href-key");
15994
- return isNavbarHrefKey(key) && !isNestedNavChild(el);
15995
- });
16151
+ function parsePx(value, rootFont = 16) {
16152
+ if (!value) return 0;
16153
+ const n = parseFloat(value);
16154
+ if (!Number.isFinite(n)) return 0;
16155
+ if (value.trim().endsWith("rem") || value.trim().endsWith("em")) return n * rootFont;
16156
+ return n;
15996
16157
  }
15997
- function resolveParentNavHrefKey(child) {
15998
- const childrenRoot = child.closest("[data-ohw-nav-children]");
15999
- const group = childrenRoot?.closest("[data-ohw-nav-group]");
16000
- if (!group) return null;
16001
- const trigger = group.querySelector(":scope > [data-ohw-href-key]") ?? Array.from(group.querySelectorAll("[data-ohw-href-key]")).find(
16002
- (el) => !el.closest("[data-ohw-nav-children]")
16003
- ) ?? null;
16004
- const key = trigger?.getAttribute("data-ohw-href-key");
16005
- return key && isNavbarHrefKey(key) ? key : null;
16158
+ function classifyHeight(height) {
16159
+ if (height < 44) return "s";
16160
+ if (height < 52) return "m";
16161
+ return "l";
16006
16162
  }
16007
- function listNavChildren(parentHrefKey, draggedEl) {
16008
- const items = listActiveNavbarItems(draggedEl);
16009
- const parent = items.find((el) => el.getAttribute("data-ohw-href-key") === parentHrefKey);
16010
- if (!parent) return [];
16011
- const group = parent.closest("[data-ohw-nav-group]");
16012
- const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
16013
- if (!childrenRoot) return [];
16014
- return Array.from(childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
16015
- (el) => isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))
16016
- );
16163
+ function scaleGeometry(geometry, scale = 0.75) {
16164
+ return {
16165
+ paddingTop: px(parsePx(geometry.paddingTop) * scale),
16166
+ paddingRight: px(parsePx(geometry.paddingRight) * scale),
16167
+ paddingBottom: px(parsePx(geometry.paddingBottom) * scale),
16168
+ paddingLeft: px(parsePx(geometry.paddingLeft) * scale),
16169
+ fontSize: px(parsePx(geometry.fontSize) * scale),
16170
+ borderRadius: px(parsePx(geometry.borderRadius) * scale),
16171
+ gap: px(parsePx(geometry.gap) * scale),
16172
+ height: Math.round(geometry.height * scale)
16173
+ };
16017
16174
  }
16018
- function isVerticalNavLayout(items, draggedEl) {
16019
- const visible = items.filter((el) => el.getClientRects().length > 0);
16020
- if (visible.length >= 2) {
16021
- const a = visible[0].getBoundingClientRect();
16022
- const b = visible[1].getBoundingClientRect();
16023
- const verticalGap = b.top - a.bottom;
16024
- const horizontalGap = b.left - a.right;
16025
- return verticalGap > horizontalGap;
16026
- }
16027
- const container = getActiveNavbarListContainer(draggedEl);
16028
- if (!container) return false;
16029
- const style = window.getComputedStyle(container);
16030
- return style.flexDirection === "column" || style.flexDirection === "column-reverse";
16175
+ function relativeLuminance(color) {
16176
+ const channel = (value) => {
16177
+ const s = value / 255;
16178
+ return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
16179
+ };
16180
+ return 0.2126 * channel(color.r) + 0.7152 * channel(color.g) + 0.0722 * channel(color.b);
16031
16181
  }
16032
- function buildVerticalRootNavDropSlots(items, draggedEl) {
16033
- const slots = [];
16034
- const barThickness = 3;
16035
- if (items.length === 0) {
16036
- const container = getActiveNavbarListContainer(draggedEl);
16037
- if (!container) return slots;
16038
- const rect = container.getBoundingClientRect();
16039
- slots.push({
16040
- insertIndex: 0,
16041
- parentId: null,
16042
- left: rect.left,
16043
- top: rect.top + rect.height / 2 - barThickness / 2,
16044
- width: Math.max(rect.width, 40),
16045
- height: barThickness,
16046
- direction: "horizontal"
16047
- });
16048
- return slots;
16049
- }
16050
- const edgeGap = (() => {
16051
- if (items.length < 2) return 16;
16052
- const a = items[0].getBoundingClientRect();
16053
- const b = items[1].getBoundingClientRect();
16054
- return Math.max(0, b.top - a.bottom);
16055
- })();
16056
- for (let i = 0; i <= items.length; i++) {
16057
- let top;
16058
- let width;
16059
- let left;
16060
- if (i === 0) {
16061
- const first = items[0].getBoundingClientRect();
16062
- top = first.top - edgeGap / 2 - barThickness / 2;
16063
- left = first.left;
16064
- width = first.width;
16065
- } else if (i === items.length) {
16066
- const last = items[items.length - 1].getBoundingClientRect();
16067
- const lastGap = items.length >= 2 ? Math.max(0, last.top - items[items.length - 2].getBoundingClientRect().bottom) : edgeGap;
16068
- top = last.bottom + lastGap / 2 - barThickness / 2;
16069
- left = last.left;
16070
- width = last.width;
16071
- } else {
16072
- const prev = items[i - 1].getBoundingClientRect();
16073
- const next = items[i].getBoundingClientRect();
16074
- top = (prev.bottom + next.top) / 2 - barThickness / 2;
16075
- left = Math.min(prev.left, next.left);
16076
- width = Math.max(prev.right, next.right) - left;
16182
+ function contrastRatio(a, b) {
16183
+ const lighter = Math.max(relativeLuminance(a), relativeLuminance(b));
16184
+ const darker = Math.min(relativeLuminance(a), relativeLuminance(b));
16185
+ return (lighter + 0.05) / (darker + 0.05);
16186
+ }
16187
+ function compositeOver(fg, bg) {
16188
+ const a = fg.a + bg.a * (1 - fg.a);
16189
+ if (a < 1e-3) return { ...bg, a: 0 };
16190
+ return {
16191
+ r: (fg.r * fg.a + bg.r * bg.a * (1 - fg.a)) / a,
16192
+ g: (fg.g * fg.a + bg.g * bg.a * (1 - fg.a)) / a,
16193
+ b: (fg.b * fg.a + bg.b * bg.a * (1 - fg.a)) / a,
16194
+ a
16195
+ };
16196
+ }
16197
+ function previewSlotForChrome(chrome, slotBackground) {
16198
+ const text = parseColor(chrome.color);
16199
+ if (!text || text.a < 0.2) return slotBackground;
16200
+ const slot = parseColor(slotBackground) ?? { r: 250, g: 250, b: 249, a: 1 };
16201
+ const fill = parseColor(chrome.background);
16202
+ const effective = fill && !isTransparent(fill) ? compositeOver(fill, slot) : slot;
16203
+ if (contrastRatio(text, effective) >= 3) return slotBackground;
16204
+ return relativeLuminance(text) > 0.55 ? "#2c2c2c" : "#f4f4f5";
16205
+ }
16206
+ function parseColor(input) {
16207
+ if (!input) return null;
16208
+ const value = input.trim().toLowerCase();
16209
+ if (value === "transparent") return { r: 0, g: 0, b: 0, a: 0 };
16210
+ const hex = value.match(/^#([0-9a-f]{3,8})$/);
16211
+ if (hex) {
16212
+ const raw = hex[1];
16213
+ const expand = (pair) => parseInt(pair.length === 1 ? pair + pair : pair, 16);
16214
+ if (raw.length === 3 || raw.length === 4) {
16215
+ return {
16216
+ r: expand(raw[0]),
16217
+ g: expand(raw[1]),
16218
+ b: expand(raw[2]),
16219
+ a: raw.length === 4 ? expand(raw[3]) / 255 : 1
16220
+ };
16221
+ }
16222
+ if (raw.length === 6 || raw.length === 8) {
16223
+ return {
16224
+ r: expand(raw.slice(0, 2)),
16225
+ g: expand(raw.slice(2, 4)),
16226
+ b: expand(raw.slice(4, 6)),
16227
+ a: raw.length === 8 ? expand(raw.slice(6, 8)) / 255 : 1
16228
+ };
16077
16229
  }
16078
- slots.push({
16079
- insertIndex: i,
16080
- parentId: null,
16081
- left,
16082
- top,
16083
- width: Math.max(width, 40),
16084
- height: barThickness,
16085
- direction: "horizontal"
16086
- });
16087
16230
  }
16088
- return slots;
16089
- }
16090
- function buildHorizontalRootNavDropSlots(items, draggedEl) {
16091
- const slots = [];
16092
- const barThickness = 3;
16093
- if (items.length === 0) {
16094
- const container = getActiveNavbarListContainer(draggedEl);
16095
- if (!container) return slots;
16096
- const rect = container.getBoundingClientRect();
16097
- slots.push({
16098
- insertIndex: 0,
16099
- parentId: null,
16100
- left: rect.left + rect.width / 2 - barThickness / 2,
16101
- top: rect.top,
16102
- width: barThickness,
16103
- height: Math.max(rect.height, 24),
16104
- direction: "vertical"
16105
- });
16106
- return slots;
16231
+ const rgb = value.match(
16232
+ /^rgba?\(\s*([\d.]+)[%\s,]+([\d.]+)[%\s,]+([\d.]+)(?:%?\s*[,/]\s*([\d.]+%?))?\s*\)$/
16233
+ );
16234
+ if (rgb) {
16235
+ const alphaRaw = rgb[4];
16236
+ const alpha = alphaRaw == null ? 1 : alphaRaw.endsWith("%") ? parseFloat(alphaRaw) / 100 : parseFloat(alphaRaw);
16237
+ return { r: Number(rgb[1]), g: Number(rgb[2]), b: Number(rgb[3]), a: alpha };
16107
16238
  }
16108
- const edgeGap = (() => {
16109
- if (items.length < 2) return 32;
16110
- const a = items[0].getBoundingClientRect();
16111
- const b = items[1].getBoundingClientRect();
16112
- return Math.max(0, b.left - a.right);
16113
- })();
16114
- for (let i = 0; i <= items.length; i++) {
16115
- let left;
16116
- let top;
16117
- let height;
16118
- if (i === 0) {
16119
- const first = items[0].getBoundingClientRect();
16120
- left = first.left - edgeGap / 2 - barThickness / 2;
16121
- top = first.top;
16122
- height = first.height;
16123
- } else if (i === items.length) {
16124
- const last = items[items.length - 1].getBoundingClientRect();
16125
- const lastGap = items.length >= 2 ? Math.max(0, last.left - items[items.length - 2].getBoundingClientRect().right) : edgeGap;
16126
- left = last.right + lastGap / 2 - barThickness / 2;
16127
- top = last.top;
16128
- height = last.height;
16129
- } else {
16130
- const prev = items[i - 1].getBoundingClientRect();
16131
- const next = items[i].getBoundingClientRect();
16132
- left = (prev.right + next.left) / 2 - barThickness / 2;
16133
- top = Math.min(prev.top, next.top);
16134
- height = Math.max(prev.bottom, next.bottom) - top;
16135
- }
16136
- slots.push({
16137
- insertIndex: i,
16138
- parentId: null,
16139
- left,
16140
- top,
16141
- width: barThickness,
16142
- height: Math.max(height, 24),
16143
- direction: "vertical"
16144
- });
16239
+ const oklch = value.match(
16240
+ /^oklch\(\s*([\d.]+%?)\s+([\d.]+)\s+([\d.]+)(?:deg)?(?:\s*\/\s*([\d.]+%?))?\s*\)$/
16241
+ );
16242
+ if (oklch) {
16243
+ const Lraw = oklch[1];
16244
+ const L = Lraw.endsWith("%") ? parseFloat(Lraw) / 100 : parseFloat(Lraw);
16245
+ const gray = Math.max(0, Math.min(1, L)) * 255;
16246
+ const alphaRaw = oklch[4];
16247
+ const alpha = alphaRaw == null ? 1 : alphaRaw.endsWith("%") ? parseFloat(alphaRaw) / 100 : parseFloat(alphaRaw);
16248
+ return { r: gray, g: gray, b: gray, a: alpha };
16145
16249
  }
16146
- return slots;
16250
+ return null;
16147
16251
  }
16148
- function buildRootNavDropSlots(draggedEl) {
16149
- const items = listReorderableNavItems(draggedEl);
16150
- if (isVerticalNavLayout(items, draggedEl)) {
16151
- return buildVerticalRootNavDropSlots(items, draggedEl);
16152
- }
16153
- return buildHorizontalRootNavDropSlots(items, draggedEl);
16252
+ function isTransparent(color) {
16253
+ return !color || color.a < 0.05;
16254
+ }
16255
+ function hasOpaqueBackground(background) {
16256
+ if (!background) return false;
16257
+ const value = background.trim().toLowerCase();
16258
+ if (!value || value === "transparent" || value === "rgba(0, 0, 0, 0)" || value === "rgb(0, 0, 0, 0)") {
16259
+ return false;
16260
+ }
16261
+ const parsed = parseColor(background);
16262
+ if (parsed) return !isTransparent(parsed);
16263
+ return true;
16264
+ }
16265
+ function toCss(color) {
16266
+ const r2 = Math.round(color.r);
16267
+ const g = Math.round(color.g);
16268
+ const b = Math.round(color.b);
16269
+ if (color.a >= 0.995) return `rgb(${r2}, ${g}, ${b})`;
16270
+ return `rgba(${r2}, ${g}, ${b}, ${Math.round(color.a * 100) / 100})`;
16271
+ }
16272
+ function colorKey(input) {
16273
+ const parsed = parseColor(input);
16274
+ if (!parsed || isTransparent(parsed)) return "transparent";
16275
+ return `${Math.round(parsed.r / 8)}-${Math.round(parsed.g / 8)}-${Math.round(parsed.b / 8)}-${Math.round(parsed.a * 4)}`;
16276
+ }
16277
+ function declaredVariant(className) {
16278
+ const match = className.match(/\bbtn--(primary|secondary|ghost)\b/);
16279
+ return match && isButtonVariantId(match[1]) ? match[1] : null;
16280
+ }
16281
+ function fillSlot(sample) {
16282
+ const width = parseFloat(sample.borderWidth);
16283
+ const border = parseColor(sample.borderColor);
16284
+ if (width > 0 && !isTransparent(border)) return "secondary";
16285
+ if (hasOpaqueBackground(sample.background)) return "primary";
16286
+ return "ghost";
16287
+ }
16288
+ function sampleSlot(sample) {
16289
+ return declaredVariant(sample.className) ?? fillSlot(sample);
16290
+ }
16291
+ function fingerprint(sample) {
16292
+ const width = parseFloat(sample.borderWidth);
16293
+ const border = width > 0 ? colorKey(sample.borderColor) : "none";
16294
+ return `${colorKey(sample.background)}|${border}|${colorKey(sample.color)}|${sampleSlot(sample)}`;
16295
+ }
16296
+ function paintColor(chrome) {
16297
+ if (hasOpaqueBackground(chrome.background)) {
16298
+ const parsed = parseColor(chrome.background);
16299
+ return parsed ? toCss({ ...parsed, a: 1 }) : chrome.background;
16300
+ }
16301
+ const width = parseFloat(chrome.borderWidth);
16302
+ if (width > 0 && hasOpaqueBackground(chrome.borderColor)) {
16303
+ const parsed = parseColor(chrome.borderColor);
16304
+ return parsed ? toCss({ ...parsed, a: 1 }) : chrome.borderColor;
16305
+ }
16306
+ const text = parseColor(chrome.color);
16307
+ return text ? toCss({ ...text, a: 1 }) : chrome.color;
16308
+ }
16309
+ function accentColor(chrome) {
16310
+ const bg = parseColor(chrome.background);
16311
+ if (bg && !isTransparent(bg) && relativeLuminance(bg) < 0.55) {
16312
+ return toCss({ ...bg, a: 1 });
16313
+ }
16314
+ const text = parseColor(chrome.color);
16315
+ if (text && !isTransparent(text)) return toCss({ ...text, a: 1 });
16316
+ return paintColor(chrome);
16317
+ }
16318
+ function chromeFrom(sample) {
16319
+ return {
16320
+ background: sample.background || TRANSPARENT,
16321
+ color: sample.color || "currentColor",
16322
+ borderColor: sample.borderColor || TRANSPARENT,
16323
+ borderWidth: sample.borderWidth || "0px"
16324
+ };
16325
+ }
16326
+ function filledPrimary(seed) {
16327
+ const paint = parseColor(paintColor(seed)) ?? { r: 15, g: 15, b: 15, a: 1 };
16328
+ const luminance = relativeLuminance(paint);
16329
+ return {
16330
+ background: toCss({ ...paint, a: 1 }),
16331
+ color: luminance > 0.55 ? "rgb(15, 15, 15)" : "rgb(255, 255, 255)",
16332
+ borderColor: TRANSPARENT,
16333
+ borderWidth: "0px"
16334
+ };
16335
+ }
16336
+ function generatedSecondary(primary) {
16337
+ const paint = accentColor(primary);
16338
+ return {
16339
+ background: TRANSPARENT,
16340
+ color: paint,
16341
+ borderColor: paint,
16342
+ borderWidth: "1px"
16343
+ };
16344
+ }
16345
+ function generatedGhost(primary) {
16346
+ return {
16347
+ background: TRANSPARENT,
16348
+ color: accentColor(primary),
16349
+ borderColor: TRANSPARENT,
16350
+ borderWidth: "0px"
16351
+ };
16352
+ }
16353
+ function normalizeOutlineChrome(outline, primary) {
16354
+ const paint = accentColor(primary);
16355
+ const text = parseColor(outline.color);
16356
+ const paintRgb = parseColor(paint);
16357
+ const primaryLabel = parseColor(primary.color);
16358
+ const matchesOnPrimary = Boolean(text && primaryLabel) && colorKey(outline.color) === colorKey(primary.color);
16359
+ const textTooLight = text ? relativeLuminance(text) > 0.7 : false;
16360
+ const paintIsDarker = paintRgb ? relativeLuminance(paintRgb) < 0.65 : hasOpaqueBackground(primary.background);
16361
+ if (!(matchesOnPrimary || textTooLight) || !paintIsDarker) return outline;
16362
+ const hasBorder = parseFloat(outline.borderWidth) > 0;
16363
+ return {
16364
+ ...outline,
16365
+ color: paint,
16366
+ borderColor: hasBorder ? paint : outline.borderColor
16367
+ };
16368
+ }
16369
+ function generateButtonVariants(samples) {
16370
+ if (samples.length === 0) return { status: "unresolved" };
16371
+ const groups = /* @__PURE__ */ new Map();
16372
+ for (const sample of samples) {
16373
+ const key = fingerprint(sample);
16374
+ const list = groups.get(key) ?? [];
16375
+ list.push(sample);
16376
+ groups.set(key, list);
16377
+ }
16378
+ if (groups.size > 3) return { status: "unresolved" };
16379
+ const bySlot = /* @__PURE__ */ new Map();
16380
+ for (const group of groups.values()) {
16381
+ const sample = group[0];
16382
+ const slot = sampleSlot(sample);
16383
+ if (bySlot.has(slot)) return { status: "unresolved" };
16384
+ bySlot.set(slot, sample);
16385
+ }
16386
+ const primarySample = bySlot.get("primary");
16387
+ const seed = chromeFrom(primarySample ?? samples[0]);
16388
+ const primary = primarySample ? seed : filledPrimary(seed);
16389
+ const secondary = normalizeOutlineChrome(
16390
+ bySlot.has("secondary") ? chromeFrom(bySlot.get("secondary")) : generatedSecondary(primary),
16391
+ primary
16392
+ );
16393
+ const ghost = normalizeOutlineChrome(
16394
+ bySlot.has("ghost") ? chromeFrom(bySlot.get("ghost")) : generatedGhost(primary),
16395
+ primary
16396
+ );
16397
+ return {
16398
+ status: "ready",
16399
+ tokens: { primary, secondary, ghost }
16400
+ };
16401
+ }
16402
+ function geometryFromSample(sample) {
16403
+ return {
16404
+ paddingTop: px(sample.paddingTop),
16405
+ paddingRight: px(sample.paddingRight),
16406
+ paddingBottom: px(sample.paddingBottom),
16407
+ paddingLeft: px(sample.paddingLeft),
16408
+ fontSize: px(sample.fontSize),
16409
+ borderRadius: px(sample.borderRadius),
16410
+ gap: px(sample.gap),
16411
+ height: Math.round(sample.height)
16412
+ };
16413
+ }
16414
+ function shiftSample(base, steps) {
16415
+ return {
16416
+ height: Math.max(24, base.height + steps * 8),
16417
+ paddingTop: Math.max(0, base.paddingTop + steps * 4),
16418
+ paddingBottom: Math.max(0, base.paddingBottom + steps * 4),
16419
+ paddingLeft: Math.max(0, base.paddingLeft + steps * 8),
16420
+ paddingRight: Math.max(0, base.paddingRight + steps * 8),
16421
+ fontSize: base.fontSize,
16422
+ borderRadius: base.borderRadius,
16423
+ gap: base.gap
16424
+ };
16425
+ }
16426
+ function clusterSizes(samples) {
16427
+ const sorted = [...samples].sort((a, b) => a.height - b.height);
16428
+ const clusters = [];
16429
+ for (const sample of sorted) {
16430
+ const current = clusters[clusters.length - 1];
16431
+ const head = current?.[0];
16432
+ if (!current || !head || Math.abs(sample.height - head.height) > 2) clusters.push([sample]);
16433
+ else current.push(sample);
16434
+ }
16435
+ return clusters.map((group) => {
16436
+ const pick = (key) => group.reduce((sum, item) => sum + item[key], 0) / group.length;
16437
+ return {
16438
+ height: pick("height"),
16439
+ paddingTop: pick("paddingTop"),
16440
+ paddingRight: pick("paddingRight"),
16441
+ paddingBottom: pick("paddingBottom"),
16442
+ paddingLeft: pick("paddingLeft"),
16443
+ fontSize: pick("fontSize"),
16444
+ borderRadius: pick("borderRadius"),
16445
+ gap: pick("gap")
16446
+ };
16447
+ });
16448
+ }
16449
+ function tokensFromSamples(bySlot, radius, gap) {
16450
+ const fill = (id) => {
16451
+ const sample = bySlot[id] ?? REFERENCE_SIZES[id];
16452
+ return geometryFromSample({ ...sample, borderRadius: radius, gap });
16453
+ };
16454
+ return { s: fill("s"), m: fill("m"), l: fill("l") };
16455
+ }
16456
+ function interpolateSizes(known) {
16457
+ const order = BUTTON_SIZE_IDS;
16458
+ const indexOf = (id) => order.indexOf(id);
16459
+ const sorted = [...known].sort((a, b) => indexOf(a.id) - indexOf(b.id));
16460
+ const left = sorted[0];
16461
+ const right = sorted[1];
16462
+ const span = indexOf(right.id) - indexOf(left.id);
16463
+ const step = (key) => (right.sample[key] - left.sample[key]) / span;
16464
+ const at = (index) => {
16465
+ const steps = index - indexOf(left.id);
16466
+ return {
16467
+ height: left.sample.height + step("height") * steps,
16468
+ paddingTop: Math.max(0, left.sample.paddingTop + step("paddingTop") * steps),
16469
+ paddingBottom: Math.max(0, left.sample.paddingBottom + step("paddingBottom") * steps),
16470
+ paddingLeft: Math.max(0, left.sample.paddingLeft + step("paddingLeft") * steps),
16471
+ paddingRight: Math.max(0, left.sample.paddingRight + step("paddingRight") * steps),
16472
+ fontSize: Math.max(10, left.sample.fontSize + step("fontSize") * steps),
16473
+ borderRadius: left.sample.borderRadius,
16474
+ gap: left.sample.gap
16475
+ };
16476
+ };
16477
+ const bySlot = {
16478
+ [left.id]: left.sample,
16479
+ [right.id]: { ...right.sample, borderRadius: left.sample.borderRadius, gap: left.sample.gap }
16480
+ };
16481
+ for (const id of order) {
16482
+ if (!bySlot[id]) bySlot[id] = at(indexOf(id));
16483
+ }
16484
+ return tokensFromSamples(bySlot, left.sample.borderRadius, left.sample.gap);
16485
+ }
16486
+ function generateButtonSizes(samples) {
16487
+ const clusters = clusterSizes(samples.filter((sample) => sample.height > 0));
16488
+ if (clusters.length === 0) {
16489
+ return { status: "ready", tokens: tokensFromSamples(REFERENCE_SIZES, 8, 8) };
16490
+ }
16491
+ if (clusters.length > 3) return { status: "unresolved" };
16492
+ if (clusters.length === 3) {
16493
+ const sorted = [...clusters].sort((a, b) => a.height - b.height);
16494
+ const radius = sorted[1].borderRadius;
16495
+ const gap = sorted[1].gap;
16496
+ return {
16497
+ status: "ready",
16498
+ tokens: {
16499
+ s: geometryFromSample({ ...sorted[0], borderRadius: radius, gap }),
16500
+ m: geometryFromSample({ ...sorted[1], borderRadius: radius, gap }),
16501
+ l: geometryFromSample({ ...sorted[2], borderRadius: radius, gap })
16502
+ }
16503
+ };
16504
+ }
16505
+ if (clusters.length === 1) {
16506
+ const known = clusters[0];
16507
+ const slot = classifyHeight(known.height);
16508
+ const offset = BUTTON_SIZE_IDS.indexOf(slot);
16509
+ const bySlot = Object.fromEntries(
16510
+ BUTTON_SIZE_IDS.map((id, index) => [id, shiftSample(known, index - offset)])
16511
+ );
16512
+ bySlot[slot] = known;
16513
+ return { status: "ready", tokens: tokensFromSamples(bySlot, known.borderRadius, known.gap) };
16514
+ }
16515
+ const mapped = clusters.map((sample) => ({ id: classifyHeight(sample.height), sample }));
16516
+ if (mapped[0].id === mapped[1].id) {
16517
+ const larger = clusters.slice().sort((a, b) => b.height - a.height)[0];
16518
+ return generateButtonSizes([larger]);
16519
+ }
16520
+ return { status: "ready", tokens: interpolateSizes(mapped) };
16521
+ }
16522
+ function parseJson(raw) {
16523
+ if (!raw) return null;
16524
+ try {
16525
+ return JSON.parse(raw);
16526
+ } catch {
16527
+ return null;
16528
+ }
16529
+ }
16530
+ function isChrome(value) {
16531
+ if (!value || typeof value !== "object") return false;
16532
+ const chrome = value;
16533
+ return typeof chrome.background === "string" && typeof chrome.color === "string";
16534
+ }
16535
+ function isGeometry(value) {
16536
+ if (!value || typeof value !== "object") return false;
16537
+ const geometry = value;
16538
+ return typeof geometry.paddingTop === "string" && typeof geometry.height === "number";
16539
+ }
16540
+ function readVariantTokens(content) {
16541
+ const parsed = parseJson(content[BUTTON_VARIANTS_KEY]);
16542
+ if (!parsed) return null;
16543
+ if (!BUTTON_VARIANT_IDS.every((id) => isChrome(parsed[id]))) return null;
16544
+ return parsed;
16545
+ }
16546
+ function readSizeTokens(content) {
16547
+ const parsed = parseJson(content[BUTTON_SIZES_KEY]);
16548
+ if (!parsed) return null;
16549
+ if (!BUTTON_SIZE_IDS.every((id) => isGeometry(parsed[id]))) return null;
16550
+ return parsed;
16551
+ }
16552
+ function readAssignments(content) {
16553
+ const parsed = parseJson(content[BUTTON_ASSIGNMENTS_KEY]);
16554
+ if (!parsed || typeof parsed !== "object") return {};
16555
+ const next = {};
16556
+ for (const [id, value] of Object.entries(parsed)) {
16557
+ if (!value || !isButtonVariantId(value.variant) || !isButtonSizeId(value.size)) continue;
16558
+ next[id] = { variant: value.variant, size: value.size };
16559
+ }
16560
+ return next;
16561
+ }
16562
+ function cssLength(style, prop) {
16563
+ return style.getPropertyValue(prop) || "0px";
16564
+ }
16565
+ function readVariantSample(el) {
16566
+ const style = getComputedStyle(el);
16567
+ return {
16568
+ background: style.backgroundColor,
16569
+ color: style.color,
16570
+ borderColor: style.borderTopColor,
16571
+ borderWidth: style.borderTopWidth,
16572
+ className: el.className || ""
16573
+ };
16574
+ }
16575
+ function readSizeSample(el) {
16576
+ const style = getComputedStyle(el);
16577
+ const rootFont = parsePx(getComputedStyle(el.ownerDocument.documentElement).fontSize) || 16;
16578
+ const rect = el.getBoundingClientRect();
16579
+ return {
16580
+ height: rect.height,
16581
+ paddingTop: parsePx(cssLength(style, "padding-top"), rootFont),
16582
+ paddingRight: parsePx(cssLength(style, "padding-right"), rootFont),
16583
+ paddingBottom: parsePx(cssLength(style, "padding-bottom"), rootFont),
16584
+ paddingLeft: parsePx(cssLength(style, "padding-left"), rootFont),
16585
+ fontSize: parsePx(style.fontSize, rootFont),
16586
+ borderRadius: parsePx(style.borderTopLeftRadius, rootFont),
16587
+ gap: parsePx(style.columnGap || style.gap, rootFont)
16588
+ };
16589
+ }
16590
+ function readTypography(el) {
16591
+ const style = getComputedStyle(el);
16592
+ return {
16593
+ fontFamily: style.fontFamily,
16594
+ fontWeight: style.fontWeight,
16595
+ letterSpacing: style.letterSpacing,
16596
+ textTransform: style.textTransform,
16597
+ textDecoration: style.textDecorationLine || style.textDecoration || "none"
16598
+ };
16599
+ }
16600
+ var buttonOriginByEl = /* @__PURE__ */ new WeakMap();
16601
+ var ORIGIN_ATTR = "data-ohw-btn-origin";
16602
+ function parseOriginAttr(raw) {
16603
+ if (!raw) return null;
16604
+ try {
16605
+ const value = JSON.parse(raw);
16606
+ if (!value?.variant || !value.size || !value.typography) return null;
16607
+ return value;
16608
+ } catch {
16609
+ return null;
16610
+ }
16611
+ }
16612
+ function measureLiveOrigin(el) {
16613
+ return {
16614
+ variant: readVariantSample(el),
16615
+ size: readSizeSample(el),
16616
+ typography: readTypography(el)
16617
+ };
16618
+ }
16619
+ function captureButtonOrigin(el) {
16620
+ const cached = buttonOriginByEl.get(el);
16621
+ if (cached) return cached;
16622
+ const fromAttr = parseOriginAttr(el.getAttribute(ORIGIN_ATTR));
16623
+ if (fromAttr) {
16624
+ buttonOriginByEl.set(el, fromAttr);
16625
+ return fromAttr;
16626
+ }
16627
+ const origin = measureLiveOrigin(el);
16628
+ buttonOriginByEl.set(el, origin);
16629
+ try {
16630
+ el.setAttribute(ORIGIN_ATTR, JSON.stringify(origin));
16631
+ } catch {
16632
+ }
16633
+ return origin;
16634
+ }
16635
+ function listStyleButtons(doc) {
16636
+ return Array.from(doc.querySelectorAll(CTA_BUTTON_SELECTOR)).filter((el) => {
16637
+ if (el.closest("[data-ohw-bridge], [data-ohw-bridge-root]")) return false;
16638
+ const rect = el.getBoundingClientRect();
16639
+ return rect.width > 0 && rect.height > 0;
16640
+ });
16641
+ }
16642
+ function generateTokensFromButtons(buttons) {
16643
+ const variants = generateButtonVariants(buttons.map(readVariantSample));
16644
+ const sizes = generateButtonSizes(buttons.map(readSizeSample));
16645
+ if (variants.status !== "ready" || sizes.status !== "ready") return { status: "unresolved" };
16646
+ return { status: "ready", tokens: { variants: variants.tokens, sizes: sizes.tokens } };
16647
+ }
16648
+ function tokensFromButton(el) {
16649
+ const origin = captureButtonOrigin(el);
16650
+ const sample = origin.variant;
16651
+ const variants = generateButtonVariants([sample]);
16652
+ const sizes = generateButtonSizes([origin.size]);
16653
+ const primary = chromeFrom(sample);
16654
+ return {
16655
+ variants: variants.status === "ready" ? variants.tokens : { primary, secondary: generatedSecondary(primary), ghost: generatedGhost(primary) },
16656
+ sizes: sizes.status === "ready" ? sizes.tokens : tokensFromSamples(REFERENCE_SIZES, 8, 8)
16657
+ };
16658
+ }
16659
+ function readStoredStyleTokens(content) {
16660
+ const variants = readVariantTokens(content);
16661
+ const sizes = readSizeTokens(content);
16662
+ if (!variants || !sizes) return null;
16663
+ return { variants, sizes };
16664
+ }
16665
+ function styleTokenNodes(tokens) {
16666
+ return [
16667
+ { key: BUTTON_VARIANTS_KEY, text: JSON.stringify(tokens.variants) },
16668
+ { key: BUTTON_SIZES_KEY, text: JSON.stringify(tokens.sizes) }
16669
+ ];
16670
+ }
16671
+ function ensureButtonStyleTokens(doc, content) {
16672
+ const stored = readStoredStyleTokens(content);
16673
+ if (stored) return { tokens: stored, nodes: [] };
16674
+ const generated = generateTokensFromButtons(listStyleButtons(doc));
16675
+ if (generated.status !== "ready") return { tokens: null, nodes: [] };
16676
+ return { tokens: generated.tokens, nodes: styleTokenNodes(generated.tokens) };
16677
+ }
16678
+ function detectVariant(el, tokens) {
16679
+ const declared = declaredVariant(el.className || "");
16680
+ if (declared) return declared;
16681
+ const stamped = el.getAttribute("data-ohw-btn-variant");
16682
+ if (stamped && isButtonVariantId(stamped)) return stamped;
16683
+ const sample = readVariantSample(el);
16684
+ if (!tokens) return sampleSlot(sample);
16685
+ const key = fingerprint(sample);
16686
+ for (const id of BUTTON_VARIANT_IDS) {
16687
+ const chrome = tokens[id];
16688
+ const tokenKey = fingerprint({
16689
+ background: chrome.background,
16690
+ color: chrome.color,
16691
+ borderColor: chrome.borderColor,
16692
+ borderWidth: chrome.borderWidth,
16693
+ className: `btn btn--${id}`
16694
+ });
16695
+ if (tokenKey === key) return id;
16696
+ }
16697
+ return sampleSlot(sample);
16698
+ }
16699
+ function detectSize(el, tokens) {
16700
+ const stamped = el.getAttribute("data-ohw-btn-size");
16701
+ if (stamped && isButtonSizeId(stamped)) return stamped;
16702
+ const height = el.getBoundingClientRect().height;
16703
+ if (!tokens) return classifyHeight(height);
16704
+ let best = "m";
16705
+ let bestDelta = Number.POSITIVE_INFINITY;
16706
+ for (const id of BUTTON_SIZE_IDS) {
16707
+ const delta = Math.abs(tokens[id].height - height);
16708
+ if (delta < bestDelta) {
16709
+ best = id;
16710
+ bestDelta = delta;
16711
+ }
16712
+ }
16713
+ return best;
16714
+ }
16715
+ function assignmentFor(el, content, tokens) {
16716
+ const id = buttonId(el);
16717
+ const stored = id ? readAssignments(content)[id] : void 0;
16718
+ if (stored) return stored;
16719
+ return {
16720
+ variant: detectVariant(el, tokens?.variants ?? null),
16721
+ size: detectSize(el, tokens?.sizes ?? null)
16722
+ };
16723
+ }
16724
+ function labelElement(el) {
16725
+ return el.querySelector('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ?? (el.matches('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ? el : null);
16726
+ }
16727
+ var paintLockByEl = /* @__PURE__ */ new WeakMap();
16728
+ var paintObserverByEl = /* @__PURE__ */ new WeakMap();
16729
+ var paintingButton = false;
16730
+ function paintButtonChrome(el, assignment, tokens) {
16731
+ const origin = captureButtonOrigin(el);
16732
+ const chrome = tokens.variants[assignment.variant];
16733
+ const originSlot = classifyHeight(origin.size.height);
16734
+ const geometry = assignment.size === originSlot ? geometryFromSample({ ...origin.size, borderRadius: origin.size.borderRadius, gap: origin.size.gap }) : tokens.sizes[assignment.size];
16735
+ const { typography } = origin;
16736
+ el.setAttribute("data-ohw-btn-variant", assignment.variant);
16737
+ el.setAttribute("data-ohw-btn-size", assignment.size);
16738
+ el.style.transition = "none";
16739
+ el.style.boxSizing = "border-box";
16740
+ el.style.display = "inline-flex";
16741
+ el.style.alignItems = "center";
16742
+ el.style.justifyContent = "center";
16743
+ el.style.background = chrome.background;
16744
+ el.style.color = chrome.color;
16745
+ el.style.borderStyle = parseFloat(chrome.borderWidth) > 0 ? "solid" : "none";
16746
+ el.style.borderColor = chrome.borderColor;
16747
+ el.style.borderWidth = chrome.borderWidth;
16748
+ el.style.padding = `${geometry.paddingTop} ${geometry.paddingRight} ${geometry.paddingBottom} ${geometry.paddingLeft}`;
16749
+ el.style.fontSize = geometry.fontSize;
16750
+ el.style.borderRadius = px(origin.size.borderRadius);
16751
+ el.style.gap = geometry.gap;
16752
+ el.style.height = px(geometry.height);
16753
+ el.style.fontFamily = typography.fontFamily;
16754
+ el.style.fontWeight = typography.fontWeight;
16755
+ el.style.letterSpacing = typography.letterSpacing;
16756
+ el.style.textTransform = typography.textTransform;
16757
+ if (typography.textDecoration && typography.textDecoration !== "none") {
16758
+ el.style.textDecoration = typography.textDecoration;
16759
+ } else {
16760
+ el.style.textDecoration = "none";
16761
+ }
16762
+ const label = labelElement(el);
16763
+ if (label && label !== el) label.style.color = chrome.color;
16764
+ }
16765
+ function guardTemplateHover(el) {
16766
+ if (paintObserverByEl.has(el)) return;
16767
+ const observer = new MutationObserver(() => {
16768
+ if (paintingButton) return;
16769
+ const lock = paintLockByEl.get(el);
16770
+ if (!lock) return;
16771
+ paintingButton = true;
16772
+ paintButtonChrome(el, lock.assignment, lock.tokens);
16773
+ paintingButton = false;
16774
+ });
16775
+ observer.observe(el, { attributes: true, attributeFilter: ["style", "class"] });
16776
+ paintObserverByEl.set(el, observer);
16777
+ }
16778
+ function applyButtonStyle(el, assignment, tokens) {
16779
+ paintLockByEl.set(el, { assignment, tokens });
16780
+ paintingButton = true;
16781
+ paintButtonChrome(el, assignment, tokens);
16782
+ paintingButton = false;
16783
+ guardTemplateHover(el);
16784
+ }
16785
+ function applyButtonStylesFromContent(doc, content) {
16786
+ const assignments = readAssignments(content);
16787
+ for (const [id, assignment] of Object.entries(assignments)) {
16788
+ const el = doc.querySelector(`[data-ohw-href-key="${CSS.escape(id)}"]`);
16789
+ if (!el) continue;
16790
+ applyButtonStyle(el, assignment, tokensFromButton(el));
16791
+ }
16792
+ }
16793
+ function withAssignment(content, id, assignment) {
16794
+ return { ...readAssignments(content), [id]: assignment };
16795
+ }
16796
+
16797
+ // src/ui/button-style-panel.tsx
16798
+ import { jsx as jsx31, jsxs as jsxs21 } from "react/jsx-runtime";
16799
+ var SIZE_LABEL = { s: "S", m: "M", l: "L" };
16800
+ function ButtonStylePanel({
16801
+ variants,
16802
+ sizes,
16803
+ variant,
16804
+ size,
16805
+ onVariant,
16806
+ onSize,
16807
+ previewLabel = "Book Now",
16808
+ slotBackground = "var(--brand-background, var(--ohw-brand-light, var(--color-light, #ffffff)))"
16809
+ }) {
16810
+ const preview = scaleGeometry(sizes.m, 0.75);
16811
+ const label = previewLabel.trim() || "Book Now";
16812
+ return /* @__PURE__ */ jsxs21("div", { className: "flex w-full flex-col gap-5", children: [
16813
+ /* @__PURE__ */ jsxs21("section", { className: "flex w-full flex-col gap-2.5", children: [
16814
+ /* @__PURE__ */ jsx31("h3", { className: "text-xs font-semibold uppercase leading-4 tracking-[0.6px] text-stone-500 font-sans", children: "Variants" }),
16815
+ /* @__PURE__ */ jsx31("div", { className: "flex w-full flex-col gap-2.5", children: BUTTON_VARIANT_IDS.map((id) => {
16816
+ const chrome = variants[id];
16817
+ const selected = id === variant;
16818
+ const hasBorder = parseFloat(chrome.borderWidth) > 0;
16819
+ const cardSlot = previewSlotForChrome(chrome, slotBackground);
16820
+ return /* @__PURE__ */ jsxs21(
16821
+ "button",
16822
+ {
16823
+ type: "button",
16824
+ "aria-pressed": selected,
16825
+ "aria-label": id,
16826
+ onMouseDown: (e) => {
16827
+ e.preventDefault();
16828
+ e.stopPropagation();
16829
+ },
16830
+ onClick: () => onVariant(id),
16831
+ className: cn(
16832
+ "relative w-full overflow-hidden rounded-xl border-solid text-left",
16833
+ selected ? "border-2 border-primary" : "border border-border"
16834
+ ),
16835
+ children: [
16836
+ /* @__PURE__ */ jsx31(
16837
+ "div",
16838
+ {
16839
+ className: "flex w-full items-center justify-center py-4",
16840
+ style: { background: cardSlot },
16841
+ children: /* @__PURE__ */ jsx31(
16842
+ "span",
16843
+ {
16844
+ className: "inline-flex items-center justify-center whitespace-nowrap font-semibold uppercase leading-none",
16845
+ style: {
16846
+ background: chrome.background,
16847
+ color: chrome.color,
16848
+ borderColor: chrome.borderColor,
16849
+ borderWidth: hasBorder ? chrome.borderWidth : "0px",
16850
+ borderStyle: hasBorder ? "solid" : "none",
16851
+ padding: `${preview.paddingTop} ${preview.paddingRight} ${preview.paddingBottom} ${preview.paddingLeft}`,
16852
+ fontSize: preview.fontSize,
16853
+ borderRadius: preview.borderRadius,
16854
+ height: preview.height,
16855
+ boxSizing: "border-box",
16856
+ letterSpacing: "0.12em"
16857
+ },
16858
+ children: label
16859
+ }
16860
+ )
16861
+ }
16862
+ ),
16863
+ selected ? /* @__PURE__ */ jsx31(
16864
+ "span",
16865
+ {
16866
+ className: "absolute right-0.5 top-0.5 flex size-5 items-center justify-center rounded-full bg-primary text-primary-foreground",
16867
+ "aria-hidden": true,
16868
+ children: /* @__PURE__ */ jsx31(Check3, { className: "size-3", strokeWidth: 3 })
16869
+ }
16870
+ ) : null
16871
+ ]
16872
+ },
16873
+ id
16874
+ );
16875
+ }) })
16876
+ ] }),
16877
+ /* @__PURE__ */ jsxs21("section", { className: "flex w-full flex-col gap-2.5", children: [
16878
+ /* @__PURE__ */ jsx31("h3", { className: "text-xs font-semibold uppercase leading-4 tracking-[0.6px] text-stone-500 font-sans", children: "Size" }),
16879
+ /* @__PURE__ */ jsx31(
16880
+ "div",
16881
+ {
16882
+ className: "flex w-full items-start rounded-lg bg-stone-100 p-1",
16883
+ role: "radiogroup",
16884
+ "aria-label": "Size",
16885
+ children: BUTTON_SIZE_IDS.map((id) => {
16886
+ const selected = id === size;
16887
+ return /* @__PURE__ */ jsx31(
16888
+ "button",
16889
+ {
16890
+ type: "button",
16891
+ role: "radio",
16892
+ "aria-checked": selected,
16893
+ onMouseDown: (e) => {
16894
+ e.preventDefault();
16895
+ e.stopPropagation();
16896
+ },
16897
+ onClick: () => onSize(id),
16898
+ className: cn(
16899
+ "flex min-w-14 flex-1 items-center justify-center rounded-md px-3 py-1.5 text-sm font-medium leading-5",
16900
+ selected ? "bg-white text-stone-950 shadow-sm" : "bg-transparent text-stone-500"
16901
+ ),
16902
+ children: SIZE_LABEL[id]
16903
+ },
16904
+ id
16905
+ );
16906
+ })
16907
+ }
16908
+ )
16909
+ ] })
16910
+ ] });
16911
+ }
16912
+
16913
+ // src/lib/button-collections.ts
16914
+ var BUTTON_COLLECTIONS_KEY = "__ohw_button_collections";
16915
+ var BUTTON_SNAPSHOTS_KEY = "__ohw_button_snapshots";
16916
+ var BUTTON_HIDDEN_KEY = "__ohw_button_hidden";
16917
+ var COLLECTION_ATTR = "data-ohw-button-collection";
16918
+ function parseJson2(raw) {
16919
+ if (!raw) return null;
16920
+ try {
16921
+ return JSON.parse(raw);
16922
+ } catch {
16923
+ return null;
16924
+ }
16925
+ }
16926
+ function readCollections(content) {
16927
+ const parsed = parseJson2(content[BUTTON_COLLECTIONS_KEY]);
16928
+ if (!parsed || typeof parsed !== "object") return {};
16929
+ const next = {};
16930
+ for (const [id, value] of Object.entries(parsed)) {
16931
+ if (!value || !Array.isArray(value.members)) continue;
16932
+ next[id] = {
16933
+ id,
16934
+ members: value.members.filter((member) => typeof member === "string" && member),
16935
+ gap: typeof value.gap === "string" ? value.gap : "12px"
16936
+ };
16937
+ }
16938
+ return next;
16939
+ }
16940
+ function readSnapshots(content) {
16941
+ const parsed = parseJson2(content[BUTTON_SNAPSHOTS_KEY]);
16942
+ if (!parsed || typeof parsed !== "object") return {};
16943
+ const next = {};
16944
+ for (const [id, value] of Object.entries(parsed)) {
16945
+ if (!value || typeof value.html !== "string") continue;
16946
+ next[id] = value;
16947
+ }
16948
+ return next;
16949
+ }
16950
+ function readHiddenButtons(content) {
16951
+ const parsed = parseJson2(content[BUTTON_HIDDEN_KEY]);
16952
+ if (!Array.isArray(parsed)) return [];
16953
+ return parsed.filter((id) => typeof id === "string" && id);
16954
+ }
16955
+ function buttonCollectionOf(el) {
16956
+ return el?.closest(`[${COLLECTION_ATTR}]`) ?? null;
16957
+ }
16958
+ function isNavbarCta(el) {
16959
+ return el.getAttribute("data-ohw-role") === "navbar-button" || Boolean(el.closest('[data-ohw-role="navbar-button"]'));
16960
+ }
16961
+ function navbarScope(el) {
16962
+ return el.closest("nav, [data-ohw-nav-container], [data-ohw-nav-drawer]") ?? el.ownerDocument;
16963
+ }
16964
+ function isLastNavbarButton(el) {
16965
+ if (!isNavbarCta(el)) return false;
16966
+ const buttons = Array.from(navbarScope(el).querySelectorAll(CTA_BUTTON_SELECTOR)).filter(
16967
+ (button) => isNavbarCta(button) && buttonId(button)
16968
+ );
16969
+ return buttons.length <= 1;
16970
+ }
16971
+ function canDeleteButton(el) {
16972
+ return !isLastNavbarButton(el);
16973
+ }
16974
+ function canDuplicateButton(el) {
16975
+ return isNavbarCta(el);
16976
+ }
16977
+ function labelElement2(el) {
16978
+ return el.querySelector('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ?? el.querySelector("[data-ohw-key]");
16979
+ }
16980
+ function buttonLabel(el) {
16981
+ return (labelElement2(el)?.textContent ?? el.textContent ?? "").trim();
16982
+ }
16983
+ function buttonHref(el) {
16984
+ return el.getAttribute("href") ?? "";
16985
+ }
16986
+ function hrefKeyBase(hrefKey) {
16987
+ return hrefKey.replace(/-href$/, "").replace(/-copy-\d+$/, "");
16988
+ }
16989
+ function labelKeyForHrefKey(hrefKey) {
16990
+ return `${hrefKey.replace(/-href$/, "")}-label`;
16991
+ }
16992
+ function cleanClone(el) {
16993
+ const copy = el.cloneNode(true);
16994
+ copy.querySelectorAll("[data-ohw-selected], [data-ohw-hovered], [data-ohw-editing]").forEach((node) => {
16995
+ node.removeAttribute("data-ohw-selected");
16996
+ node.removeAttribute("data-ohw-hovered");
16997
+ node.removeAttribute("data-ohw-editing");
16998
+ node.removeAttribute("contenteditable");
16999
+ });
17000
+ copy.removeAttribute("data-ohw-selected");
17001
+ copy.removeAttribute("data-ohw-hovered");
17002
+ copy.removeAttribute("data-ohw-editing");
17003
+ copy.removeAttribute("contenteditable");
17004
+ copy.removeAttribute("data-ohw-drag-disabled");
17005
+ return copy;
17006
+ }
17007
+ function nextCopyId(sourceId, content, doc) {
17008
+ const used = /* @__PURE__ */ new Set([
17009
+ ...Object.keys(readSnapshots(content)),
17010
+ ...Object.keys(readAssignments(content)),
17011
+ ...Array.from(doc.querySelectorAll("[data-ohw-href-key]")).map(
17012
+ (el) => el.getAttribute("data-ohw-href-key") ?? ""
17013
+ )
17014
+ ]);
17015
+ const base = hrefKeyBase(sourceId);
17016
+ let n = 2;
17017
+ while (used.has(`${base}-copy-${n}-href`)) n += 1;
17018
+ return `${base}-copy-${n}-href`;
17019
+ }
17020
+ function nextCollectionId(content, doc) {
17021
+ const used = /* @__PURE__ */ new Set([
17022
+ ...Object.keys(readCollections(content)),
17023
+ ...Array.from(doc.querySelectorAll(`[${COLLECTION_ATTR}]`)).map(
17024
+ (el) => el.getAttribute(COLLECTION_ATTR) ?? ""
17025
+ )
17026
+ ]);
17027
+ let n = 1;
17028
+ while (used.has(`bc-${n}`)) n += 1;
17029
+ return `bc-${n}`;
17030
+ }
17031
+ var COLLECTION_GAP_MAX_PX = 12;
17032
+ var COLLECTION_GAP_DEFAULT = "12px";
17033
+ function usableGap(value) {
17034
+ if (!value || value === "normal") return null;
17035
+ const n = parseFloat(value);
17036
+ if (!Number.isFinite(n) || n <= 0) return null;
17037
+ if (n > COLLECTION_GAP_MAX_PX) return COLLECTION_GAP_DEFAULT;
17038
+ return `${Math.round(n * 100) / 100}px`;
17039
+ }
17040
+ function collectionGap(el, tokens) {
17041
+ const fromCollection = usableGap(buttonCollectionOf(el)?.style.gap);
17042
+ if (fromCollection) return fromCollection;
17043
+ const parent = el.parentElement;
17044
+ if (parent) {
17045
+ const style = getComputedStyle(parent);
17046
+ const fromParent = usableGap(style.columnGap) ?? usableGap(style.gap);
17047
+ if (fromParent) return fromParent;
17048
+ }
17049
+ const fromTokens = usableGap(tokens?.sizes.m.gap);
17050
+ if (fromTokens) return fromTokens;
17051
+ return COLLECTION_GAP_DEFAULT;
17052
+ }
17053
+ function styleCollection(collection, gap) {
17054
+ collection.style.display = "inline-flex";
17055
+ collection.style.alignItems = "center";
17056
+ collection.style.flexWrap = "wrap";
17057
+ collection.style.gap = gap;
17058
+ collection.style.maxWidth = "100%";
17059
+ }
17060
+ function createCollection(doc, id, gap) {
17061
+ const collection = doc.createElement("div");
17062
+ collection.setAttribute(COLLECTION_ATTR, id);
17063
+ styleCollection(collection, gap);
17064
+ return collection;
17065
+ }
17066
+ function setButtonDragAttr(el) {
17067
+ if (isNavbarCta(el) && buttonCollectionOf(el)) el.removeAttribute("data-ohw-drag-disabled");
17068
+ else el.setAttribute("data-ohw-drag-disabled", "true");
17069
+ }
17070
+ function rekeyCopy(copy, id) {
17071
+ copy.setAttribute("data-ohw-href-key", id);
17072
+ const label = labelElement2(copy);
17073
+ const labelKey = labelKeyForHrefKey(id);
17074
+ if (label) label.setAttribute("data-ohw-key", labelKey);
17075
+ setButtonDragAttr(copy);
17076
+ return { labelKey };
17077
+ }
17078
+ function stampVariantClass(el, variant) {
17079
+ const raw = typeof el.className === "string" ? el.className : el.getAttribute("class") || "";
17080
+ const without = raw.replace(/\bbtn--(?:primary|secondary|ghost)\b/g, "").replace(/\s+/g, " ").trim();
17081
+ el.className = without ? `${without} btn--${variant}` : `btn btn--${variant}`;
17082
+ }
17083
+ function snapshotOf(el) {
17084
+ const id = buttonId(el) ?? "";
17085
+ const label = labelElement2(el);
17086
+ return {
17087
+ id,
17088
+ href: buttonHref(el),
17089
+ label: buttonLabel(el),
17090
+ labelKey: label?.getAttribute("data-ohw-key") ?? labelKeyForHrefKey(id),
17091
+ html: cleanClone(el).outerHTML
17092
+ };
17093
+ }
17094
+ function htmlToElement(doc, html) {
17095
+ const wrap = doc.createElement("div");
17096
+ wrap.innerHTML = html;
17097
+ const el = wrap.firstElementChild;
17098
+ return el instanceof HTMLElement ? el : null;
17099
+ }
17100
+ function findButton(doc, id) {
17101
+ return doc.querySelector(`[data-ohw-href-key="${CSS.escape(id)}"]`);
17102
+ }
17103
+ function duplicateButton(source, content) {
17104
+ if (!canDuplicateButton(source)) return null;
17105
+ const sourceId = buttonId(source);
17106
+ if (!sourceId) return null;
17107
+ const doc = source.ownerDocument;
17108
+ const tokens = tokensFromButton(source);
17109
+ const copyId = nextCopyId(sourceId, content, doc);
17110
+ const copy = cleanClone(source);
17111
+ const { labelKey } = rekeyCopy(copy, copyId);
17112
+ const href = buttonHref(source);
17113
+ const label = buttonLabel(source);
17114
+ if (href) copy.setAttribute("href", href);
17115
+ const sourceAssignment = readAssignments(content)[sourceId] ?? assignmentFor(source, content, tokens);
17116
+ const copyAssignment = {
17117
+ variant: "secondary",
17118
+ size: sourceAssignment.size
17119
+ };
17120
+ stampVariantClass(copy, "secondary");
17121
+ applyButtonStyle(copy, copyAssignment, tokens);
17122
+ const copyLabelEl = labelElement2(copy);
17123
+ if (copyLabelEl) copyLabelEl.textContent = label;
17124
+ let collection = buttonCollectionOf(source);
17125
+ const collections = readCollections(content);
17126
+ let collectionId = collection?.getAttribute(COLLECTION_ATTR) ?? "";
17127
+ let members = collectionId && collections[collectionId] ? [...collections[collectionId].members] : [];
17128
+ const gap = collectionGap(source, tokens);
17129
+ if (!collection) {
17130
+ collectionId = nextCollectionId(content, doc);
17131
+ collection = createCollection(doc, collectionId, gap);
17132
+ source.parentElement?.insertBefore(collection, source);
17133
+ collection.append(source, copy);
17134
+ setButtonDragAttr(source);
17135
+ setButtonDragAttr(copy);
17136
+ members = [sourceId, copyId];
17137
+ } else {
17138
+ styleCollection(collection, gap);
17139
+ source.after(copy);
17140
+ setButtonDragAttr(copy);
17141
+ if (!members.includes(sourceId)) members = listCollectionMembers(collection);
17142
+ const at = members.indexOf(sourceId);
17143
+ members.splice(at >= 0 ? at + 1 : members.length, 0, copyId);
17144
+ }
17145
+ const sourceLabelKey = labelElement2(source)?.getAttribute("data-ohw-key");
17146
+ const snapshots = {
17147
+ ...readSnapshots(content),
17148
+ [sourceId]: snapshotOf(source),
17149
+ [copyId]: snapshotOf(copy)
17150
+ };
17151
+ const nextCollections = {
17152
+ ...collections,
17153
+ [collectionId]: { id: collectionId, members, gap }
17154
+ };
17155
+ const assignments = withAssignment(content, copyId, copyAssignment);
17156
+ const nextContent = {
17157
+ ...content,
17158
+ [copyId]: href,
17159
+ [labelKey]: label,
17160
+ [BUTTON_COLLECTIONS_KEY]: JSON.stringify(nextCollections),
17161
+ [BUTTON_SNAPSHOTS_KEY]: JSON.stringify(snapshots),
17162
+ [BUTTON_ASSIGNMENTS_KEY]: JSON.stringify(assignments)
17163
+ };
17164
+ if (sourceLabelKey && nextContent[sourceLabelKey] === void 0) {
17165
+ nextContent[sourceLabelKey] = label;
17166
+ }
17167
+ const nodeKeys = [
17168
+ copyId,
17169
+ labelKey,
17170
+ BUTTON_COLLECTIONS_KEY,
17171
+ BUTTON_SNAPSHOTS_KEY,
17172
+ BUTTON_ASSIGNMENTS_KEY
17173
+ ];
17174
+ if (sourceLabelKey) nodeKeys.push(sourceLabelKey);
17175
+ return {
17176
+ copy,
17177
+ content: nextContent,
17178
+ nodes: contentNodes(nextContent, nodeKeys)
17179
+ };
17180
+ }
17181
+ function listCollectionMembers(collection) {
17182
+ return Array.from(collection.querySelectorAll(CTA_BUTTON_SELECTOR)).map((el) => buttonId(el)).filter((id) => Boolean(id));
17183
+ }
17184
+ function contentNodes(content, keys) {
17185
+ return keys.filter((key) => content[key] !== void 0).map((key) => ({ key, text: content[key] }));
17186
+ }
17187
+ function deleteButton(source, content) {
17188
+ if (!canDeleteButton(source)) return null;
17189
+ const id = buttonId(source);
17190
+ if (!id) return null;
17191
+ const parent = source.parentElement;
17192
+ const nextSibling = source.nextSibling;
17193
+ const removed = source;
17194
+ const collection = buttonCollectionOf(source);
17195
+ const collectionId = collection?.getAttribute(COLLECTION_ATTR) ?? "";
17196
+ const previousCollections = readCollections(content);
17197
+ const previousHidden = readHiddenButtons(content);
17198
+ const snapshots = { ...readSnapshots(content) };
17199
+ const assignments = { ...readAssignments(content) };
17200
+ delete assignments[id];
17201
+ const remainingBeforeUnwrap = collection ? listCollectionMembers(collection).filter((member) => member !== id) : [];
17202
+ removed.remove();
17203
+ let nextCollections = { ...previousCollections };
17204
+ let unwrapped = null;
17205
+ if (collection && collectionId) {
17206
+ const members = listCollectionMembers(collection);
17207
+ if (members.length <= 1) {
17208
+ unwrapped = collection;
17209
+ unwrapCollection(collection);
17210
+ delete nextCollections[collectionId];
17211
+ } else {
17212
+ nextCollections[collectionId] = {
17213
+ id: collectionId,
17214
+ members,
17215
+ gap: collection.style.gap || previousCollections[collectionId]?.gap || "12px"
17216
+ };
17217
+ }
17218
+ }
17219
+ const hidden = previousHidden.includes(id) ? previousHidden : [...previousHidden, id];
17220
+ const nextContent = {
17221
+ ...content,
17222
+ [id]: "",
17223
+ [BUTTON_COLLECTIONS_KEY]: JSON.stringify(nextCollections),
17224
+ [BUTTON_HIDDEN_KEY]: JSON.stringify(hidden),
17225
+ [BUTTON_SNAPSHOTS_KEY]: JSON.stringify(snapshots),
17226
+ [BUTTON_ASSIGNMENTS_KEY]: JSON.stringify(assignments)
17227
+ };
17228
+ return {
17229
+ content: nextContent,
17230
+ nodes: contentNodes(nextContent, [id, BUTTON_COLLECTIONS_KEY, BUTTON_HIDDEN_KEY, BUTTON_SNAPSHOTS_KEY, BUTTON_ASSIGNMENTS_KEY]),
17231
+ undo: () => {
17232
+ if (unwrapped && remainingBeforeUnwrap.length === 1) {
17233
+ const remaining = findButton(removed.ownerDocument, remainingBeforeUnwrap[0]);
17234
+ const host = remaining?.parentElement;
17235
+ if (remaining && host) {
17236
+ const restoredCollection = createCollection(
17237
+ removed.ownerDocument,
17238
+ collectionId,
17239
+ previousCollections[collectionId]?.gap || "12px"
17240
+ );
17241
+ host.insertBefore(restoredCollection, remaining);
17242
+ restoredCollection.append(remaining, removed);
17243
+ return;
17244
+ }
17245
+ }
17246
+ if (nextSibling && nextSibling.parentNode === parent) parent?.insertBefore(removed, nextSibling);
17247
+ else parent?.appendChild(removed);
17248
+ }
17249
+ };
17250
+ }
17251
+ function unwrapCollection(collection) {
17252
+ const parent = collection.parentElement;
17253
+ if (!parent) return;
17254
+ while (collection.firstChild) parent.insertBefore(collection.firstChild, collection);
17255
+ collection.remove();
17256
+ }
17257
+ function reorderButtonInCollection(button, clientX) {
17258
+ const collection = buttonCollectionOf(button);
17259
+ const id = buttonId(button);
17260
+ if (!collection || !id) return null;
17261
+ const siblings = Array.from(collection.querySelectorAll(CTA_BUTTON_SELECTOR)).filter((el) => el !== button);
17262
+ let placed = false;
17263
+ for (const sibling of siblings) {
17264
+ const rect = sibling.getBoundingClientRect();
17265
+ if (clientX < rect.left + rect.width / 2) {
17266
+ sibling.before(button);
17267
+ placed = true;
17268
+ break;
17269
+ }
17270
+ }
17271
+ if (!placed) collection.append(button);
17272
+ return listCollectionMembers(collection);
17273
+ }
17274
+ function listVisibleCollectionButtons(collection) {
17275
+ return Array.from(collection.querySelectorAll(CTA_BUTTON_SELECTOR)).filter((el) => {
17276
+ if (!buttonId(el)) return false;
17277
+ return el.getClientRects().length > 0;
17278
+ });
17279
+ }
17280
+ function isVerticalButtonLayout(items, collection) {
17281
+ if (items.length >= 2) {
17282
+ const a = items[0].getBoundingClientRect();
17283
+ const b = items[1].getBoundingClientRect();
17284
+ return b.top - a.bottom > b.left - a.right;
17285
+ }
17286
+ const style = window.getComputedStyle(collection);
17287
+ return style.flexDirection === "column" || style.flexDirection === "column-reverse";
17288
+ }
17289
+ function buildButtonCollectionDropSlots(collection) {
17290
+ const items = listVisibleCollectionButtons(collection);
17291
+ const barThickness = 3;
17292
+ const slots = [];
17293
+ if (items.length === 0) {
17294
+ const rect = collection.getBoundingClientRect();
17295
+ slots.push({
17296
+ insertIndex: 0,
17297
+ left: rect.left + rect.width / 2 - barThickness / 2,
17298
+ top: rect.top,
17299
+ width: barThickness,
17300
+ height: Math.max(rect.height, 24),
17301
+ direction: "vertical"
17302
+ });
17303
+ return slots;
17304
+ }
17305
+ if (isVerticalButtonLayout(items, collection)) {
17306
+ const edgeGap2 = items.length >= 2 ? Math.max(0, items[1].getBoundingClientRect().top - items[0].getBoundingClientRect().bottom) : 16;
17307
+ for (let i = 0; i <= items.length; i++) {
17308
+ let top;
17309
+ let left;
17310
+ let width;
17311
+ if (i === 0) {
17312
+ const first = items[0].getBoundingClientRect();
17313
+ top = first.top - edgeGap2 / 2 - barThickness / 2;
17314
+ left = first.left;
17315
+ width = first.width;
17316
+ } else if (i === items.length) {
17317
+ const last = items[items.length - 1].getBoundingClientRect();
17318
+ const lastGap = items.length >= 2 ? Math.max(0, last.top - items[items.length - 2].getBoundingClientRect().bottom) : edgeGap2;
17319
+ top = last.bottom + lastGap / 2 - barThickness / 2;
17320
+ left = last.left;
17321
+ width = last.width;
17322
+ } else {
17323
+ const prev = items[i - 1].getBoundingClientRect();
17324
+ const next = items[i].getBoundingClientRect();
17325
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
17326
+ left = Math.min(prev.left, next.left);
17327
+ width = Math.max(prev.right, next.right) - left;
17328
+ }
17329
+ slots.push({
17330
+ insertIndex: i,
17331
+ left,
17332
+ top,
17333
+ width: Math.max(width, 40),
17334
+ height: barThickness,
17335
+ direction: "horizontal"
17336
+ });
17337
+ }
17338
+ return slots;
17339
+ }
17340
+ const edgeGap = items.length >= 2 ? Math.max(0, items[1].getBoundingClientRect().left - items[0].getBoundingClientRect().right) : 12;
17341
+ for (let i = 0; i <= items.length; i++) {
17342
+ let left;
17343
+ let top;
17344
+ let height;
17345
+ if (i === 0) {
17346
+ const first = items[0].getBoundingClientRect();
17347
+ left = first.left - edgeGap / 2 - barThickness / 2;
17348
+ top = first.top;
17349
+ height = first.height;
17350
+ } else if (i === items.length) {
17351
+ const last = items[items.length - 1].getBoundingClientRect();
17352
+ const lastGap = items.length >= 2 ? Math.max(0, last.left - items[items.length - 2].getBoundingClientRect().right) : edgeGap;
17353
+ left = last.right + lastGap / 2 - barThickness / 2;
17354
+ top = last.top;
17355
+ height = last.height;
17356
+ } else {
17357
+ const prev = items[i - 1].getBoundingClientRect();
17358
+ const next = items[i].getBoundingClientRect();
17359
+ left = (prev.right + next.left) / 2 - barThickness / 2;
17360
+ top = Math.min(prev.top, next.top);
17361
+ height = Math.max(prev.bottom, next.bottom) - top;
17362
+ }
17363
+ slots.push({
17364
+ insertIndex: i,
17365
+ left,
17366
+ top,
17367
+ width: barThickness,
17368
+ height: Math.max(height, 24),
17369
+ direction: "vertical"
17370
+ });
17371
+ }
17372
+ return slots;
17373
+ }
17374
+ function hitTestButtonCollectionDropSlot(collection, clientX, clientY) {
17375
+ const slots = buildButtonCollectionDropSlots(collection);
17376
+ let best = null;
17377
+ for (const slot of slots) {
17378
+ const cx2 = slot.left + slot.width / 2;
17379
+ const cy = slot.top + slot.height / 2;
17380
+ const dist = slot.direction === "vertical" ? Math.abs(clientX - cx2) + Math.abs(clientY - cy) * 0.25 : Math.abs(clientY - cy) + Math.abs(clientX - cx2) * 0.25;
17381
+ if (!best || dist < best.dist) best = { slot, dist };
17382
+ }
17383
+ return best?.slot ?? null;
17384
+ }
17385
+ function insertButtonAtCollectionIndex(button, insertIndex) {
17386
+ const collection = buttonCollectionOf(button);
17387
+ if (!collection) return null;
17388
+ const items = listVisibleCollectionButtons(collection);
17389
+ const from = items.indexOf(button);
17390
+ if (from < 0) return null;
17391
+ const others = items.filter((el) => el !== button);
17392
+ let index = insertIndex;
17393
+ if (from < insertIndex) index -= 1;
17394
+ if (index <= 0) {
17395
+ const first = others[0];
17396
+ if (first) first.before(button);
17397
+ else collection.append(button);
17398
+ } else if (index >= others.length) {
17399
+ collection.append(button);
17400
+ } else {
17401
+ others[index].before(button);
17402
+ }
17403
+ return listCollectionMembers(collection);
17404
+ }
17405
+ function persistCollectionOrder(collection, content) {
17406
+ const id = collection.getAttribute(COLLECTION_ATTR);
17407
+ if (!id) return null;
17408
+ const collections = readCollections(content);
17409
+ const current = collections[id];
17410
+ const members = listCollectionMembers(collection);
17411
+ const next = {
17412
+ ...collections,
17413
+ [id]: { id, members, gap: collection.style.gap || current?.gap || "12px" }
17414
+ };
17415
+ const nextContent = { ...content, [BUTTON_COLLECTIONS_KEY]: JSON.stringify(next) };
17416
+ return { content: nextContent, nodes: [{ key: BUTTON_COLLECTIONS_KEY, text: nextContent[BUTTON_COLLECTIONS_KEY] }] };
17417
+ }
17418
+ function hideButton(el) {
17419
+ if (el.getAttribute("data-ohw-button-hidden") === "") return;
17420
+ el.setAttribute("data-ohw-button-hidden", "");
17421
+ el.style.display = "none";
17422
+ }
17423
+ function syncButtonsFromContent(doc, content) {
17424
+ reconcileButtonCollections(doc, content);
17425
+ applyButtonContentFromContent(doc, content);
17426
+ applyButtonStylesFromContent(doc, content);
17427
+ }
17428
+ function applyButtonContentFromContent(doc, content) {
17429
+ const snapshots = readSnapshots(content);
17430
+ for (const snapshot of Object.values(snapshots)) {
17431
+ const el = findButton(doc, snapshot.id);
17432
+ if (!el) continue;
17433
+ applySnapshotContent(el, snapshot, content);
17434
+ }
17435
+ }
17436
+ function applySnapshotContent(el, snapshot, content) {
17437
+ const href = content[snapshot.id] ?? snapshot.href;
17438
+ if (href) el.setAttribute("href", href);
17439
+ const labelKey = snapshot.labelKey || labelKeyForHrefKey(snapshot.id);
17440
+ if (labelKey && !el.querySelector(`[data-ohw-key="${CSS.escape(labelKey)}"]`)) {
17441
+ const label2 = labelElement2(el);
17442
+ if (label2) label2.setAttribute("data-ohw-key", labelKey);
17443
+ }
17444
+ const text = content[labelKey];
17445
+ if (text === void 0) return;
17446
+ const label = labelElement2(el);
17447
+ if (label) {
17448
+ if (label.getAttribute("data-ohw-key") !== labelKey) label.setAttribute("data-ohw-key", labelKey);
17449
+ if (label.textContent !== text) label.textContent = text;
17450
+ }
17451
+ }
17452
+ function reconcileButtonCollections(doc, content) {
17453
+ const hidden = new Set(readHiddenButtons(content));
17454
+ for (const id of hidden) {
17455
+ const el = findButton(doc, id);
17456
+ if (el) hideButton(el);
17457
+ }
17458
+ const snapshots = readSnapshots(content);
17459
+ const collections = readCollections(content);
17460
+ for (const collectionState of Object.values(collections)) {
17461
+ const live = collectionState.members.map((id) => findButton(doc, id) ?? restoreSnapshot(doc, snapshots[id], content)).filter((el) => Boolean(el && !hidden.has(buttonId(el) ?? "")));
17462
+ if (live.length === 0) continue;
17463
+ if (live.length === 1) {
17464
+ const existing = buttonCollectionOf(live[0]);
17465
+ if (existing) unwrapCollection(existing);
17466
+ continue;
17467
+ }
17468
+ const anchor = live.find((el) => el.isConnected) ?? live[0];
17469
+ let collection = live.map((el) => buttonCollectionOf(el)).find((el) => el?.getAttribute(COLLECTION_ATTR) === collectionState.id) ?? null;
17470
+ if (!collection) {
17471
+ if (!anchor.parentElement) continue;
17472
+ collection = createCollection(doc, collectionState.id, collectionState.gap);
17473
+ anchor.parentElement.insertBefore(collection, anchor);
17474
+ }
17475
+ styleCollection(collection, collectionState.gap);
17476
+ const current = listCollectionMembers(collection);
17477
+ const sameOrder = current.length === live.length && current.every((id, index) => id === buttonId(live[index]));
17478
+ if (!sameOrder) {
17479
+ for (const member of live) collection.append(member);
17480
+ }
17481
+ for (const member of live) setButtonDragAttr(member);
17482
+ }
17483
+ }
17484
+ function restoreSnapshot(doc, snapshot, content) {
17485
+ if (!snapshot) return null;
17486
+ const existing = findButton(doc, snapshot.id);
17487
+ if (existing) {
17488
+ applySnapshotContent(existing, snapshot, content);
17489
+ return existing;
17490
+ }
17491
+ const el = htmlToElement(doc, snapshot.html);
17492
+ if (!el) return null;
17493
+ el.setAttribute("data-ohw-href-key", snapshot.id);
17494
+ const label = labelElement2(el);
17495
+ const labelKey = snapshot.labelKey || labelKeyForHrefKey(snapshot.id);
17496
+ if (label) label.setAttribute("data-ohw-key", labelKey);
17497
+ applySnapshotContent(el, snapshot, content);
17498
+ setButtonDragAttr(el);
17499
+ return el;
17500
+ }
17501
+ function navbarCtasInScope(el) {
17502
+ const scope = el.closest("nav, [data-ohw-nav-root], [data-ohw-nav-drawer]") ?? el.parentElement;
17503
+ if (!scope) return [];
17504
+ return Array.from(scope.querySelectorAll(CTA_BUTTON_SELECTOR)).filter(
17505
+ (button) => isNavbarCta(button) && buttonId(button)
17506
+ );
17507
+ }
17508
+ function ensureNavbarButtonCollection(button, content) {
17509
+ const existing = buttonCollectionOf(button);
17510
+ if (existing) return existing;
17511
+ if (!isNavbarCta(button) || !buttonId(button)) return null;
17512
+ const parent = button.parentElement;
17513
+ if (!parent) return null;
17514
+ const siblings = navbarCtasInScope(button).filter((el) => el.parentElement === parent);
17515
+ if (siblings.length < 2) return null;
17516
+ const id = nextCollectionId(content, button.ownerDocument);
17517
+ const collection = createCollection(button.ownerDocument, id, collectionGap(button, tokensFromButton(button)));
17518
+ siblings[0].before(collection);
17519
+ for (const member of siblings) {
17520
+ collection.append(member);
17521
+ setButtonDragAttr(member);
17522
+ }
17523
+ return collection;
17524
+ }
17525
+ function collectionDragEnabled(el) {
17526
+ if (!isNavbarCta(el) || !buttonId(el)) return false;
17527
+ if (buttonCollectionOf(el)) return true;
17528
+ return navbarCtasInScope(el).length >= 2;
17529
+ }
17530
+
17531
+ // src/lib/item-drag-interaction.ts
17532
+ function disableNativeHrefDrag(el) {
17533
+ if (el.draggable) el.draggable = false;
17534
+ if (el.getAttribute("draggable") !== "false") {
17535
+ el.setAttribute("draggable", "false");
17536
+ }
17537
+ }
17538
+ function clearTextSelection() {
17539
+ const sel = window.getSelection();
17540
+ if (sel && !sel.isCollapsed) sel.removeAllRanges();
17541
+ }
17542
+ function armItemPressDrag() {
17543
+ document.documentElement.setAttribute("data-ohw-footer-press-drag", "");
17544
+ }
17545
+ function lockItemDuringDrag() {
17546
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
17547
+ document.documentElement.setAttribute("data-ohw-item-dragging", "");
17548
+ clearTextSelection();
17549
+ }
17550
+ function unlockItemDragInteraction() {
17551
+ const wasDragging = document.documentElement.hasAttribute("data-ohw-item-dragging");
17552
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
17553
+ document.documentElement.removeAttribute("data-ohw-item-dragging");
17554
+ if (wasDragging) clearTextSelection();
17555
+ }
17556
+ var armFooterPressDrag = armItemPressDrag;
17557
+ var lockFooterDuringDrag = lockItemDuringDrag;
17558
+ var unlockFooterDragInteraction = unlockItemDragInteraction;
17559
+
17560
+ // src/lib/nav-dnd.ts
17561
+ function listReorderableNavItems(draggedEl) {
17562
+ return listActiveNavbarItems(draggedEl).filter((el) => {
17563
+ const key = el.getAttribute("data-ohw-href-key");
17564
+ return isNavbarHrefKey(key) && !isNestedNavChild(el);
17565
+ });
17566
+ }
17567
+ function resolveParentNavHrefKey(child) {
17568
+ const childrenRoot = child.closest("[data-ohw-nav-children]");
17569
+ const group = childrenRoot?.closest("[data-ohw-nav-group]");
17570
+ if (!group) return null;
17571
+ const trigger = group.querySelector(":scope > [data-ohw-href-key]") ?? Array.from(group.querySelectorAll("[data-ohw-href-key]")).find(
17572
+ (el) => !el.closest("[data-ohw-nav-children]")
17573
+ ) ?? null;
17574
+ const key = trigger?.getAttribute("data-ohw-href-key");
17575
+ return key && isNavbarHrefKey(key) ? key : null;
17576
+ }
17577
+ function listNavChildren(parentHrefKey, draggedEl) {
17578
+ const items = listActiveNavbarItems(draggedEl);
17579
+ const parent = items.find((el) => el.getAttribute("data-ohw-href-key") === parentHrefKey);
17580
+ if (!parent) return [];
17581
+ const group = parent.closest("[data-ohw-nav-group]");
17582
+ const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
17583
+ if (!childrenRoot) return [];
17584
+ return Array.from(childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
17585
+ (el) => isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))
17586
+ );
17587
+ }
17588
+ function isVerticalNavLayout(items, draggedEl) {
17589
+ const visible = items.filter((el) => el.getClientRects().length > 0);
17590
+ if (visible.length >= 2) {
17591
+ const a = visible[0].getBoundingClientRect();
17592
+ const b = visible[1].getBoundingClientRect();
17593
+ const verticalGap = b.top - a.bottom;
17594
+ const horizontalGap = b.left - a.right;
17595
+ return verticalGap > horizontalGap;
17596
+ }
17597
+ const container = getActiveNavbarListContainer(draggedEl);
17598
+ if (!container) return false;
17599
+ const style = window.getComputedStyle(container);
17600
+ return style.flexDirection === "column" || style.flexDirection === "column-reverse";
17601
+ }
17602
+ function buildVerticalRootNavDropSlots(items, draggedEl) {
17603
+ const slots = [];
17604
+ const barThickness = 3;
17605
+ if (items.length === 0) {
17606
+ const container = getActiveNavbarListContainer(draggedEl);
17607
+ if (!container) return slots;
17608
+ const rect = container.getBoundingClientRect();
17609
+ slots.push({
17610
+ insertIndex: 0,
17611
+ parentId: null,
17612
+ left: rect.left,
17613
+ top: rect.top + rect.height / 2 - barThickness / 2,
17614
+ width: Math.max(rect.width, 40),
17615
+ height: barThickness,
17616
+ direction: "horizontal"
17617
+ });
17618
+ return slots;
17619
+ }
17620
+ const edgeGap = (() => {
17621
+ if (items.length < 2) return 16;
17622
+ const a = items[0].getBoundingClientRect();
17623
+ const b = items[1].getBoundingClientRect();
17624
+ return Math.max(0, b.top - a.bottom);
17625
+ })();
17626
+ for (let i = 0; i <= items.length; i++) {
17627
+ let top;
17628
+ let width;
17629
+ let left;
17630
+ if (i === 0) {
17631
+ const first = items[0].getBoundingClientRect();
17632
+ top = first.top - edgeGap / 2 - barThickness / 2;
17633
+ left = first.left;
17634
+ width = first.width;
17635
+ } else if (i === items.length) {
17636
+ const last = items[items.length - 1].getBoundingClientRect();
17637
+ const lastGap = items.length >= 2 ? Math.max(0, last.top - items[items.length - 2].getBoundingClientRect().bottom) : edgeGap;
17638
+ top = last.bottom + lastGap / 2 - barThickness / 2;
17639
+ left = last.left;
17640
+ width = last.width;
17641
+ } else {
17642
+ const prev = items[i - 1].getBoundingClientRect();
17643
+ const next = items[i].getBoundingClientRect();
17644
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
17645
+ left = Math.min(prev.left, next.left);
17646
+ width = Math.max(prev.right, next.right) - left;
17647
+ }
17648
+ slots.push({
17649
+ insertIndex: i,
17650
+ parentId: null,
17651
+ left,
17652
+ top,
17653
+ width: Math.max(width, 40),
17654
+ height: barThickness,
17655
+ direction: "horizontal"
17656
+ });
17657
+ }
17658
+ return slots;
17659
+ }
17660
+ function buildHorizontalRootNavDropSlots(items, draggedEl) {
17661
+ const slots = [];
17662
+ const barThickness = 3;
17663
+ if (items.length === 0) {
17664
+ const container = getActiveNavbarListContainer(draggedEl);
17665
+ if (!container) return slots;
17666
+ const rect = container.getBoundingClientRect();
17667
+ slots.push({
17668
+ insertIndex: 0,
17669
+ parentId: null,
17670
+ left: rect.left + rect.width / 2 - barThickness / 2,
17671
+ top: rect.top,
17672
+ width: barThickness,
17673
+ height: Math.max(rect.height, 24),
17674
+ direction: "vertical"
17675
+ });
17676
+ return slots;
17677
+ }
17678
+ const edgeGap = (() => {
17679
+ if (items.length < 2) return 32;
17680
+ const a = items[0].getBoundingClientRect();
17681
+ const b = items[1].getBoundingClientRect();
17682
+ return Math.max(0, b.left - a.right);
17683
+ })();
17684
+ for (let i = 0; i <= items.length; i++) {
17685
+ let left;
17686
+ let top;
17687
+ let height;
17688
+ if (i === 0) {
17689
+ const first = items[0].getBoundingClientRect();
17690
+ left = first.left - edgeGap / 2 - barThickness / 2;
17691
+ top = first.top;
17692
+ height = first.height;
17693
+ } else if (i === items.length) {
17694
+ const last = items[items.length - 1].getBoundingClientRect();
17695
+ const lastGap = items.length >= 2 ? Math.max(0, last.left - items[items.length - 2].getBoundingClientRect().right) : edgeGap;
17696
+ left = last.right + lastGap / 2 - barThickness / 2;
17697
+ top = last.top;
17698
+ height = last.height;
17699
+ } else {
17700
+ const prev = items[i - 1].getBoundingClientRect();
17701
+ const next = items[i].getBoundingClientRect();
17702
+ left = (prev.right + next.left) / 2 - barThickness / 2;
17703
+ top = Math.min(prev.top, next.top);
17704
+ height = Math.max(prev.bottom, next.bottom) - top;
17705
+ }
17706
+ slots.push({
17707
+ insertIndex: i,
17708
+ parentId: null,
17709
+ left,
17710
+ top,
17711
+ width: barThickness,
17712
+ height: Math.max(height, 24),
17713
+ direction: "vertical"
17714
+ });
17715
+ }
17716
+ return slots;
17717
+ }
17718
+ function buildRootNavDropSlots(draggedEl) {
17719
+ const items = listReorderableNavItems(draggedEl);
17720
+ if (isVerticalNavLayout(items, draggedEl)) {
17721
+ return buildVerticalRootNavDropSlots(items, draggedEl);
17722
+ }
17723
+ return buildHorizontalRootNavDropSlots(items, draggedEl);
16154
17724
  }
16155
17725
  function buildChildNavDropSlots(parentHrefKey, draggedEl) {
16156
17726
  const children = listNavChildren(parentHrefKey, draggedEl);
@@ -16387,7 +17957,7 @@ function useNavItemDrag({
16387
17957
  document.querySelectorAll(
16388
17958
  "nav [data-ohw-href-key], [data-ohw-nav-container] [data-ohw-href-key], [data-ohw-nav-drawer] [data-ohw-href-key]"
16389
17959
  ).forEach((el) => {
16390
- if (isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))) {
17960
+ if (isNavbarHrefKey(el.getAttribute("data-ohw-href-key")) || isCtaButton(el)) {
16391
17961
  disableNativeHrefDrag(el);
16392
17962
  }
16393
17963
  });
@@ -16696,12 +18266,6 @@ var SECTION_DRAG_EXCLUDED_SELECTOR = [
16696
18266
  "[data-ohw-media-overlay]",
16697
18267
  "[data-ohw-media-skeleton]"
16698
18268
  ].join(", ");
16699
- function visibleClip(ps) {
16700
- if (!ps) return null;
16701
- const top = Math.max(0, ps.headerH - ps.iframeOffsetTop);
16702
- const bottom = Math.min(window.innerHeight, ps.headerH + ps.canvasH - ps.iframeOffsetTop);
16703
- return { top, bottom: Math.max(top, bottom) };
16704
- }
16705
18269
  function useSectionDrag({
16706
18270
  isEditMode,
16707
18271
  editContentRef,
@@ -16738,7 +18302,7 @@ function useSectionDrag({
16738
18302
  }, [postToParentRef, stopAutoScroll]);
16739
18303
  const updateAutoScroll = useCallback9(
16740
18304
  (clientY) => {
16741
- const clip = visibleClip(parentScrollRef.current);
18305
+ const clip = getIframeChromeClip(parentScrollRef.current);
16742
18306
  let delta = 0;
16743
18307
  if (clip) {
16744
18308
  const distTop = clientY - clip.top;
@@ -16945,7 +18509,7 @@ function useSectionDrag({
16945
18509
 
16946
18510
  // src/ui/footer-container-chrome.tsx
16947
18511
  import { Plus as Plus2 } from "lucide-react";
16948
- import { jsx as jsx31, jsxs as jsxs21 } from "react/jsx-runtime";
18512
+ import { jsx as jsx32, jsxs as jsxs22 } from "react/jsx-runtime";
16949
18513
  function FooterContainerChrome({
16950
18514
  rect,
16951
18515
  onAdd,
@@ -16953,7 +18517,7 @@ function FooterContainerChrome({
16953
18517
  }) {
16954
18518
  const chromeGap = 6;
16955
18519
  const buttonMargin = 7;
16956
- return /* @__PURE__ */ jsx31(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx31(
18520
+ return /* @__PURE__ */ jsx32(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx32(
16957
18521
  "div",
16958
18522
  {
16959
18523
  "data-ohw-footer-container-chrome": "",
@@ -16965,8 +18529,8 @@ function FooterContainerChrome({
16965
18529
  width: rect.width + chromeGap * 2,
16966
18530
  height: rect.height + chromeGap * 2
16967
18531
  },
16968
- children: /* @__PURE__ */ jsxs21(Tooltip, { children: [
16969
- /* @__PURE__ */ jsx31(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx31(
18532
+ children: /* @__PURE__ */ jsxs22(Tooltip, { children: [
18533
+ /* @__PURE__ */ jsx32(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx32(
16970
18534
  "button",
16971
18535
  {
16972
18536
  type: "button",
@@ -16985,10 +18549,10 @@ function FooterContainerChrome({
16985
18549
  if (addDisabled) return;
16986
18550
  onAdd();
16987
18551
  },
16988
- children: /* @__PURE__ */ jsx31(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
18552
+ children: /* @__PURE__ */ jsx32(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
16989
18553
  }
16990
18554
  ) }),
16991
- /* @__PURE__ */ jsx31(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
18555
+ /* @__PURE__ */ jsx32(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
16992
18556
  ] })
16993
18557
  }
16994
18558
  ) });
@@ -17150,7 +18714,14 @@ function collectEditableNodes(extraContent, root = document) {
17150
18714
  // so collecting the DOM alone left them behind: the draft knew the row was showing icons and
17151
18715
  // had gained an item, and the published page went back to the template's own (OHH-736).
17152
18716
  SOCIALS_ORDER_KEY,
17153
- SOCIALS_DISPLAY_KEY
18717
+ SOCIALS_DISPLAY_KEY,
18718
+ // Button variant/size tokens and per-CTA assignments — JSON in editContent, applied on hydrate.
18719
+ BUTTON_VARIANTS_KEY,
18720
+ BUTTON_SIZES_KEY,
18721
+ BUTTON_ASSIGNMENTS_KEY,
18722
+ BUTTON_COLLECTIONS_KEY,
18723
+ BUTTON_SNAPSHOTS_KEY,
18724
+ BUTTON_HIDDEN_KEY
17154
18725
  ]) {
17155
18726
  const text = extraContent[key];
17156
18727
  if (typeof text === "string" && text.length > 0) {
@@ -17471,13 +19042,13 @@ function deleteSelectedNavFooterItem(deps) {
17471
19042
 
17472
19043
  // src/ui/navbar-container-chrome.tsx
17473
19044
  import { Plus as Plus3 } from "lucide-react";
17474
- import { jsx as jsx32 } from "react/jsx-runtime";
19045
+ import { jsx as jsx33 } from "react/jsx-runtime";
17475
19046
  function NavbarContainerChrome({
17476
19047
  rect,
17477
19048
  onAdd
17478
19049
  }) {
17479
19050
  const chromeGap = 6;
17480
- return /* @__PURE__ */ jsx32(
19051
+ return /* @__PURE__ */ jsx33(
17481
19052
  "div",
17482
19053
  {
17483
19054
  "data-ohw-navbar-container-chrome": "",
@@ -17489,7 +19060,7 @@ function NavbarContainerChrome({
17489
19060
  width: rect.width + chromeGap * 2,
17490
19061
  height: rect.height + chromeGap * 2
17491
19062
  },
17492
- children: /* @__PURE__ */ jsx32(
19063
+ children: /* @__PURE__ */ jsx33(
17493
19064
  "button",
17494
19065
  {
17495
19066
  type: "button",
@@ -17506,7 +19077,7 @@ function NavbarContainerChrome({
17506
19077
  e.stopPropagation();
17507
19078
  onAdd();
17508
19079
  },
17509
- children: /* @__PURE__ */ jsx32(Plus3, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
19080
+ children: /* @__PURE__ */ jsx33(Plus3, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
17510
19081
  }
17511
19082
  )
17512
19083
  }
@@ -17515,7 +19086,7 @@ function NavbarContainerChrome({
17515
19086
 
17516
19087
  // src/ui/drop-indicator.tsx
17517
19088
  import * as React11 from "react";
17518
- import { jsx as jsx33 } from "react/jsx-runtime";
19089
+ import { jsx as jsx34 } from "react/jsx-runtime";
17519
19090
  var dropIndicatorVariants = cva(
17520
19091
  "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
17521
19092
  {
@@ -17539,7 +19110,7 @@ var dropIndicatorVariants = cva(
17539
19110
  );
17540
19111
  var DropIndicator = React11.forwardRef(
17541
19112
  ({ className, direction, state, ...props }, ref) => {
17542
- return /* @__PURE__ */ jsx33(
19113
+ return /* @__PURE__ */ jsx34(
17543
19114
  "div",
17544
19115
  {
17545
19116
  ref,
@@ -17556,7 +19127,7 @@ var DropIndicator = React11.forwardRef(
17556
19127
  DropIndicator.displayName = "DropIndicator";
17557
19128
 
17558
19129
  // src/ui/badge.tsx
17559
- import { jsx as jsx34 } from "react/jsx-runtime";
19130
+ import { jsx as jsx35 } from "react/jsx-runtime";
17560
19131
  var badgeVariants = cva(
17561
19132
  "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
17562
19133
  {
@@ -17574,12 +19145,12 @@ var badgeVariants = cva(
17574
19145
  }
17575
19146
  );
17576
19147
  function Badge({ className, variant, ...props }) {
17577
- return /* @__PURE__ */ jsx34("div", { className: cn(badgeVariants({ variant }), className), ...props });
19148
+ return /* @__PURE__ */ jsx35("div", { className: cn(badgeVariants({ variant }), className), ...props });
17578
19149
  }
17579
19150
 
17580
19151
  // src/OhhwellsBridge.tsx
17581
- import { Link as Link2, Plus as Plus4, Settings } from "lucide-react";
17582
- import { Fragment as Fragment10, jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
19152
+ import { Brush as Brush2, Link as Link2, Plus as Plus4, Settings } from "lucide-react";
19153
+ import { Fragment as Fragment10, jsx as jsx36, jsxs as jsxs23 } from "react/jsx-runtime";
17583
19154
  var PRIMARY5 = "#0885FE";
17584
19155
  var IMAGE_FADE_MS = 300;
17585
19156
  var MEDIA_SETTLE_TIMEOUT_MS = 2500;
@@ -17701,7 +19272,7 @@ function duplicateSchedulingWidget(instanceId, scheduleId) {
17701
19272
  schedulingRoots.set(container, root);
17702
19273
  flushSync2(() => {
17703
19274
  root.render(
17704
- /* @__PURE__ */ jsx35(
19275
+ /* @__PURE__ */ jsx36(
17705
19276
  SchedulingWidget,
17706
19277
  {
17707
19278
  notifyOnConnect: false,
@@ -17839,7 +19410,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
17839
19410
  schedulingRoots.set(container, root);
17840
19411
  flushSync2(() => {
17841
19412
  root.render(
17842
- /* @__PURE__ */ jsx35(
19413
+ /* @__PURE__ */ jsx36(
17843
19414
  SchedulingWidget,
17844
19415
  {
17845
19416
  notifyOnConnect,
@@ -18002,8 +19573,8 @@ function lockVideoBox(video) {
18002
19573
  }
18003
19574
  const fit = video.style.objectFit || getComputedStyle(video).objectFit;
18004
19575
  const bg = getComputedStyle(video).backgroundColor;
18005
- const isTransparent = bg === "rgba(0, 0, 0, 0)" || bg === "transparent" || !bg;
18006
- if (fit === "contain" && isTransparent) {
19576
+ const isTransparent2 = bg === "rgba(0, 0, 0, 0)" || bg === "transparent" || !bg;
19577
+ if (fit === "contain" && isTransparent2) {
18007
19578
  video.style.backgroundColor = "var(--color-dark, #000)";
18008
19579
  }
18009
19580
  }
@@ -18153,7 +19724,10 @@ function listNavigationItems() {
18153
19724
  ).filter((el) => isNavigationItem2(el));
18154
19725
  }
18155
19726
  function getNavigationItemReorderState(anchor) {
18156
- if (isCtaButton(anchor)) return { key: null, disabled: false };
19727
+ if (isCtaButton(anchor)) {
19728
+ if (!collectionDragEnabled(anchor)) return { key: null, disabled: true };
19729
+ return { key: buttonId(anchor), disabled: false };
19730
+ }
18157
19731
  return getReorderHandleStateFromAnchor(anchor);
18158
19732
  }
18159
19733
  function getReorderHandleState(el) {
@@ -18252,6 +19826,12 @@ function isPointOverNavigation(x, y) {
18252
19826
  const r2 = panel.getBoundingClientRect();
18253
19827
  if (x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom) return true;
18254
19828
  }
19829
+ for (const el of document.querySelectorAll('[data-ohw-role="navbar-button"]')) {
19830
+ const r2 = el.getBoundingClientRect();
19831
+ if (r2.width > 0 && r2.height > 0 && x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom) {
19832
+ return true;
19833
+ }
19834
+ }
18255
19835
  return false;
18256
19836
  }
18257
19837
  function findHoveredNavOrFooterContainer(x, y) {
@@ -18585,7 +20165,7 @@ function EditGlowChrome({
18585
20165
  hideHandle = false
18586
20166
  }) {
18587
20167
  const GAP = SELECTION_CHROME_GAP2;
18588
- return /* @__PURE__ */ jsxs22(
20168
+ return /* @__PURE__ */ jsxs23(
18589
20169
  "div",
18590
20170
  {
18591
20171
  ref: elRef,
@@ -18600,7 +20180,7 @@ function EditGlowChrome({
18600
20180
  zIndex: 2147483646
18601
20181
  },
18602
20182
  children: [
18603
- /* @__PURE__ */ jsx35(
20183
+ /* @__PURE__ */ jsx36(
18604
20184
  "div",
18605
20185
  {
18606
20186
  style: {
@@ -18614,7 +20194,7 @@ function EditGlowChrome({
18614
20194
  }
18615
20195
  }
18616
20196
  ),
18617
- reorderHrefKey && !hideHandle && /* @__PURE__ */ jsx35(
20197
+ reorderHrefKey && !hideHandle && /* @__PURE__ */ jsx36(
18618
20198
  "div",
18619
20199
  {
18620
20200
  "data-ohw-drag-handle-container": "",
@@ -18626,7 +20206,7 @@ function EditGlowChrome({
18626
20206
  transform: "translate(calc(-100% - 7px), -50%)",
18627
20207
  pointerEvents: dragDisabled ? "none" : "auto"
18628
20208
  },
18629
- children: /* @__PURE__ */ jsx35(
20209
+ children: /* @__PURE__ */ jsx36(
18630
20210
  DragHandle,
18631
20211
  {
18632
20212
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -18640,22 +20220,12 @@ function EditGlowChrome({
18640
20220
  }
18641
20221
  );
18642
20222
  }
18643
- function getIframeVisibleClip(parentScroll) {
18644
- if (!parentScroll) return null;
18645
- const { iframeOffsetTop, headerH: visibleCanvasTop, canvasH } = parentScroll;
18646
- const clipTop = Math.max(0, visibleCanvasTop - iframeOffsetTop);
18647
- const clipBottom = Math.min(
18648
- window.innerHeight,
18649
- visibleCanvasTop + canvasH - iframeOffsetTop
18650
- );
18651
- return { top: clipTop, bottom: Math.max(clipTop, clipBottom) };
18652
- }
18653
20223
  function isRectOutsideClip(rect, clip) {
18654
20224
  if (!clip) return false;
18655
20225
  return rect.bottom < clip.top || rect.top > clip.bottom;
18656
20226
  }
18657
20227
  function applyVisibleViewport(el, parentScroll) {
18658
- const clip = getIframeVisibleClip(parentScroll);
20228
+ const clip = getIframeChromeClip(parentScroll);
18659
20229
  const top = clip?.top ?? 0;
18660
20230
  const height = clip ? clip.bottom - clip.top : window.innerHeight;
18661
20231
  el.style.top = `${top}px`;
@@ -18695,7 +20265,7 @@ function clampToolbarLeft(centerX, toolbarWidth) {
18695
20265
  function calcToolbarPos(rect, parentScroll, toolbarW = 306) {
18696
20266
  const GAP = TOOLBAR_OFFSET_FROM_ELEMENT;
18697
20267
  const APPROX_H = 32;
18698
- const clip = getIframeVisibleClip(parentScroll);
20268
+ const clip = getIframeChromeClip(parentScroll);
18699
20269
  const clipTop = clip?.top ?? 0;
18700
20270
  const canvasTopInIframe = parentScroll != null ? parentScroll.headerH - parentScroll.iframeOffsetTop : 0;
18701
20271
  const visibleTop = rect.top - canvasTopInIframe;
@@ -18840,7 +20410,7 @@ function FloatingToolbar({
18840
20410
  return () => ro.disconnect();
18841
20411
  }, [showEditLink, activeCommands]);
18842
20412
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, measuredW);
18843
- return /* @__PURE__ */ jsx35(
20413
+ return /* @__PURE__ */ jsx36(
18844
20414
  "div",
18845
20415
  {
18846
20416
  ref: setRefs,
@@ -18852,12 +20422,12 @@ function FloatingToolbar({
18852
20422
  zIndex: 2147483647,
18853
20423
  pointerEvents: "auto"
18854
20424
  },
18855
- children: /* @__PURE__ */ jsxs22(CustomToolbar, { children: [
18856
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs22(React12.Fragment, { children: [
18857
- gi > 0 && /* @__PURE__ */ jsx35(CustomToolbarDivider, {}),
20425
+ children: /* @__PURE__ */ jsxs23(CustomToolbar, { children: [
20426
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs23(React12.Fragment, { children: [
20427
+ gi > 0 && /* @__PURE__ */ jsx36(CustomToolbarDivider, {}),
18858
20428
  btns.map((btn) => {
18859
20429
  const isActive = activeCommands.has(btn.cmd);
18860
- return /* @__PURE__ */ jsx35(
20430
+ return /* @__PURE__ */ jsx36(
18861
20431
  CustomToolbarButton,
18862
20432
  {
18863
20433
  title: btn.title,
@@ -18866,7 +20436,7 @@ function FloatingToolbar({
18866
20436
  e.preventDefault();
18867
20437
  onCommand(btn.cmd);
18868
20438
  },
18869
- children: /* @__PURE__ */ jsx35(
20439
+ children: /* @__PURE__ */ jsx36(
18870
20440
  "svg",
18871
20441
  {
18872
20442
  width: "16",
@@ -18887,7 +20457,7 @@ function FloatingToolbar({
18887
20457
  );
18888
20458
  })
18889
20459
  ] }, gi)),
18890
- showEditLink ? /* @__PURE__ */ jsx35(
20460
+ showEditLink ? /* @__PURE__ */ jsx36(
18891
20461
  CustomToolbarButton,
18892
20462
  {
18893
20463
  type: "button",
@@ -18901,7 +20471,7 @@ function FloatingToolbar({
18901
20471
  e.preventDefault();
18902
20472
  e.stopPropagation();
18903
20473
  },
18904
- children: /* @__PURE__ */ jsx35(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
20474
+ children: /* @__PURE__ */ jsx36(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
18905
20475
  }
18906
20476
  ) : null
18907
20477
  ] })
@@ -18918,7 +20488,7 @@ function StateToggle({
18918
20488
  states,
18919
20489
  onStateChange
18920
20490
  }) {
18921
- return /* @__PURE__ */ jsx35(
20491
+ return /* @__PURE__ */ jsx36(
18922
20492
  ToggleGroup,
18923
20493
  {
18924
20494
  "data-ohw-state-toggle": "",
@@ -18932,7 +20502,7 @@ function StateToggle({
18932
20502
  left: rect.right - 8,
18933
20503
  transform: "translateX(-100%)"
18934
20504
  },
18935
- children: states.map((state) => /* @__PURE__ */ jsx35(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
20505
+ children: states.map((state) => /* @__PURE__ */ jsx36(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
18936
20506
  }
18937
20507
  );
18938
20508
  }
@@ -18948,9 +20518,9 @@ var OHW_LOADER_STYLE = {
18948
20518
  justifyContent: "center"
18949
20519
  };
18950
20520
  function OhwLoaderSpinner() {
18951
- return /* @__PURE__ */ jsxs22("svg", { width: "28", height: "28", viewBox: "0 0 28 28", fill: "none", "aria-hidden": true, children: [
18952
- /* @__PURE__ */ jsx35("circle", { cx: "14", cy: "14", r: "11", stroke: "#E7E5E4", strokeWidth: "3" }),
18953
- /* @__PURE__ */ jsx35(
20521
+ return /* @__PURE__ */ jsxs23("svg", { width: "28", height: "28", viewBox: "0 0 28 28", fill: "none", "aria-hidden": true, children: [
20522
+ /* @__PURE__ */ jsx36("circle", { cx: "14", cy: "14", r: "11", stroke: "#E7E5E4", strokeWidth: "3" }),
20523
+ /* @__PURE__ */ jsx36(
18954
20524
  "circle",
18955
20525
  {
18956
20526
  cx: "14",
@@ -18960,7 +20530,7 @@ function OhwLoaderSpinner() {
18960
20530
  strokeWidth: "3",
18961
20531
  strokeDasharray: "17 52",
18962
20532
  strokeLinecap: "round",
18963
- children: /* @__PURE__ */ jsx35(
20533
+ children: /* @__PURE__ */ jsx36(
18964
20534
  "animateTransform",
18965
20535
  {
18966
20536
  attributeName: "transform",
@@ -18976,7 +20546,7 @@ function OhwLoaderSpinner() {
18976
20546
  ] });
18977
20547
  }
18978
20548
  function OhwBrandMark() {
18979
- return /* @__PURE__ */ jsxs22(
20549
+ return /* @__PURE__ */ jsxs23(
18980
20550
  "svg",
18981
20551
  {
18982
20552
  width: "16",
@@ -18987,7 +20557,7 @@ function OhwBrandMark() {
18987
20557
  style: { display: "block", flexShrink: 0 },
18988
20558
  xmlns: "http://www.w3.org/2000/svg",
18989
20559
  children: [
18990
- /* @__PURE__ */ jsx35(
20560
+ /* @__PURE__ */ jsx36(
18991
20561
  "mask",
18992
20562
  {
18993
20563
  id: "ohw-badge-mark",
@@ -18997,15 +20567,15 @@ function OhwBrandMark() {
18997
20567
  y: "0",
18998
20568
  width: "48",
18999
20569
  height: "48",
19000
- children: /* @__PURE__ */ jsx35("path", { d: "M23.8741 48C37.0594 48 47.7481 37.2548 47.7481 24C47.7481 10.7452 37.0594 0 23.8741 0C10.6888 0 0 10.7452 0 24C0 37.2548 10.6888 48 23.8741 48Z", fill: "white" })
20570
+ children: /* @__PURE__ */ jsx36("path", { d: "M23.8741 48C37.0594 48 47.7481 37.2548 47.7481 24C47.7481 10.7452 37.0594 0 23.8741 0C10.6888 0 0 10.7452 0 24C0 37.2548 10.6888 48 23.8741 48Z", fill: "white" })
19001
20571
  }
19002
20572
  ),
19003
- /* @__PURE__ */ jsxs22("g", { mask: "url(#ohw-badge-mark)", children: [
19004
- /* @__PURE__ */ jsx35("path", { d: "M23.8731 48.0497C37.0584 48.0497 47.7472 37.3046 47.7472 24.0497C47.7472 10.7949 37.0584 0.0497208 23.8731 0.0497208C10.6878 0.0497208 -0.000976562 10.7949 -0.000976562 24.0497C-0.000976562 37.3046 10.6878 48.0497 23.8731 48.0497Z", fill: "#0078E5" }),
19005
- /* @__PURE__ */ jsx35("path", { d: "M17.1307 14.7172C13.1687 14.7172 9.38102 18.1154 8.65114 22.34C8.38885 23.8488 8.5598 25.2581 9.06929 26.4451C6.20005 29.1677 1.77216 27.8721 -1.40212 26.1536C-2.73618 25.4317 -3.92695 27.4745 -2.59037 28.1981C1.33389 30.3226 6.86037 31.6621 10.4402 28.4188C11.4718 29.3859 12.867 29.9621 14.4894 29.9621C18.4161 29.9621 22.2038 26.5318 22.9337 22.34C23.6636 18.1162 21.0566 14.7172 17.1298 14.7172H17.1307ZM19.9798 22.34C19.5281 25.0399 17.2689 27.231 14.9754 27.231C12.6466 27.231 11.1877 25.0399 11.6394 22.34C12.1262 19.6401 14.3151 17.4482 16.6438 17.4482C18.9374 17.4482 20.4667 19.6401 19.9798 22.34Z", fill: "white" }),
19006
- /* @__PURE__ */ jsx35("path", { d: "M40.0017 27.0262C39.1797 27.081 38.2721 26.995 37.4668 26.7415C37.3344 26.6993 37.28 26.5401 37.3529 26.4205C37.5959 26.0212 37.8255 25.6152 38.009 25.1889C38.1071 24.9918 38.2018 24.793 38.2897 24.5908C38.3274 24.5041 38.4163 24.451 38.5101 24.4619C38.63 24.4754 38.7054 24.4821 38.8881 24.4821L39.1529 24.4796L39.8283 24.4543C45.9229 24.0492 50.4765 20.4319 54.8466 16.9014C56.0172 15.9554 57.6932 17.6208 56.5116 18.5752C51.7687 22.4065 47.1966 26.5081 40.9319 26.9689", fill: "white" }),
19007
- /* @__PURE__ */ jsx35("path", { d: "M37.9687 24.27C38.4472 23.1319 38.7656 21.9045 38.9609 20.6991C39.5927 16.76 38.5058 14.2193 36.1553 14.2193C34.1835 14.2193 33.1469 17.2427 32.9199 18.8694C32.743 19.9872 32.5914 22.1219 33.6028 23.9524C33.7553 24.259 34.15 24.7712 34.471 25.1259C34.5447 25.2067 34.6746 25.2 34.7349 25.1082C34.9528 24.7788 35.1615 24.4039 35.3584 24.0257C35.5444 23.6677 35.5888 23.6138 35.8587 23.0207C35.8838 22.966 35.8813 22.9002 35.8478 22.8505C35.5888 22.4597 35.2168 21.9787 35.1204 21.4614C34.9184 20.4455 34.9436 19.2257 35.2218 18.1078C35.4413 17.3118 35.7195 16.7844 35.9039 16.5106C35.9466 16.4466 36.0279 16.4129 36.0975 16.4449C36.369 16.5671 36.5827 16.8838 36.7385 17.396C37.0167 18.2089 37.0167 19.3782 36.814 20.6999C36.6991 21.5271 36.4771 22.3729 36.1746 23.1673C36.1293 23.308 36.0757 23.4461 36.0187 23.5826C36.0187 23.5868 36.0187 23.591 36.0187 23.5961C35.9911 23.6946 35.9207 23.8067 35.8846 23.901C35.5536 24.5497 35.2344 25.1697 34.8439 25.7838C34.8388 25.7863 34.8346 25.7914 34.8296 25.7931C34.6528 26.0525 34.4718 26.2901 34.2866 26.4965C34.2774 26.5099 34.2682 26.5234 34.2589 26.5369C34.2405 26.5638 34.2179 26.5815 34.1944 26.595C33.5064 27.3212 32.7665 27.6893 32.0123 27.6893C31.8606 27.6893 31.6838 27.664 31.507 27.4349C31.3042 27.1299 30.85 26.0879 31.2539 22.9112C31.4785 21.3949 31.8011 20.0268 31.931 19.5408C31.9579 19.4413 31.8908 19.3419 31.7886 19.3293L30.0062 19.1162C29.9241 19.1061 29.8478 19.1566 29.8252 19.2366C29.2704 21.1615 27.0305 27.6885 24.9599 27.6885C24.328 27.6885 24.1512 26.6211 24.1001 26.2909C23.775 23.6264 25.528 18.5492 29.5302 16.2267C29.6048 16.1838 29.635 16.0928 29.5998 16.0136L28.9042 14.467C28.8632 14.3752 28.7501 14.3389 28.6638 14.3886C25.8079 16.0414 24.1847 18.5231 23.2915 20.3436C22.2046 22.5793 21.6993 25.0189 21.9515 26.8738C22.1795 28.7794 23.1398 29.872 24.6054 29.872C26.0543 29.872 27.4369 28.9066 28.7098 27.0187C28.7953 26.8915 28.9889 26.9311 29.0149 27.0819C29.2646 28.5283 29.9811 29.872 31.6579 29.872C33.5282 29.872 35.3232 28.7288 36.7134 26.6447C36.7712 26.5874 36.7972 26.5411 36.8181 26.4906C36.8232 26.4931 36.8282 26.4948 36.8332 26.4973C37.01 26.2025 37.181 25.9051 37.3444 25.6027C37.4508 25.4005 37.5572 25.1992 37.6586 24.9945C37.7181 24.874 37.7743 24.7527 37.8262 24.6289C37.838 24.6002 37.8547 24.5665 37.8706 24.5337", fill: "white" }),
19008
- /* @__PURE__ */ jsx35("path", { d: "M30.5839 31.6397C25.7546 34.8577 19.4773 34.7853 14.6907 31.5243C13.5368 30.7384 12.5044 32.6498 13.6474 33.4281C19.034 37.0985 26.3077 37.096 31.7218 33.488C32.8791 32.7172 31.7478 30.8639 30.5839 31.6397Z", fill: "white" })
20573
+ /* @__PURE__ */ jsxs23("g", { mask: "url(#ohw-badge-mark)", children: [
20574
+ /* @__PURE__ */ jsx36("path", { d: "M23.8731 48.0497C37.0584 48.0497 47.7472 37.3046 47.7472 24.0497C47.7472 10.7949 37.0584 0.0497208 23.8731 0.0497208C10.6878 0.0497208 -0.000976562 10.7949 -0.000976562 24.0497C-0.000976562 37.3046 10.6878 48.0497 23.8731 48.0497Z", fill: "#0078E5" }),
20575
+ /* @__PURE__ */ jsx36("path", { d: "M17.1307 14.7172C13.1687 14.7172 9.38102 18.1154 8.65114 22.34C8.38885 23.8488 8.5598 25.2581 9.06929 26.4451C6.20005 29.1677 1.77216 27.8721 -1.40212 26.1536C-2.73618 25.4317 -3.92695 27.4745 -2.59037 28.1981C1.33389 30.3226 6.86037 31.6621 10.4402 28.4188C11.4718 29.3859 12.867 29.9621 14.4894 29.9621C18.4161 29.9621 22.2038 26.5318 22.9337 22.34C23.6636 18.1162 21.0566 14.7172 17.1298 14.7172H17.1307ZM19.9798 22.34C19.5281 25.0399 17.2689 27.231 14.9754 27.231C12.6466 27.231 11.1877 25.0399 11.6394 22.34C12.1262 19.6401 14.3151 17.4482 16.6438 17.4482C18.9374 17.4482 20.4667 19.6401 19.9798 22.34Z", fill: "white" }),
20576
+ /* @__PURE__ */ jsx36("path", { d: "M40.0017 27.0262C39.1797 27.081 38.2721 26.995 37.4668 26.7415C37.3344 26.6993 37.28 26.5401 37.3529 26.4205C37.5959 26.0212 37.8255 25.6152 38.009 25.1889C38.1071 24.9918 38.2018 24.793 38.2897 24.5908C38.3274 24.5041 38.4163 24.451 38.5101 24.4619C38.63 24.4754 38.7054 24.4821 38.8881 24.4821L39.1529 24.4796L39.8283 24.4543C45.9229 24.0492 50.4765 20.4319 54.8466 16.9014C56.0172 15.9554 57.6932 17.6208 56.5116 18.5752C51.7687 22.4065 47.1966 26.5081 40.9319 26.9689", fill: "white" }),
20577
+ /* @__PURE__ */ jsx36("path", { d: "M37.9687 24.27C38.4472 23.1319 38.7656 21.9045 38.9609 20.6991C39.5927 16.76 38.5058 14.2193 36.1553 14.2193C34.1835 14.2193 33.1469 17.2427 32.9199 18.8694C32.743 19.9872 32.5914 22.1219 33.6028 23.9524C33.7553 24.259 34.15 24.7712 34.471 25.1259C34.5447 25.2067 34.6746 25.2 34.7349 25.1082C34.9528 24.7788 35.1615 24.4039 35.3584 24.0257C35.5444 23.6677 35.5888 23.6138 35.8587 23.0207C35.8838 22.966 35.8813 22.9002 35.8478 22.8505C35.5888 22.4597 35.2168 21.9787 35.1204 21.4614C34.9184 20.4455 34.9436 19.2257 35.2218 18.1078C35.4413 17.3118 35.7195 16.7844 35.9039 16.5106C35.9466 16.4466 36.0279 16.4129 36.0975 16.4449C36.369 16.5671 36.5827 16.8838 36.7385 17.396C37.0167 18.2089 37.0167 19.3782 36.814 20.6999C36.6991 21.5271 36.4771 22.3729 36.1746 23.1673C36.1293 23.308 36.0757 23.4461 36.0187 23.5826C36.0187 23.5868 36.0187 23.591 36.0187 23.5961C35.9911 23.6946 35.9207 23.8067 35.8846 23.901C35.5536 24.5497 35.2344 25.1697 34.8439 25.7838C34.8388 25.7863 34.8346 25.7914 34.8296 25.7931C34.6528 26.0525 34.4718 26.2901 34.2866 26.4965C34.2774 26.5099 34.2682 26.5234 34.2589 26.5369C34.2405 26.5638 34.2179 26.5815 34.1944 26.595C33.5064 27.3212 32.7665 27.6893 32.0123 27.6893C31.8606 27.6893 31.6838 27.664 31.507 27.4349C31.3042 27.1299 30.85 26.0879 31.2539 22.9112C31.4785 21.3949 31.8011 20.0268 31.931 19.5408C31.9579 19.4413 31.8908 19.3419 31.7886 19.3293L30.0062 19.1162C29.9241 19.1061 29.8478 19.1566 29.8252 19.2366C29.2704 21.1615 27.0305 27.6885 24.9599 27.6885C24.328 27.6885 24.1512 26.6211 24.1001 26.2909C23.775 23.6264 25.528 18.5492 29.5302 16.2267C29.6048 16.1838 29.635 16.0928 29.5998 16.0136L28.9042 14.467C28.8632 14.3752 28.7501 14.3389 28.6638 14.3886C25.8079 16.0414 24.1847 18.5231 23.2915 20.3436C22.2046 22.5793 21.6993 25.0189 21.9515 26.8738C22.1795 28.7794 23.1398 29.872 24.6054 29.872C26.0543 29.872 27.4369 28.9066 28.7098 27.0187C28.7953 26.8915 28.9889 26.9311 29.0149 27.0819C29.2646 28.5283 29.9811 29.872 31.6579 29.872C33.5282 29.872 35.3232 28.7288 36.7134 26.6447C36.7712 26.5874 36.7972 26.5411 36.8181 26.4906C36.8232 26.4931 36.8282 26.4948 36.8332 26.4973C37.01 26.2025 37.181 25.9051 37.3444 25.6027C37.4508 25.4005 37.5572 25.1992 37.6586 24.9945C37.7181 24.874 37.7743 24.7527 37.8262 24.6289C37.838 24.6002 37.8547 24.5665 37.8706 24.5337", fill: "white" }),
20578
+ /* @__PURE__ */ jsx36("path", { d: "M30.5839 31.6397C25.7546 34.8577 19.4773 34.7853 14.6907 31.5243C13.5368 30.7384 12.5044 32.6498 13.6474 33.4281C19.034 37.0985 26.3077 37.096 31.7218 33.488C32.8791 32.7172 31.7478 30.8639 30.5839 31.6397Z", fill: "white" })
19009
20579
  ] })
19010
20580
  ]
19011
20581
  }
@@ -19042,7 +20612,7 @@ var OHW_BADGE_LABEL_STYLE = {
19042
20612
  whiteSpace: "nowrap"
19043
20613
  };
19044
20614
  function MadeWithOhhWells() {
19045
- return /* @__PURE__ */ jsxs22(
20615
+ return /* @__PURE__ */ jsxs23(
19046
20616
  "a",
19047
20617
  {
19048
20618
  href: "https://ohhwells.com",
@@ -19052,8 +20622,8 @@ function MadeWithOhhWells() {
19052
20622
  "data-ohw-badge": "",
19053
20623
  style: OHW_BADGE_STYLE,
19054
20624
  children: [
19055
- /* @__PURE__ */ jsx35(OhwBrandMark, {}),
19056
- /* @__PURE__ */ jsx35("span", { style: OHW_BADGE_LABEL_STYLE, children: "Made with OhhWells" })
20625
+ /* @__PURE__ */ jsx36(OhwBrandMark, {}),
20626
+ /* @__PURE__ */ jsx36("span", { style: OHW_BADGE_LABEL_STYLE, children: "Made with OhhWells" })
19057
20627
  ]
19058
20628
  }
19059
20629
  );
@@ -19543,7 +21113,15 @@ function OhhwellsBridge() {
19543
21113
  const [floatingPanel, setFloatingPanel] = useState16(null);
19544
21114
  const floatingPanelOpenRef = useRef12(false);
19545
21115
  floatingPanelOpenRef.current = floatingPanel !== null;
21116
+ const floatingPanelRef = useRef12(null);
21117
+ floatingPanelRef.current = floatingPanel;
19546
21118
  const [floatingPanelPos, setFloatingPanelPos] = useState16(null);
21119
+ const buttonCollectionDragRef = useRef12(null);
21120
+ const buttonDropInsertIndexRef = useRef12(null);
21121
+ const [buttonDropSlots, setButtonDropSlots] = useState16([]);
21122
+ const [activeButtonDropIndex, setActiveButtonDropIndex] = useState16(null);
21123
+ const buttonPointerDragRef = useRef12(null);
21124
+ const [buttonStyleTick, setButtonStyleTick] = useState16(0);
19547
21125
  const [logoSizeDraft, setLogoSizeDraft] = useState16(null);
19548
21126
  const [editorViewport, setEditorViewport] = useState16("desktop");
19549
21127
  const [parentScrollSnap, setParentScrollSnap] = useState16(null);
@@ -19844,6 +21422,7 @@ function OhhwellsBridge() {
19844
21422
  hoveredItemElRef.current = null;
19845
21423
  setHoveredItemRect(null);
19846
21424
  setFloatingPanel(null);
21425
+ setFloatingPanelPos(null);
19847
21426
  setLogoSizeDraft(null);
19848
21427
  if (!activeElRef.current) {
19849
21428
  if (nestedGroup?.isConnected) {
@@ -20444,6 +22023,47 @@ function OhhwellsBridge() {
20444
22023
  },
20445
22024
  [beginFooterDrag]
20446
22025
  );
22026
+ const persistButtonCollectionDrag = useCallback10(
22027
+ (button, clientX) => {
22028
+ const insertIndex = buttonDropInsertIndexRef.current;
22029
+ if (insertIndex != null) insertButtonAtCollectionIndex(button, insertIndex);
22030
+ else if (typeof clientX === "number" && clientX !== 0) reorderButtonInCollection(button, clientX);
22031
+ buttonDropInsertIndexRef.current = null;
22032
+ const collection = buttonCollectionOf(button);
22033
+ if (collection) {
22034
+ const persisted = persistCollectionOrder(collection, editContentRef.current);
22035
+ if (persisted) {
22036
+ editContentRef.current = persisted.content;
22037
+ postToParent2({ type: "ow:change", nodes: persisted.nodes });
22038
+ }
22039
+ }
22040
+ buttonCollectionDragRef.current = null;
22041
+ buttonPointerDragRef.current = null;
22042
+ setButtonDropSlots([]);
22043
+ setActiveButtonDropIndex(null);
22044
+ setIsItemDragging(false);
22045
+ unlockFooterDragInteraction();
22046
+ if (button.isConnected) setToolbarRect(button.getBoundingClientRect());
22047
+ },
22048
+ [postToParent2]
22049
+ );
22050
+ const refreshButtonDragVisuals = useCallback10((button, clientX, clientY) => {
22051
+ const collection = buttonCollectionOf(button);
22052
+ if (!collection) {
22053
+ setButtonDropSlots([]);
22054
+ setActiveButtonDropIndex(null);
22055
+ buttonDropInsertIndexRef.current = null;
22056
+ return;
22057
+ }
22058
+ const slots = buildButtonCollectionDropSlots(collection);
22059
+ const active = hitTestButtonCollectionDropSlot(collection, clientX, clientY);
22060
+ setButtonDropSlots(slots);
22061
+ const idx = active ? slots.findIndex((slot) => slot.insertIndex === active.insertIndex) : -1;
22062
+ setActiveButtonDropIndex(idx >= 0 ? idx : null);
22063
+ buttonDropInsertIndexRef.current = active?.insertIndex ?? null;
22064
+ setDraggedItemRect(button.getBoundingClientRect());
22065
+ if (selectedElRef.current === button) setToolbarRect(button.getBoundingClientRect());
22066
+ }, []);
20447
22067
  const handleItemDragStart = useCallback10(
20448
22068
  (e) => {
20449
22069
  const selected = selectedElRef.current;
@@ -20453,6 +22073,17 @@ function OhhwellsBridge() {
20453
22073
  }
20454
22074
  const clientX = e?.clientX ?? selected.getBoundingClientRect().left + 8;
20455
22075
  const clientY = e?.clientY ?? selected.getBoundingClientRect().top + 8;
22076
+ if (isCtaButton(selected) && collectionDragEnabled(selected)) {
22077
+ buttonCollectionDragRef.current = selected;
22078
+ setMediaHover(null);
22079
+ if (e?.dataTransfer) {
22080
+ e.dataTransfer.effectAllowed = "move";
22081
+ e.dataTransfer.setData("text/plain", buttonId(selected) ?? "button");
22082
+ }
22083
+ setIsItemDragging(true);
22084
+ refreshButtonDragVisuals(selected, clientX, clientY);
22085
+ return;
22086
+ }
20456
22087
  if (startFooterLinkDrag(selected, clientX, clientY, true)) return;
20457
22088
  if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup2(selected) && selected.closest("footer")) {
20458
22089
  if (startFooterColumnDrag(selected, clientX, clientY, true)) return;
@@ -20462,10 +22093,15 @@ function OhhwellsBridge() {
20462
22093
  setSiblingHintRect(null);
20463
22094
  setIsItemDragging(true);
20464
22095
  },
20465
- [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag]
22096
+ [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag, refreshButtonDragVisuals]
20466
22097
  );
20467
22098
  const handleItemDragEnd = useCallback10(
20468
22099
  (e) => {
22100
+ const collectionButton = buttonCollectionDragRef.current;
22101
+ if (collectionButton) {
22102
+ persistButtonCollectionDrag(collectionButton, e?.clientX);
22103
+ return;
22104
+ }
20469
22105
  if (footerDragRef.current) {
20470
22106
  const x = e?.clientX;
20471
22107
  const y = e?.clientY;
@@ -20488,12 +22124,22 @@ function OhhwellsBridge() {
20488
22124
  }
20489
22125
  setIsItemDragging(false);
20490
22126
  },
20491
- [commitFooterDrag, commitNavDrag, navDragRef]
22127
+ [commitFooterDrag, commitNavDrag, navDragRef, persistButtonCollectionDrag]
20492
22128
  );
20493
22129
  const handleItemChromePointerDown = useCallback10((e) => {
20494
22130
  if (e.button !== 0) return;
20495
22131
  const selected = selectedElRef.current;
20496
22132
  if (!selected) return;
22133
+ if (isCtaButton(selected) && collectionDragEnabled(selected)) {
22134
+ buttonPointerDragRef.current = {
22135
+ el: selected,
22136
+ startX: e.clientX,
22137
+ startY: e.clientY,
22138
+ pointerId: e.pointerId,
22139
+ started: false
22140
+ };
22141
+ return;
22142
+ }
20497
22143
  const hrefKey = selected.getAttribute("data-ohw-href-key");
20498
22144
  if (hrefKey && isFooterHrefKey(hrefKey)) {
20499
22145
  footerPointerDragRef.current = {
@@ -20572,10 +22218,29 @@ function OhhwellsBridge() {
20572
22218
  setIsFooterFrameSelection(false);
20573
22219
  setToolbarVariant("link-action");
20574
22220
  setToolbarRect(anchor.getBoundingClientRect());
22221
+ if (glowElRef.current) glowElRef.current.style.display = "";
20575
22222
  setToolbarShowEditLink(false);
20576
22223
  setActiveCommands(/* @__PURE__ */ new Set());
20577
- setFloatingPanel(null);
20578
- setLogoSizeDraft(null);
22224
+ const panel = floatingPanelRef.current;
22225
+ if (panel?.kind === "button-style" && isCtaButton(anchor)) {
22226
+ const id = buttonId(anchor);
22227
+ if (id && panel.buttonId !== id) {
22228
+ captureButtonOrigin(anchor);
22229
+ setFloatingPanelPos(null);
22230
+ setParentScrollSnap(parentScrollRef.current);
22231
+ setFloatingPanel({
22232
+ ...panel,
22233
+ key: `button-style:${id}`,
22234
+ buttonId: id,
22235
+ context: buttonStyleContext(anchor)
22236
+ });
22237
+ setButtonStyleTick((n) => n + 1);
22238
+ }
22239
+ } else {
22240
+ setFloatingPanel(null);
22241
+ setFloatingPanelPos(null);
22242
+ setLogoSizeDraft(null);
22243
+ }
20579
22244
  }, [deactivate, markSelected]);
20580
22245
  const selectFrame = useCallback10((el) => {
20581
22246
  if (!isNavigationContainer(el)) return;
@@ -20615,6 +22280,7 @@ function OhhwellsBridge() {
20615
22280
  setToolbarShowEditLink(false);
20616
22281
  setActiveCommands(/* @__PURE__ */ new Set());
20617
22282
  setFloatingPanel(null);
22283
+ setFloatingPanelPos(null);
20618
22284
  setLogoSizeDraft(null);
20619
22285
  }, [deactivate, markSelected, postToParent2]);
20620
22286
  const selectLogo = useCallback10(
@@ -20660,6 +22326,7 @@ function OhhwellsBridge() {
20660
22326
  });
20661
22327
  }, []);
20662
22328
  const openSocialsDisplayPanel = useCallback10((row) => {
22329
+ setFloatingPanelPos(null);
20663
22330
  setParentScrollSnap(parentScrollRef.current);
20664
22331
  setFloatingPanel({
20665
22332
  key: "socials-display",
@@ -20669,6 +22336,87 @@ function OhhwellsBridge() {
20669
22336
  row
20670
22337
  });
20671
22338
  }, []);
22339
+ const rememberButtonTokens = useCallback10(
22340
+ (content, tokens, nodes) => {
22341
+ if (nodes.length === 0) return content;
22342
+ const next = { ...content };
22343
+ for (const node of nodes) next[node.key] = node.text;
22344
+ editContentRef.current = next;
22345
+ postToParent2({ type: "ow:change", nodes });
22346
+ return next;
22347
+ },
22348
+ [postToParent2]
22349
+ );
22350
+ const tokensForButton = useCallback10(
22351
+ (el, persistFallback = false) => {
22352
+ const stored = readStoredStyleTokens(editContentRef.current);
22353
+ if (stored) return stored;
22354
+ const ensured = ensureButtonStyleTokens(document, editContentRef.current);
22355
+ if (ensured.tokens) {
22356
+ rememberButtonTokens(editContentRef.current, ensured.tokens, ensured.nodes);
22357
+ return ensured.tokens;
22358
+ }
22359
+ const fallback = tokensFromButton(el);
22360
+ if (persistFallback) rememberButtonTokens(editContentRef.current, fallback, styleTokenNodes(fallback));
22361
+ return fallback;
22362
+ },
22363
+ [rememberButtonTokens]
22364
+ );
22365
+ const openButtonStylePanel = useCallback10(
22366
+ (anchor) => {
22367
+ const id = buttonId(anchor);
22368
+ if (!id) return;
22369
+ captureButtonOrigin(anchor);
22370
+ tokensForButton(anchor);
22371
+ setFloatingPanelPos(null);
22372
+ setParentScrollSnap(parentScrollRef.current);
22373
+ setFloatingPanel({
22374
+ key: `button-style:${id}`,
22375
+ title: "Style",
22376
+ context: buttonStyleContext(anchor),
22377
+ kind: "button-style",
22378
+ buttonId: id
22379
+ });
22380
+ },
22381
+ [tokensForButton]
22382
+ );
22383
+ const applySelectedButtonStyle = useCallback10(
22384
+ (patch) => {
22385
+ const panel = floatingPanelRef.current;
22386
+ const id = panel?.kind === "button-style" ? panel.buttonId : null;
22387
+ const el = id ? document.querySelector(`[data-ohw-href-key="${CSS.escape(id)}"]`) : null;
22388
+ if (!el || !id || !isCtaButton(el)) return;
22389
+ const tokens = tokensFromButton(el);
22390
+ const next = { ...assignmentFor(el, editContentRef.current, tokens), ...patch };
22391
+ const assignments = withAssignment(editContentRef.current, id, next);
22392
+ const text = JSON.stringify(assignments);
22393
+ editContentRef.current = { ...editContentRef.current, [BUTTON_ASSIGNMENTS_KEY]: text };
22394
+ applyButtonStyle(el, next, tokens);
22395
+ postToParent2({ type: "ow:change", nodes: [{ key: BUTTON_ASSIGNMENTS_KEY, text }] });
22396
+ setButtonStyleTick((n) => n + 1);
22397
+ if (selectedElRef.current === el) setToolbarRect(el.getBoundingClientRect());
22398
+ },
22399
+ [postToParent2]
22400
+ );
22401
+ useEffect15(() => {
22402
+ const onDragOver = (e) => {
22403
+ const dragged = buttonCollectionDragRef.current;
22404
+ if (!dragged) return;
22405
+ e.preventDefault();
22406
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
22407
+ refreshButtonDragVisuals(dragged, e.clientX, e.clientY);
22408
+ };
22409
+ const onDrop = (e) => {
22410
+ if (!buttonCollectionDragRef.current) return;
22411
+ e.preventDefault();
22412
+ };
22413
+ document.addEventListener("dragover", onDragOver);
22414
+ document.addEventListener("drop", onDrop);
22415
+ return () => {
22416
+ document.removeEventListener("dragover", onDragOver);
22417
+ document.removeEventListener("drop", onDrop);
22418
+ };
22419
+ }, [refreshButtonDragVisuals]);
20672
22420
  const isEditModeRef = useRef12(false);
20673
22421
  const requestMissingSocialIconsRef = useRef12(() => {
20674
22422
  });
@@ -20711,11 +22459,13 @@ function OhhwellsBridge() {
20711
22459
  );
20712
22460
  const closeFloatingPanelOnly = useCallback10(() => {
20713
22461
  setFloatingPanel(null);
22462
+ setFloatingPanelPos(null);
20714
22463
  setLogoSizeDraft(null);
20715
22464
  }, []);
20716
22465
  closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
20717
22466
  const closeFloatingPanelAndDeselect = useCallback10(() => {
20718
22467
  setFloatingPanel(null);
22468
+ setFloatingPanelPos(null);
20719
22469
  setLogoSizeDraft(null);
20720
22470
  deselectRef.current();
20721
22471
  }, []);
@@ -20927,6 +22677,7 @@ function OhhwellsBridge() {
20927
22677
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
20928
22678
  }
20929
22679
  applyBrandChrome(content);
22680
+ applyCustomCodeToHead(content);
20930
22681
  initSectionInstancesFromContent(content, window.location.pathname);
20931
22682
  for (const [key, val] of Object.entries(content)) {
20932
22683
  if (key === "__ohw_sections") continue;
@@ -20935,6 +22686,7 @@ function OhhwellsBridge() {
20935
22686
  if (key === STYLE_STORE_KEY) continue;
20936
22687
  if (BRAND_CHROME_KEYS.has(key)) continue;
20937
22688
  if (key === LOGO_PLACEHOLDER_KEY) continue;
22689
+ if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
20938
22690
  if (LOGO_IMAGE_KEYS.includes(key)) continue;
20939
22691
  if (applyVideoSettingNode(key, val)) continue;
20940
22692
  if (applyCarouselNode(key, val)) continue;
@@ -20982,6 +22734,7 @@ function OhhwellsBridge() {
20982
22734
  reconcileSocialsFromContent(content);
20983
22735
  applySocialsDisplayFromContent(content);
20984
22736
  applySocialsLabelsFromContent(content, document, { fillEmptyOnly: isEditModeRef.current });
22737
+ syncButtonsFromContent(document, content);
20985
22738
  if (isEditModeRef.current) requestMissingSocialIconsRef.current();
20986
22739
  if (typeof content[STYLE_STORE_KEY] === "string") {
20987
22740
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
@@ -21182,6 +22935,7 @@ function OhhwellsBridge() {
21182
22935
  reconcileSocialsFromContent(content);
21183
22936
  applySocialsDisplayFromContent(content);
21184
22937
  applySocialsLabelsFromContent(content, document, { fillEmptyOnly: isEditModeRef.current });
22938
+ syncButtonsFromContent(document, content);
21185
22939
  if (isEditModeRef.current) requestMissingSocialIconsRef.current();
21186
22940
  if (typeof content[STYLE_STORE_KEY] === "string") {
21187
22941
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
@@ -21270,7 +23024,10 @@ function OhhwellsBridge() {
21270
23024
  if (roots.length === 0) {
21271
23025
  reconcileNavbarItemsFromContent(contentForNav());
21272
23026
  reconcileFooterOrderFromContent(contentForNav());
21273
- if (isEditMode) syncNavigationDragCursorAttrs();
23027
+ if (isEditMode) {
23028
+ syncButtonsFromContent(document, contentForNav());
23029
+ syncNavigationDragCursorAttrs();
23030
+ }
21274
23031
  return;
21275
23032
  }
21276
23033
  let rafId = null;
@@ -21282,7 +23039,7 @@ function OhhwellsBridge() {
21282
23039
  }
21283
23040
  };
21284
23041
  const run = () => {
21285
- if (footerDragRef.current || navDragRef.current || applying) return;
23042
+ if (footerDragRef.current || navDragRef.current || buttonCollectionDragRef.current || applying) return;
21286
23043
  applying = true;
21287
23044
  observer?.disconnect();
21288
23045
  try {
@@ -21292,6 +23049,7 @@ function OhhwellsBridge() {
21292
23049
  reconcileSocialsFromContent(content);
21293
23050
  applySocialsDisplayFromContent(content);
21294
23051
  applySocialsLabelsFromContent(content, document, { fillEmptyOnly: isEditModeRef.current });
23052
+ syncButtonsFromContent(document, content);
21295
23053
  if (isEditModeRef.current) requestMissingSocialIconsRef.current();
21296
23054
  document.querySelectorAll("footer [data-ohw-href-key]").forEach((el) => {
21297
23055
  if (isFooterHrefKey(el.getAttribute("data-ohw-href-key")) || getSocialItem(el)) {
@@ -21299,9 +23057,9 @@ function OhhwellsBridge() {
21299
23057
  }
21300
23058
  });
21301
23059
  document.querySelectorAll(
21302
- "nav [data-ohw-href-key], [data-ohw-nav-container] [data-ohw-href-key], [data-ohw-nav-drawer] [data-ohw-href-key]"
23060
+ 'nav [data-ohw-href-key], [data-ohw-nav-container] [data-ohw-href-key], [data-ohw-nav-drawer] [data-ohw-href-key], [data-ohw-role="navbar-button"]'
21303
23061
  ).forEach((el) => {
21304
- if (isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))) {
23062
+ if (isNavbarHrefKey(el.getAttribute("data-ohw-href-key")) || isCtaButton(el)) {
21305
23063
  disableNativeHrefDrag(el);
21306
23064
  }
21307
23065
  });
@@ -21326,6 +23084,44 @@ function OhhwellsBridge() {
21326
23084
  observer?.disconnect();
21327
23085
  };
21328
23086
  }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
23087
+ useEffect15(() => {
23088
+ if (!isEditMode) return;
23089
+ let applying = false;
23090
+ let rafId = null;
23091
+ const run = () => {
23092
+ if (applying || buttonCollectionDragRef.current) return;
23093
+ const content = editContentRef.current;
23094
+ if (!content[BUTTON_COLLECTIONS_KEY] && !content[BUTTON_SNAPSHOTS_KEY]) return;
23095
+ applying = true;
23096
+ try {
23097
+ syncButtonsFromContent(document, content);
23098
+ } finally {
23099
+ applying = false;
23100
+ }
23101
+ };
23102
+ const schedule = () => {
23103
+ if (rafId != null) return;
23104
+ rafId = requestAnimationFrame(() => {
23105
+ rafId = null;
23106
+ run();
23107
+ });
23108
+ };
23109
+ const observer = new MutationObserver((mutations) => {
23110
+ if (mutations.every((m) => {
23111
+ const t = m.target;
23112
+ return t instanceof Element && Boolean(t.closest("[data-ohw-bridge], [data-ohw-bridge-root], [data-ohw-button-collection]"));
23113
+ })) {
23114
+ return;
23115
+ }
23116
+ schedule();
23117
+ });
23118
+ observer.observe(document.body, { childList: true, subtree: true });
23119
+ run();
23120
+ return () => {
23121
+ if (rafId != null) cancelAnimationFrame(rafId);
23122
+ observer.disconnect();
23123
+ };
23124
+ }, [isEditMode, pathname]);
21329
23125
  useEffect15(() => {
21330
23126
  if (!isEditMode) return;
21331
23127
  let lastPosted = 0;
@@ -21852,14 +23648,6 @@ function OhhwellsBridge() {
21852
23648
  }
21853
23649
  const clickedButton = findClosestButtonLike(target);
21854
23650
  const buttonOnMedia = Boolean(clickedButton && isMediaEditable(editable) && editable.contains(clickedButton));
21855
- console.log("[click-debug]", {
21856
- editableType: editable.dataset.ohwEditable,
21857
- editableTag: editable.tagName,
21858
- targetTag: target.tagName,
21859
- clickedButtonTag: clickedButton?.tagName ?? null,
21860
- buttonOnMedia,
21861
- isMediaEditableEditable: isMediaEditable(editable)
21862
- });
21863
23651
  if (isMediaEditable(editable) && !buttonOnMedia) {
21864
23652
  e.preventDefault();
21865
23653
  e.stopPropagation();
@@ -21894,11 +23682,6 @@ function OhhwellsBridge() {
21894
23682
  const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
21895
23683
  const hrefCtx = getHrefKeyFromElement(hrefLookupTarget);
21896
23684
  const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
21897
- console.log("[click-debug 2]", {
21898
- hrefLookupTargetTag: hrefLookupTarget.tagName,
21899
- hrefCtx: hrefCtx ? { key: hrefCtx.key } : null,
21900
- navAnchorTag: navAnchor?.tagName ?? null
21901
- });
21902
23685
  if (navAnchor) {
21903
23686
  e.preventDefault();
21904
23687
  e.stopPropagation();
@@ -22414,6 +24197,20 @@ function OhhwellsBridge() {
22414
24197
  }
22415
24198
  return;
22416
24199
  }
24200
+ const navbarCtaHit = findNavbarCtaAtPoint(x, y);
24201
+ if (navbarCtaHit) {
24202
+ hoveredNavContainerRef.current = null;
24203
+ setHoveredNavContainerRect(null);
24204
+ if (selected !== navbarCtaHit && !selected?.contains(navbarCtaHit)) {
24205
+ clearHrefKeyHover(navbarCtaHit);
24206
+ hoveredItemElRef.current = navbarCtaHit;
24207
+ setHoveredItemRect(navbarCtaHit.getBoundingClientRect());
24208
+ } else if (hoveredItemElRef.current === navbarCtaHit) {
24209
+ hoveredItemElRef.current = null;
24210
+ setHoveredItemRect(null);
24211
+ }
24212
+ return;
24213
+ }
22417
24214
  const navContainers = Array.from(
22418
24215
  document.querySelectorAll("[data-ohw-nav-container]")
22419
24216
  );
@@ -22516,6 +24313,24 @@ function OhhwellsBridge() {
22516
24313
  return;
22517
24314
  }
22518
24315
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
24316
+ if (!isDragOver) {
24317
+ const buttonHit = findButtonAtPoint(x, y);
24318
+ if (buttonHit) {
24319
+ dismissImageHover();
24320
+ if (findNavbarCtaAtPoint(x, y)) {
24321
+ probeNavigationHoverAt(x, y);
24322
+ return;
24323
+ }
24324
+ document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
24325
+ const selected = selectedElRef.current;
24326
+ if (selected !== buttonHit && !selected?.contains(buttonHit)) {
24327
+ clearHrefKeyHover(buttonHit);
24328
+ hoveredItemElRef.current = buttonHit;
24329
+ setHoveredItemRect(buttonHit.getBoundingClientRect());
24330
+ }
24331
+ return;
24332
+ }
24333
+ }
22519
24334
  if (isPointOverNavigation(x, y)) {
22520
24335
  dismissImageHover();
22521
24336
  probeNavigationHoverAt(x, y);
@@ -22937,6 +24752,11 @@ function OhhwellsBridge() {
22937
24752
  return;
22938
24753
  }
22939
24754
  if (onNavDragOver(e)) return;
24755
+ if (buttonCollectionDragRef.current) {
24756
+ e.preventDefault();
24757
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
24758
+ return;
24759
+ }
22940
24760
  e.preventDefault();
22941
24761
  const el = findImageAtPoint(e.clientX, e.clientY, false);
22942
24762
  if (!el) {
@@ -22961,6 +24781,7 @@ function OhhwellsBridge() {
22961
24781
  };
22962
24782
  const handleDrop = (e) => {
22963
24783
  e.preventDefault();
24784
+ if (buttonCollectionDragRef.current || navDragRef.current || footerDragRef.current) return;
22964
24785
  const el = findImageAtPoint(e.clientX, e.clientY, false);
22965
24786
  if (!el) return;
22966
24787
  const file = e.dataTransfer?.files?.[0];
@@ -23263,6 +25084,7 @@ function OhhwellsBridge() {
23263
25084
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
23264
25085
  }
23265
25086
  applyBrandChrome(content);
25087
+ applyCustomCodeToHead(content);
23266
25088
  initSectionInstancesFromContent(content, window.location.pathname);
23267
25089
  let sectionsJson = null;
23268
25090
  for (const [key, val] of Object.entries(content)) {
@@ -23275,6 +25097,7 @@ function OhhwellsBridge() {
23275
25097
  if (key === STYLE_STORE_KEY) continue;
23276
25098
  if (BRAND_CHROME_KEYS.has(key)) continue;
23277
25099
  if (key === LOGO_PLACEHOLDER_KEY) continue;
25100
+ if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
23278
25101
  if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
23279
25102
  if (applyVideoSettingNode(key, val)) continue;
23280
25103
  if (applyCarouselNode(key, val)) continue;
@@ -23314,6 +25137,7 @@ function OhhwellsBridge() {
23314
25137
  editContentRef.current = { ...editContentRef.current, ...content };
23315
25138
  reconcileNavbarItemsFromContent(editContentRef.current);
23316
25139
  reconcileFooterOrderFromContent(editContentRef.current);
25140
+ syncButtonsFromContent(document, editContentRef.current);
23317
25141
  syncNavigationDragCursorAttrs();
23318
25142
  if (typeof content[STYLE_STORE_KEY] === "string") {
23319
25143
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
@@ -23986,6 +25810,16 @@ function OhhwellsBridge() {
23986
25810
  if (brandKitRef.current) {
23987
25811
  nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
23988
25812
  }
25813
+ nodes.push({
25814
+ key: CUSTOM_CODE_BEFORE_HEAD_KEY,
25815
+ type: "text",
25816
+ text: editContentRef.current[CUSTOM_CODE_BEFORE_HEAD_KEY] ?? ""
25817
+ });
25818
+ nodes.push({
25819
+ key: CUSTOM_CODE_AFTER_HEAD_KEY,
25820
+ type: "text",
25821
+ text: editContentRef.current[CUSTOM_CODE_AFTER_HEAD_KEY] ?? ""
25822
+ });
23989
25823
  document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
23990
25824
  const formKey = formKeyOf(form);
23991
25825
  if (!formKey) return;
@@ -24213,11 +26047,8 @@ function OhhwellsBridge() {
24213
26047
  };
24214
26048
  const handleParentScroll = (e) => {
24215
26049
  if (e.data?.type !== "ow:parent-scroll") return;
24216
- const { iframeOffsetTop, headerH, canvasH } = e.data;
24217
- parentScrollRef.current = { iframeOffsetTop, headerH, canvasH };
24218
- if (floatingPanelOpenRef.current) {
24219
- setParentScrollSnap({ iframeOffsetTop, headerH, canvasH });
24220
- }
26050
+ const { iframeOffsetTop, headerH, canvasH, promptBarH = 0 } = e.data;
26051
+ parentScrollRef.current = { iframeOffsetTop, headerH, canvasH, promptBarH };
24221
26052
  if (visibleViewportRef.current) {
24222
26053
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
24223
26054
  }
@@ -24607,6 +26438,97 @@ function OhhwellsBridge() {
24607
26438
  unlockFooterDragInteraction();
24608
26439
  };
24609
26440
  }, [isEditMode]);
26441
+ useEffect15(() => {
26442
+ if (!isEditMode) return;
26443
+ const THRESHOLD = 10;
26444
+ const onPointerDown = (e) => {
26445
+ if (e.button !== 0) return;
26446
+ if (buttonCollectionDragRef.current || navDragRef.current || footerDragRef.current) return;
26447
+ if (buttonPointerDragRef.current) return;
26448
+ const target = e.target;
26449
+ if (!target) return;
26450
+ if (target.closest(
26451
+ '[data-ohw-drag-handle-container], [data-slot="drag-handle"], [data-ohw-toolbar], [data-ohw-item-toolbar-anchor], [data-ohw-link-popover-root], [data-ohw-floating-panel]'
26452
+ )) {
26453
+ return;
26454
+ }
26455
+ const fromTarget = target.closest('[data-ohw-role="navbar-button"]');
26456
+ const anchor = (fromTarget && !fromTarget.closest("[data-ohw-bridge]") ? fromTarget : null) ?? findNavbarCtaAtPoint(e.clientX, e.clientY);
26457
+ if (!anchor || !collectionDragEnabled(anchor)) return;
26458
+ disableNativeHrefDrag(anchor);
26459
+ setMediaHover(null);
26460
+ buttonPointerDragRef.current = {
26461
+ el: anchor,
26462
+ startX: e.clientX,
26463
+ startY: e.clientY,
26464
+ pointerId: e.pointerId,
26465
+ started: false
26466
+ };
26467
+ };
26468
+ const onPointerMove = (e) => {
26469
+ const pending = buttonPointerDragRef.current;
26470
+ if (!pending) return;
26471
+ if (pending.started) {
26472
+ e.preventDefault();
26473
+ clearTextSelection();
26474
+ const dragged = buttonCollectionDragRef.current;
26475
+ if (!dragged) return;
26476
+ refreshButtonDragVisuals(dragged, e.clientX, e.clientY);
26477
+ return;
26478
+ }
26479
+ const dx = e.clientX - pending.startX;
26480
+ const dy = e.clientY - pending.startY;
26481
+ if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
26482
+ e.preventDefault();
26483
+ pending.started = true;
26484
+ armFooterPressDrag();
26485
+ clearTextSelection();
26486
+ try {
26487
+ document.body.setPointerCapture(pending.pointerId);
26488
+ } catch {
26489
+ }
26490
+ if (linkPopoverOpenRef.current) setLinkPopoverRef.current(null);
26491
+ if (activeElRef.current) deactivateRef.current();
26492
+ ensureNavbarButtonCollection(pending.el, editContentRef.current);
26493
+ buttonCollectionDragRef.current = pending.el;
26494
+ lockFooterDuringDrag();
26495
+ setIsItemDragging(true);
26496
+ refreshButtonDragVisuals(pending.el, e.clientX, e.clientY);
26497
+ };
26498
+ const endPointerDrag = (e) => {
26499
+ const pending = buttonPointerDragRef.current;
26500
+ buttonPointerDragRef.current = null;
26501
+ try {
26502
+ if (document.body.hasPointerCapture(e.pointerId)) {
26503
+ document.body.releasePointerCapture(e.pointerId);
26504
+ }
26505
+ } catch {
26506
+ }
26507
+ if (!pending) return;
26508
+ if (!pending.started) {
26509
+ unlockFooterDragInteraction();
26510
+ return;
26511
+ }
26512
+ suppressNextClickRef.current = true;
26513
+ suppressClickUntilRef.current = Date.now() + 500;
26514
+ persistButtonCollectionDrag(pending.el, e.clientX);
26515
+ };
26516
+ const blockSelectStart = (e) => {
26517
+ if (buttonCollectionDragRef.current || buttonPointerDragRef.current?.started) e.preventDefault();
26518
+ };
26519
+ document.addEventListener("pointerdown", onPointerDown, true);
26520
+ document.addEventListener("pointermove", onPointerMove, true);
26521
+ document.addEventListener("pointerup", endPointerDrag, true);
26522
+ document.addEventListener("pointercancel", endPointerDrag, true);
26523
+ document.addEventListener("selectstart", blockSelectStart, true);
26524
+ return () => {
26525
+ document.removeEventListener("pointerdown", onPointerDown, true);
26526
+ document.removeEventListener("pointermove", onPointerMove, true);
26527
+ document.removeEventListener("pointerup", endPointerDrag, true);
26528
+ document.removeEventListener("pointercancel", endPointerDrag, true);
26529
+ document.removeEventListener("selectstart", blockSelectStart, true);
26530
+ };
26531
+ }, [isEditMode, persistButtonCollectionDrag, refreshButtonDragVisuals]);
24610
26532
  useEffect15(() => {
24611
26533
  const handler = (e) => {
24612
26534
  if (e.data?.type !== "ow:request-schedule-config") return;
@@ -24647,7 +26569,7 @@ function OhhwellsBridge() {
24647
26569
  postToParent2({
24648
26570
  type: "ow:ready",
24649
26571
  version: "1",
24650
- bridgeVersion: "0.1.119",
26572
+ bridgeVersion: "0.1.121",
24651
26573
  path: pathname,
24652
26574
  nodes: collectEditableNodes(editContentRef.current),
24653
26575
  sections
@@ -24726,12 +26648,12 @@ function OhhwellsBridge() {
24726
26648
  return;
24727
26649
  }
24728
26650
  if (type !== "ow:logo-size-set") return;
24729
- const px = Number(e.data.px);
24730
- if (!Number.isFinite(px)) return;
24731
- const next = editorViewport === "mobile" ? { ...logoSizeDraft, mobilePx: px, mobileFollowing: false } : {
26651
+ const px2 = Number(e.data.px);
26652
+ if (!Number.isFinite(px2)) return;
26653
+ const next = editorViewport === "mobile" ? { ...logoSizeDraft, mobilePx: px2, mobileFollowing: false } : {
24732
26654
  ...logoSizeDraft,
24733
- desktopPx: px,
24734
- mobilePx: logoSizeDraft.mobileFollowing ? px : logoSizeDraft.mobilePx
26655
+ desktopPx: px2,
26656
+ mobilePx: logoSizeDraft.mobileFollowing ? px2 : logoSizeDraft.mobilePx
24735
26657
  };
24736
26658
  setLogoSizeDraft(next);
24737
26659
  persistLogoSizeDraft(session.placement, next);
@@ -24829,6 +26751,36 @@ function OhhwellsBridge() {
24829
26751
  const handleDuplicateSelected = useCallback10(() => {
24830
26752
  const selected = selectedElRef.current;
24831
26753
  if (!selected || !isNavigationItem2(selected)) return;
26754
+ if (isCtaButton(selected)) {
26755
+ const id = buttonId(selected);
26756
+ if (!id) return;
26757
+ const tokens = tokensForButton(selected, true);
26758
+ if (!readAssignments(editContentRef.current)[id]) {
26759
+ const assignment = assignmentFor(selected, editContentRef.current, tokens);
26760
+ const assignments = withAssignment(editContentRef.current, id, assignment);
26761
+ editContentRef.current = {
26762
+ ...editContentRef.current,
26763
+ [BUTTON_ASSIGNMENTS_KEY]: JSON.stringify(assignments)
26764
+ };
26765
+ applyButtonStyle(selected, assignment, tokens);
26766
+ }
26767
+ const result = duplicateButton(selected, editContentRef.current);
26768
+ if (!result) return;
26769
+ editContentRef.current = result.content;
26770
+ const copyHref = result.copy.getAttribute("data-ohw-href-key");
26771
+ const copyHrefVal = copyHref ? result.content[copyHref] : void 0;
26772
+ if (copyHref && copyHrefVal !== void 0) applyLinkByKey(copyHref, copyHrefVal);
26773
+ postToParent2({ type: "ow:change", nodes: result.nodes });
26774
+ enforceLinkHrefs();
26775
+ requestAnimationFrame(() => {
26776
+ selectRef.current(result.copy);
26777
+ requestAnimationFrame(() => {
26778
+ const el = selectedElRef.current;
26779
+ if (el?.isConnected) setToolbarRect(el.getBoundingClientRect());
26780
+ });
26781
+ });
26782
+ return;
26783
+ }
24832
26784
  const hrefKey = selected.getAttribute("data-ohw-href-key");
24833
26785
  if (!hrefKey) return;
24834
26786
  const social = getSocialItem(selected);
@@ -24958,7 +26910,7 @@ function OhhwellsBridge() {
24958
26910
  selectRef.current(result.anchor);
24959
26911
  });
24960
26912
  }
24961
- }, [postToParent2]);
26913
+ }, [postToParent2, tokensForButton]);
24962
26914
  const runPendingDeleteUndo = useCallback10(() => {
24963
26915
  const pending = pendingDeleteUndoRef.current;
24964
26916
  if (!pending) return false;
@@ -24970,6 +26922,38 @@ function OhhwellsBridge() {
24970
26922
  const handleDeleteSelected = useCallback10(() => {
24971
26923
  const selected = selectedElRef.current;
24972
26924
  if (!selected) return false;
26925
+ if (isCtaButton(selected)) {
26926
+ if (!canDeleteButton(selected)) return false;
26927
+ const previousContent = editContentRef.current;
26928
+ const result = deleteButton(selected, previousContent);
26929
+ if (!result) return false;
26930
+ editContentRef.current = result.content;
26931
+ postToParent2({ type: "ow:change", nodes: result.nodes });
26932
+ const actionId = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `delete-${Date.now()}`;
26933
+ const restoreNodes = result.nodes.map((node) => ({
26934
+ key: node.key,
26935
+ text: previousContent[node.key] ?? ""
26936
+ }));
26937
+ pendingDeleteUndoRef.current = {
26938
+ actionId,
26939
+ restore: () => {
26940
+ result.undo();
26941
+ editContentRef.current = { ...editContentRef.current, ...Object.fromEntries(restoreNodes.map((node) => [node.key, node.text])) };
26942
+ postToParent2({ type: "ow:change", nodes: restoreNodes });
26943
+ syncButtonsFromContent(document, editContentRef.current);
26944
+ }
26945
+ };
26946
+ deselectRef.current();
26947
+ postToParent2({
26948
+ type: "ow:toast",
26949
+ title: "Button deleted",
26950
+ toastType: "success",
26951
+ actionLabel: "Undo",
26952
+ actionId,
26953
+ duration: 6e3
26954
+ });
26955
+ return true;
26956
+ }
24973
26957
  return deleteSelectedNavFooterItem({
24974
26958
  selected,
24975
26959
  isTextEditing: Boolean(activeElRef.current),
@@ -25107,14 +27091,14 @@ function OhhwellsBridge() {
25107
27091
  },
25108
27092
  [postToParent2]
25109
27093
  );
25110
- return /* @__PURE__ */ jsxs22(Fragment10, { children: [
25111
- /* @__PURE__ */ jsx35("div", { id: "ohw-loader", suppressHydrationWarning: true, style: { ...OHW_LOADER_STYLE, display: "none" }, children: /* @__PURE__ */ jsx35(OhwLoaderSpinner, {}) }),
25112
- /* @__PURE__ */ jsx35("script", { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: OHW_LOADER_PREHYDRATE_SCRIPT } }),
25113
- subdomain && !isEditMode && showBranding && /* @__PURE__ */ jsx35(MadeWithOhhWells, {}),
27094
+ return /* @__PURE__ */ jsxs23(Fragment10, { children: [
27095
+ /* @__PURE__ */ jsx36("div", { id: "ohw-loader", suppressHydrationWarning: true, style: { ...OHW_LOADER_STYLE, display: "none" }, children: /* @__PURE__ */ jsx36(OhwLoaderSpinner, {}) }),
27096
+ /* @__PURE__ */ jsx36("script", { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: OHW_LOADER_PREHYDRATE_SCRIPT } }),
27097
+ subdomain && !isEditMode && showBranding && /* @__PURE__ */ jsx36(MadeWithOhhWells, {}),
25114
27098
  bridgeRoot ? createPortal3(
25115
- /* @__PURE__ */ jsxs22(Fragment10, { children: [
25116
- /* @__PURE__ */ jsx35("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
25117
- isEditMode && /* @__PURE__ */ jsx35(
27099
+ /* @__PURE__ */ jsxs23(Fragment10, { children: [
27100
+ /* @__PURE__ */ jsx36("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
27101
+ isEditMode && /* @__PURE__ */ jsx36(
25118
27102
  AiSectionOverlay,
25119
27103
  {
25120
27104
  postToParent: postToParent2,
@@ -25122,7 +27106,7 @@ function OhhwellsBridge() {
25122
27106
  onSelectionChange: handleAiSectionSelectionChange
25123
27107
  }
25124
27108
  ),
25125
- isEditMode && /* @__PURE__ */ jsx35(
27109
+ isEditMode && /* @__PURE__ */ jsx36(
25126
27110
  AiMediaStripOverlay,
25127
27111
  {
25128
27112
  postToParent: postToParent2,
@@ -25131,7 +27115,7 @@ function OhhwellsBridge() {
25131
27115
  clearInteractionChromeRef
25132
27116
  }
25133
27117
  ),
25134
- isEditMode && /* @__PURE__ */ jsx35(
27118
+ isEditMode && /* @__PURE__ */ jsx36(
25135
27119
  AiPricingCardOverlay,
25136
27120
  {
25137
27121
  postToParent: postToParent2,
@@ -25139,12 +27123,12 @@ function OhhwellsBridge() {
25139
27123
  applyTreeLocalRef
25140
27124
  }
25141
27125
  ),
25142
- isSectionDragging && sectionDropSlots.map((slot, i) => /* @__PURE__ */ jsx35(
27126
+ isSectionDragging && sectionDropSlots.map((slot, i) => /* @__PURE__ */ jsx36(
25143
27127
  "div",
25144
27128
  {
25145
27129
  className: "pointer-events-none fixed z-2147483646",
25146
27130
  style: { left: slot.left, top: slot.y, width: slot.width, height: 3, transform: "translateY(-50%)" },
25147
- children: /* @__PURE__ */ jsx35(
27131
+ children: /* @__PURE__ */ jsx36(
25148
27132
  DropIndicator,
25149
27133
  {
25150
27134
  direction: "horizontal",
@@ -25155,7 +27139,7 @@ function OhhwellsBridge() {
25155
27139
  },
25156
27140
  `section-drop-${slot.insertIndex}-${i}`
25157
27141
  )),
25158
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx35(
27142
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx36(
25159
27143
  MediaOverlay,
25160
27144
  {
25161
27145
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -25166,7 +27150,7 @@ function OhhwellsBridge() {
25166
27150
  },
25167
27151
  `uploading-${key}`
25168
27152
  )),
25169
- mediaHover && !(mediaHover.key in uploadingRects) && mediaHover.key !== selectedMedia?.key && /* @__PURE__ */ jsx35(
27153
+ mediaHover && !(mediaHover.key in uploadingRects) && mediaHover.key !== selectedMedia?.key && /* @__PURE__ */ jsx36(
25170
27154
  MediaOverlay,
25171
27155
  {
25172
27156
  hover: mediaHover,
@@ -25177,7 +27161,7 @@ function OhhwellsBridge() {
25177
27161
  onVideoSettingsChange: handleVideoSettingsChange
25178
27162
  }
25179
27163
  ),
25180
- selectedMedia && !(selectedMedia.key in uploadingRects) && !linkPopover && /* @__PURE__ */ jsx35(
27164
+ selectedMedia && !(selectedMedia.key in uploadingRects) && !linkPopover && /* @__PURE__ */ jsx36(
25181
27165
  MediaOverlay,
25182
27166
  {
25183
27167
  hover: selectedMedia,
@@ -25189,7 +27173,7 @@ function OhhwellsBridge() {
25189
27173
  onVideoSettingsChange: handleVideoSettingsChange
25190
27174
  }
25191
27175
  ),
25192
- carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx35(
27176
+ carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx36(
25193
27177
  CarouselOverlay,
25194
27178
  {
25195
27179
  hover: carouselHover,
@@ -25197,10 +27181,10 @@ function OhhwellsBridge() {
25197
27181
  cueOnly: !!carouselHover.fullBleedSectionId && carouselHover.fullBleedSectionId !== selectedAiSectionId
25198
27182
  }
25199
27183
  ),
25200
- siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx35(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
25201
- siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx35(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
25202
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx35(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
25203
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx35(
27184
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx36(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
27185
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx36(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
27186
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && selectedElRef.current !== buttonCollectionDragRef.current && /* @__PURE__ */ jsx36(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
27187
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx36(
25204
27188
  "div",
25205
27189
  {
25206
27190
  className: "pointer-events-none fixed z-2147483646",
@@ -25210,7 +27194,7 @@ function OhhwellsBridge() {
25210
27194
  width: slot.width,
25211
27195
  height: slot.height
25212
27196
  },
25213
- children: /* @__PURE__ */ jsx35(
27197
+ children: /* @__PURE__ */ jsx36(
25214
27198
  DropIndicator,
25215
27199
  {
25216
27200
  direction: slot.direction,
@@ -25221,7 +27205,7 @@ function OhhwellsBridge() {
25221
27205
  },
25222
27206
  `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
25223
27207
  )),
25224
- isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx35(
27208
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx36(
25225
27209
  "div",
25226
27210
  {
25227
27211
  className: "pointer-events-none fixed z-2147483646",
@@ -25231,7 +27215,7 @@ function OhhwellsBridge() {
25231
27215
  width: slot.width,
25232
27216
  height: slot.height
25233
27217
  },
25234
- children: /* @__PURE__ */ jsx35(
27218
+ children: /* @__PURE__ */ jsx36(
25235
27219
  DropIndicator,
25236
27220
  {
25237
27221
  direction: slot.direction,
@@ -25242,10 +27226,31 @@ function OhhwellsBridge() {
25242
27226
  },
25243
27227
  `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
25244
27228
  )),
25245
- hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx35(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
25246
- hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx35(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
25247
- hoveredTextRect && !hoveredNavContainerRect && !hoveredItemRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx35(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
25248
- formPickRect && !isItemDragging && /* @__PURE__ */ jsx35(
27229
+ isItemDragging && buttonDropSlots.map((slot, i) => /* @__PURE__ */ jsx36(
27230
+ "div",
27231
+ {
27232
+ className: "pointer-events-none fixed z-2147483646",
27233
+ style: {
27234
+ left: slot.left,
27235
+ top: slot.top,
27236
+ width: slot.width,
27237
+ height: slot.height
27238
+ },
27239
+ children: /* @__PURE__ */ jsx36(
27240
+ DropIndicator,
27241
+ {
27242
+ direction: slot.direction,
27243
+ state: activeButtonDropIndex === i ? "dragActive" : "dragIdle",
27244
+ className: "!h-full !w-full"
27245
+ }
27246
+ )
27247
+ },
27248
+ `button-drop-${slot.direction}-${slot.insertIndex}-${i}`
27249
+ )),
27250
+ hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx36(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
27251
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx36(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
27252
+ hoveredTextRect && !hoveredNavContainerRect && !hoveredItemRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx36(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
27253
+ formPickRect && !isItemDragging && /* @__PURE__ */ jsx36(
25249
27254
  ItemInteractionLayer,
25250
27255
  {
25251
27256
  rect: formPickRect,
@@ -25253,13 +27258,13 @@ function OhhwellsBridge() {
25253
27258
  itemDragSurface: false,
25254
27259
  toolbarAlign: "left",
25255
27260
  chromeGap: 24,
25256
- toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ jsxs22(
27261
+ toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ jsxs23(
25257
27262
  "div",
25258
27263
  {
25259
27264
  "data-ohw-form-toolbar": "",
25260
27265
  className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
25261
27266
  children: [
25262
- /* @__PURE__ */ jsx35(
27267
+ /* @__PURE__ */ jsx36(
25263
27268
  "button",
25264
27269
  {
25265
27270
  type: "button",
@@ -25267,11 +27272,11 @@ function OhhwellsBridge() {
25267
27272
  className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
25268
27273
  onClick: () => setFieldTypePickerOpen((open) => !open),
25269
27274
  "data-ohw-add-field": "",
25270
- children: /* @__PURE__ */ jsx35(Plus4, { size: 15, "aria-hidden": true })
27275
+ children: /* @__PURE__ */ jsx36(Plus4, { size: 15, "aria-hidden": true })
25271
27276
  }
25272
27277
  ),
25273
- /* @__PURE__ */ jsx35("div", { className: "mx-0.5 h-5 w-px bg-border" }),
25274
- /* @__PURE__ */ jsxs22(
27278
+ /* @__PURE__ */ jsx36("div", { className: "mx-0.5 h-5 w-px bg-border" }),
27279
+ /* @__PURE__ */ jsxs23(
25275
27280
  "button",
25276
27281
  {
25277
27282
  type: "button",
@@ -25287,11 +27292,11 @@ function OhhwellsBridge() {
25287
27292
  });
25288
27293
  },
25289
27294
  children: [
25290
- /* @__PURE__ */ jsx35(Settings, { size: 14, "aria-hidden": true }),
27295
+ /* @__PURE__ */ jsx36(Settings, { size: 14, "aria-hidden": true }),
25291
27296
  "Form settings",
25292
27297
  formPickCount ? (
25293
27298
  // Counter pill, per the design — not a text suffix.
25294
- /* @__PURE__ */ jsx35(
27299
+ /* @__PURE__ */ jsx36(
25295
27300
  "span",
25296
27301
  {
25297
27302
  "data-ohw-form-count": "",
@@ -25303,8 +27308,8 @@ function OhhwellsBridge() {
25303
27308
  ]
25304
27309
  }
25305
27310
  ),
25306
- /* @__PURE__ */ jsx35("div", { className: "mx-0.5 h-5 w-px bg-border" }),
25307
- /* @__PURE__ */ jsx35("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ jsx35(
27311
+ /* @__PURE__ */ jsx36("div", { className: "mx-0.5 h-5 w-px bg-border" }),
27312
+ /* @__PURE__ */ jsx36("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ jsx36(
25308
27313
  "button",
25309
27314
  {
25310
27315
  type: "button",
@@ -25335,7 +27340,7 @@ function OhhwellsBridge() {
25335
27340
  )
25336
27341
  }
25337
27342
  ),
25338
- formHoverRect && !isItemDragging && /* @__PURE__ */ jsx35(
27343
+ formHoverRect && !isItemDragging && /* @__PURE__ */ jsx36(
25339
27344
  ItemInteractionLayer,
25340
27345
  {
25341
27346
  rect: formHoverRect,
@@ -25343,7 +27348,7 @@ function OhhwellsBridge() {
25343
27348
  chromeGap: formHoverElRef.current && getFieldWrapper(formHoverElRef.current) ? 8 : 24
25344
27349
  }
25345
27350
  ),
25346
- fieldPickRect && fieldPickState && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx35(
27351
+ fieldPickRect && fieldPickState && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx36(
25347
27352
  ItemInteractionLayer,
25348
27353
  {
25349
27354
  rect: fieldPickRect,
@@ -25355,7 +27360,7 @@ function OhhwellsBridge() {
25355
27360
  dragHandleLabel: "Reorder field",
25356
27361
  onDragHandleDragStart: handleFieldDragStart,
25357
27362
  onDragHandleDragEnd: handleFieldDragEnd,
25358
- toolbar: /* @__PURE__ */ jsx35(
27363
+ toolbar: /* @__PURE__ */ jsx36(
25359
27364
  FormFieldToolbar,
25360
27365
  {
25361
27366
  type: fieldPickState.type,
@@ -25368,12 +27373,12 @@ function OhhwellsBridge() {
25368
27373
  )
25369
27374
  }
25370
27375
  ),
25371
- fieldDragging ? fieldDropSlots.map((slot, i) => /* @__PURE__ */ jsx35(
27376
+ fieldDragging ? fieldDropSlots.map((slot, i) => /* @__PURE__ */ jsx36(
25372
27377
  "div",
25373
27378
  {
25374
27379
  className: "pointer-events-none fixed z-[2147483644]",
25375
27380
  style: { top: slot.top, left: slot.left, width: slot.width },
25376
- children: /* @__PURE__ */ jsx35(
27381
+ children: /* @__PURE__ */ jsx36(
25377
27382
  DropIndicator,
25378
27383
  {
25379
27384
  direction: "horizontal",
@@ -25386,7 +27391,7 @@ function OhhwellsBridge() {
25386
27391
  )) : null,
25387
27392
  fieldTypePickerOpen && formPickRect ? (() => {
25388
27393
  const toolbar = document.querySelector("[data-ohw-form-toolbar]")?.getBoundingClientRect();
25389
- return /* @__PURE__ */ jsx35(
27394
+ return /* @__PURE__ */ jsx36(
25390
27395
  "div",
25391
27396
  {
25392
27397
  className: "pointer-events-none fixed z-[2147483645]",
@@ -25394,12 +27399,12 @@ function OhhwellsBridge() {
25394
27399
  top: toolbar ? toolbar.bottom + 6 : formPickRect.top + 16,
25395
27400
  left: toolbar ? toolbar.left : formPickRect.left + 24
25396
27401
  },
25397
- children: /* @__PURE__ */ jsx35(FieldTypePicker, { onPick: handleAddField })
27402
+ children: /* @__PURE__ */ jsx36(FieldTypePicker, { onPick: handleAddField })
25398
27403
  }
25399
27404
  );
25400
27405
  })() : null,
25401
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx35(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
25402
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx35(
27406
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx36(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
27407
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx36(
25403
27408
  FooterContainerChrome,
25404
27409
  {
25405
27410
  rect: toolbarRect,
@@ -25407,13 +27412,17 @@ function OhhwellsBridge() {
25407
27412
  addDisabled: !canAddFooterColumn()
25408
27413
  }
25409
27414
  ),
25410
- toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ jsx35(
27415
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ jsx36(
25411
27416
  ItemInteractionLayer,
25412
27417
  {
25413
- rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
27418
+ rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected || selectedElRef.current === buttonCollectionDragRef.current) ? draggedItemRect : toolbarRect,
25414
27419
  toolbarBelowRect: selectedElRef.current && isNavigationItem2(selectedElRef.current) ? getOpenNavDropdownPanelRect(selectedElRef.current) : null,
25415
27420
  toolbarBelowEl: selectedElRef.current && isNavigationItem2(selectedElRef.current) ? getOpenNavDropdownPanel(selectedElRef.current) : null,
25416
27421
  elRef: glowElRef,
27422
+ hidden: isRectOutsideClip(
27423
+ isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected || selectedElRef.current === buttonCollectionDragRef.current) ? draggedItemRect : toolbarRect,
27424
+ getIframeVisibleClip(parentScrollRef.current)
27425
+ ),
25417
27426
  state: isItemDragging ? "dragging" : selectedElRef.current && isNavigationItem2(selectedElRef.current) && getOpenNavDropdownPanel(selectedElRef.current) ? "active-bottom" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
25418
27427
  showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
25419
27428
  dragDisabled: reorderDragDisabled,
@@ -25423,18 +27432,25 @@ function OhhwellsBridge() {
25423
27432
  onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
25424
27433
  onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
25425
27434
  itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection && !selectedIsSocialsRow,
25426
- toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ jsx35(
27435
+ toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ jsx36(
25427
27436
  ItemActionToolbar,
25428
27437
  {
25429
27438
  onEditLink: openLinkPopoverForSelected,
25430
27439
  onStyle: () => {
25431
27440
  const row = selectedElRef.current;
25432
27441
  if (!row) return;
25433
- if (floatingPanel?.kind === "socials-display") closeFloatingPanelOnly();
27442
+ suppressNextClickRef.current = true;
27443
+ suppressClickUntilRef.current = Date.now() + 500;
27444
+ if (isCtaButton(row)) {
27445
+ if (floatingPanelRef.current?.kind === "button-style") closeFloatingPanelOnly();
27446
+ else openButtonStylePanel(row);
27447
+ return;
27448
+ }
27449
+ if (floatingPanelRef.current?.kind === "socials-display") closeFloatingPanelOnly();
25434
27450
  else openSocialsDisplayPanel(row);
25435
27451
  },
25436
- showStyle: selectedIsSocialsRow,
25437
- styleActive: floatingPanel?.kind === "socials-display",
27452
+ showStyle: selectedIsSocialsRow || selectedIsCta,
27453
+ styleActive: floatingPanel?.kind === "socials-display" || floatingPanel?.kind === "button-style",
25438
27454
  onAddItem: handleAddChildItem,
25439
27455
  onSelectParent: handleSelectParent,
25440
27456
  onDuplicate: handleDuplicateSelected,
@@ -25445,11 +27461,11 @@ function OhhwellsBridge() {
25445
27461
  })(),
25446
27462
  editLinkDisabled: false,
25447
27463
  moreDisabled: false,
25448
- deleteDisabled: selectedElRef.current !== null && (() => {
27464
+ deleteDisabled: selectedElRef.current !== null && isCtaButton(selectedElRef.current) && !canDeleteButton(selectedElRef.current) || selectedElRef.current !== null && (() => {
25449
27465
  const social = getSocialItem(selectedElRef.current);
25450
27466
  return social ? !canRemoveSocialItem(social) : false;
25451
27467
  })(),
25452
- duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow || selectedElRef.current !== null && (() => {
27468
+ duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow || selectedElRef.current !== null && isCtaButton(selectedElRef.current) && !canDuplicateButton(selectedElRef.current) || selectedElRef.current !== null && (() => {
25453
27469
  const social = getSocialItem(selectedElRef.current);
25454
27470
  const row = social ? findSocialsRow(social) : null;
25455
27471
  return row ? !canAddSocialItem(row) : false;
@@ -25458,7 +27474,7 @@ function OhhwellsBridge() {
25458
27474
  showAddItem: isFooterFrameSelection || selectedIsSocialsRow || !selectedIsCta && Boolean(
25459
27475
  selectedElRef.current && isNavbarHrefKey(selectedElRef.current.getAttribute("data-ohw-href-key")) && !isNestedNavChild(selectedElRef.current)
25460
27476
  ),
25461
- showMore: (!selectedIsCta || isFooterFrameSelection) && !selectedIsSocialsRow,
27477
+ showMore: !selectedIsSocialsRow,
25462
27478
  dropdownOpen: navDropdownPreviewOpen,
25463
27479
  onDropdownOpenChange: handleNavDropdownOpenChange,
25464
27480
  headingVisible: footerHeadingVisible,
@@ -25467,8 +27483,8 @@ function OhhwellsBridge() {
25467
27483
  ) : void 0
25468
27484
  }
25469
27485
  ),
25470
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && !isRectOutsideClip(toolbarRect, getIframeVisibleClip(parentScrollRef.current)) && /* @__PURE__ */ jsxs22(Fragment10, { children: [
25471
- /* @__PURE__ */ jsx35(
27486
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && !isRectOutsideClip(toolbarRect, getIframeVisibleClip(parentScrollRef.current)) && /* @__PURE__ */ jsxs23(Fragment10, { children: [
27487
+ /* @__PURE__ */ jsx36(
25472
27488
  EditGlowChrome,
25473
27489
  {
25474
27490
  rect: toolbarRect,
@@ -25478,7 +27494,7 @@ function OhhwellsBridge() {
25478
27494
  hideHandle: isItemDragging
25479
27495
  }
25480
27496
  ),
25481
- /* @__PURE__ */ jsx35(
27497
+ /* @__PURE__ */ jsx36(
25482
27498
  FloatingToolbar,
25483
27499
  {
25484
27500
  rect: toolbarRect,
@@ -25491,7 +27507,7 @@ function OhhwellsBridge() {
25491
27507
  }
25492
27508
  )
25493
27509
  ] }),
25494
- maxBadge && /* @__PURE__ */ jsxs22(
27510
+ maxBadge && /* @__PURE__ */ jsxs23(
25495
27511
  "div",
25496
27512
  {
25497
27513
  "data-ohw-max-badge": "",
@@ -25517,7 +27533,7 @@ function OhhwellsBridge() {
25517
27533
  ]
25518
27534
  }
25519
27535
  ),
25520
- toggleState && !linkPopover && /* @__PURE__ */ jsx35(
27536
+ toggleState && !linkPopover && /* @__PURE__ */ jsx36(
25521
27537
  StateToggle,
25522
27538
  {
25523
27539
  rect: toggleState.rect,
@@ -25526,15 +27542,15 @@ function OhhwellsBridge() {
25526
27542
  onStateChange: handleStateChange
25527
27543
  }
25528
27544
  ),
25529
- sectionGap && !linkPopover && /* @__PURE__ */ jsxs22(
27545
+ sectionGap && !linkPopover && /* @__PURE__ */ jsxs23(
25530
27546
  "div",
25531
27547
  {
25532
27548
  "data-ohw-section-insert-line": "",
25533
27549
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
25534
27550
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
25535
27551
  children: [
25536
- /* @__PURE__ */ jsx35("div", { className: "flex-1 bg-bridge-primary", style: { height: 3 } }),
25537
- /* @__PURE__ */ jsx35(
27552
+ /* @__PURE__ */ jsx36("div", { className: "flex-1 bg-bridge-primary", style: { height: 3 } }),
27553
+ /* @__PURE__ */ jsx36(
25538
27554
  Badge,
25539
27555
  {
25540
27556
  className: "px-8 py-1 bg-bridge-primary hover:bg-bridge-primary text-primary-foreground text-xs font-medium shrink-0 rounded-full cursor-pointer pointer-events-auto",
@@ -25551,11 +27567,11 @@ function OhhwellsBridge() {
25551
27567
  children: "Add Section"
25552
27568
  }
25553
27569
  ),
25554
- /* @__PURE__ */ jsx35("div", { className: "flex-1 bg-bridge-primary", style: { height: 3 } })
27570
+ /* @__PURE__ */ jsx36("div", { className: "flex-1 bg-bridge-primary", style: { height: 3 } })
25555
27571
  ]
25556
27572
  }
25557
27573
  ),
25558
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx35(
27574
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx36(
25559
27575
  LinkPopover,
25560
27576
  {
25561
27577
  panelRef: linkPopoverPanelRef,
@@ -25572,7 +27588,7 @@ function OhhwellsBridge() {
25572
27588
  },
25573
27589
  linkPopover.key
25574
27590
  ) : null,
25575
- floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx35(
27591
+ floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx36(
25576
27592
  FloatingPanel,
25577
27593
  {
25578
27594
  open: true,
@@ -25582,7 +27598,7 @@ function OhhwellsBridge() {
25582
27598
  onPositionChange: setFloatingPanelPos,
25583
27599
  parentScroll: parentScrollSnap ?? parentScrollRef.current,
25584
27600
  onClose: closeFloatingPanelOnly,
25585
- children: /* @__PURE__ */ jsx35(
27601
+ children: /* @__PURE__ */ jsx36(
25586
27602
  SocialsDisplayPanel,
25587
27603
  {
25588
27604
  display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
@@ -25593,7 +27609,48 @@ function OhhwellsBridge() {
25593
27609
  }
25594
27610
  )
25595
27611
  }
25596
- ) : null
27612
+ ) : null,
27613
+ floatingPanel && floatingPanel.kind === "button-style" ? (() => {
27614
+ const button = document.querySelector(
27615
+ `[data-ohw-href-key="${CSS.escape(floatingPanel.buttonId)}"]`
27616
+ );
27617
+ if (!button) return null;
27618
+ captureButtonOrigin(button);
27619
+ const tokens = tokensFromButton(button);
27620
+ const current = assignmentFor(button, editContentRef.current, tokens);
27621
+ void buttonStyleTick;
27622
+ return /* @__PURE__ */ jsx36(
27623
+ FloatingPanel,
27624
+ {
27625
+ open: true,
27626
+ title: floatingPanel.title,
27627
+ context: floatingPanel.context,
27628
+ icon: /* @__PURE__ */ jsx36(Brush2, { className: "size-4 shrink-0", "aria-hidden": true }),
27629
+ position: floatingPanelPos,
27630
+ onPositionChange: setFloatingPanelPos,
27631
+ parentScroll: parentScrollSnap ?? parentScrollRef.current,
27632
+ onClose: closeFloatingPanelOnly,
27633
+ bodyClassName: "gap-5",
27634
+ children: /* @__PURE__ */ jsx36(
27635
+ ButtonStylePanel,
27636
+ {
27637
+ variants: tokens.variants,
27638
+ sizes: tokens.sizes,
27639
+ variant: current.variant,
27640
+ size: current.size,
27641
+ slotBackground: previewSlotBackground(button),
27642
+ previewLabel: (button.querySelector(
27643
+ '[data-ohw-editable="text"], [data-ohw-editable="plain"]'
27644
+ )?.textContent ?? button.textContent ?? "Book Now").trim() || "Book Now",
27645
+ onVariant: (variant) => applySelectedButtonStyle({ variant }),
27646
+ onSize: (size) => applySelectedButtonStyle({ size })
27647
+ },
27648
+ floatingPanel.key
27649
+ )
27650
+ },
27651
+ floatingPanel.key
27652
+ );
27653
+ })() : null
25597
27654
  ] }),
25598
27655
  bridgeRoot
25599
27656
  ) : null