@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.cjs CHANGED
@@ -501,6 +501,40 @@ function applyBrandToDom(kit) {
501
501
  }
502
502
  }
503
503
 
504
+ // src/lib/custom-code.ts
505
+ var ALLOWED_HEAD_TAGS = /* @__PURE__ */ new Set(["style", "meta", "link", "script", "noscript", "base"]);
506
+ var CUSTOM_CODE_AFTER_HEAD_KEY = "__ohw_custom_code_after_head";
507
+ var CUSTOM_CODE_BEFORE_HEAD_KEY = "__ohw_custom_code_before_head";
508
+ function clearExisting(slot) {
509
+ document.querySelectorAll(`[data-ohw-custom-code="${slot}"]`).forEach((el) => el.remove());
510
+ }
511
+ function buildNodes(raw, slot) {
512
+ if (!raw || !raw.trim()) return [];
513
+ const parsed = new DOMParser().parseFromString(raw, "text/html");
514
+ const source = [...parsed.head.childNodes, ...parsed.body.childNodes];
515
+ const nodes = [];
516
+ for (const node of source) {
517
+ if (node.nodeType !== Node.ELEMENT_NODE) continue;
518
+ const el = node;
519
+ const tag = el.tagName.toLowerCase();
520
+ if (!ALLOWED_HEAD_TAGS.has(tag)) continue;
521
+ const built = document.createElement(tag);
522
+ for (const attr of Array.from(el.attributes)) built.setAttribute(attr.name, attr.value);
523
+ if (tag === "script" || tag === "style" || tag === "noscript") built.textContent = el.textContent;
524
+ built.dataset.ohwCustomCode = slot;
525
+ nodes.push(built);
526
+ }
527
+ return nodes;
528
+ }
529
+ function applyCustomCodeToHead(content) {
530
+ clearExisting("after");
531
+ clearExisting("before");
532
+ const afterNodes = buildNodes(content[CUSTOM_CODE_AFTER_HEAD_KEY], "after");
533
+ if (afterNodes.length > 0) document.head.prepend(...afterNodes);
534
+ const beforeNodes = buildNodes(content[CUSTOM_CODE_BEFORE_HEAD_KEY], "before");
535
+ if (beforeNodes.length > 0) document.head.append(...beforeNodes);
536
+ }
537
+
504
538
  // src/lib/section-styles.ts
505
539
  var STYLE_STORE_KEY = "__ohw_styles";
506
540
  var STYLE_SHEET_ID = "ohw-section-styles";
@@ -564,9 +598,9 @@ function styleSheetCss() {
564
598
  );
565
599
  }
566
600
  const pad = { tight: 40, balanced: 64, airy: 96 };
567
- for (const [spacing, px] of Object.entries(pad)) {
601
+ for (const [spacing, px2] of Object.entries(pad)) {
568
602
  rules.push(
569
- `[data-ohw-style-spacing="${spacing}"] { padding-top: ${px}px !important; padding-bottom: ${px}px !important; }`
603
+ `[data-ohw-style-spacing="${spacing}"] { padding-top: ${px2}px !important; padding-bottom: ${px2}px !important; }`
570
604
  );
571
605
  }
572
606
  rules.push(
@@ -3133,12 +3167,12 @@ function deriveTemplateButtonStyle() {
3133
3167
  cs.borderBottomLeftRadius
3134
3168
  ].map((v) => v || "0px");
3135
3169
  const radius = corners.every((v) => v === corners[0]) ? corners[0] : corners.join(" ");
3136
- const px = (v) => parseFloat(v) || 0;
3137
- const lineHeight = px(cs.lineHeight) || px(cs.fontSize) * 1.2;
3138
- const contentH = btn.getBoundingClientRect().height - px(cs.borderTopWidth) - px(cs.borderBottomWidth);
3170
+ const px2 = (v) => parseFloat(v) || 0;
3171
+ const lineHeight = px2(cs.lineHeight) || px2(cs.fontSize) * 1.2;
3172
+ const contentH = btn.getBoundingClientRect().height - px2(cs.borderTopWidth) - px2(cs.borderBottomWidth);
3139
3173
  const impliedY = Math.round(Math.max(0, (contentH - lineHeight) / 2));
3140
- const padY = Math.max(px(cs.paddingTop), px(cs.paddingBottom), impliedY);
3141
- const padX = Math.max(px(cs.paddingLeft), px(cs.paddingRight));
3174
+ const padY = Math.max(px2(cs.paddingTop), px2(cs.paddingBottom), impliedY);
3175
+ const padX = Math.max(px2(cs.paddingLeft), px2(cs.paddingRight));
3142
3176
  return {
3143
3177
  radius: radius || "10px",
3144
3178
  padding: `${padY}px ${padX}px`,
@@ -8055,6 +8089,14 @@ function ItemActionToolbar({
8055
8089
  e.preventDefault();
8056
8090
  e.stopPropagation();
8057
8091
  onStyle?.();
8092
+ },
8093
+ // Same as Edit link: mousedown opens the panel and remounts this toolbar
8094
+ // (styleActive flips). Without stopping click, the gesture lands on whatever
8095
+ // is now under the cursor — navbar CTAs park the toolbar below the button, so
8096
+ // that miss deselects and the panel closes as soon as it opens.
8097
+ onClick: (e) => {
8098
+ e.preventDefault();
8099
+ e.stopPropagation();
8058
8100
  }
8059
8101
  },
8060
8102
  children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react3.Brush, { className: "size-4 shrink-0", "aria-hidden": true })
@@ -8377,20 +8419,32 @@ function ItemInteractionLayer({
8377
8419
  itemDragSurface = true,
8378
8420
  chromeGap,
8379
8421
  toolbarAlign = "center",
8422
+ hidden = false,
8380
8423
  className
8381
8424
  }) {
8425
+ const localRef = (0, import_react7.useRef)(null);
8426
+ (0, import_react7.useLayoutEffect)(() => {
8427
+ const node = localRef.current;
8428
+ if (!node) return;
8429
+ node.style.display = hidden ? "none" : "block";
8430
+ }, [hidden, rect.top, rect.left, rect.width, rect.height, state]);
8382
8431
  if (state === "default") return null;
8383
8432
  const gap = chromeGap ?? (state === "hover" ? HOVER_CHROME_GAP : SELECTION_CHROME_GAP);
8384
8433
  const isActive = state === "active-top" || state === "active-bottom";
8385
8434
  const isDragging = state === "dragging";
8386
8435
  const showToolbar = isActive && toolbar;
8387
8436
  const showDragHandle = (isActive || isDragging) && showHandle;
8388
- const itemDragEnabled = itemDragSurface && isActive && showHandle && !isDragging && !dragDisabled;
8437
+ const itemDragEnabled = itemDragSurface && isActive && !isDragging && !dragDisabled && Boolean(showHandle || onItemPointerDown);
8389
8438
  const useDetachedBelowToolbar = Boolean(showToolbar && state === "active-bottom" && toolbarBelowRect);
8439
+ const setRootRef = (node) => {
8440
+ localRef.current = node;
8441
+ if (typeof elRef === "function") elRef(node);
8442
+ else if (elRef) elRef.current = node;
8443
+ };
8390
8444
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
8391
8445
  "div",
8392
8446
  {
8393
- ref: elRef,
8447
+ ref: setRootRef,
8394
8448
  "data-ohw-item-interaction": "",
8395
8449
  "data-ohw-item-interaction-state": state,
8396
8450
  className: cn("pointer-events-none", className),
@@ -8400,7 +8454,8 @@ function ItemInteractionLayer({
8400
8454
  left: rect.left - gap,
8401
8455
  width: rect.width + gap * 2,
8402
8456
  height: rect.height + gap * 2,
8403
- zIndex: getChromeZIndex(state)
8457
+ zIndex: getChromeZIndex(state),
8458
+ display: hidden ? "none" : "block"
8404
8459
  },
8405
8460
  children: [
8406
8461
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
@@ -11221,6 +11276,13 @@ function findButtonAtPoint(x, y, media) {
11221
11276
  if (hits.length === 0) return null;
11222
11277
  return hits.reduce((best, el) => best.contains(el) ? el : best);
11223
11278
  }
11279
+ function findNavbarCtaAtPoint(x, y) {
11280
+ const hits = Array.from(
11281
+ document.querySelectorAll('[data-ohw-role="navbar-button"]')
11282
+ ).filter((el) => containsPoint(el, x, y) && !el.closest("[data-ohw-bridge]"));
11283
+ if (hits.length === 0) return null;
11284
+ return hits.reduce((best, el) => best.contains(el) ? el : best);
11285
+ }
11224
11286
 
11225
11287
  // src/lib/session-search.ts
11226
11288
  var STORAGE_KEY = "ohw-preserved-search";
@@ -11956,6 +12018,26 @@ var import_react12 = require("react");
11956
12018
  var import_react_dom3 = require("react-dom");
11957
12019
  var import_lucide_react15 = require("lucide-react");
11958
12020
  var import_navigation2 = require("next/navigation");
12021
+
12022
+ // src/lib/parent-scroll.ts
12023
+ function visibleBand(parentScroll) {
12024
+ const { iframeOffsetTop, headerH: visibleCanvasTop, canvasH } = parentScroll;
12025
+ const top = Math.max(0, visibleCanvasTop - iframeOffsetTop);
12026
+ const bottom = Math.min(window.innerHeight, visibleCanvasTop + canvasH - iframeOffsetTop);
12027
+ return { top, bottom: Math.max(top, bottom) };
12028
+ }
12029
+ function getIframeVisibleClip(parentScroll) {
12030
+ if (!parentScroll) return null;
12031
+ return visibleBand(parentScroll);
12032
+ }
12033
+ function getIframeChromeClip(parentScroll) {
12034
+ if (!parentScroll) return null;
12035
+ const band = visibleBand(parentScroll);
12036
+ const promptBarH = Math.max(0, parentScroll.promptBarH ?? 0);
12037
+ return { top: band.top, bottom: Math.max(band.top, band.bottom - promptBarH) };
12038
+ }
12039
+
12040
+ // src/ui/link-modal/SectionPickerOverlay.tsx
11959
12041
  var import_jsx_runtime27 = require("react/jsx-runtime");
11960
12042
  var DIM_OVERLAY = "rgba(0, 0, 0, 0.45)";
11961
12043
  function rectsEqual(a, b) {
@@ -12093,20 +12175,22 @@ function SectionPickerOverlay({
12093
12175
  []
12094
12176
  );
12095
12177
  (0, import_react12.useEffect)(() => {
12096
- const applyClip = (iframeOffsetTop, visibleCanvasTop, canvasH) => {
12097
- const top = Math.max(0, visibleCanvasTop - iframeOffsetTop);
12098
- const bottom = Math.min(
12099
- window.innerHeight,
12100
- visibleCanvasTop + canvasH - iframeOffsetTop
12101
- );
12178
+ const applyClip = (iframeOffsetTop, visibleCanvasTop, canvasH, promptBarH = 0) => {
12179
+ const next = getIframeVisibleClip({
12180
+ iframeOffsetTop,
12181
+ headerH: visibleCanvasTop,
12182
+ canvasH,
12183
+ promptBarH
12184
+ });
12185
+ if (!next) return;
12102
12186
  setChromeClip(
12103
- (prev) => prev.top === top && prev.bottom === bottom ? prev : { top, bottom: Math.max(top, bottom) }
12187
+ (prev) => prev.top === next.top && prev.bottom === next.bottom ? prev : next
12104
12188
  );
12105
12189
  };
12106
12190
  const onParentScroll = (e) => {
12107
12191
  if (e.data?.type !== "ow:parent-scroll") return;
12108
- const { iframeOffsetTop, headerH, canvasH } = e.data;
12109
- applyClip(iframeOffsetTop, headerH, canvasH);
12192
+ const { iframeOffsetTop, headerH, canvasH, promptBarH = 0 } = e.data;
12193
+ applyClip(iframeOffsetTop, headerH, canvasH, promptBarH);
12110
12194
  iframeOffsetTopRef.current = iframeOffsetTop;
12111
12195
  if (!pointerScreenRef.current && pointerRef.current) {
12112
12196
  pointerScreenRef.current = {
@@ -15770,36 +15854,50 @@ var import_lucide_react16 = require("lucide-react");
15770
15854
  var import_jsx_runtime29 = require("react/jsx-runtime");
15771
15855
  var PANEL_WIDTH = 256;
15772
15856
  var EDGE_MARGIN = 16;
15857
+ var HEADER_MIN = 56;
15773
15858
  function getVisibleClip(parentScroll) {
15774
15859
  const left = 0;
15775
15860
  const right = window.innerWidth;
15776
- if (!parentScroll) {
15861
+ const band = getIframeChromeClip(parentScroll);
15862
+ if (!band) {
15777
15863
  return { top: 0, bottom: window.innerHeight, left, right };
15778
15864
  }
15779
- const { iframeOffsetTop, headerH: visibleCanvasTop, canvasH } = parentScroll;
15780
- const top = Math.max(0, visibleCanvasTop - iframeOffsetTop);
15781
- const bottom = Math.min(window.innerHeight, visibleCanvasTop + canvasH - iframeOffsetTop);
15782
- return { top, bottom: Math.max(top, bottom), left, right };
15865
+ return { ...band, left, right };
15783
15866
  }
15784
- function defaultFloatingPanelPosition(parentScroll, panelHeight = 280) {
15867
+ function defaultFloatingPanelPosition(parentScroll, _panelHeight = 280) {
15785
15868
  const clip = getVisibleClip(parentScroll);
15786
15869
  return {
15787
15870
  x: Math.max(EDGE_MARGIN, clip.right - PANEL_WIDTH - EDGE_MARGIN),
15788
- y: Math.min(
15789
- Math.max(clip.top + EDGE_MARGIN, EDGE_MARGIN),
15790
- Math.max(clip.top + EDGE_MARGIN, clip.bottom - panelHeight - EDGE_MARGIN)
15791
- )
15871
+ y: clip.top + EDGE_MARGIN
15792
15872
  };
15793
15873
  }
15794
- function clampPosition(pos, parentScroll, panelW, panelH) {
15874
+ function clampX(x, panelW) {
15875
+ const left = EDGE_MARGIN;
15876
+ const maxX = Math.max(left, window.innerWidth - panelW - EDGE_MARGIN);
15877
+ return Math.min(Math.max(x, left), maxX);
15878
+ }
15879
+ function clampDragPosition(pos, parentScroll, panelW) {
15795
15880
  const clip = getVisibleClip(parentScroll);
15796
- const maxX = Math.max(clip.left + EDGE_MARGIN, clip.right - panelW - EDGE_MARGIN);
15797
- const maxY = Math.max(clip.top + EDGE_MARGIN, clip.bottom - panelH - EDGE_MARGIN);
15881
+ const minY = clip.top + EDGE_MARGIN;
15882
+ const maxY = Math.max(minY, clip.bottom - EDGE_MARGIN - HEADER_MIN);
15798
15883
  return {
15799
- x: Math.min(Math.max(pos.x, clip.left + EDGE_MARGIN), maxX),
15800
- y: Math.min(Math.max(pos.y, clip.top + EDGE_MARGIN), maxY)
15884
+ x: clampX(pos.x, panelW),
15885
+ y: Math.min(Math.max(pos.y, minY), maxY)
15801
15886
  };
15802
15887
  }
15888
+ function fitPanelInClip(homeY, parentScroll, preferredHeight) {
15889
+ const clip = getVisibleClip(parentScroll);
15890
+ const minY = clip.top + EDGE_MARGIN;
15891
+ const maxBottom = clip.bottom - EDGE_MARGIN;
15892
+ const band = Math.max(HEADER_MIN, maxBottom - minY);
15893
+ let y = Math.max(homeY, minY);
15894
+ const want = Math.min(Math.max(preferredHeight, HEADER_MIN), band);
15895
+ if (y + want > maxBottom) {
15896
+ y = Math.max(minY, maxBottom - want);
15897
+ }
15898
+ const maxHeight = Math.max(HEADER_MIN, Math.floor(maxBottom - y));
15899
+ return { y, maxHeight };
15900
+ }
15803
15901
  function FloatingPanel({
15804
15902
  open,
15805
15903
  title,
@@ -15816,19 +15914,54 @@ function FloatingPanel({
15816
15914
  const panelRef = (0, import_react15.useRef)(null);
15817
15915
  const [measured, setMeasured] = (0, import_react15.useState)({ w: PANEL_WIDTH, h: 280 });
15818
15916
  const dragRef = (0, import_react15.useRef)(null);
15819
- const resolved = position ?? defaultFloatingPanelPosition(parentScroll, measured.h);
15820
- const clamped = clampPosition(resolved, parentScroll, measured.w, measured.h);
15917
+ const homeRef = (0, import_react15.useRef)(null);
15918
+ if (!open) {
15919
+ homeRef.current = null;
15920
+ } else if (position) {
15921
+ homeRef.current = position;
15922
+ } else if (homeRef.current == null) {
15923
+ homeRef.current = defaultFloatingPanelPosition(parentScroll);
15924
+ }
15925
+ const [liveScroll, setLiveScroll] = (0, import_react15.useState)(parentScroll);
15926
+ const home = position ?? homeRef.current ?? defaultFloatingPanelPosition(parentScroll);
15927
+ const { y: fittedY, maxHeight } = fitPanelInClip(home.y, liveScroll, measured.h);
15928
+ const clamped = {
15929
+ x: clampX(home.x, measured.w),
15930
+ y: fittedY
15931
+ };
15932
+ (0, import_react15.useEffect)(() => {
15933
+ setLiveScroll(parentScroll);
15934
+ }, [parentScroll]);
15935
+ (0, import_react15.useEffect)(() => {
15936
+ if (!open) return;
15937
+ const onParentScroll = (e) => {
15938
+ if (e.data?.type !== "ow:parent-scroll") return;
15939
+ const { iframeOffsetTop, headerH, canvasH, promptBarH = 0 } = e.data;
15940
+ setLiveScroll({ iframeOffsetTop, headerH, canvasH, promptBarH });
15941
+ };
15942
+ window.addEventListener("message", onParentScroll);
15943
+ return () => window.removeEventListener("message", onParentScroll);
15944
+ }, [open]);
15945
+ const positionRef = (0, import_react15.useRef)(position);
15946
+ (0, import_react15.useLayoutEffect)(() => {
15947
+ if (!open) {
15948
+ positionRef.current = position;
15949
+ return;
15950
+ }
15951
+ if (position == null && positionRef.current != null) {
15952
+ homeRef.current = defaultFloatingPanelPosition(liveScroll ?? parentScroll);
15953
+ }
15954
+ positionRef.current = position;
15955
+ }, [open, position, liveScroll, parentScroll]);
15821
15956
  (0, import_react15.useLayoutEffect)(() => {
15822
15957
  if (!open || !panelRef.current) return;
15823
15958
  const el = panelRef.current;
15824
- const next = { w: el.offsetWidth || PANEL_WIDTH, h: el.offsetHeight || 280 };
15959
+ const header = el.querySelector("[data-ohw-floating-panel-header]");
15960
+ const body = el.querySelector("[data-ohw-floating-panel-body]");
15961
+ const contentH = (header?.offsetHeight ?? 0) + (body?.scrollHeight ?? 0) || el.scrollHeight || 280;
15962
+ const next = { w: el.offsetWidth || PANEL_WIDTH, h: contentH };
15825
15963
  setMeasured((prev) => prev.w === next.w && prev.h === next.h ? prev : next);
15826
15964
  }, [open, children, title, context]);
15827
- (0, import_react15.useEffect)(() => {
15828
- if (!open || !position || !onPositionChange) return;
15829
- const next = clampPosition(position, parentScroll, measured.w, measured.h);
15830
- if (next.x !== position.x || next.y !== position.y) onPositionChange(next);
15831
- }, [open, parentScroll, measured.w, measured.h, position, onPositionChange]);
15832
15965
  const onHeaderPointerDown = (0, import_react15.useCallback)(
15833
15966
  (e) => {
15834
15967
  if (e.button !== 0) return;
@@ -15862,18 +15995,17 @@ function FloatingPanel({
15862
15995
  return;
15863
15996
  }
15864
15997
  e.preventDefault();
15865
- const next = clampPosition(
15998
+ const next = clampDragPosition(
15866
15999
  {
15867
16000
  x: drag.originX + (e.clientX - drag.startX),
15868
16001
  y: drag.originY + (e.clientY - drag.startY)
15869
16002
  },
15870
- parentScroll,
15871
- measured.w,
15872
- measured.h
16003
+ liveScroll,
16004
+ measured.w
15873
16005
  );
15874
16006
  onPositionChange?.(next);
15875
16007
  },
15876
- [measured.h, measured.w, onPositionChange, parentScroll]
16008
+ [measured.w, onPositionChange, liveScroll]
15877
16009
  );
15878
16010
  const endDrag = (0, import_react15.useCallback)((e) => {
15879
16011
  const drag = dragRef.current;
@@ -15903,7 +16035,7 @@ function FloatingPanel({
15903
16035
  "fixed z-[2147483647] flex w-64 flex-col overflow-hidden rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
15904
16036
  className
15905
16037
  ),
15906
- style: { left: clamped.x, top: clamped.y },
16038
+ style: { left: clamped.x, top: clamped.y, maxHeight },
15907
16039
  onMouseDown: (e) => e.stopPropagation(),
15908
16040
  onPointerDown: (e) => e.stopPropagation(),
15909
16041
  onClick: (e) => e.stopPropagation(),
@@ -15912,7 +16044,7 @@ function FloatingPanel({
15912
16044
  "div",
15913
16045
  {
15914
16046
  "data-ohw-floating-panel-header": "",
15915
- className: "relative flex cursor-grab items-start gap-2 border-b border-border py-5 pl-5 pr-11 active:cursor-grabbing",
16047
+ className: "relative flex shrink-0 cursor-grab items-start gap-2 border-b border-border py-4! pl-5! pr-11! active:cursor-grabbing",
15916
16048
  onPointerDown: onHeaderPointerDown,
15917
16049
  onPointerMove: onHeaderPointerMove,
15918
16050
  onPointerUp: endDrag,
@@ -15947,7 +16079,11 @@ function FloatingPanel({
15947
16079
  "div",
15948
16080
  {
15949
16081
  "data-ohw-floating-panel-body": "",
15950
- className: cn("flex w-full flex-col gap-4 p-5", bodyClassName),
16082
+ className: cn(
16083
+ "flex min-h-0 w-full flex-1 flex-col gap-4 overflow-y-auto overscroll-contain p-4",
16084
+ bodyClassName
16085
+ ),
16086
+ style: { WebkitOverflowScrolling: "touch" },
15951
16087
  children
15952
16088
  }
15953
16089
  )
@@ -16025,198 +16161,1632 @@ function SocialsDisplayPanel({ display, onChange, className }) {
16025
16161
  ] });
16026
16162
  }
16027
16163
 
16028
- // src/lib/item-drag-interaction.ts
16029
- function disableNativeHrefDrag(el) {
16030
- if (el.draggable) el.draggable = false;
16031
- if (el.getAttribute("draggable") !== "false") {
16032
- el.setAttribute("draggable", "false");
16164
+ // src/ui/button-style-panel.tsx
16165
+ var import_lucide_react17 = require("lucide-react");
16166
+
16167
+ // src/lib/button-styles.ts
16168
+ var BUTTON_VARIANTS_KEY = "__ohw_button_variants";
16169
+ var BUTTON_SIZES_KEY = "__ohw_button_sizes";
16170
+ var BUTTON_ASSIGNMENTS_KEY = "__ohw_button_assignments";
16171
+ var BUTTON_VARIANT_IDS = ["primary", "secondary", "ghost"];
16172
+ var BUTTON_SIZE_IDS = ["s", "m", "l"];
16173
+ var TRANSPARENT = "rgba(0, 0, 0, 0)";
16174
+ var REFERENCE_SIZES = {
16175
+ s: { height: 40, paddingTop: 8, paddingRight: 16, paddingBottom: 8, paddingLeft: 16, fontSize: 15, borderRadius: 8, gap: 8 },
16176
+ m: { height: 48, paddingTop: 12, paddingRight: 24, paddingBottom: 12, paddingLeft: 24, fontSize: 15, borderRadius: 8, gap: 8 },
16177
+ l: { height: 56, paddingTop: 16, paddingRight: 32, paddingBottom: 16, paddingLeft: 32, fontSize: 15, borderRadius: 8, gap: 8 }
16178
+ };
16179
+ function buttonStyleContext(_el) {
16180
+ return "Button";
16181
+ }
16182
+ function previewSlotBackground(el) {
16183
+ let node = el.parentElement;
16184
+ while (node && node !== el.ownerDocument.documentElement) {
16185
+ if (node.closest("[data-ohw-bridge], [data-ohw-bridge-root]")) {
16186
+ node = node.parentElement;
16187
+ continue;
16188
+ }
16189
+ const bg = getComputedStyle(node).backgroundColor;
16190
+ const parsed = parseColor(bg);
16191
+ if (parsed && !isTransparent(parsed)) return bg;
16192
+ node = node.parentElement;
16193
+ }
16194
+ const root = el.ownerDocument.documentElement;
16195
+ const style = getComputedStyle(root);
16196
+ for (const name of ["--brand-surface", "--brand-background", "--ohw-brand-light", "--color-light"]) {
16197
+ const value = style.getPropertyValue(name).trim();
16198
+ if (value) return value;
16033
16199
  }
16200
+ return "var(--brand-background, var(--ohw-brand-light, var(--color-light, #ffffff)))";
16034
16201
  }
16035
- function clearTextSelection() {
16036
- const sel = window.getSelection();
16037
- if (sel && !sel.isCollapsed) sel.removeAllRanges();
16202
+ function buttonId(el) {
16203
+ if (!el) return null;
16204
+ const button = el.matches(CTA_BUTTON_SELECTOR) ? el : el.closest(CTA_BUTTON_SELECTOR);
16205
+ const id = button?.getAttribute("data-ohw-href-key")?.trim();
16206
+ return id || null;
16038
16207
  }
16039
- function armItemPressDrag() {
16040
- document.documentElement.setAttribute("data-ohw-footer-press-drag", "");
16208
+ function isButtonVariantId(value) {
16209
+ return BUTTON_VARIANT_IDS.includes(value);
16041
16210
  }
16042
- function lockItemDuringDrag() {
16043
- document.documentElement.removeAttribute("data-ohw-footer-press-drag");
16044
- document.documentElement.setAttribute("data-ohw-item-dragging", "");
16045
- clearTextSelection();
16211
+ function isButtonSizeId(value) {
16212
+ return BUTTON_SIZE_IDS.includes(value);
16046
16213
  }
16047
- function unlockItemDragInteraction() {
16048
- const wasDragging = document.documentElement.hasAttribute("data-ohw-item-dragging");
16049
- document.documentElement.removeAttribute("data-ohw-footer-press-drag");
16050
- document.documentElement.removeAttribute("data-ohw-item-dragging");
16051
- if (wasDragging) clearTextSelection();
16214
+ function px(value) {
16215
+ const rounded = Math.round(value * 100) / 100;
16216
+ return `${rounded}px`;
16052
16217
  }
16053
- var armFooterPressDrag = armItemPressDrag;
16054
- var lockFooterDuringDrag = lockItemDuringDrag;
16055
- var unlockFooterDragInteraction = unlockItemDragInteraction;
16056
-
16057
- // src/lib/nav-dnd.ts
16058
- function listReorderableNavItems(draggedEl) {
16059
- return listActiveNavbarItems(draggedEl).filter((el) => {
16060
- const key = el.getAttribute("data-ohw-href-key");
16061
- return isNavbarHrefKey(key) && !isNestedNavChild(el);
16062
- });
16218
+ function parsePx(value, rootFont = 16) {
16219
+ if (!value) return 0;
16220
+ const n = parseFloat(value);
16221
+ if (!Number.isFinite(n)) return 0;
16222
+ if (value.trim().endsWith("rem") || value.trim().endsWith("em")) return n * rootFont;
16223
+ return n;
16063
16224
  }
16064
- function resolveParentNavHrefKey(child) {
16065
- const childrenRoot = child.closest("[data-ohw-nav-children]");
16066
- const group = childrenRoot?.closest("[data-ohw-nav-group]");
16067
- if (!group) return null;
16068
- const trigger = group.querySelector(":scope > [data-ohw-href-key]") ?? Array.from(group.querySelectorAll("[data-ohw-href-key]")).find(
16069
- (el) => !el.closest("[data-ohw-nav-children]")
16070
- ) ?? null;
16071
- const key = trigger?.getAttribute("data-ohw-href-key");
16072
- return key && isNavbarHrefKey(key) ? key : null;
16225
+ function classifyHeight(height) {
16226
+ if (height < 44) return "s";
16227
+ if (height < 52) return "m";
16228
+ return "l";
16073
16229
  }
16074
- function listNavChildren(parentHrefKey, draggedEl) {
16075
- const items = listActiveNavbarItems(draggedEl);
16076
- const parent = items.find((el) => el.getAttribute("data-ohw-href-key") === parentHrefKey);
16077
- if (!parent) return [];
16078
- const group = parent.closest("[data-ohw-nav-group]");
16079
- const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
16080
- if (!childrenRoot) return [];
16081
- return Array.from(childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
16082
- (el) => isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))
16083
- );
16230
+ function scaleGeometry(geometry, scale = 0.75) {
16231
+ return {
16232
+ paddingTop: px(parsePx(geometry.paddingTop) * scale),
16233
+ paddingRight: px(parsePx(geometry.paddingRight) * scale),
16234
+ paddingBottom: px(parsePx(geometry.paddingBottom) * scale),
16235
+ paddingLeft: px(parsePx(geometry.paddingLeft) * scale),
16236
+ fontSize: px(parsePx(geometry.fontSize) * scale),
16237
+ borderRadius: px(parsePx(geometry.borderRadius) * scale),
16238
+ gap: px(parsePx(geometry.gap) * scale),
16239
+ height: Math.round(geometry.height * scale)
16240
+ };
16084
16241
  }
16085
- function isVerticalNavLayout(items, draggedEl) {
16086
- const visible = items.filter((el) => el.getClientRects().length > 0);
16087
- if (visible.length >= 2) {
16088
- const a = visible[0].getBoundingClientRect();
16089
- const b = visible[1].getBoundingClientRect();
16090
- const verticalGap = b.top - a.bottom;
16091
- const horizontalGap = b.left - a.right;
16092
- return verticalGap > horizontalGap;
16093
- }
16094
- const container = getActiveNavbarListContainer(draggedEl);
16095
- if (!container) return false;
16096
- const style = window.getComputedStyle(container);
16097
- return style.flexDirection === "column" || style.flexDirection === "column-reverse";
16242
+ function relativeLuminance(color) {
16243
+ const channel = (value) => {
16244
+ const s = value / 255;
16245
+ return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
16246
+ };
16247
+ return 0.2126 * channel(color.r) + 0.7152 * channel(color.g) + 0.0722 * channel(color.b);
16098
16248
  }
16099
- function buildVerticalRootNavDropSlots(items, draggedEl) {
16100
- const slots = [];
16101
- const barThickness = 3;
16102
- if (items.length === 0) {
16103
- const container = getActiveNavbarListContainer(draggedEl);
16104
- if (!container) return slots;
16105
- const rect = container.getBoundingClientRect();
16106
- slots.push({
16107
- insertIndex: 0,
16108
- parentId: null,
16109
- left: rect.left,
16110
- top: rect.top + rect.height / 2 - barThickness / 2,
16111
- width: Math.max(rect.width, 40),
16112
- height: barThickness,
16113
- direction: "horizontal"
16114
- });
16115
- return slots;
16116
- }
16117
- const edgeGap = (() => {
16118
- if (items.length < 2) return 16;
16119
- const a = items[0].getBoundingClientRect();
16120
- const b = items[1].getBoundingClientRect();
16121
- return Math.max(0, b.top - a.bottom);
16122
- })();
16123
- for (let i = 0; i <= items.length; i++) {
16124
- let top;
16125
- let width;
16126
- let left;
16127
- if (i === 0) {
16128
- const first = items[0].getBoundingClientRect();
16129
- top = first.top - edgeGap / 2 - barThickness / 2;
16130
- left = first.left;
16131
- width = first.width;
16132
- } else if (i === items.length) {
16133
- const last = items[items.length - 1].getBoundingClientRect();
16134
- const lastGap = items.length >= 2 ? Math.max(0, last.top - items[items.length - 2].getBoundingClientRect().bottom) : edgeGap;
16135
- top = last.bottom + lastGap / 2 - barThickness / 2;
16136
- left = last.left;
16137
- width = last.width;
16138
- } else {
16139
- const prev = items[i - 1].getBoundingClientRect();
16140
- const next = items[i].getBoundingClientRect();
16141
- top = (prev.bottom + next.top) / 2 - barThickness / 2;
16142
- left = Math.min(prev.left, next.left);
16143
- width = Math.max(prev.right, next.right) - left;
16249
+ function contrastRatio(a, b) {
16250
+ const lighter = Math.max(relativeLuminance(a), relativeLuminance(b));
16251
+ const darker = Math.min(relativeLuminance(a), relativeLuminance(b));
16252
+ return (lighter + 0.05) / (darker + 0.05);
16253
+ }
16254
+ function compositeOver(fg, bg) {
16255
+ const a = fg.a + bg.a * (1 - fg.a);
16256
+ if (a < 1e-3) return { ...bg, a: 0 };
16257
+ return {
16258
+ r: (fg.r * fg.a + bg.r * bg.a * (1 - fg.a)) / a,
16259
+ g: (fg.g * fg.a + bg.g * bg.a * (1 - fg.a)) / a,
16260
+ b: (fg.b * fg.a + bg.b * bg.a * (1 - fg.a)) / a,
16261
+ a
16262
+ };
16263
+ }
16264
+ function previewSlotForChrome(chrome, slotBackground) {
16265
+ const text = parseColor(chrome.color);
16266
+ if (!text || text.a < 0.2) return slotBackground;
16267
+ const slot = parseColor(slotBackground) ?? { r: 250, g: 250, b: 249, a: 1 };
16268
+ const fill = parseColor(chrome.background);
16269
+ const effective = fill && !isTransparent(fill) ? compositeOver(fill, slot) : slot;
16270
+ if (contrastRatio(text, effective) >= 3) return slotBackground;
16271
+ return relativeLuminance(text) > 0.55 ? "#2c2c2c" : "#f4f4f5";
16272
+ }
16273
+ function parseColor(input) {
16274
+ if (!input) return null;
16275
+ const value = input.trim().toLowerCase();
16276
+ if (value === "transparent") return { r: 0, g: 0, b: 0, a: 0 };
16277
+ const hex = value.match(/^#([0-9a-f]{3,8})$/);
16278
+ if (hex) {
16279
+ const raw = hex[1];
16280
+ const expand = (pair) => parseInt(pair.length === 1 ? pair + pair : pair, 16);
16281
+ if (raw.length === 3 || raw.length === 4) {
16282
+ return {
16283
+ r: expand(raw[0]),
16284
+ g: expand(raw[1]),
16285
+ b: expand(raw[2]),
16286
+ a: raw.length === 4 ? expand(raw[3]) / 255 : 1
16287
+ };
16288
+ }
16289
+ if (raw.length === 6 || raw.length === 8) {
16290
+ return {
16291
+ r: expand(raw.slice(0, 2)),
16292
+ g: expand(raw.slice(2, 4)),
16293
+ b: expand(raw.slice(4, 6)),
16294
+ a: raw.length === 8 ? expand(raw.slice(6, 8)) / 255 : 1
16295
+ };
16144
16296
  }
16145
- slots.push({
16146
- insertIndex: i,
16147
- parentId: null,
16148
- left,
16149
- top,
16150
- width: Math.max(width, 40),
16151
- height: barThickness,
16152
- direction: "horizontal"
16153
- });
16154
16297
  }
16155
- return slots;
16156
- }
16157
- function buildHorizontalRootNavDropSlots(items, draggedEl) {
16158
- const slots = [];
16159
- const barThickness = 3;
16160
- if (items.length === 0) {
16161
- const container = getActiveNavbarListContainer(draggedEl);
16162
- if (!container) return slots;
16163
- const rect = container.getBoundingClientRect();
16164
- slots.push({
16165
- insertIndex: 0,
16166
- parentId: null,
16167
- left: rect.left + rect.width / 2 - barThickness / 2,
16168
- top: rect.top,
16169
- width: barThickness,
16170
- height: Math.max(rect.height, 24),
16171
- direction: "vertical"
16172
- });
16173
- return slots;
16298
+ const rgb = value.match(
16299
+ /^rgba?\(\s*([\d.]+)[%\s,]+([\d.]+)[%\s,]+([\d.]+)(?:%?\s*[,/]\s*([\d.]+%?))?\s*\)$/
16300
+ );
16301
+ if (rgb) {
16302
+ const alphaRaw = rgb[4];
16303
+ const alpha = alphaRaw == null ? 1 : alphaRaw.endsWith("%") ? parseFloat(alphaRaw) / 100 : parseFloat(alphaRaw);
16304
+ return { r: Number(rgb[1]), g: Number(rgb[2]), b: Number(rgb[3]), a: alpha };
16174
16305
  }
16175
- const edgeGap = (() => {
16176
- if (items.length < 2) return 32;
16177
- const a = items[0].getBoundingClientRect();
16178
- const b = items[1].getBoundingClientRect();
16179
- return Math.max(0, b.left - a.right);
16180
- })();
16181
- for (let i = 0; i <= items.length; i++) {
16182
- let left;
16183
- let top;
16184
- let height;
16185
- if (i === 0) {
16186
- const first = items[0].getBoundingClientRect();
16187
- left = first.left - edgeGap / 2 - barThickness / 2;
16188
- top = first.top;
16189
- height = first.height;
16190
- } else if (i === items.length) {
16191
- const last = items[items.length - 1].getBoundingClientRect();
16192
- const lastGap = items.length >= 2 ? Math.max(0, last.left - items[items.length - 2].getBoundingClientRect().right) : edgeGap;
16193
- left = last.right + lastGap / 2 - barThickness / 2;
16194
- top = last.top;
16195
- height = last.height;
16196
- } else {
16197
- const prev = items[i - 1].getBoundingClientRect();
16198
- const next = items[i].getBoundingClientRect();
16199
- left = (prev.right + next.left) / 2 - barThickness / 2;
16200
- top = Math.min(prev.top, next.top);
16201
- height = Math.max(prev.bottom, next.bottom) - top;
16202
- }
16203
- slots.push({
16204
- insertIndex: i,
16205
- parentId: null,
16206
- left,
16207
- top,
16208
- width: barThickness,
16209
- height: Math.max(height, 24),
16210
- direction: "vertical"
16211
- });
16306
+ const oklch = value.match(
16307
+ /^oklch\(\s*([\d.]+%?)\s+([\d.]+)\s+([\d.]+)(?:deg)?(?:\s*\/\s*([\d.]+%?))?\s*\)$/
16308
+ );
16309
+ if (oklch) {
16310
+ const Lraw = oklch[1];
16311
+ const L = Lraw.endsWith("%") ? parseFloat(Lraw) / 100 : parseFloat(Lraw);
16312
+ const gray = Math.max(0, Math.min(1, L)) * 255;
16313
+ const alphaRaw = oklch[4];
16314
+ const alpha = alphaRaw == null ? 1 : alphaRaw.endsWith("%") ? parseFloat(alphaRaw) / 100 : parseFloat(alphaRaw);
16315
+ return { r: gray, g: gray, b: gray, a: alpha };
16212
16316
  }
16213
- return slots;
16317
+ return null;
16214
16318
  }
16215
- function buildRootNavDropSlots(draggedEl) {
16216
- const items = listReorderableNavItems(draggedEl);
16217
- if (isVerticalNavLayout(items, draggedEl)) {
16218
- return buildVerticalRootNavDropSlots(items, draggedEl);
16219
- }
16319
+ function isTransparent(color) {
16320
+ return !color || color.a < 0.05;
16321
+ }
16322
+ function hasOpaqueBackground(background) {
16323
+ if (!background) return false;
16324
+ const value = background.trim().toLowerCase();
16325
+ if (!value || value === "transparent" || value === "rgba(0, 0, 0, 0)" || value === "rgb(0, 0, 0, 0)") {
16326
+ return false;
16327
+ }
16328
+ const parsed = parseColor(background);
16329
+ if (parsed) return !isTransparent(parsed);
16330
+ return true;
16331
+ }
16332
+ function toCss(color) {
16333
+ const r2 = Math.round(color.r);
16334
+ const g = Math.round(color.g);
16335
+ const b = Math.round(color.b);
16336
+ if (color.a >= 0.995) return `rgb(${r2}, ${g}, ${b})`;
16337
+ return `rgba(${r2}, ${g}, ${b}, ${Math.round(color.a * 100) / 100})`;
16338
+ }
16339
+ function colorKey(input) {
16340
+ const parsed = parseColor(input);
16341
+ if (!parsed || isTransparent(parsed)) return "transparent";
16342
+ return `${Math.round(parsed.r / 8)}-${Math.round(parsed.g / 8)}-${Math.round(parsed.b / 8)}-${Math.round(parsed.a * 4)}`;
16343
+ }
16344
+ function declaredVariant(className) {
16345
+ const match = className.match(/\bbtn--(primary|secondary|ghost)\b/);
16346
+ return match && isButtonVariantId(match[1]) ? match[1] : null;
16347
+ }
16348
+ function fillSlot(sample) {
16349
+ const width = parseFloat(sample.borderWidth);
16350
+ const border = parseColor(sample.borderColor);
16351
+ if (width > 0 && !isTransparent(border)) return "secondary";
16352
+ if (hasOpaqueBackground(sample.background)) return "primary";
16353
+ return "ghost";
16354
+ }
16355
+ function sampleSlot(sample) {
16356
+ return declaredVariant(sample.className) ?? fillSlot(sample);
16357
+ }
16358
+ function fingerprint(sample) {
16359
+ const width = parseFloat(sample.borderWidth);
16360
+ const border = width > 0 ? colorKey(sample.borderColor) : "none";
16361
+ return `${colorKey(sample.background)}|${border}|${colorKey(sample.color)}|${sampleSlot(sample)}`;
16362
+ }
16363
+ function paintColor(chrome) {
16364
+ if (hasOpaqueBackground(chrome.background)) {
16365
+ const parsed = parseColor(chrome.background);
16366
+ return parsed ? toCss({ ...parsed, a: 1 }) : chrome.background;
16367
+ }
16368
+ const width = parseFloat(chrome.borderWidth);
16369
+ if (width > 0 && hasOpaqueBackground(chrome.borderColor)) {
16370
+ const parsed = parseColor(chrome.borderColor);
16371
+ return parsed ? toCss({ ...parsed, a: 1 }) : chrome.borderColor;
16372
+ }
16373
+ const text = parseColor(chrome.color);
16374
+ return text ? toCss({ ...text, a: 1 }) : chrome.color;
16375
+ }
16376
+ function accentColor(chrome) {
16377
+ const bg = parseColor(chrome.background);
16378
+ if (bg && !isTransparent(bg) && relativeLuminance(bg) < 0.55) {
16379
+ return toCss({ ...bg, a: 1 });
16380
+ }
16381
+ const text = parseColor(chrome.color);
16382
+ if (text && !isTransparent(text)) return toCss({ ...text, a: 1 });
16383
+ return paintColor(chrome);
16384
+ }
16385
+ function chromeFrom(sample) {
16386
+ return {
16387
+ background: sample.background || TRANSPARENT,
16388
+ color: sample.color || "currentColor",
16389
+ borderColor: sample.borderColor || TRANSPARENT,
16390
+ borderWidth: sample.borderWidth || "0px"
16391
+ };
16392
+ }
16393
+ function filledPrimary(seed) {
16394
+ const paint = parseColor(paintColor(seed)) ?? { r: 15, g: 15, b: 15, a: 1 };
16395
+ const luminance = relativeLuminance(paint);
16396
+ return {
16397
+ background: toCss({ ...paint, a: 1 }),
16398
+ color: luminance > 0.55 ? "rgb(15, 15, 15)" : "rgb(255, 255, 255)",
16399
+ borderColor: TRANSPARENT,
16400
+ borderWidth: "0px"
16401
+ };
16402
+ }
16403
+ function generatedSecondary(primary) {
16404
+ const paint = accentColor(primary);
16405
+ return {
16406
+ background: TRANSPARENT,
16407
+ color: paint,
16408
+ borderColor: paint,
16409
+ borderWidth: "1px"
16410
+ };
16411
+ }
16412
+ function generatedGhost(primary) {
16413
+ return {
16414
+ background: TRANSPARENT,
16415
+ color: accentColor(primary),
16416
+ borderColor: TRANSPARENT,
16417
+ borderWidth: "0px"
16418
+ };
16419
+ }
16420
+ function normalizeOutlineChrome(outline, primary) {
16421
+ const paint = accentColor(primary);
16422
+ const text = parseColor(outline.color);
16423
+ const paintRgb = parseColor(paint);
16424
+ const primaryLabel = parseColor(primary.color);
16425
+ const matchesOnPrimary = Boolean(text && primaryLabel) && colorKey(outline.color) === colorKey(primary.color);
16426
+ const textTooLight = text ? relativeLuminance(text) > 0.7 : false;
16427
+ const paintIsDarker = paintRgb ? relativeLuminance(paintRgb) < 0.65 : hasOpaqueBackground(primary.background);
16428
+ if (!(matchesOnPrimary || textTooLight) || !paintIsDarker) return outline;
16429
+ const hasBorder = parseFloat(outline.borderWidth) > 0;
16430
+ return {
16431
+ ...outline,
16432
+ color: paint,
16433
+ borderColor: hasBorder ? paint : outline.borderColor
16434
+ };
16435
+ }
16436
+ function generateButtonVariants(samples) {
16437
+ if (samples.length === 0) return { status: "unresolved" };
16438
+ const groups = /* @__PURE__ */ new Map();
16439
+ for (const sample of samples) {
16440
+ const key = fingerprint(sample);
16441
+ const list = groups.get(key) ?? [];
16442
+ list.push(sample);
16443
+ groups.set(key, list);
16444
+ }
16445
+ if (groups.size > 3) return { status: "unresolved" };
16446
+ const bySlot = /* @__PURE__ */ new Map();
16447
+ for (const group of groups.values()) {
16448
+ const sample = group[0];
16449
+ const slot = sampleSlot(sample);
16450
+ if (bySlot.has(slot)) return { status: "unresolved" };
16451
+ bySlot.set(slot, sample);
16452
+ }
16453
+ const primarySample = bySlot.get("primary");
16454
+ const seed = chromeFrom(primarySample ?? samples[0]);
16455
+ const primary = primarySample ? seed : filledPrimary(seed);
16456
+ const secondary = normalizeOutlineChrome(
16457
+ bySlot.has("secondary") ? chromeFrom(bySlot.get("secondary")) : generatedSecondary(primary),
16458
+ primary
16459
+ );
16460
+ const ghost = normalizeOutlineChrome(
16461
+ bySlot.has("ghost") ? chromeFrom(bySlot.get("ghost")) : generatedGhost(primary),
16462
+ primary
16463
+ );
16464
+ return {
16465
+ status: "ready",
16466
+ tokens: { primary, secondary, ghost }
16467
+ };
16468
+ }
16469
+ function geometryFromSample(sample) {
16470
+ return {
16471
+ paddingTop: px(sample.paddingTop),
16472
+ paddingRight: px(sample.paddingRight),
16473
+ paddingBottom: px(sample.paddingBottom),
16474
+ paddingLeft: px(sample.paddingLeft),
16475
+ fontSize: px(sample.fontSize),
16476
+ borderRadius: px(sample.borderRadius),
16477
+ gap: px(sample.gap),
16478
+ height: Math.round(sample.height)
16479
+ };
16480
+ }
16481
+ function shiftSample(base, steps) {
16482
+ return {
16483
+ height: Math.max(24, base.height + steps * 8),
16484
+ paddingTop: Math.max(0, base.paddingTop + steps * 4),
16485
+ paddingBottom: Math.max(0, base.paddingBottom + steps * 4),
16486
+ paddingLeft: Math.max(0, base.paddingLeft + steps * 8),
16487
+ paddingRight: Math.max(0, base.paddingRight + steps * 8),
16488
+ fontSize: base.fontSize,
16489
+ borderRadius: base.borderRadius,
16490
+ gap: base.gap
16491
+ };
16492
+ }
16493
+ function clusterSizes(samples) {
16494
+ const sorted = [...samples].sort((a, b) => a.height - b.height);
16495
+ const clusters = [];
16496
+ for (const sample of sorted) {
16497
+ const current = clusters[clusters.length - 1];
16498
+ const head = current?.[0];
16499
+ if (!current || !head || Math.abs(sample.height - head.height) > 2) clusters.push([sample]);
16500
+ else current.push(sample);
16501
+ }
16502
+ return clusters.map((group) => {
16503
+ const pick = (key) => group.reduce((sum, item) => sum + item[key], 0) / group.length;
16504
+ return {
16505
+ height: pick("height"),
16506
+ paddingTop: pick("paddingTop"),
16507
+ paddingRight: pick("paddingRight"),
16508
+ paddingBottom: pick("paddingBottom"),
16509
+ paddingLeft: pick("paddingLeft"),
16510
+ fontSize: pick("fontSize"),
16511
+ borderRadius: pick("borderRadius"),
16512
+ gap: pick("gap")
16513
+ };
16514
+ });
16515
+ }
16516
+ function tokensFromSamples(bySlot, radius, gap) {
16517
+ const fill = (id) => {
16518
+ const sample = bySlot[id] ?? REFERENCE_SIZES[id];
16519
+ return geometryFromSample({ ...sample, borderRadius: radius, gap });
16520
+ };
16521
+ return { s: fill("s"), m: fill("m"), l: fill("l") };
16522
+ }
16523
+ function interpolateSizes(known) {
16524
+ const order = BUTTON_SIZE_IDS;
16525
+ const indexOf = (id) => order.indexOf(id);
16526
+ const sorted = [...known].sort((a, b) => indexOf(a.id) - indexOf(b.id));
16527
+ const left = sorted[0];
16528
+ const right = sorted[1];
16529
+ const span = indexOf(right.id) - indexOf(left.id);
16530
+ const step = (key) => (right.sample[key] - left.sample[key]) / span;
16531
+ const at = (index) => {
16532
+ const steps = index - indexOf(left.id);
16533
+ return {
16534
+ height: left.sample.height + step("height") * steps,
16535
+ paddingTop: Math.max(0, left.sample.paddingTop + step("paddingTop") * steps),
16536
+ paddingBottom: Math.max(0, left.sample.paddingBottom + step("paddingBottom") * steps),
16537
+ paddingLeft: Math.max(0, left.sample.paddingLeft + step("paddingLeft") * steps),
16538
+ paddingRight: Math.max(0, left.sample.paddingRight + step("paddingRight") * steps),
16539
+ fontSize: Math.max(10, left.sample.fontSize + step("fontSize") * steps),
16540
+ borderRadius: left.sample.borderRadius,
16541
+ gap: left.sample.gap
16542
+ };
16543
+ };
16544
+ const bySlot = {
16545
+ [left.id]: left.sample,
16546
+ [right.id]: { ...right.sample, borderRadius: left.sample.borderRadius, gap: left.sample.gap }
16547
+ };
16548
+ for (const id of order) {
16549
+ if (!bySlot[id]) bySlot[id] = at(indexOf(id));
16550
+ }
16551
+ return tokensFromSamples(bySlot, left.sample.borderRadius, left.sample.gap);
16552
+ }
16553
+ function generateButtonSizes(samples) {
16554
+ const clusters = clusterSizes(samples.filter((sample) => sample.height > 0));
16555
+ if (clusters.length === 0) {
16556
+ return { status: "ready", tokens: tokensFromSamples(REFERENCE_SIZES, 8, 8) };
16557
+ }
16558
+ if (clusters.length > 3) return { status: "unresolved" };
16559
+ if (clusters.length === 3) {
16560
+ const sorted = [...clusters].sort((a, b) => a.height - b.height);
16561
+ const radius = sorted[1].borderRadius;
16562
+ const gap = sorted[1].gap;
16563
+ return {
16564
+ status: "ready",
16565
+ tokens: {
16566
+ s: geometryFromSample({ ...sorted[0], borderRadius: radius, gap }),
16567
+ m: geometryFromSample({ ...sorted[1], borderRadius: radius, gap }),
16568
+ l: geometryFromSample({ ...sorted[2], borderRadius: radius, gap })
16569
+ }
16570
+ };
16571
+ }
16572
+ if (clusters.length === 1) {
16573
+ const known = clusters[0];
16574
+ const slot = classifyHeight(known.height);
16575
+ const offset = BUTTON_SIZE_IDS.indexOf(slot);
16576
+ const bySlot = Object.fromEntries(
16577
+ BUTTON_SIZE_IDS.map((id, index) => [id, shiftSample(known, index - offset)])
16578
+ );
16579
+ bySlot[slot] = known;
16580
+ return { status: "ready", tokens: tokensFromSamples(bySlot, known.borderRadius, known.gap) };
16581
+ }
16582
+ const mapped = clusters.map((sample) => ({ id: classifyHeight(sample.height), sample }));
16583
+ if (mapped[0].id === mapped[1].id) {
16584
+ const larger = clusters.slice().sort((a, b) => b.height - a.height)[0];
16585
+ return generateButtonSizes([larger]);
16586
+ }
16587
+ return { status: "ready", tokens: interpolateSizes(mapped) };
16588
+ }
16589
+ function parseJson(raw) {
16590
+ if (!raw) return null;
16591
+ try {
16592
+ return JSON.parse(raw);
16593
+ } catch {
16594
+ return null;
16595
+ }
16596
+ }
16597
+ function isChrome(value) {
16598
+ if (!value || typeof value !== "object") return false;
16599
+ const chrome = value;
16600
+ return typeof chrome.background === "string" && typeof chrome.color === "string";
16601
+ }
16602
+ function isGeometry(value) {
16603
+ if (!value || typeof value !== "object") return false;
16604
+ const geometry = value;
16605
+ return typeof geometry.paddingTop === "string" && typeof geometry.height === "number";
16606
+ }
16607
+ function readVariantTokens(content) {
16608
+ const parsed = parseJson(content[BUTTON_VARIANTS_KEY]);
16609
+ if (!parsed) return null;
16610
+ if (!BUTTON_VARIANT_IDS.every((id) => isChrome(parsed[id]))) return null;
16611
+ return parsed;
16612
+ }
16613
+ function readSizeTokens(content) {
16614
+ const parsed = parseJson(content[BUTTON_SIZES_KEY]);
16615
+ if (!parsed) return null;
16616
+ if (!BUTTON_SIZE_IDS.every((id) => isGeometry(parsed[id]))) return null;
16617
+ return parsed;
16618
+ }
16619
+ function readAssignments(content) {
16620
+ const parsed = parseJson(content[BUTTON_ASSIGNMENTS_KEY]);
16621
+ if (!parsed || typeof parsed !== "object") return {};
16622
+ const next = {};
16623
+ for (const [id, value] of Object.entries(parsed)) {
16624
+ if (!value || !isButtonVariantId(value.variant) || !isButtonSizeId(value.size)) continue;
16625
+ next[id] = { variant: value.variant, size: value.size };
16626
+ }
16627
+ return next;
16628
+ }
16629
+ function cssLength(style, prop) {
16630
+ return style.getPropertyValue(prop) || "0px";
16631
+ }
16632
+ function readVariantSample(el) {
16633
+ const style = getComputedStyle(el);
16634
+ return {
16635
+ background: style.backgroundColor,
16636
+ color: style.color,
16637
+ borderColor: style.borderTopColor,
16638
+ borderWidth: style.borderTopWidth,
16639
+ className: el.className || ""
16640
+ };
16641
+ }
16642
+ function readSizeSample(el) {
16643
+ const style = getComputedStyle(el);
16644
+ const rootFont = parsePx(getComputedStyle(el.ownerDocument.documentElement).fontSize) || 16;
16645
+ const rect = el.getBoundingClientRect();
16646
+ return {
16647
+ height: rect.height,
16648
+ paddingTop: parsePx(cssLength(style, "padding-top"), rootFont),
16649
+ paddingRight: parsePx(cssLength(style, "padding-right"), rootFont),
16650
+ paddingBottom: parsePx(cssLength(style, "padding-bottom"), rootFont),
16651
+ paddingLeft: parsePx(cssLength(style, "padding-left"), rootFont),
16652
+ fontSize: parsePx(style.fontSize, rootFont),
16653
+ borderRadius: parsePx(style.borderTopLeftRadius, rootFont),
16654
+ gap: parsePx(style.columnGap || style.gap, rootFont)
16655
+ };
16656
+ }
16657
+ function readTypography(el) {
16658
+ const style = getComputedStyle(el);
16659
+ return {
16660
+ fontFamily: style.fontFamily,
16661
+ fontWeight: style.fontWeight,
16662
+ letterSpacing: style.letterSpacing,
16663
+ textTransform: style.textTransform,
16664
+ textDecoration: style.textDecorationLine || style.textDecoration || "none"
16665
+ };
16666
+ }
16667
+ var buttonOriginByEl = /* @__PURE__ */ new WeakMap();
16668
+ var ORIGIN_ATTR = "data-ohw-btn-origin";
16669
+ function parseOriginAttr(raw) {
16670
+ if (!raw) return null;
16671
+ try {
16672
+ const value = JSON.parse(raw);
16673
+ if (!value?.variant || !value.size || !value.typography) return null;
16674
+ return value;
16675
+ } catch {
16676
+ return null;
16677
+ }
16678
+ }
16679
+ function measureLiveOrigin(el) {
16680
+ return {
16681
+ variant: readVariantSample(el),
16682
+ size: readSizeSample(el),
16683
+ typography: readTypography(el)
16684
+ };
16685
+ }
16686
+ function captureButtonOrigin(el) {
16687
+ const cached = buttonOriginByEl.get(el);
16688
+ if (cached) return cached;
16689
+ const fromAttr = parseOriginAttr(el.getAttribute(ORIGIN_ATTR));
16690
+ if (fromAttr) {
16691
+ buttonOriginByEl.set(el, fromAttr);
16692
+ return fromAttr;
16693
+ }
16694
+ const origin = measureLiveOrigin(el);
16695
+ buttonOriginByEl.set(el, origin);
16696
+ try {
16697
+ el.setAttribute(ORIGIN_ATTR, JSON.stringify(origin));
16698
+ } catch {
16699
+ }
16700
+ return origin;
16701
+ }
16702
+ function listStyleButtons(doc) {
16703
+ return Array.from(doc.querySelectorAll(CTA_BUTTON_SELECTOR)).filter((el) => {
16704
+ if (el.closest("[data-ohw-bridge], [data-ohw-bridge-root]")) return false;
16705
+ const rect = el.getBoundingClientRect();
16706
+ return rect.width > 0 && rect.height > 0;
16707
+ });
16708
+ }
16709
+ function generateTokensFromButtons(buttons) {
16710
+ const variants = generateButtonVariants(buttons.map(readVariantSample));
16711
+ const sizes = generateButtonSizes(buttons.map(readSizeSample));
16712
+ if (variants.status !== "ready" || sizes.status !== "ready") return { status: "unresolved" };
16713
+ return { status: "ready", tokens: { variants: variants.tokens, sizes: sizes.tokens } };
16714
+ }
16715
+ function tokensFromButton(el) {
16716
+ const origin = captureButtonOrigin(el);
16717
+ const sample = origin.variant;
16718
+ const variants = generateButtonVariants([sample]);
16719
+ const sizes = generateButtonSizes([origin.size]);
16720
+ const primary = chromeFrom(sample);
16721
+ return {
16722
+ variants: variants.status === "ready" ? variants.tokens : { primary, secondary: generatedSecondary(primary), ghost: generatedGhost(primary) },
16723
+ sizes: sizes.status === "ready" ? sizes.tokens : tokensFromSamples(REFERENCE_SIZES, 8, 8)
16724
+ };
16725
+ }
16726
+ function readStoredStyleTokens(content) {
16727
+ const variants = readVariantTokens(content);
16728
+ const sizes = readSizeTokens(content);
16729
+ if (!variants || !sizes) return null;
16730
+ return { variants, sizes };
16731
+ }
16732
+ function styleTokenNodes(tokens) {
16733
+ return [
16734
+ { key: BUTTON_VARIANTS_KEY, text: JSON.stringify(tokens.variants) },
16735
+ { key: BUTTON_SIZES_KEY, text: JSON.stringify(tokens.sizes) }
16736
+ ];
16737
+ }
16738
+ function ensureButtonStyleTokens(doc, content) {
16739
+ const stored = readStoredStyleTokens(content);
16740
+ if (stored) return { tokens: stored, nodes: [] };
16741
+ const generated = generateTokensFromButtons(listStyleButtons(doc));
16742
+ if (generated.status !== "ready") return { tokens: null, nodes: [] };
16743
+ return { tokens: generated.tokens, nodes: styleTokenNodes(generated.tokens) };
16744
+ }
16745
+ function detectVariant(el, tokens) {
16746
+ const declared = declaredVariant(el.className || "");
16747
+ if (declared) return declared;
16748
+ const stamped = el.getAttribute("data-ohw-btn-variant");
16749
+ if (stamped && isButtonVariantId(stamped)) return stamped;
16750
+ const sample = readVariantSample(el);
16751
+ if (!tokens) return sampleSlot(sample);
16752
+ const key = fingerprint(sample);
16753
+ for (const id of BUTTON_VARIANT_IDS) {
16754
+ const chrome = tokens[id];
16755
+ const tokenKey = fingerprint({
16756
+ background: chrome.background,
16757
+ color: chrome.color,
16758
+ borderColor: chrome.borderColor,
16759
+ borderWidth: chrome.borderWidth,
16760
+ className: `btn btn--${id}`
16761
+ });
16762
+ if (tokenKey === key) return id;
16763
+ }
16764
+ return sampleSlot(sample);
16765
+ }
16766
+ function detectSize(el, tokens) {
16767
+ const stamped = el.getAttribute("data-ohw-btn-size");
16768
+ if (stamped && isButtonSizeId(stamped)) return stamped;
16769
+ const height = el.getBoundingClientRect().height;
16770
+ if (!tokens) return classifyHeight(height);
16771
+ let best = "m";
16772
+ let bestDelta = Number.POSITIVE_INFINITY;
16773
+ for (const id of BUTTON_SIZE_IDS) {
16774
+ const delta = Math.abs(tokens[id].height - height);
16775
+ if (delta < bestDelta) {
16776
+ best = id;
16777
+ bestDelta = delta;
16778
+ }
16779
+ }
16780
+ return best;
16781
+ }
16782
+ function assignmentFor(el, content, tokens) {
16783
+ const id = buttonId(el);
16784
+ const stored = id ? readAssignments(content)[id] : void 0;
16785
+ if (stored) return stored;
16786
+ return {
16787
+ variant: detectVariant(el, tokens?.variants ?? null),
16788
+ size: detectSize(el, tokens?.sizes ?? null)
16789
+ };
16790
+ }
16791
+ function labelElement(el) {
16792
+ return el.querySelector('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ?? (el.matches('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ? el : null);
16793
+ }
16794
+ var paintLockByEl = /* @__PURE__ */ new WeakMap();
16795
+ var paintObserverByEl = /* @__PURE__ */ new WeakMap();
16796
+ var paintingButton = false;
16797
+ function paintButtonChrome(el, assignment, tokens) {
16798
+ const origin = captureButtonOrigin(el);
16799
+ const chrome = tokens.variants[assignment.variant];
16800
+ const originSlot = classifyHeight(origin.size.height);
16801
+ const geometry = assignment.size === originSlot ? geometryFromSample({ ...origin.size, borderRadius: origin.size.borderRadius, gap: origin.size.gap }) : tokens.sizes[assignment.size];
16802
+ const { typography } = origin;
16803
+ el.setAttribute("data-ohw-btn-variant", assignment.variant);
16804
+ el.setAttribute("data-ohw-btn-size", assignment.size);
16805
+ el.style.transition = "none";
16806
+ el.style.boxSizing = "border-box";
16807
+ el.style.display = "inline-flex";
16808
+ el.style.alignItems = "center";
16809
+ el.style.justifyContent = "center";
16810
+ el.style.background = chrome.background;
16811
+ el.style.color = chrome.color;
16812
+ el.style.borderStyle = parseFloat(chrome.borderWidth) > 0 ? "solid" : "none";
16813
+ el.style.borderColor = chrome.borderColor;
16814
+ el.style.borderWidth = chrome.borderWidth;
16815
+ el.style.padding = `${geometry.paddingTop} ${geometry.paddingRight} ${geometry.paddingBottom} ${geometry.paddingLeft}`;
16816
+ el.style.fontSize = geometry.fontSize;
16817
+ el.style.borderRadius = px(origin.size.borderRadius);
16818
+ el.style.gap = geometry.gap;
16819
+ el.style.height = px(geometry.height);
16820
+ el.style.fontFamily = typography.fontFamily;
16821
+ el.style.fontWeight = typography.fontWeight;
16822
+ el.style.letterSpacing = typography.letterSpacing;
16823
+ el.style.textTransform = typography.textTransform;
16824
+ if (typography.textDecoration && typography.textDecoration !== "none") {
16825
+ el.style.textDecoration = typography.textDecoration;
16826
+ } else {
16827
+ el.style.textDecoration = "none";
16828
+ }
16829
+ const label = labelElement(el);
16830
+ if (label && label !== el) label.style.color = chrome.color;
16831
+ }
16832
+ function guardTemplateHover(el) {
16833
+ if (paintObserverByEl.has(el)) return;
16834
+ const observer = new MutationObserver(() => {
16835
+ if (paintingButton) return;
16836
+ const lock = paintLockByEl.get(el);
16837
+ if (!lock) return;
16838
+ paintingButton = true;
16839
+ paintButtonChrome(el, lock.assignment, lock.tokens);
16840
+ paintingButton = false;
16841
+ });
16842
+ observer.observe(el, { attributes: true, attributeFilter: ["style", "class"] });
16843
+ paintObserverByEl.set(el, observer);
16844
+ }
16845
+ function applyButtonStyle(el, assignment, tokens) {
16846
+ paintLockByEl.set(el, { assignment, tokens });
16847
+ paintingButton = true;
16848
+ paintButtonChrome(el, assignment, tokens);
16849
+ paintingButton = false;
16850
+ guardTemplateHover(el);
16851
+ }
16852
+ function applyButtonStylesFromContent(doc, content) {
16853
+ const assignments = readAssignments(content);
16854
+ for (const [id, assignment] of Object.entries(assignments)) {
16855
+ const el = doc.querySelector(`[data-ohw-href-key="${CSS.escape(id)}"]`);
16856
+ if (!el) continue;
16857
+ applyButtonStyle(el, assignment, tokensFromButton(el));
16858
+ }
16859
+ }
16860
+ function withAssignment(content, id, assignment) {
16861
+ return { ...readAssignments(content), [id]: assignment };
16862
+ }
16863
+
16864
+ // src/ui/button-style-panel.tsx
16865
+ var import_jsx_runtime31 = require("react/jsx-runtime");
16866
+ var SIZE_LABEL = { s: "S", m: "M", l: "L" };
16867
+ function ButtonStylePanel({
16868
+ variants,
16869
+ sizes,
16870
+ variant,
16871
+ size,
16872
+ onVariant,
16873
+ onSize,
16874
+ previewLabel = "Book Now",
16875
+ slotBackground = "var(--brand-background, var(--ohw-brand-light, var(--color-light, #ffffff)))"
16876
+ }) {
16877
+ const preview = scaleGeometry(sizes.m, 0.75);
16878
+ const label = previewLabel.trim() || "Book Now";
16879
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex w-full flex-col gap-5", children: [
16880
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("section", { className: "flex w-full flex-col gap-2.5", children: [
16881
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("h3", { className: "text-xs font-semibold uppercase leading-4 tracking-[0.6px] text-stone-500 font-sans", children: "Variants" }),
16882
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "flex w-full flex-col gap-2.5", children: BUTTON_VARIANT_IDS.map((id) => {
16883
+ const chrome = variants[id];
16884
+ const selected = id === variant;
16885
+ const hasBorder = parseFloat(chrome.borderWidth) > 0;
16886
+ const cardSlot = previewSlotForChrome(chrome, slotBackground);
16887
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
16888
+ "button",
16889
+ {
16890
+ type: "button",
16891
+ "aria-pressed": selected,
16892
+ "aria-label": id,
16893
+ onMouseDown: (e) => {
16894
+ e.preventDefault();
16895
+ e.stopPropagation();
16896
+ },
16897
+ onClick: () => onVariant(id),
16898
+ className: cn(
16899
+ "relative w-full overflow-hidden rounded-xl border-solid text-left",
16900
+ selected ? "border-2 border-primary" : "border border-border"
16901
+ ),
16902
+ children: [
16903
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
16904
+ "div",
16905
+ {
16906
+ className: "flex w-full items-center justify-center py-4",
16907
+ style: { background: cardSlot },
16908
+ children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
16909
+ "span",
16910
+ {
16911
+ className: "inline-flex items-center justify-center whitespace-nowrap font-semibold uppercase leading-none",
16912
+ style: {
16913
+ background: chrome.background,
16914
+ color: chrome.color,
16915
+ borderColor: chrome.borderColor,
16916
+ borderWidth: hasBorder ? chrome.borderWidth : "0px",
16917
+ borderStyle: hasBorder ? "solid" : "none",
16918
+ padding: `${preview.paddingTop} ${preview.paddingRight} ${preview.paddingBottom} ${preview.paddingLeft}`,
16919
+ fontSize: preview.fontSize,
16920
+ borderRadius: preview.borderRadius,
16921
+ height: preview.height,
16922
+ boxSizing: "border-box",
16923
+ letterSpacing: "0.12em"
16924
+ },
16925
+ children: label
16926
+ }
16927
+ )
16928
+ }
16929
+ ),
16930
+ selected ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
16931
+ "span",
16932
+ {
16933
+ className: "absolute right-0.5 top-0.5 flex size-5 items-center justify-center rounded-full bg-primary text-primary-foreground",
16934
+ "aria-hidden": true,
16935
+ children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react17.Check, { className: "size-3", strokeWidth: 3 })
16936
+ }
16937
+ ) : null
16938
+ ]
16939
+ },
16940
+ id
16941
+ );
16942
+ }) })
16943
+ ] }),
16944
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("section", { className: "flex w-full flex-col gap-2.5", children: [
16945
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("h3", { className: "text-xs font-semibold uppercase leading-4 tracking-[0.6px] text-stone-500 font-sans", children: "Size" }),
16946
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
16947
+ "div",
16948
+ {
16949
+ className: "flex w-full items-start rounded-lg bg-stone-100 p-1",
16950
+ role: "radiogroup",
16951
+ "aria-label": "Size",
16952
+ children: BUTTON_SIZE_IDS.map((id) => {
16953
+ const selected = id === size;
16954
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
16955
+ "button",
16956
+ {
16957
+ type: "button",
16958
+ role: "radio",
16959
+ "aria-checked": selected,
16960
+ onMouseDown: (e) => {
16961
+ e.preventDefault();
16962
+ e.stopPropagation();
16963
+ },
16964
+ onClick: () => onSize(id),
16965
+ className: cn(
16966
+ "flex min-w-14 flex-1 items-center justify-center rounded-md px-3 py-1.5 text-sm font-medium leading-5",
16967
+ selected ? "bg-white text-stone-950 shadow-sm" : "bg-transparent text-stone-500"
16968
+ ),
16969
+ children: SIZE_LABEL[id]
16970
+ },
16971
+ id
16972
+ );
16973
+ })
16974
+ }
16975
+ )
16976
+ ] })
16977
+ ] });
16978
+ }
16979
+
16980
+ // src/lib/button-collections.ts
16981
+ var BUTTON_COLLECTIONS_KEY = "__ohw_button_collections";
16982
+ var BUTTON_SNAPSHOTS_KEY = "__ohw_button_snapshots";
16983
+ var BUTTON_HIDDEN_KEY = "__ohw_button_hidden";
16984
+ var COLLECTION_ATTR = "data-ohw-button-collection";
16985
+ function parseJson2(raw) {
16986
+ if (!raw) return null;
16987
+ try {
16988
+ return JSON.parse(raw);
16989
+ } catch {
16990
+ return null;
16991
+ }
16992
+ }
16993
+ function readCollections(content) {
16994
+ const parsed = parseJson2(content[BUTTON_COLLECTIONS_KEY]);
16995
+ if (!parsed || typeof parsed !== "object") return {};
16996
+ const next = {};
16997
+ for (const [id, value] of Object.entries(parsed)) {
16998
+ if (!value || !Array.isArray(value.members)) continue;
16999
+ next[id] = {
17000
+ id,
17001
+ members: value.members.filter((member) => typeof member === "string" && member),
17002
+ gap: typeof value.gap === "string" ? value.gap : "12px"
17003
+ };
17004
+ }
17005
+ return next;
17006
+ }
17007
+ function readSnapshots(content) {
17008
+ const parsed = parseJson2(content[BUTTON_SNAPSHOTS_KEY]);
17009
+ if (!parsed || typeof parsed !== "object") return {};
17010
+ const next = {};
17011
+ for (const [id, value] of Object.entries(parsed)) {
17012
+ if (!value || typeof value.html !== "string") continue;
17013
+ next[id] = value;
17014
+ }
17015
+ return next;
17016
+ }
17017
+ function readHiddenButtons(content) {
17018
+ const parsed = parseJson2(content[BUTTON_HIDDEN_KEY]);
17019
+ if (!Array.isArray(parsed)) return [];
17020
+ return parsed.filter((id) => typeof id === "string" && id);
17021
+ }
17022
+ function buttonCollectionOf(el) {
17023
+ return el?.closest(`[${COLLECTION_ATTR}]`) ?? null;
17024
+ }
17025
+ function isNavbarCta(el) {
17026
+ return el.getAttribute("data-ohw-role") === "navbar-button" || Boolean(el.closest('[data-ohw-role="navbar-button"]'));
17027
+ }
17028
+ function navbarScope(el) {
17029
+ return el.closest("nav, [data-ohw-nav-container], [data-ohw-nav-drawer]") ?? el.ownerDocument;
17030
+ }
17031
+ function isLastNavbarButton(el) {
17032
+ if (!isNavbarCta(el)) return false;
17033
+ const buttons = Array.from(navbarScope(el).querySelectorAll(CTA_BUTTON_SELECTOR)).filter(
17034
+ (button) => isNavbarCta(button) && buttonId(button)
17035
+ );
17036
+ return buttons.length <= 1;
17037
+ }
17038
+ function canDeleteButton(el) {
17039
+ return !isLastNavbarButton(el);
17040
+ }
17041
+ function canDuplicateButton(el) {
17042
+ return isNavbarCta(el);
17043
+ }
17044
+ function labelElement2(el) {
17045
+ return el.querySelector('[data-ohw-editable="text"], [data-ohw-editable="plain"]') ?? el.querySelector("[data-ohw-key]");
17046
+ }
17047
+ function buttonLabel(el) {
17048
+ return (labelElement2(el)?.textContent ?? el.textContent ?? "").trim();
17049
+ }
17050
+ function buttonHref(el) {
17051
+ return el.getAttribute("href") ?? "";
17052
+ }
17053
+ function hrefKeyBase(hrefKey) {
17054
+ return hrefKey.replace(/-href$/, "").replace(/-copy-\d+$/, "");
17055
+ }
17056
+ function labelKeyForHrefKey(hrefKey) {
17057
+ return `${hrefKey.replace(/-href$/, "")}-label`;
17058
+ }
17059
+ function cleanClone(el) {
17060
+ const copy = el.cloneNode(true);
17061
+ copy.querySelectorAll("[data-ohw-selected], [data-ohw-hovered], [data-ohw-editing]").forEach((node) => {
17062
+ node.removeAttribute("data-ohw-selected");
17063
+ node.removeAttribute("data-ohw-hovered");
17064
+ node.removeAttribute("data-ohw-editing");
17065
+ node.removeAttribute("contenteditable");
17066
+ });
17067
+ copy.removeAttribute("data-ohw-selected");
17068
+ copy.removeAttribute("data-ohw-hovered");
17069
+ copy.removeAttribute("data-ohw-editing");
17070
+ copy.removeAttribute("contenteditable");
17071
+ copy.removeAttribute("data-ohw-drag-disabled");
17072
+ return copy;
17073
+ }
17074
+ function nextCopyId(sourceId, content, doc) {
17075
+ const used = /* @__PURE__ */ new Set([
17076
+ ...Object.keys(readSnapshots(content)),
17077
+ ...Object.keys(readAssignments(content)),
17078
+ ...Array.from(doc.querySelectorAll("[data-ohw-href-key]")).map(
17079
+ (el) => el.getAttribute("data-ohw-href-key") ?? ""
17080
+ )
17081
+ ]);
17082
+ const base = hrefKeyBase(sourceId);
17083
+ let n = 2;
17084
+ while (used.has(`${base}-copy-${n}-href`)) n += 1;
17085
+ return `${base}-copy-${n}-href`;
17086
+ }
17087
+ function nextCollectionId(content, doc) {
17088
+ const used = /* @__PURE__ */ new Set([
17089
+ ...Object.keys(readCollections(content)),
17090
+ ...Array.from(doc.querySelectorAll(`[${COLLECTION_ATTR}]`)).map(
17091
+ (el) => el.getAttribute(COLLECTION_ATTR) ?? ""
17092
+ )
17093
+ ]);
17094
+ let n = 1;
17095
+ while (used.has(`bc-${n}`)) n += 1;
17096
+ return `bc-${n}`;
17097
+ }
17098
+ var COLLECTION_GAP_MAX_PX = 12;
17099
+ var COLLECTION_GAP_DEFAULT = "12px";
17100
+ function usableGap(value) {
17101
+ if (!value || value === "normal") return null;
17102
+ const n = parseFloat(value);
17103
+ if (!Number.isFinite(n) || n <= 0) return null;
17104
+ if (n > COLLECTION_GAP_MAX_PX) return COLLECTION_GAP_DEFAULT;
17105
+ return `${Math.round(n * 100) / 100}px`;
17106
+ }
17107
+ function collectionGap(el, tokens) {
17108
+ const fromCollection = usableGap(buttonCollectionOf(el)?.style.gap);
17109
+ if (fromCollection) return fromCollection;
17110
+ const parent = el.parentElement;
17111
+ if (parent) {
17112
+ const style = getComputedStyle(parent);
17113
+ const fromParent = usableGap(style.columnGap) ?? usableGap(style.gap);
17114
+ if (fromParent) return fromParent;
17115
+ }
17116
+ const fromTokens = usableGap(tokens?.sizes.m.gap);
17117
+ if (fromTokens) return fromTokens;
17118
+ return COLLECTION_GAP_DEFAULT;
17119
+ }
17120
+ function styleCollection(collection, gap) {
17121
+ collection.style.display = "inline-flex";
17122
+ collection.style.alignItems = "center";
17123
+ collection.style.flexWrap = "wrap";
17124
+ collection.style.gap = gap;
17125
+ collection.style.maxWidth = "100%";
17126
+ }
17127
+ function createCollection(doc, id, gap) {
17128
+ const collection = doc.createElement("div");
17129
+ collection.setAttribute(COLLECTION_ATTR, id);
17130
+ styleCollection(collection, gap);
17131
+ return collection;
17132
+ }
17133
+ function setButtonDragAttr(el) {
17134
+ if (isNavbarCta(el) && buttonCollectionOf(el)) el.removeAttribute("data-ohw-drag-disabled");
17135
+ else el.setAttribute("data-ohw-drag-disabled", "true");
17136
+ }
17137
+ function rekeyCopy(copy, id) {
17138
+ copy.setAttribute("data-ohw-href-key", id);
17139
+ const label = labelElement2(copy);
17140
+ const labelKey = labelKeyForHrefKey(id);
17141
+ if (label) label.setAttribute("data-ohw-key", labelKey);
17142
+ setButtonDragAttr(copy);
17143
+ return { labelKey };
17144
+ }
17145
+ function stampVariantClass(el, variant) {
17146
+ const raw = typeof el.className === "string" ? el.className : el.getAttribute("class") || "";
17147
+ const without = raw.replace(/\bbtn--(?:primary|secondary|ghost)\b/g, "").replace(/\s+/g, " ").trim();
17148
+ el.className = without ? `${without} btn--${variant}` : `btn btn--${variant}`;
17149
+ }
17150
+ function snapshotOf(el) {
17151
+ const id = buttonId(el) ?? "";
17152
+ const label = labelElement2(el);
17153
+ return {
17154
+ id,
17155
+ href: buttonHref(el),
17156
+ label: buttonLabel(el),
17157
+ labelKey: label?.getAttribute("data-ohw-key") ?? labelKeyForHrefKey(id),
17158
+ html: cleanClone(el).outerHTML
17159
+ };
17160
+ }
17161
+ function htmlToElement(doc, html) {
17162
+ const wrap = doc.createElement("div");
17163
+ wrap.innerHTML = html;
17164
+ const el = wrap.firstElementChild;
17165
+ return el instanceof HTMLElement ? el : null;
17166
+ }
17167
+ function findButton(doc, id) {
17168
+ return doc.querySelector(`[data-ohw-href-key="${CSS.escape(id)}"]`);
17169
+ }
17170
+ function duplicateButton(source, content) {
17171
+ if (!canDuplicateButton(source)) return null;
17172
+ const sourceId = buttonId(source);
17173
+ if (!sourceId) return null;
17174
+ const doc = source.ownerDocument;
17175
+ const tokens = tokensFromButton(source);
17176
+ const copyId = nextCopyId(sourceId, content, doc);
17177
+ const copy = cleanClone(source);
17178
+ const { labelKey } = rekeyCopy(copy, copyId);
17179
+ const href = buttonHref(source);
17180
+ const label = buttonLabel(source);
17181
+ if (href) copy.setAttribute("href", href);
17182
+ const sourceAssignment = readAssignments(content)[sourceId] ?? assignmentFor(source, content, tokens);
17183
+ const copyAssignment = {
17184
+ variant: "secondary",
17185
+ size: sourceAssignment.size
17186
+ };
17187
+ stampVariantClass(copy, "secondary");
17188
+ applyButtonStyle(copy, copyAssignment, tokens);
17189
+ const copyLabelEl = labelElement2(copy);
17190
+ if (copyLabelEl) copyLabelEl.textContent = label;
17191
+ let collection = buttonCollectionOf(source);
17192
+ const collections = readCollections(content);
17193
+ let collectionId = collection?.getAttribute(COLLECTION_ATTR) ?? "";
17194
+ let members = collectionId && collections[collectionId] ? [...collections[collectionId].members] : [];
17195
+ const gap = collectionGap(source, tokens);
17196
+ if (!collection) {
17197
+ collectionId = nextCollectionId(content, doc);
17198
+ collection = createCollection(doc, collectionId, gap);
17199
+ source.parentElement?.insertBefore(collection, source);
17200
+ collection.append(source, copy);
17201
+ setButtonDragAttr(source);
17202
+ setButtonDragAttr(copy);
17203
+ members = [sourceId, copyId];
17204
+ } else {
17205
+ styleCollection(collection, gap);
17206
+ source.after(copy);
17207
+ setButtonDragAttr(copy);
17208
+ if (!members.includes(sourceId)) members = listCollectionMembers(collection);
17209
+ const at = members.indexOf(sourceId);
17210
+ members.splice(at >= 0 ? at + 1 : members.length, 0, copyId);
17211
+ }
17212
+ const sourceLabelKey = labelElement2(source)?.getAttribute("data-ohw-key");
17213
+ const snapshots = {
17214
+ ...readSnapshots(content),
17215
+ [sourceId]: snapshotOf(source),
17216
+ [copyId]: snapshotOf(copy)
17217
+ };
17218
+ const nextCollections = {
17219
+ ...collections,
17220
+ [collectionId]: { id: collectionId, members, gap }
17221
+ };
17222
+ const assignments = withAssignment(content, copyId, copyAssignment);
17223
+ const nextContent = {
17224
+ ...content,
17225
+ [copyId]: href,
17226
+ [labelKey]: label,
17227
+ [BUTTON_COLLECTIONS_KEY]: JSON.stringify(nextCollections),
17228
+ [BUTTON_SNAPSHOTS_KEY]: JSON.stringify(snapshots),
17229
+ [BUTTON_ASSIGNMENTS_KEY]: JSON.stringify(assignments)
17230
+ };
17231
+ if (sourceLabelKey && nextContent[sourceLabelKey] === void 0) {
17232
+ nextContent[sourceLabelKey] = label;
17233
+ }
17234
+ const nodeKeys = [
17235
+ copyId,
17236
+ labelKey,
17237
+ BUTTON_COLLECTIONS_KEY,
17238
+ BUTTON_SNAPSHOTS_KEY,
17239
+ BUTTON_ASSIGNMENTS_KEY
17240
+ ];
17241
+ if (sourceLabelKey) nodeKeys.push(sourceLabelKey);
17242
+ return {
17243
+ copy,
17244
+ content: nextContent,
17245
+ nodes: contentNodes(nextContent, nodeKeys)
17246
+ };
17247
+ }
17248
+ function listCollectionMembers(collection) {
17249
+ return Array.from(collection.querySelectorAll(CTA_BUTTON_SELECTOR)).map((el) => buttonId(el)).filter((id) => Boolean(id));
17250
+ }
17251
+ function contentNodes(content, keys) {
17252
+ return keys.filter((key) => content[key] !== void 0).map((key) => ({ key, text: content[key] }));
17253
+ }
17254
+ function deleteButton(source, content) {
17255
+ if (!canDeleteButton(source)) return null;
17256
+ const id = buttonId(source);
17257
+ if (!id) return null;
17258
+ const parent = source.parentElement;
17259
+ const nextSibling = source.nextSibling;
17260
+ const removed = source;
17261
+ const collection = buttonCollectionOf(source);
17262
+ const collectionId = collection?.getAttribute(COLLECTION_ATTR) ?? "";
17263
+ const previousCollections = readCollections(content);
17264
+ const previousHidden = readHiddenButtons(content);
17265
+ const snapshots = { ...readSnapshots(content) };
17266
+ const assignments = { ...readAssignments(content) };
17267
+ delete assignments[id];
17268
+ const remainingBeforeUnwrap = collection ? listCollectionMembers(collection).filter((member) => member !== id) : [];
17269
+ removed.remove();
17270
+ let nextCollections = { ...previousCollections };
17271
+ let unwrapped = null;
17272
+ if (collection && collectionId) {
17273
+ const members = listCollectionMembers(collection);
17274
+ if (members.length <= 1) {
17275
+ unwrapped = collection;
17276
+ unwrapCollection(collection);
17277
+ delete nextCollections[collectionId];
17278
+ } else {
17279
+ nextCollections[collectionId] = {
17280
+ id: collectionId,
17281
+ members,
17282
+ gap: collection.style.gap || previousCollections[collectionId]?.gap || "12px"
17283
+ };
17284
+ }
17285
+ }
17286
+ const hidden = previousHidden.includes(id) ? previousHidden : [...previousHidden, id];
17287
+ const nextContent = {
17288
+ ...content,
17289
+ [id]: "",
17290
+ [BUTTON_COLLECTIONS_KEY]: JSON.stringify(nextCollections),
17291
+ [BUTTON_HIDDEN_KEY]: JSON.stringify(hidden),
17292
+ [BUTTON_SNAPSHOTS_KEY]: JSON.stringify(snapshots),
17293
+ [BUTTON_ASSIGNMENTS_KEY]: JSON.stringify(assignments)
17294
+ };
17295
+ return {
17296
+ content: nextContent,
17297
+ nodes: contentNodes(nextContent, [id, BUTTON_COLLECTIONS_KEY, BUTTON_HIDDEN_KEY, BUTTON_SNAPSHOTS_KEY, BUTTON_ASSIGNMENTS_KEY]),
17298
+ undo: () => {
17299
+ if (unwrapped && remainingBeforeUnwrap.length === 1) {
17300
+ const remaining = findButton(removed.ownerDocument, remainingBeforeUnwrap[0]);
17301
+ const host = remaining?.parentElement;
17302
+ if (remaining && host) {
17303
+ const restoredCollection = createCollection(
17304
+ removed.ownerDocument,
17305
+ collectionId,
17306
+ previousCollections[collectionId]?.gap || "12px"
17307
+ );
17308
+ host.insertBefore(restoredCollection, remaining);
17309
+ restoredCollection.append(remaining, removed);
17310
+ return;
17311
+ }
17312
+ }
17313
+ if (nextSibling && nextSibling.parentNode === parent) parent?.insertBefore(removed, nextSibling);
17314
+ else parent?.appendChild(removed);
17315
+ }
17316
+ };
17317
+ }
17318
+ function unwrapCollection(collection) {
17319
+ const parent = collection.parentElement;
17320
+ if (!parent) return;
17321
+ while (collection.firstChild) parent.insertBefore(collection.firstChild, collection);
17322
+ collection.remove();
17323
+ }
17324
+ function reorderButtonInCollection(button, clientX) {
17325
+ const collection = buttonCollectionOf(button);
17326
+ const id = buttonId(button);
17327
+ if (!collection || !id) return null;
17328
+ const siblings = Array.from(collection.querySelectorAll(CTA_BUTTON_SELECTOR)).filter((el) => el !== button);
17329
+ let placed = false;
17330
+ for (const sibling of siblings) {
17331
+ const rect = sibling.getBoundingClientRect();
17332
+ if (clientX < rect.left + rect.width / 2) {
17333
+ sibling.before(button);
17334
+ placed = true;
17335
+ break;
17336
+ }
17337
+ }
17338
+ if (!placed) collection.append(button);
17339
+ return listCollectionMembers(collection);
17340
+ }
17341
+ function listVisibleCollectionButtons(collection) {
17342
+ return Array.from(collection.querySelectorAll(CTA_BUTTON_SELECTOR)).filter((el) => {
17343
+ if (!buttonId(el)) return false;
17344
+ return el.getClientRects().length > 0;
17345
+ });
17346
+ }
17347
+ function isVerticalButtonLayout(items, collection) {
17348
+ if (items.length >= 2) {
17349
+ const a = items[0].getBoundingClientRect();
17350
+ const b = items[1].getBoundingClientRect();
17351
+ return b.top - a.bottom > b.left - a.right;
17352
+ }
17353
+ const style = window.getComputedStyle(collection);
17354
+ return style.flexDirection === "column" || style.flexDirection === "column-reverse";
17355
+ }
17356
+ function buildButtonCollectionDropSlots(collection) {
17357
+ const items = listVisibleCollectionButtons(collection);
17358
+ const barThickness = 3;
17359
+ const slots = [];
17360
+ if (items.length === 0) {
17361
+ const rect = collection.getBoundingClientRect();
17362
+ slots.push({
17363
+ insertIndex: 0,
17364
+ left: rect.left + rect.width / 2 - barThickness / 2,
17365
+ top: rect.top,
17366
+ width: barThickness,
17367
+ height: Math.max(rect.height, 24),
17368
+ direction: "vertical"
17369
+ });
17370
+ return slots;
17371
+ }
17372
+ if (isVerticalButtonLayout(items, collection)) {
17373
+ const edgeGap2 = items.length >= 2 ? Math.max(0, items[1].getBoundingClientRect().top - items[0].getBoundingClientRect().bottom) : 16;
17374
+ for (let i = 0; i <= items.length; i++) {
17375
+ let top;
17376
+ let left;
17377
+ let width;
17378
+ if (i === 0) {
17379
+ const first = items[0].getBoundingClientRect();
17380
+ top = first.top - edgeGap2 / 2 - barThickness / 2;
17381
+ left = first.left;
17382
+ width = first.width;
17383
+ } else if (i === items.length) {
17384
+ const last = items[items.length - 1].getBoundingClientRect();
17385
+ const lastGap = items.length >= 2 ? Math.max(0, last.top - items[items.length - 2].getBoundingClientRect().bottom) : edgeGap2;
17386
+ top = last.bottom + lastGap / 2 - barThickness / 2;
17387
+ left = last.left;
17388
+ width = last.width;
17389
+ } else {
17390
+ const prev = items[i - 1].getBoundingClientRect();
17391
+ const next = items[i].getBoundingClientRect();
17392
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
17393
+ left = Math.min(prev.left, next.left);
17394
+ width = Math.max(prev.right, next.right) - left;
17395
+ }
17396
+ slots.push({
17397
+ insertIndex: i,
17398
+ left,
17399
+ top,
17400
+ width: Math.max(width, 40),
17401
+ height: barThickness,
17402
+ direction: "horizontal"
17403
+ });
17404
+ }
17405
+ return slots;
17406
+ }
17407
+ const edgeGap = items.length >= 2 ? Math.max(0, items[1].getBoundingClientRect().left - items[0].getBoundingClientRect().right) : 12;
17408
+ for (let i = 0; i <= items.length; i++) {
17409
+ let left;
17410
+ let top;
17411
+ let height;
17412
+ if (i === 0) {
17413
+ const first = items[0].getBoundingClientRect();
17414
+ left = first.left - edgeGap / 2 - barThickness / 2;
17415
+ top = first.top;
17416
+ height = first.height;
17417
+ } else if (i === items.length) {
17418
+ const last = items[items.length - 1].getBoundingClientRect();
17419
+ const lastGap = items.length >= 2 ? Math.max(0, last.left - items[items.length - 2].getBoundingClientRect().right) : edgeGap;
17420
+ left = last.right + lastGap / 2 - barThickness / 2;
17421
+ top = last.top;
17422
+ height = last.height;
17423
+ } else {
17424
+ const prev = items[i - 1].getBoundingClientRect();
17425
+ const next = items[i].getBoundingClientRect();
17426
+ left = (prev.right + next.left) / 2 - barThickness / 2;
17427
+ top = Math.min(prev.top, next.top);
17428
+ height = Math.max(prev.bottom, next.bottom) - top;
17429
+ }
17430
+ slots.push({
17431
+ insertIndex: i,
17432
+ left,
17433
+ top,
17434
+ width: barThickness,
17435
+ height: Math.max(height, 24),
17436
+ direction: "vertical"
17437
+ });
17438
+ }
17439
+ return slots;
17440
+ }
17441
+ function hitTestButtonCollectionDropSlot(collection, clientX, clientY) {
17442
+ const slots = buildButtonCollectionDropSlots(collection);
17443
+ let best = null;
17444
+ for (const slot of slots) {
17445
+ const cx2 = slot.left + slot.width / 2;
17446
+ const cy = slot.top + slot.height / 2;
17447
+ 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;
17448
+ if (!best || dist < best.dist) best = { slot, dist };
17449
+ }
17450
+ return best?.slot ?? null;
17451
+ }
17452
+ function insertButtonAtCollectionIndex(button, insertIndex) {
17453
+ const collection = buttonCollectionOf(button);
17454
+ if (!collection) return null;
17455
+ const items = listVisibleCollectionButtons(collection);
17456
+ const from = items.indexOf(button);
17457
+ if (from < 0) return null;
17458
+ const others = items.filter((el) => el !== button);
17459
+ let index = insertIndex;
17460
+ if (from < insertIndex) index -= 1;
17461
+ if (index <= 0) {
17462
+ const first = others[0];
17463
+ if (first) first.before(button);
17464
+ else collection.append(button);
17465
+ } else if (index >= others.length) {
17466
+ collection.append(button);
17467
+ } else {
17468
+ others[index].before(button);
17469
+ }
17470
+ return listCollectionMembers(collection);
17471
+ }
17472
+ function persistCollectionOrder(collection, content) {
17473
+ const id = collection.getAttribute(COLLECTION_ATTR);
17474
+ if (!id) return null;
17475
+ const collections = readCollections(content);
17476
+ const current = collections[id];
17477
+ const members = listCollectionMembers(collection);
17478
+ const next = {
17479
+ ...collections,
17480
+ [id]: { id, members, gap: collection.style.gap || current?.gap || "12px" }
17481
+ };
17482
+ const nextContent = { ...content, [BUTTON_COLLECTIONS_KEY]: JSON.stringify(next) };
17483
+ return { content: nextContent, nodes: [{ key: BUTTON_COLLECTIONS_KEY, text: nextContent[BUTTON_COLLECTIONS_KEY] }] };
17484
+ }
17485
+ function hideButton(el) {
17486
+ if (el.getAttribute("data-ohw-button-hidden") === "") return;
17487
+ el.setAttribute("data-ohw-button-hidden", "");
17488
+ el.style.display = "none";
17489
+ }
17490
+ function syncButtonsFromContent(doc, content) {
17491
+ reconcileButtonCollections(doc, content);
17492
+ applyButtonContentFromContent(doc, content);
17493
+ applyButtonStylesFromContent(doc, content);
17494
+ }
17495
+ function applyButtonContentFromContent(doc, content) {
17496
+ const snapshots = readSnapshots(content);
17497
+ for (const snapshot of Object.values(snapshots)) {
17498
+ const el = findButton(doc, snapshot.id);
17499
+ if (!el) continue;
17500
+ applySnapshotContent(el, snapshot, content);
17501
+ }
17502
+ }
17503
+ function applySnapshotContent(el, snapshot, content) {
17504
+ const href = content[snapshot.id] ?? snapshot.href;
17505
+ if (href) el.setAttribute("href", href);
17506
+ const labelKey = snapshot.labelKey || labelKeyForHrefKey(snapshot.id);
17507
+ if (labelKey && !el.querySelector(`[data-ohw-key="${CSS.escape(labelKey)}"]`)) {
17508
+ const label2 = labelElement2(el);
17509
+ if (label2) label2.setAttribute("data-ohw-key", labelKey);
17510
+ }
17511
+ const text = content[labelKey];
17512
+ if (text === void 0) return;
17513
+ const label = labelElement2(el);
17514
+ if (label) {
17515
+ if (label.getAttribute("data-ohw-key") !== labelKey) label.setAttribute("data-ohw-key", labelKey);
17516
+ if (label.textContent !== text) label.textContent = text;
17517
+ }
17518
+ }
17519
+ function reconcileButtonCollections(doc, content) {
17520
+ const hidden = new Set(readHiddenButtons(content));
17521
+ for (const id of hidden) {
17522
+ const el = findButton(doc, id);
17523
+ if (el) hideButton(el);
17524
+ }
17525
+ const snapshots = readSnapshots(content);
17526
+ const collections = readCollections(content);
17527
+ for (const collectionState of Object.values(collections)) {
17528
+ const live = collectionState.members.map((id) => findButton(doc, id) ?? restoreSnapshot(doc, snapshots[id], content)).filter((el) => Boolean(el && !hidden.has(buttonId(el) ?? "")));
17529
+ if (live.length === 0) continue;
17530
+ if (live.length === 1) {
17531
+ const existing = buttonCollectionOf(live[0]);
17532
+ if (existing) unwrapCollection(existing);
17533
+ continue;
17534
+ }
17535
+ const anchor = live.find((el) => el.isConnected) ?? live[0];
17536
+ let collection = live.map((el) => buttonCollectionOf(el)).find((el) => el?.getAttribute(COLLECTION_ATTR) === collectionState.id) ?? null;
17537
+ if (!collection) {
17538
+ if (!anchor.parentElement) continue;
17539
+ collection = createCollection(doc, collectionState.id, collectionState.gap);
17540
+ anchor.parentElement.insertBefore(collection, anchor);
17541
+ }
17542
+ styleCollection(collection, collectionState.gap);
17543
+ const current = listCollectionMembers(collection);
17544
+ const sameOrder = current.length === live.length && current.every((id, index) => id === buttonId(live[index]));
17545
+ if (!sameOrder) {
17546
+ for (const member of live) collection.append(member);
17547
+ }
17548
+ for (const member of live) setButtonDragAttr(member);
17549
+ }
17550
+ }
17551
+ function restoreSnapshot(doc, snapshot, content) {
17552
+ if (!snapshot) return null;
17553
+ const existing = findButton(doc, snapshot.id);
17554
+ if (existing) {
17555
+ applySnapshotContent(existing, snapshot, content);
17556
+ return existing;
17557
+ }
17558
+ const el = htmlToElement(doc, snapshot.html);
17559
+ if (!el) return null;
17560
+ el.setAttribute("data-ohw-href-key", snapshot.id);
17561
+ const label = labelElement2(el);
17562
+ const labelKey = snapshot.labelKey || labelKeyForHrefKey(snapshot.id);
17563
+ if (label) label.setAttribute("data-ohw-key", labelKey);
17564
+ applySnapshotContent(el, snapshot, content);
17565
+ setButtonDragAttr(el);
17566
+ return el;
17567
+ }
17568
+ function navbarCtasInScope(el) {
17569
+ const scope = el.closest("nav, [data-ohw-nav-root], [data-ohw-nav-drawer]") ?? el.parentElement;
17570
+ if (!scope) return [];
17571
+ return Array.from(scope.querySelectorAll(CTA_BUTTON_SELECTOR)).filter(
17572
+ (button) => isNavbarCta(button) && buttonId(button)
17573
+ );
17574
+ }
17575
+ function ensureNavbarButtonCollection(button, content) {
17576
+ const existing = buttonCollectionOf(button);
17577
+ if (existing) return existing;
17578
+ if (!isNavbarCta(button) || !buttonId(button)) return null;
17579
+ const parent = button.parentElement;
17580
+ if (!parent) return null;
17581
+ const siblings = navbarCtasInScope(button).filter((el) => el.parentElement === parent);
17582
+ if (siblings.length < 2) return null;
17583
+ const id = nextCollectionId(content, button.ownerDocument);
17584
+ const collection = createCollection(button.ownerDocument, id, collectionGap(button, tokensFromButton(button)));
17585
+ siblings[0].before(collection);
17586
+ for (const member of siblings) {
17587
+ collection.append(member);
17588
+ setButtonDragAttr(member);
17589
+ }
17590
+ return collection;
17591
+ }
17592
+ function collectionDragEnabled(el) {
17593
+ if (!isNavbarCta(el) || !buttonId(el)) return false;
17594
+ if (buttonCollectionOf(el)) return true;
17595
+ return navbarCtasInScope(el).length >= 2;
17596
+ }
17597
+
17598
+ // src/lib/item-drag-interaction.ts
17599
+ function disableNativeHrefDrag(el) {
17600
+ if (el.draggable) el.draggable = false;
17601
+ if (el.getAttribute("draggable") !== "false") {
17602
+ el.setAttribute("draggable", "false");
17603
+ }
17604
+ }
17605
+ function clearTextSelection() {
17606
+ const sel = window.getSelection();
17607
+ if (sel && !sel.isCollapsed) sel.removeAllRanges();
17608
+ }
17609
+ function armItemPressDrag() {
17610
+ document.documentElement.setAttribute("data-ohw-footer-press-drag", "");
17611
+ }
17612
+ function lockItemDuringDrag() {
17613
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
17614
+ document.documentElement.setAttribute("data-ohw-item-dragging", "");
17615
+ clearTextSelection();
17616
+ }
17617
+ function unlockItemDragInteraction() {
17618
+ const wasDragging = document.documentElement.hasAttribute("data-ohw-item-dragging");
17619
+ document.documentElement.removeAttribute("data-ohw-footer-press-drag");
17620
+ document.documentElement.removeAttribute("data-ohw-item-dragging");
17621
+ if (wasDragging) clearTextSelection();
17622
+ }
17623
+ var armFooterPressDrag = armItemPressDrag;
17624
+ var lockFooterDuringDrag = lockItemDuringDrag;
17625
+ var unlockFooterDragInteraction = unlockItemDragInteraction;
17626
+
17627
+ // src/lib/nav-dnd.ts
17628
+ function listReorderableNavItems(draggedEl) {
17629
+ return listActiveNavbarItems(draggedEl).filter((el) => {
17630
+ const key = el.getAttribute("data-ohw-href-key");
17631
+ return isNavbarHrefKey(key) && !isNestedNavChild(el);
17632
+ });
17633
+ }
17634
+ function resolveParentNavHrefKey(child) {
17635
+ const childrenRoot = child.closest("[data-ohw-nav-children]");
17636
+ const group = childrenRoot?.closest("[data-ohw-nav-group]");
17637
+ if (!group) return null;
17638
+ const trigger = group.querySelector(":scope > [data-ohw-href-key]") ?? Array.from(group.querySelectorAll("[data-ohw-href-key]")).find(
17639
+ (el) => !el.closest("[data-ohw-nav-children]")
17640
+ ) ?? null;
17641
+ const key = trigger?.getAttribute("data-ohw-href-key");
17642
+ return key && isNavbarHrefKey(key) ? key : null;
17643
+ }
17644
+ function listNavChildren(parentHrefKey, draggedEl) {
17645
+ const items = listActiveNavbarItems(draggedEl);
17646
+ const parent = items.find((el) => el.getAttribute("data-ohw-href-key") === parentHrefKey);
17647
+ if (!parent) return [];
17648
+ const group = parent.closest("[data-ohw-nav-group]");
17649
+ const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
17650
+ if (!childrenRoot) return [];
17651
+ return Array.from(childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")).filter(
17652
+ (el) => isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))
17653
+ );
17654
+ }
17655
+ function isVerticalNavLayout(items, draggedEl) {
17656
+ const visible = items.filter((el) => el.getClientRects().length > 0);
17657
+ if (visible.length >= 2) {
17658
+ const a = visible[0].getBoundingClientRect();
17659
+ const b = visible[1].getBoundingClientRect();
17660
+ const verticalGap = b.top - a.bottom;
17661
+ const horizontalGap = b.left - a.right;
17662
+ return verticalGap > horizontalGap;
17663
+ }
17664
+ const container = getActiveNavbarListContainer(draggedEl);
17665
+ if (!container) return false;
17666
+ const style = window.getComputedStyle(container);
17667
+ return style.flexDirection === "column" || style.flexDirection === "column-reverse";
17668
+ }
17669
+ function buildVerticalRootNavDropSlots(items, draggedEl) {
17670
+ const slots = [];
17671
+ const barThickness = 3;
17672
+ if (items.length === 0) {
17673
+ const container = getActiveNavbarListContainer(draggedEl);
17674
+ if (!container) return slots;
17675
+ const rect = container.getBoundingClientRect();
17676
+ slots.push({
17677
+ insertIndex: 0,
17678
+ parentId: null,
17679
+ left: rect.left,
17680
+ top: rect.top + rect.height / 2 - barThickness / 2,
17681
+ width: Math.max(rect.width, 40),
17682
+ height: barThickness,
17683
+ direction: "horizontal"
17684
+ });
17685
+ return slots;
17686
+ }
17687
+ const edgeGap = (() => {
17688
+ if (items.length < 2) return 16;
17689
+ const a = items[0].getBoundingClientRect();
17690
+ const b = items[1].getBoundingClientRect();
17691
+ return Math.max(0, b.top - a.bottom);
17692
+ })();
17693
+ for (let i = 0; i <= items.length; i++) {
17694
+ let top;
17695
+ let width;
17696
+ let left;
17697
+ if (i === 0) {
17698
+ const first = items[0].getBoundingClientRect();
17699
+ top = first.top - edgeGap / 2 - barThickness / 2;
17700
+ left = first.left;
17701
+ width = first.width;
17702
+ } else if (i === items.length) {
17703
+ const last = items[items.length - 1].getBoundingClientRect();
17704
+ const lastGap = items.length >= 2 ? Math.max(0, last.top - items[items.length - 2].getBoundingClientRect().bottom) : edgeGap;
17705
+ top = last.bottom + lastGap / 2 - barThickness / 2;
17706
+ left = last.left;
17707
+ width = last.width;
17708
+ } else {
17709
+ const prev = items[i - 1].getBoundingClientRect();
17710
+ const next = items[i].getBoundingClientRect();
17711
+ top = (prev.bottom + next.top) / 2 - barThickness / 2;
17712
+ left = Math.min(prev.left, next.left);
17713
+ width = Math.max(prev.right, next.right) - left;
17714
+ }
17715
+ slots.push({
17716
+ insertIndex: i,
17717
+ parentId: null,
17718
+ left,
17719
+ top,
17720
+ width: Math.max(width, 40),
17721
+ height: barThickness,
17722
+ direction: "horizontal"
17723
+ });
17724
+ }
17725
+ return slots;
17726
+ }
17727
+ function buildHorizontalRootNavDropSlots(items, draggedEl) {
17728
+ const slots = [];
17729
+ const barThickness = 3;
17730
+ if (items.length === 0) {
17731
+ const container = getActiveNavbarListContainer(draggedEl);
17732
+ if (!container) return slots;
17733
+ const rect = container.getBoundingClientRect();
17734
+ slots.push({
17735
+ insertIndex: 0,
17736
+ parentId: null,
17737
+ left: rect.left + rect.width / 2 - barThickness / 2,
17738
+ top: rect.top,
17739
+ width: barThickness,
17740
+ height: Math.max(rect.height, 24),
17741
+ direction: "vertical"
17742
+ });
17743
+ return slots;
17744
+ }
17745
+ const edgeGap = (() => {
17746
+ if (items.length < 2) return 32;
17747
+ const a = items[0].getBoundingClientRect();
17748
+ const b = items[1].getBoundingClientRect();
17749
+ return Math.max(0, b.left - a.right);
17750
+ })();
17751
+ for (let i = 0; i <= items.length; i++) {
17752
+ let left;
17753
+ let top;
17754
+ let height;
17755
+ if (i === 0) {
17756
+ const first = items[0].getBoundingClientRect();
17757
+ left = first.left - edgeGap / 2 - barThickness / 2;
17758
+ top = first.top;
17759
+ height = first.height;
17760
+ } else if (i === items.length) {
17761
+ const last = items[items.length - 1].getBoundingClientRect();
17762
+ const lastGap = items.length >= 2 ? Math.max(0, last.left - items[items.length - 2].getBoundingClientRect().right) : edgeGap;
17763
+ left = last.right + lastGap / 2 - barThickness / 2;
17764
+ top = last.top;
17765
+ height = last.height;
17766
+ } else {
17767
+ const prev = items[i - 1].getBoundingClientRect();
17768
+ const next = items[i].getBoundingClientRect();
17769
+ left = (prev.right + next.left) / 2 - barThickness / 2;
17770
+ top = Math.min(prev.top, next.top);
17771
+ height = Math.max(prev.bottom, next.bottom) - top;
17772
+ }
17773
+ slots.push({
17774
+ insertIndex: i,
17775
+ parentId: null,
17776
+ left,
17777
+ top,
17778
+ width: barThickness,
17779
+ height: Math.max(height, 24),
17780
+ direction: "vertical"
17781
+ });
17782
+ }
17783
+ return slots;
17784
+ }
17785
+ function buildRootNavDropSlots(draggedEl) {
17786
+ const items = listReorderableNavItems(draggedEl);
17787
+ if (isVerticalNavLayout(items, draggedEl)) {
17788
+ return buildVerticalRootNavDropSlots(items, draggedEl);
17789
+ }
16220
17790
  return buildHorizontalRootNavDropSlots(items, draggedEl);
16221
17791
  }
16222
17792
  function buildChildNavDropSlots(parentHrefKey, draggedEl) {
@@ -16454,7 +18024,7 @@ function useNavItemDrag({
16454
18024
  document.querySelectorAll(
16455
18025
  "nav [data-ohw-href-key], [data-ohw-nav-container] [data-ohw-href-key], [data-ohw-nav-drawer] [data-ohw-href-key]"
16456
18026
  ).forEach((el) => {
16457
- if (isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))) {
18027
+ if (isNavbarHrefKey(el.getAttribute("data-ohw-href-key")) || isCtaButton(el)) {
16458
18028
  disableNativeHrefDrag(el);
16459
18029
  }
16460
18030
  });
@@ -16763,12 +18333,6 @@ var SECTION_DRAG_EXCLUDED_SELECTOR = [
16763
18333
  "[data-ohw-media-overlay]",
16764
18334
  "[data-ohw-media-skeleton]"
16765
18335
  ].join(", ");
16766
- function visibleClip(ps) {
16767
- if (!ps) return null;
16768
- const top = Math.max(0, ps.headerH - ps.iframeOffsetTop);
16769
- const bottom = Math.min(window.innerHeight, ps.headerH + ps.canvasH - ps.iframeOffsetTop);
16770
- return { top, bottom: Math.max(top, bottom) };
16771
- }
16772
18336
  function useSectionDrag({
16773
18337
  isEditMode,
16774
18338
  editContentRef,
@@ -16805,7 +18369,7 @@ function useSectionDrag({
16805
18369
  }, [postToParentRef, stopAutoScroll]);
16806
18370
  const updateAutoScroll = (0, import_react17.useCallback)(
16807
18371
  (clientY) => {
16808
- const clip = visibleClip(parentScrollRef.current);
18372
+ const clip = getIframeChromeClip(parentScrollRef.current);
16809
18373
  let delta = 0;
16810
18374
  if (clip) {
16811
18375
  const distTop = clientY - clip.top;
@@ -17011,8 +18575,8 @@ function useSectionDrag({
17011
18575
  }
17012
18576
 
17013
18577
  // src/ui/footer-container-chrome.tsx
17014
- var import_lucide_react17 = require("lucide-react");
17015
- var import_jsx_runtime31 = require("react/jsx-runtime");
18578
+ var import_lucide_react18 = require("lucide-react");
18579
+ var import_jsx_runtime32 = require("react/jsx-runtime");
17016
18580
  function FooterContainerChrome({
17017
18581
  rect,
17018
18582
  onAdd,
@@ -17020,7 +18584,7 @@ function FooterContainerChrome({
17020
18584
  }) {
17021
18585
  const chromeGap = 6;
17022
18586
  const buttonMargin = 7;
17023
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
18587
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
17024
18588
  "div",
17025
18589
  {
17026
18590
  "data-ohw-footer-container-chrome": "",
@@ -17032,8 +18596,8 @@ function FooterContainerChrome({
17032
18596
  width: rect.width + chromeGap * 2,
17033
18597
  height: rect.height + chromeGap * 2
17034
18598
  },
17035
- children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(Tooltip, { children: [
17036
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
18599
+ children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(Tooltip, { children: [
18600
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
17037
18601
  "button",
17038
18602
  {
17039
18603
  type: "button",
@@ -17052,10 +18616,10 @@ function FooterContainerChrome({
17052
18616
  if (addDisabled) return;
17053
18617
  onAdd();
17054
18618
  },
17055
- children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react17.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
18619
+ children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react18.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
17056
18620
  }
17057
18621
  ) }),
17058
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
18622
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
17059
18623
  ] })
17060
18624
  }
17061
18625
  ) });
@@ -17217,7 +18781,14 @@ function collectEditableNodes(extraContent, root = document) {
17217
18781
  // so collecting the DOM alone left them behind: the draft knew the row was showing icons and
17218
18782
  // had gained an item, and the published page went back to the template's own (OHH-736).
17219
18783
  SOCIALS_ORDER_KEY,
17220
- SOCIALS_DISPLAY_KEY
18784
+ SOCIALS_DISPLAY_KEY,
18785
+ // Button variant/size tokens and per-CTA assignments — JSON in editContent, applied on hydrate.
18786
+ BUTTON_VARIANTS_KEY,
18787
+ BUTTON_SIZES_KEY,
18788
+ BUTTON_ASSIGNMENTS_KEY,
18789
+ BUTTON_COLLECTIONS_KEY,
18790
+ BUTTON_SNAPSHOTS_KEY,
18791
+ BUTTON_HIDDEN_KEY
17221
18792
  ]) {
17222
18793
  const text = extraContent[key];
17223
18794
  if (typeof text === "string" && text.length > 0) {
@@ -17537,14 +19108,14 @@ function deleteSelectedNavFooterItem(deps) {
17537
19108
  }
17538
19109
 
17539
19110
  // src/ui/navbar-container-chrome.tsx
17540
- var import_lucide_react18 = require("lucide-react");
17541
- var import_jsx_runtime32 = require("react/jsx-runtime");
19111
+ var import_lucide_react19 = require("lucide-react");
19112
+ var import_jsx_runtime33 = require("react/jsx-runtime");
17542
19113
  function NavbarContainerChrome({
17543
19114
  rect,
17544
19115
  onAdd
17545
19116
  }) {
17546
19117
  const chromeGap = 6;
17547
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
19118
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17548
19119
  "div",
17549
19120
  {
17550
19121
  "data-ohw-navbar-container-chrome": "",
@@ -17556,7 +19127,7 @@ function NavbarContainerChrome({
17556
19127
  width: rect.width + chromeGap * 2,
17557
19128
  height: rect.height + chromeGap * 2
17558
19129
  },
17559
- children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
19130
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17560
19131
  "button",
17561
19132
  {
17562
19133
  type: "button",
@@ -17573,7 +19144,7 @@ function NavbarContainerChrome({
17573
19144
  e.stopPropagation();
17574
19145
  onAdd();
17575
19146
  },
17576
- children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react18.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
19147
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react19.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
17577
19148
  }
17578
19149
  )
17579
19150
  }
@@ -17582,7 +19153,7 @@ function NavbarContainerChrome({
17582
19153
 
17583
19154
  // src/ui/drop-indicator.tsx
17584
19155
  var React11 = __toESM(require("react"), 1);
17585
- var import_jsx_runtime33 = require("react/jsx-runtime");
19156
+ var import_jsx_runtime34 = require("react/jsx-runtime");
17586
19157
  var dropIndicatorVariants = cva(
17587
19158
  "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
17588
19159
  {
@@ -17606,7 +19177,7 @@ var dropIndicatorVariants = cva(
17606
19177
  );
17607
19178
  var DropIndicator = React11.forwardRef(
17608
19179
  ({ className, direction, state, ...props }, ref) => {
17609
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19180
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
17610
19181
  "div",
17611
19182
  {
17612
19183
  ref,
@@ -17623,7 +19194,7 @@ var DropIndicator = React11.forwardRef(
17623
19194
  DropIndicator.displayName = "DropIndicator";
17624
19195
 
17625
19196
  // src/ui/badge.tsx
17626
- var import_jsx_runtime34 = require("react/jsx-runtime");
19197
+ var import_jsx_runtime35 = require("react/jsx-runtime");
17627
19198
  var badgeVariants = cva(
17628
19199
  "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",
17629
19200
  {
@@ -17641,12 +19212,12 @@ var badgeVariants = cva(
17641
19212
  }
17642
19213
  );
17643
19214
  function Badge({ className, variant, ...props }) {
17644
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
19215
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
17645
19216
  }
17646
19217
 
17647
19218
  // src/OhhwellsBridge.tsx
17648
- var import_lucide_react19 = require("lucide-react");
17649
- var import_jsx_runtime35 = require("react/jsx-runtime");
19219
+ var import_lucide_react20 = require("lucide-react");
19220
+ var import_jsx_runtime36 = require("react/jsx-runtime");
17650
19221
  var PRIMARY5 = "#0885FE";
17651
19222
  var IMAGE_FADE_MS = 300;
17652
19223
  var MEDIA_SETTLE_TIMEOUT_MS = 2500;
@@ -17768,7 +19339,7 @@ function duplicateSchedulingWidget(instanceId, scheduleId) {
17768
19339
  schedulingRoots.set(container, root);
17769
19340
  (0, import_react_dom4.flushSync)(() => {
17770
19341
  root.render(
17771
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
19342
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
17772
19343
  SchedulingWidget,
17773
19344
  {
17774
19345
  notifyOnConnect: false,
@@ -17906,7 +19477,7 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
17906
19477
  schedulingRoots.set(container, root);
17907
19478
  (0, import_react_dom4.flushSync)(() => {
17908
19479
  root.render(
17909
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
19480
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
17910
19481
  SchedulingWidget,
17911
19482
  {
17912
19483
  notifyOnConnect,
@@ -18069,8 +19640,8 @@ function lockVideoBox(video) {
18069
19640
  }
18070
19641
  const fit = video.style.objectFit || getComputedStyle(video).objectFit;
18071
19642
  const bg = getComputedStyle(video).backgroundColor;
18072
- const isTransparent = bg === "rgba(0, 0, 0, 0)" || bg === "transparent" || !bg;
18073
- if (fit === "contain" && isTransparent) {
19643
+ const isTransparent2 = bg === "rgba(0, 0, 0, 0)" || bg === "transparent" || !bg;
19644
+ if (fit === "contain" && isTransparent2) {
18074
19645
  video.style.backgroundColor = "var(--color-dark, #000)";
18075
19646
  }
18076
19647
  }
@@ -18220,7 +19791,10 @@ function listNavigationItems() {
18220
19791
  ).filter((el) => isNavigationItem2(el));
18221
19792
  }
18222
19793
  function getNavigationItemReorderState(anchor) {
18223
- if (isCtaButton(anchor)) return { key: null, disabled: false };
19794
+ if (isCtaButton(anchor)) {
19795
+ if (!collectionDragEnabled(anchor)) return { key: null, disabled: true };
19796
+ return { key: buttonId(anchor), disabled: false };
19797
+ }
18224
19798
  return getReorderHandleStateFromAnchor(anchor);
18225
19799
  }
18226
19800
  function getReorderHandleState(el) {
@@ -18319,6 +19893,12 @@ function isPointOverNavigation(x, y) {
18319
19893
  const r2 = panel.getBoundingClientRect();
18320
19894
  if (x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom) return true;
18321
19895
  }
19896
+ for (const el of document.querySelectorAll('[data-ohw-role="navbar-button"]')) {
19897
+ const r2 = el.getBoundingClientRect();
19898
+ if (r2.width > 0 && r2.height > 0 && x >= r2.left && x <= r2.right && y >= r2.top && y <= r2.bottom) {
19899
+ return true;
19900
+ }
19901
+ }
18322
19902
  return false;
18323
19903
  }
18324
19904
  function findHoveredNavOrFooterContainer(x, y) {
@@ -18652,7 +20232,7 @@ function EditGlowChrome({
18652
20232
  hideHandle = false
18653
20233
  }) {
18654
20234
  const GAP = SELECTION_CHROME_GAP2;
18655
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
20235
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
18656
20236
  "div",
18657
20237
  {
18658
20238
  ref: elRef,
@@ -18667,7 +20247,7 @@ function EditGlowChrome({
18667
20247
  zIndex: 2147483646
18668
20248
  },
18669
20249
  children: [
18670
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
20250
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
18671
20251
  "div",
18672
20252
  {
18673
20253
  style: {
@@ -18681,7 +20261,7 @@ function EditGlowChrome({
18681
20261
  }
18682
20262
  }
18683
20263
  ),
18684
- reorderHrefKey && !hideHandle && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
20264
+ reorderHrefKey && !hideHandle && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
18685
20265
  "div",
18686
20266
  {
18687
20267
  "data-ohw-drag-handle-container": "",
@@ -18693,7 +20273,7 @@ function EditGlowChrome({
18693
20273
  transform: "translate(calc(-100% - 7px), -50%)",
18694
20274
  pointerEvents: dragDisabled ? "none" : "auto"
18695
20275
  },
18696
- children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
20276
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
18697
20277
  DragHandle,
18698
20278
  {
18699
20279
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -18707,22 +20287,12 @@ function EditGlowChrome({
18707
20287
  }
18708
20288
  );
18709
20289
  }
18710
- function getIframeVisibleClip(parentScroll) {
18711
- if (!parentScroll) return null;
18712
- const { iframeOffsetTop, headerH: visibleCanvasTop, canvasH } = parentScroll;
18713
- const clipTop = Math.max(0, visibleCanvasTop - iframeOffsetTop);
18714
- const clipBottom = Math.min(
18715
- window.innerHeight,
18716
- visibleCanvasTop + canvasH - iframeOffsetTop
18717
- );
18718
- return { top: clipTop, bottom: Math.max(clipTop, clipBottom) };
18719
- }
18720
20290
  function isRectOutsideClip(rect, clip) {
18721
20291
  if (!clip) return false;
18722
20292
  return rect.bottom < clip.top || rect.top > clip.bottom;
18723
20293
  }
18724
20294
  function applyVisibleViewport(el, parentScroll) {
18725
- const clip = getIframeVisibleClip(parentScroll);
20295
+ const clip = getIframeChromeClip(parentScroll);
18726
20296
  const top = clip?.top ?? 0;
18727
20297
  const height = clip ? clip.bottom - clip.top : window.innerHeight;
18728
20298
  el.style.top = `${top}px`;
@@ -18762,7 +20332,7 @@ function clampToolbarLeft(centerX, toolbarWidth) {
18762
20332
  function calcToolbarPos(rect, parentScroll, toolbarW = 306) {
18763
20333
  const GAP = TOOLBAR_OFFSET_FROM_ELEMENT;
18764
20334
  const APPROX_H = 32;
18765
- const clip = getIframeVisibleClip(parentScroll);
20335
+ const clip = getIframeChromeClip(parentScroll);
18766
20336
  const clipTop = clip?.top ?? 0;
18767
20337
  const canvasTopInIframe = parentScroll != null ? parentScroll.headerH - parentScroll.iframeOffsetTop : 0;
18768
20338
  const visibleTop = rect.top - canvasTopInIframe;
@@ -18907,7 +20477,7 @@ function FloatingToolbar({
18907
20477
  return () => ro.disconnect();
18908
20478
  }, [showEditLink, activeCommands]);
18909
20479
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, measuredW);
18910
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
20480
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
18911
20481
  "div",
18912
20482
  {
18913
20483
  ref: setRefs,
@@ -18919,12 +20489,12 @@ function FloatingToolbar({
18919
20489
  zIndex: 2147483647,
18920
20490
  pointerEvents: "auto"
18921
20491
  },
18922
- children: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(CustomToolbar, { children: [
18923
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_react19.default.Fragment, { children: [
18924
- gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(CustomToolbarDivider, {}),
20492
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(CustomToolbar, { children: [
20493
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_react19.default.Fragment, { children: [
20494
+ gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(CustomToolbarDivider, {}),
18925
20495
  btns.map((btn) => {
18926
20496
  const isActive = activeCommands.has(btn.cmd);
18927
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
20497
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
18928
20498
  CustomToolbarButton,
18929
20499
  {
18930
20500
  title: btn.title,
@@ -18933,7 +20503,7 @@ function FloatingToolbar({
18933
20503
  e.preventDefault();
18934
20504
  onCommand(btn.cmd);
18935
20505
  },
18936
- children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
20506
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
18937
20507
  "svg",
18938
20508
  {
18939
20509
  width: "16",
@@ -18954,7 +20524,7 @@ function FloatingToolbar({
18954
20524
  );
18955
20525
  })
18956
20526
  ] }, gi)),
18957
- showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
20527
+ showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
18958
20528
  CustomToolbarButton,
18959
20529
  {
18960
20530
  type: "button",
@@ -18968,7 +20538,7 @@ function FloatingToolbar({
18968
20538
  e.preventDefault();
18969
20539
  e.stopPropagation();
18970
20540
  },
18971
- children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_lucide_react19.Link, { className: "size-4 shrink-0", "aria-hidden": true })
20541
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_lucide_react20.Link, { className: "size-4 shrink-0", "aria-hidden": true })
18972
20542
  }
18973
20543
  ) : null
18974
20544
  ] })
@@ -18985,7 +20555,7 @@ function StateToggle({
18985
20555
  states,
18986
20556
  onStateChange
18987
20557
  }) {
18988
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
20558
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
18989
20559
  ToggleGroup,
18990
20560
  {
18991
20561
  "data-ohw-state-toggle": "",
@@ -18999,7 +20569,7 @@ function StateToggle({
18999
20569
  left: rect.right - 8,
19000
20570
  transform: "translateX(-100%)"
19001
20571
  },
19002
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
20572
+ children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
19003
20573
  }
19004
20574
  );
19005
20575
  }
@@ -19015,9 +20585,9 @@ var OHW_LOADER_STYLE = {
19015
20585
  justifyContent: "center"
19016
20586
  };
19017
20587
  function OhwLoaderSpinner() {
19018
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("svg", { width: "28", height: "28", viewBox: "0 0 28 28", fill: "none", "aria-hidden": true, children: [
19019
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("circle", { cx: "14", cy: "14", r: "11", stroke: "#E7E5E4", strokeWidth: "3" }),
19020
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
20588
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("svg", { width: "28", height: "28", viewBox: "0 0 28 28", fill: "none", "aria-hidden": true, children: [
20589
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("circle", { cx: "14", cy: "14", r: "11", stroke: "#E7E5E4", strokeWidth: "3" }),
20590
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
19021
20591
  "circle",
19022
20592
  {
19023
20593
  cx: "14",
@@ -19027,7 +20597,7 @@ function OhwLoaderSpinner() {
19027
20597
  strokeWidth: "3",
19028
20598
  strokeDasharray: "17 52",
19029
20599
  strokeLinecap: "round",
19030
- children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
20600
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
19031
20601
  "animateTransform",
19032
20602
  {
19033
20603
  attributeName: "transform",
@@ -19043,7 +20613,7 @@ function OhwLoaderSpinner() {
19043
20613
  ] });
19044
20614
  }
19045
20615
  function OhwBrandMark() {
19046
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
20616
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
19047
20617
  "svg",
19048
20618
  {
19049
20619
  width: "16",
@@ -19054,7 +20624,7 @@ function OhwBrandMark() {
19054
20624
  style: { display: "block", flexShrink: 0 },
19055
20625
  xmlns: "http://www.w3.org/2000/svg",
19056
20626
  children: [
19057
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
20627
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
19058
20628
  "mask",
19059
20629
  {
19060
20630
  id: "ohw-badge-mark",
@@ -19064,15 +20634,15 @@ function OhwBrandMark() {
19064
20634
  y: "0",
19065
20635
  width: "48",
19066
20636
  height: "48",
19067
- children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("path", { d: "M23.8741 48C37.0594 48 47.7481 37.2548 47.7481 24C47.7481 10.7452 37.0594 0 23.8741 0C10.6888 0 0 10.7452 0 24C0 37.2548 10.6888 48 23.8741 48Z", fill: "white" })
20637
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { d: "M23.8741 48C37.0594 48 47.7481 37.2548 47.7481 24C47.7481 10.7452 37.0594 0 23.8741 0C10.6888 0 0 10.7452 0 24C0 37.2548 10.6888 48 23.8741 48Z", fill: "white" })
19068
20638
  }
19069
20639
  ),
19070
- /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("g", { mask: "url(#ohw-badge-mark)", children: [
19071
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("path", { d: "M23.8731 48.0497C37.0584 48.0497 47.7472 37.3046 47.7472 24.0497C47.7472 10.7949 37.0584 0.0497208 23.8731 0.0497208C10.6878 0.0497208 -0.000976562 10.7949 -0.000976562 24.0497C-0.000976562 37.3046 10.6878 48.0497 23.8731 48.0497Z", fill: "#0078E5" }),
19072
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("path", { d: "M17.1307 14.7172C13.1687 14.7172 9.38102 18.1154 8.65114 22.34C8.38885 23.8488 8.5598 25.2581 9.06929 26.4451C6.20005 29.1677 1.77216 27.8721 -1.40212 26.1536C-2.73618 25.4317 -3.92695 27.4745 -2.59037 28.1981C1.33389 30.3226 6.86037 31.6621 10.4402 28.4188C11.4718 29.3859 12.867 29.9621 14.4894 29.9621C18.4161 29.9621 22.2038 26.5318 22.9337 22.34C23.6636 18.1162 21.0566 14.7172 17.1298 14.7172H17.1307ZM19.9798 22.34C19.5281 25.0399 17.2689 27.231 14.9754 27.231C12.6466 27.231 11.1877 25.0399 11.6394 22.34C12.1262 19.6401 14.3151 17.4482 16.6438 17.4482C18.9374 17.4482 20.4667 19.6401 19.9798 22.34Z", fill: "white" }),
19073
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("path", { d: "M40.0017 27.0262C39.1797 27.081 38.2721 26.995 37.4668 26.7415C37.3344 26.6993 37.28 26.5401 37.3529 26.4205C37.5959 26.0212 37.8255 25.6152 38.009 25.1889C38.1071 24.9918 38.2018 24.793 38.2897 24.5908C38.3274 24.5041 38.4163 24.451 38.5101 24.4619C38.63 24.4754 38.7054 24.4821 38.8881 24.4821L39.1529 24.4796L39.8283 24.4543C45.9229 24.0492 50.4765 20.4319 54.8466 16.9014C56.0172 15.9554 57.6932 17.6208 56.5116 18.5752C51.7687 22.4065 47.1966 26.5081 40.9319 26.9689", fill: "white" }),
19074
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("path", { d: "M37.9687 24.27C38.4472 23.1319 38.7656 21.9045 38.9609 20.6991C39.5927 16.76 38.5058 14.2193 36.1553 14.2193C34.1835 14.2193 33.1469 17.2427 32.9199 18.8694C32.743 19.9872 32.5914 22.1219 33.6028 23.9524C33.7553 24.259 34.15 24.7712 34.471 25.1259C34.5447 25.2067 34.6746 25.2 34.7349 25.1082C34.9528 24.7788 35.1615 24.4039 35.3584 24.0257C35.5444 23.6677 35.5888 23.6138 35.8587 23.0207C35.8838 22.966 35.8813 22.9002 35.8478 22.8505C35.5888 22.4597 35.2168 21.9787 35.1204 21.4614C34.9184 20.4455 34.9436 19.2257 35.2218 18.1078C35.4413 17.3118 35.7195 16.7844 35.9039 16.5106C35.9466 16.4466 36.0279 16.4129 36.0975 16.4449C36.369 16.5671 36.5827 16.8838 36.7385 17.396C37.0167 18.2089 37.0167 19.3782 36.814 20.6999C36.6991 21.5271 36.4771 22.3729 36.1746 23.1673C36.1293 23.308 36.0757 23.4461 36.0187 23.5826C36.0187 23.5868 36.0187 23.591 36.0187 23.5961C35.9911 23.6946 35.9207 23.8067 35.8846 23.901C35.5536 24.5497 35.2344 25.1697 34.8439 25.7838C34.8388 25.7863 34.8346 25.7914 34.8296 25.7931C34.6528 26.0525 34.4718 26.2901 34.2866 26.4965C34.2774 26.5099 34.2682 26.5234 34.2589 26.5369C34.2405 26.5638 34.2179 26.5815 34.1944 26.595C33.5064 27.3212 32.7665 27.6893 32.0123 27.6893C31.8606 27.6893 31.6838 27.664 31.507 27.4349C31.3042 27.1299 30.85 26.0879 31.2539 22.9112C31.4785 21.3949 31.8011 20.0268 31.931 19.5408C31.9579 19.4413 31.8908 19.3419 31.7886 19.3293L30.0062 19.1162C29.9241 19.1061 29.8478 19.1566 29.8252 19.2366C29.2704 21.1615 27.0305 27.6885 24.9599 27.6885C24.328 27.6885 24.1512 26.6211 24.1001 26.2909C23.775 23.6264 25.528 18.5492 29.5302 16.2267C29.6048 16.1838 29.635 16.0928 29.5998 16.0136L28.9042 14.467C28.8632 14.3752 28.7501 14.3389 28.6638 14.3886C25.8079 16.0414 24.1847 18.5231 23.2915 20.3436C22.2046 22.5793 21.6993 25.0189 21.9515 26.8738C22.1795 28.7794 23.1398 29.872 24.6054 29.872C26.0543 29.872 27.4369 28.9066 28.7098 27.0187C28.7953 26.8915 28.9889 26.9311 29.0149 27.0819C29.2646 28.5283 29.9811 29.872 31.6579 29.872C33.5282 29.872 35.3232 28.7288 36.7134 26.6447C36.7712 26.5874 36.7972 26.5411 36.8181 26.4906C36.8232 26.4931 36.8282 26.4948 36.8332 26.4973C37.01 26.2025 37.181 25.9051 37.3444 25.6027C37.4508 25.4005 37.5572 25.1992 37.6586 24.9945C37.7181 24.874 37.7743 24.7527 37.8262 24.6289C37.838 24.6002 37.8547 24.5665 37.8706 24.5337", fill: "white" }),
19075
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("path", { d: "M30.5839 31.6397C25.7546 34.8577 19.4773 34.7853 14.6907 31.5243C13.5368 30.7384 12.5044 32.6498 13.6474 33.4281C19.034 37.0985 26.3077 37.096 31.7218 33.488C32.8791 32.7172 31.7478 30.8639 30.5839 31.6397Z", fill: "white" })
20640
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("g", { mask: "url(#ohw-badge-mark)", children: [
20641
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { d: "M23.8731 48.0497C37.0584 48.0497 47.7472 37.3046 47.7472 24.0497C47.7472 10.7949 37.0584 0.0497208 23.8731 0.0497208C10.6878 0.0497208 -0.000976562 10.7949 -0.000976562 24.0497C-0.000976562 37.3046 10.6878 48.0497 23.8731 48.0497Z", fill: "#0078E5" }),
20642
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { d: "M17.1307 14.7172C13.1687 14.7172 9.38102 18.1154 8.65114 22.34C8.38885 23.8488 8.5598 25.2581 9.06929 26.4451C6.20005 29.1677 1.77216 27.8721 -1.40212 26.1536C-2.73618 25.4317 -3.92695 27.4745 -2.59037 28.1981C1.33389 30.3226 6.86037 31.6621 10.4402 28.4188C11.4718 29.3859 12.867 29.9621 14.4894 29.9621C18.4161 29.9621 22.2038 26.5318 22.9337 22.34C23.6636 18.1162 21.0566 14.7172 17.1298 14.7172H17.1307ZM19.9798 22.34C19.5281 25.0399 17.2689 27.231 14.9754 27.231C12.6466 27.231 11.1877 25.0399 11.6394 22.34C12.1262 19.6401 14.3151 17.4482 16.6438 17.4482C18.9374 17.4482 20.4667 19.6401 19.9798 22.34Z", fill: "white" }),
20643
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { d: "M40.0017 27.0262C39.1797 27.081 38.2721 26.995 37.4668 26.7415C37.3344 26.6993 37.28 26.5401 37.3529 26.4205C37.5959 26.0212 37.8255 25.6152 38.009 25.1889C38.1071 24.9918 38.2018 24.793 38.2897 24.5908C38.3274 24.5041 38.4163 24.451 38.5101 24.4619C38.63 24.4754 38.7054 24.4821 38.8881 24.4821L39.1529 24.4796L39.8283 24.4543C45.9229 24.0492 50.4765 20.4319 54.8466 16.9014C56.0172 15.9554 57.6932 17.6208 56.5116 18.5752C51.7687 22.4065 47.1966 26.5081 40.9319 26.9689", fill: "white" }),
20644
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { d: "M37.9687 24.27C38.4472 23.1319 38.7656 21.9045 38.9609 20.6991C39.5927 16.76 38.5058 14.2193 36.1553 14.2193C34.1835 14.2193 33.1469 17.2427 32.9199 18.8694C32.743 19.9872 32.5914 22.1219 33.6028 23.9524C33.7553 24.259 34.15 24.7712 34.471 25.1259C34.5447 25.2067 34.6746 25.2 34.7349 25.1082C34.9528 24.7788 35.1615 24.4039 35.3584 24.0257C35.5444 23.6677 35.5888 23.6138 35.8587 23.0207C35.8838 22.966 35.8813 22.9002 35.8478 22.8505C35.5888 22.4597 35.2168 21.9787 35.1204 21.4614C34.9184 20.4455 34.9436 19.2257 35.2218 18.1078C35.4413 17.3118 35.7195 16.7844 35.9039 16.5106C35.9466 16.4466 36.0279 16.4129 36.0975 16.4449C36.369 16.5671 36.5827 16.8838 36.7385 17.396C37.0167 18.2089 37.0167 19.3782 36.814 20.6999C36.6991 21.5271 36.4771 22.3729 36.1746 23.1673C36.1293 23.308 36.0757 23.4461 36.0187 23.5826C36.0187 23.5868 36.0187 23.591 36.0187 23.5961C35.9911 23.6946 35.9207 23.8067 35.8846 23.901C35.5536 24.5497 35.2344 25.1697 34.8439 25.7838C34.8388 25.7863 34.8346 25.7914 34.8296 25.7931C34.6528 26.0525 34.4718 26.2901 34.2866 26.4965C34.2774 26.5099 34.2682 26.5234 34.2589 26.5369C34.2405 26.5638 34.2179 26.5815 34.1944 26.595C33.5064 27.3212 32.7665 27.6893 32.0123 27.6893C31.8606 27.6893 31.6838 27.664 31.507 27.4349C31.3042 27.1299 30.85 26.0879 31.2539 22.9112C31.4785 21.3949 31.8011 20.0268 31.931 19.5408C31.9579 19.4413 31.8908 19.3419 31.7886 19.3293L30.0062 19.1162C29.9241 19.1061 29.8478 19.1566 29.8252 19.2366C29.2704 21.1615 27.0305 27.6885 24.9599 27.6885C24.328 27.6885 24.1512 26.6211 24.1001 26.2909C23.775 23.6264 25.528 18.5492 29.5302 16.2267C29.6048 16.1838 29.635 16.0928 29.5998 16.0136L28.9042 14.467C28.8632 14.3752 28.7501 14.3389 28.6638 14.3886C25.8079 16.0414 24.1847 18.5231 23.2915 20.3436C22.2046 22.5793 21.6993 25.0189 21.9515 26.8738C22.1795 28.7794 23.1398 29.872 24.6054 29.872C26.0543 29.872 27.4369 28.9066 28.7098 27.0187C28.7953 26.8915 28.9889 26.9311 29.0149 27.0819C29.2646 28.5283 29.9811 29.872 31.6579 29.872C33.5282 29.872 35.3232 28.7288 36.7134 26.6447C36.7712 26.5874 36.7972 26.5411 36.8181 26.4906C36.8232 26.4931 36.8282 26.4948 36.8332 26.4973C37.01 26.2025 37.181 25.9051 37.3444 25.6027C37.4508 25.4005 37.5572 25.1992 37.6586 24.9945C37.7181 24.874 37.7743 24.7527 37.8262 24.6289C37.838 24.6002 37.8547 24.5665 37.8706 24.5337", fill: "white" }),
20645
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { d: "M30.5839 31.6397C25.7546 34.8577 19.4773 34.7853 14.6907 31.5243C13.5368 30.7384 12.5044 32.6498 13.6474 33.4281C19.034 37.0985 26.3077 37.096 31.7218 33.488C32.8791 32.7172 31.7478 30.8639 30.5839 31.6397Z", fill: "white" })
19076
20646
  ] })
19077
20647
  ]
19078
20648
  }
@@ -19109,7 +20679,7 @@ var OHW_BADGE_LABEL_STYLE = {
19109
20679
  whiteSpace: "nowrap"
19110
20680
  };
19111
20681
  function MadeWithOhhWells() {
19112
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
20682
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
19113
20683
  "a",
19114
20684
  {
19115
20685
  href: "https://ohhwells.com",
@@ -19119,8 +20689,8 @@ function MadeWithOhhWells() {
19119
20689
  "data-ohw-badge": "",
19120
20690
  style: OHW_BADGE_STYLE,
19121
20691
  children: [
19122
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(OhwBrandMark, {}),
19123
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { style: OHW_BADGE_LABEL_STYLE, children: "Made with OhhWells" })
20692
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(OhwBrandMark, {}),
20693
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { style: OHW_BADGE_LABEL_STYLE, children: "Made with OhhWells" })
19124
20694
  ]
19125
20695
  }
19126
20696
  );
@@ -19610,7 +21180,15 @@ function OhhwellsBridge() {
19610
21180
  const [floatingPanel, setFloatingPanel] = (0, import_react19.useState)(null);
19611
21181
  const floatingPanelOpenRef = (0, import_react19.useRef)(false);
19612
21182
  floatingPanelOpenRef.current = floatingPanel !== null;
21183
+ const floatingPanelRef = (0, import_react19.useRef)(null);
21184
+ floatingPanelRef.current = floatingPanel;
19613
21185
  const [floatingPanelPos, setFloatingPanelPos] = (0, import_react19.useState)(null);
21186
+ const buttonCollectionDragRef = (0, import_react19.useRef)(null);
21187
+ const buttonDropInsertIndexRef = (0, import_react19.useRef)(null);
21188
+ const [buttonDropSlots, setButtonDropSlots] = (0, import_react19.useState)([]);
21189
+ const [activeButtonDropIndex, setActiveButtonDropIndex] = (0, import_react19.useState)(null);
21190
+ const buttonPointerDragRef = (0, import_react19.useRef)(null);
21191
+ const [buttonStyleTick, setButtonStyleTick] = (0, import_react19.useState)(0);
19614
21192
  const [logoSizeDraft, setLogoSizeDraft] = (0, import_react19.useState)(null);
19615
21193
  const [editorViewport, setEditorViewport] = (0, import_react19.useState)("desktop");
19616
21194
  const [parentScrollSnap, setParentScrollSnap] = (0, import_react19.useState)(null);
@@ -19911,6 +21489,7 @@ function OhhwellsBridge() {
19911
21489
  hoveredItemElRef.current = null;
19912
21490
  setHoveredItemRect(null);
19913
21491
  setFloatingPanel(null);
21492
+ setFloatingPanelPos(null);
19914
21493
  setLogoSizeDraft(null);
19915
21494
  if (!activeElRef.current) {
19916
21495
  if (nestedGroup?.isConnected) {
@@ -20511,6 +22090,47 @@ function OhhwellsBridge() {
20511
22090
  },
20512
22091
  [beginFooterDrag]
20513
22092
  );
22093
+ const persistButtonCollectionDrag = (0, import_react19.useCallback)(
22094
+ (button, clientX) => {
22095
+ const insertIndex = buttonDropInsertIndexRef.current;
22096
+ if (insertIndex != null) insertButtonAtCollectionIndex(button, insertIndex);
22097
+ else if (typeof clientX === "number" && clientX !== 0) reorderButtonInCollection(button, clientX);
22098
+ buttonDropInsertIndexRef.current = null;
22099
+ const collection = buttonCollectionOf(button);
22100
+ if (collection) {
22101
+ const persisted = persistCollectionOrder(collection, editContentRef.current);
22102
+ if (persisted) {
22103
+ editContentRef.current = persisted.content;
22104
+ postToParent2({ type: "ow:change", nodes: persisted.nodes });
22105
+ }
22106
+ }
22107
+ buttonCollectionDragRef.current = null;
22108
+ buttonPointerDragRef.current = null;
22109
+ setButtonDropSlots([]);
22110
+ setActiveButtonDropIndex(null);
22111
+ setIsItemDragging(false);
22112
+ unlockFooterDragInteraction();
22113
+ if (button.isConnected) setToolbarRect(button.getBoundingClientRect());
22114
+ },
22115
+ [postToParent2]
22116
+ );
22117
+ const refreshButtonDragVisuals = (0, import_react19.useCallback)((button, clientX, clientY) => {
22118
+ const collection = buttonCollectionOf(button);
22119
+ if (!collection) {
22120
+ setButtonDropSlots([]);
22121
+ setActiveButtonDropIndex(null);
22122
+ buttonDropInsertIndexRef.current = null;
22123
+ return;
22124
+ }
22125
+ const slots = buildButtonCollectionDropSlots(collection);
22126
+ const active = hitTestButtonCollectionDropSlot(collection, clientX, clientY);
22127
+ setButtonDropSlots(slots);
22128
+ const idx = active ? slots.findIndex((slot) => slot.insertIndex === active.insertIndex) : -1;
22129
+ setActiveButtonDropIndex(idx >= 0 ? idx : null);
22130
+ buttonDropInsertIndexRef.current = active?.insertIndex ?? null;
22131
+ setDraggedItemRect(button.getBoundingClientRect());
22132
+ if (selectedElRef.current === button) setToolbarRect(button.getBoundingClientRect());
22133
+ }, []);
20514
22134
  const handleItemDragStart = (0, import_react19.useCallback)(
20515
22135
  (e) => {
20516
22136
  const selected = selectedElRef.current;
@@ -20520,6 +22140,17 @@ function OhhwellsBridge() {
20520
22140
  }
20521
22141
  const clientX = e?.clientX ?? selected.getBoundingClientRect().left + 8;
20522
22142
  const clientY = e?.clientY ?? selected.getBoundingClientRect().top + 8;
22143
+ if (isCtaButton(selected) && collectionDragEnabled(selected)) {
22144
+ buttonCollectionDragRef.current = selected;
22145
+ setMediaHover(null);
22146
+ if (e?.dataTransfer) {
22147
+ e.dataTransfer.effectAllowed = "move";
22148
+ e.dataTransfer.setData("text/plain", buttonId(selected) ?? "button");
22149
+ }
22150
+ setIsItemDragging(true);
22151
+ refreshButtonDragVisuals(selected, clientX, clientY);
22152
+ return;
22153
+ }
20523
22154
  if (startFooterLinkDrag(selected, clientX, clientY, true)) return;
20524
22155
  if (selected.hasAttribute("data-ohw-footer-col") || isInferredFooterGroup2(selected) && selected.closest("footer")) {
20525
22156
  if (startFooterColumnDrag(selected, clientX, clientY, true)) return;
@@ -20529,10 +22160,15 @@ function OhhwellsBridge() {
20529
22160
  setSiblingHintRect(null);
20530
22161
  setIsItemDragging(true);
20531
22162
  },
20532
- [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag]
22163
+ [startFooterColumnDrag, startFooterLinkDrag, startNavLinkDrag, refreshButtonDragVisuals]
20533
22164
  );
20534
22165
  const handleItemDragEnd = (0, import_react19.useCallback)(
20535
22166
  (e) => {
22167
+ const collectionButton = buttonCollectionDragRef.current;
22168
+ if (collectionButton) {
22169
+ persistButtonCollectionDrag(collectionButton, e?.clientX);
22170
+ return;
22171
+ }
20536
22172
  if (footerDragRef.current) {
20537
22173
  const x = e?.clientX;
20538
22174
  const y = e?.clientY;
@@ -20555,12 +22191,22 @@ function OhhwellsBridge() {
20555
22191
  }
20556
22192
  setIsItemDragging(false);
20557
22193
  },
20558
- [commitFooterDrag, commitNavDrag, navDragRef]
22194
+ [commitFooterDrag, commitNavDrag, navDragRef, persistButtonCollectionDrag]
20559
22195
  );
20560
22196
  const handleItemChromePointerDown = (0, import_react19.useCallback)((e) => {
20561
22197
  if (e.button !== 0) return;
20562
22198
  const selected = selectedElRef.current;
20563
22199
  if (!selected) return;
22200
+ if (isCtaButton(selected) && collectionDragEnabled(selected)) {
22201
+ buttonPointerDragRef.current = {
22202
+ el: selected,
22203
+ startX: e.clientX,
22204
+ startY: e.clientY,
22205
+ pointerId: e.pointerId,
22206
+ started: false
22207
+ };
22208
+ return;
22209
+ }
20564
22210
  const hrefKey = selected.getAttribute("data-ohw-href-key");
20565
22211
  if (hrefKey && isFooterHrefKey(hrefKey)) {
20566
22212
  footerPointerDragRef.current = {
@@ -20639,10 +22285,29 @@ function OhhwellsBridge() {
20639
22285
  setIsFooterFrameSelection(false);
20640
22286
  setToolbarVariant("link-action");
20641
22287
  setToolbarRect(anchor.getBoundingClientRect());
22288
+ if (glowElRef.current) glowElRef.current.style.display = "";
20642
22289
  setToolbarShowEditLink(false);
20643
22290
  setActiveCommands(/* @__PURE__ */ new Set());
20644
- setFloatingPanel(null);
20645
- setLogoSizeDraft(null);
22291
+ const panel = floatingPanelRef.current;
22292
+ if (panel?.kind === "button-style" && isCtaButton(anchor)) {
22293
+ const id = buttonId(anchor);
22294
+ if (id && panel.buttonId !== id) {
22295
+ captureButtonOrigin(anchor);
22296
+ setFloatingPanelPos(null);
22297
+ setParentScrollSnap(parentScrollRef.current);
22298
+ setFloatingPanel({
22299
+ ...panel,
22300
+ key: `button-style:${id}`,
22301
+ buttonId: id,
22302
+ context: buttonStyleContext(anchor)
22303
+ });
22304
+ setButtonStyleTick((n) => n + 1);
22305
+ }
22306
+ } else {
22307
+ setFloatingPanel(null);
22308
+ setFloatingPanelPos(null);
22309
+ setLogoSizeDraft(null);
22310
+ }
20646
22311
  }, [deactivate, markSelected]);
20647
22312
  const selectFrame = (0, import_react19.useCallback)((el) => {
20648
22313
  if (!isNavigationContainer(el)) return;
@@ -20682,6 +22347,7 @@ function OhhwellsBridge() {
20682
22347
  setToolbarShowEditLink(false);
20683
22348
  setActiveCommands(/* @__PURE__ */ new Set());
20684
22349
  setFloatingPanel(null);
22350
+ setFloatingPanelPos(null);
20685
22351
  setLogoSizeDraft(null);
20686
22352
  }, [deactivate, markSelected, postToParent2]);
20687
22353
  const selectLogo = (0, import_react19.useCallback)(
@@ -20727,6 +22393,7 @@ function OhhwellsBridge() {
20727
22393
  });
20728
22394
  }, []);
20729
22395
  const openSocialsDisplayPanel = (0, import_react19.useCallback)((row) => {
22396
+ setFloatingPanelPos(null);
20730
22397
  setParentScrollSnap(parentScrollRef.current);
20731
22398
  setFloatingPanel({
20732
22399
  key: "socials-display",
@@ -20736,6 +22403,87 @@ function OhhwellsBridge() {
20736
22403
  row
20737
22404
  });
20738
22405
  }, []);
22406
+ const rememberButtonTokens = (0, import_react19.useCallback)(
22407
+ (content, tokens, nodes) => {
22408
+ if (nodes.length === 0) return content;
22409
+ const next = { ...content };
22410
+ for (const node of nodes) next[node.key] = node.text;
22411
+ editContentRef.current = next;
22412
+ postToParent2({ type: "ow:change", nodes });
22413
+ return next;
22414
+ },
22415
+ [postToParent2]
22416
+ );
22417
+ const tokensForButton = (0, import_react19.useCallback)(
22418
+ (el, persistFallback = false) => {
22419
+ const stored = readStoredStyleTokens(editContentRef.current);
22420
+ if (stored) return stored;
22421
+ const ensured = ensureButtonStyleTokens(document, editContentRef.current);
22422
+ if (ensured.tokens) {
22423
+ rememberButtonTokens(editContentRef.current, ensured.tokens, ensured.nodes);
22424
+ return ensured.tokens;
22425
+ }
22426
+ const fallback = tokensFromButton(el);
22427
+ if (persistFallback) rememberButtonTokens(editContentRef.current, fallback, styleTokenNodes(fallback));
22428
+ return fallback;
22429
+ },
22430
+ [rememberButtonTokens]
22431
+ );
22432
+ const openButtonStylePanel = (0, import_react19.useCallback)(
22433
+ (anchor) => {
22434
+ const id = buttonId(anchor);
22435
+ if (!id) return;
22436
+ captureButtonOrigin(anchor);
22437
+ tokensForButton(anchor);
22438
+ setFloatingPanelPos(null);
22439
+ setParentScrollSnap(parentScrollRef.current);
22440
+ setFloatingPanel({
22441
+ key: `button-style:${id}`,
22442
+ title: "Style",
22443
+ context: buttonStyleContext(anchor),
22444
+ kind: "button-style",
22445
+ buttonId: id
22446
+ });
22447
+ },
22448
+ [tokensForButton]
22449
+ );
22450
+ const applySelectedButtonStyle = (0, import_react19.useCallback)(
22451
+ (patch) => {
22452
+ const panel = floatingPanelRef.current;
22453
+ const id = panel?.kind === "button-style" ? panel.buttonId : null;
22454
+ const el = id ? document.querySelector(`[data-ohw-href-key="${CSS.escape(id)}"]`) : null;
22455
+ if (!el || !id || !isCtaButton(el)) return;
22456
+ const tokens = tokensFromButton(el);
22457
+ const next = { ...assignmentFor(el, editContentRef.current, tokens), ...patch };
22458
+ const assignments = withAssignment(editContentRef.current, id, next);
22459
+ const text = JSON.stringify(assignments);
22460
+ editContentRef.current = { ...editContentRef.current, [BUTTON_ASSIGNMENTS_KEY]: text };
22461
+ applyButtonStyle(el, next, tokens);
22462
+ postToParent2({ type: "ow:change", nodes: [{ key: BUTTON_ASSIGNMENTS_KEY, text }] });
22463
+ setButtonStyleTick((n) => n + 1);
22464
+ if (selectedElRef.current === el) setToolbarRect(el.getBoundingClientRect());
22465
+ },
22466
+ [postToParent2]
22467
+ );
22468
+ (0, import_react19.useEffect)(() => {
22469
+ const onDragOver = (e) => {
22470
+ const dragged = buttonCollectionDragRef.current;
22471
+ if (!dragged) return;
22472
+ e.preventDefault();
22473
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
22474
+ refreshButtonDragVisuals(dragged, e.clientX, e.clientY);
22475
+ };
22476
+ const onDrop = (e) => {
22477
+ if (!buttonCollectionDragRef.current) return;
22478
+ e.preventDefault();
22479
+ };
22480
+ document.addEventListener("dragover", onDragOver);
22481
+ document.addEventListener("drop", onDrop);
22482
+ return () => {
22483
+ document.removeEventListener("dragover", onDragOver);
22484
+ document.removeEventListener("drop", onDrop);
22485
+ };
22486
+ }, [refreshButtonDragVisuals]);
20739
22487
  const isEditModeRef = (0, import_react19.useRef)(false);
20740
22488
  const requestMissingSocialIconsRef = (0, import_react19.useRef)(() => {
20741
22489
  });
@@ -20778,11 +22526,13 @@ function OhhwellsBridge() {
20778
22526
  );
20779
22527
  const closeFloatingPanelOnly = (0, import_react19.useCallback)(() => {
20780
22528
  setFloatingPanel(null);
22529
+ setFloatingPanelPos(null);
20781
22530
  setLogoSizeDraft(null);
20782
22531
  }, []);
20783
22532
  closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
20784
22533
  const closeFloatingPanelAndDeselect = (0, import_react19.useCallback)(() => {
20785
22534
  setFloatingPanel(null);
22535
+ setFloatingPanelPos(null);
20786
22536
  setLogoSizeDraft(null);
20787
22537
  deselectRef.current();
20788
22538
  }, []);
@@ -20994,6 +22744,7 @@ function OhhwellsBridge() {
20994
22744
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
20995
22745
  }
20996
22746
  applyBrandChrome(content);
22747
+ applyCustomCodeToHead(content);
20997
22748
  initSectionInstancesFromContent(content, window.location.pathname);
20998
22749
  for (const [key, val] of Object.entries(content)) {
20999
22750
  if (key === "__ohw_sections") continue;
@@ -21002,6 +22753,7 @@ function OhhwellsBridge() {
21002
22753
  if (key === STYLE_STORE_KEY) continue;
21003
22754
  if (BRAND_CHROME_KEYS.has(key)) continue;
21004
22755
  if (key === LOGO_PLACEHOLDER_KEY) continue;
22756
+ if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
21005
22757
  if (LOGO_IMAGE_KEYS.includes(key)) continue;
21006
22758
  if (applyVideoSettingNode(key, val)) continue;
21007
22759
  if (applyCarouselNode(key, val)) continue;
@@ -21049,6 +22801,7 @@ function OhhwellsBridge() {
21049
22801
  reconcileSocialsFromContent(content);
21050
22802
  applySocialsDisplayFromContent(content);
21051
22803
  applySocialsLabelsFromContent(content, document, { fillEmptyOnly: isEditModeRef.current });
22804
+ syncButtonsFromContent(document, content);
21052
22805
  if (isEditModeRef.current) requestMissingSocialIconsRef.current();
21053
22806
  if (typeof content[STYLE_STORE_KEY] === "string") {
21054
22807
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
@@ -21249,6 +23002,7 @@ function OhhwellsBridge() {
21249
23002
  reconcileSocialsFromContent(content);
21250
23003
  applySocialsDisplayFromContent(content);
21251
23004
  applySocialsLabelsFromContent(content, document, { fillEmptyOnly: isEditModeRef.current });
23005
+ syncButtonsFromContent(document, content);
21252
23006
  if (isEditModeRef.current) requestMissingSocialIconsRef.current();
21253
23007
  if (typeof content[STYLE_STORE_KEY] === "string") {
21254
23008
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
@@ -21337,7 +23091,10 @@ function OhhwellsBridge() {
21337
23091
  if (roots.length === 0) {
21338
23092
  reconcileNavbarItemsFromContent(contentForNav());
21339
23093
  reconcileFooterOrderFromContent(contentForNav());
21340
- if (isEditMode) syncNavigationDragCursorAttrs();
23094
+ if (isEditMode) {
23095
+ syncButtonsFromContent(document, contentForNav());
23096
+ syncNavigationDragCursorAttrs();
23097
+ }
21341
23098
  return;
21342
23099
  }
21343
23100
  let rafId = null;
@@ -21349,7 +23106,7 @@ function OhhwellsBridge() {
21349
23106
  }
21350
23107
  };
21351
23108
  const run = () => {
21352
- if (footerDragRef.current || navDragRef.current || applying) return;
23109
+ if (footerDragRef.current || navDragRef.current || buttonCollectionDragRef.current || applying) return;
21353
23110
  applying = true;
21354
23111
  observer?.disconnect();
21355
23112
  try {
@@ -21359,6 +23116,7 @@ function OhhwellsBridge() {
21359
23116
  reconcileSocialsFromContent(content);
21360
23117
  applySocialsDisplayFromContent(content);
21361
23118
  applySocialsLabelsFromContent(content, document, { fillEmptyOnly: isEditModeRef.current });
23119
+ syncButtonsFromContent(document, content);
21362
23120
  if (isEditModeRef.current) requestMissingSocialIconsRef.current();
21363
23121
  document.querySelectorAll("footer [data-ohw-href-key]").forEach((el) => {
21364
23122
  if (isFooterHrefKey(el.getAttribute("data-ohw-href-key")) || getSocialItem(el)) {
@@ -21366,9 +23124,9 @@ function OhhwellsBridge() {
21366
23124
  }
21367
23125
  });
21368
23126
  document.querySelectorAll(
21369
- "nav [data-ohw-href-key], [data-ohw-nav-container] [data-ohw-href-key], [data-ohw-nav-drawer] [data-ohw-href-key]"
23127
+ '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"]'
21370
23128
  ).forEach((el) => {
21371
- if (isNavbarHrefKey(el.getAttribute("data-ohw-href-key"))) {
23129
+ if (isNavbarHrefKey(el.getAttribute("data-ohw-href-key")) || isCtaButton(el)) {
21372
23130
  disableNativeHrefDrag(el);
21373
23131
  }
21374
23132
  });
@@ -21393,6 +23151,44 @@ function OhhwellsBridge() {
21393
23151
  observer?.disconnect();
21394
23152
  };
21395
23153
  }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
23154
+ (0, import_react19.useEffect)(() => {
23155
+ if (!isEditMode) return;
23156
+ let applying = false;
23157
+ let rafId = null;
23158
+ const run = () => {
23159
+ if (applying || buttonCollectionDragRef.current) return;
23160
+ const content = editContentRef.current;
23161
+ if (!content[BUTTON_COLLECTIONS_KEY] && !content[BUTTON_SNAPSHOTS_KEY]) return;
23162
+ applying = true;
23163
+ try {
23164
+ syncButtonsFromContent(document, content);
23165
+ } finally {
23166
+ applying = false;
23167
+ }
23168
+ };
23169
+ const schedule = () => {
23170
+ if (rafId != null) return;
23171
+ rafId = requestAnimationFrame(() => {
23172
+ rafId = null;
23173
+ run();
23174
+ });
23175
+ };
23176
+ const observer = new MutationObserver((mutations) => {
23177
+ if (mutations.every((m) => {
23178
+ const t = m.target;
23179
+ return t instanceof Element && Boolean(t.closest("[data-ohw-bridge], [data-ohw-bridge-root], [data-ohw-button-collection]"));
23180
+ })) {
23181
+ return;
23182
+ }
23183
+ schedule();
23184
+ });
23185
+ observer.observe(document.body, { childList: true, subtree: true });
23186
+ run();
23187
+ return () => {
23188
+ if (rafId != null) cancelAnimationFrame(rafId);
23189
+ observer.disconnect();
23190
+ };
23191
+ }, [isEditMode, pathname]);
21396
23192
  (0, import_react19.useEffect)(() => {
21397
23193
  if (!isEditMode) return;
21398
23194
  let lastPosted = 0;
@@ -21919,14 +23715,6 @@ function OhhwellsBridge() {
21919
23715
  }
21920
23716
  const clickedButton = findClosestButtonLike(target);
21921
23717
  const buttonOnMedia = Boolean(clickedButton && isMediaEditable(editable) && editable.contains(clickedButton));
21922
- console.log("[click-debug]", {
21923
- editableType: editable.dataset.ohwEditable,
21924
- editableTag: editable.tagName,
21925
- targetTag: target.tagName,
21926
- clickedButtonTag: clickedButton?.tagName ?? null,
21927
- buttonOnMedia,
21928
- isMediaEditableEditable: isMediaEditable(editable)
21929
- });
21930
23718
  if (isMediaEditable(editable) && !buttonOnMedia) {
21931
23719
  e.preventDefault();
21932
23720
  e.stopPropagation();
@@ -21961,11 +23749,6 @@ function OhhwellsBridge() {
21961
23749
  const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
21962
23750
  const hrefCtx = getHrefKeyFromElement(hrefLookupTarget);
21963
23751
  const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
21964
- console.log("[click-debug 2]", {
21965
- hrefLookupTargetTag: hrefLookupTarget.tagName,
21966
- hrefCtx: hrefCtx ? { key: hrefCtx.key } : null,
21967
- navAnchorTag: navAnchor?.tagName ?? null
21968
- });
21969
23752
  if (navAnchor) {
21970
23753
  e.preventDefault();
21971
23754
  e.stopPropagation();
@@ -22481,6 +24264,20 @@ function OhhwellsBridge() {
22481
24264
  }
22482
24265
  return;
22483
24266
  }
24267
+ const navbarCtaHit = findNavbarCtaAtPoint(x, y);
24268
+ if (navbarCtaHit) {
24269
+ hoveredNavContainerRef.current = null;
24270
+ setHoveredNavContainerRect(null);
24271
+ if (selected !== navbarCtaHit && !selected?.contains(navbarCtaHit)) {
24272
+ clearHrefKeyHover(navbarCtaHit);
24273
+ hoveredItemElRef.current = navbarCtaHit;
24274
+ setHoveredItemRect(navbarCtaHit.getBoundingClientRect());
24275
+ } else if (hoveredItemElRef.current === navbarCtaHit) {
24276
+ hoveredItemElRef.current = null;
24277
+ setHoveredItemRect(null);
24278
+ }
24279
+ return;
24280
+ }
22484
24281
  const navContainers = Array.from(
22485
24282
  document.querySelectorAll("[data-ohw-nav-container]")
22486
24283
  );
@@ -22583,6 +24380,24 @@ function OhhwellsBridge() {
22583
24380
  return;
22584
24381
  }
22585
24382
  const { x, y } = toProbeCoords(clientX, clientY, fromParentViewport);
24383
+ if (!isDragOver) {
24384
+ const buttonHit = findButtonAtPoint(x, y);
24385
+ if (buttonHit) {
24386
+ dismissImageHover();
24387
+ if (findNavbarCtaAtPoint(x, y)) {
24388
+ probeNavigationHoverAt(x, y);
24389
+ return;
24390
+ }
24391
+ document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
24392
+ const selected = selectedElRef.current;
24393
+ if (selected !== buttonHit && !selected?.contains(buttonHit)) {
24394
+ clearHrefKeyHover(buttonHit);
24395
+ hoveredItemElRef.current = buttonHit;
24396
+ setHoveredItemRect(buttonHit.getBoundingClientRect());
24397
+ }
24398
+ return;
24399
+ }
24400
+ }
22586
24401
  if (isPointOverNavigation(x, y)) {
22587
24402
  dismissImageHover();
22588
24403
  probeNavigationHoverAt(x, y);
@@ -23004,6 +24819,11 @@ function OhhwellsBridge() {
23004
24819
  return;
23005
24820
  }
23006
24821
  if (onNavDragOver(e)) return;
24822
+ if (buttonCollectionDragRef.current) {
24823
+ e.preventDefault();
24824
+ if (e.dataTransfer) e.dataTransfer.dropEffect = "move";
24825
+ return;
24826
+ }
23007
24827
  e.preventDefault();
23008
24828
  const el = findImageAtPoint(e.clientX, e.clientY, false);
23009
24829
  if (!el) {
@@ -23028,6 +24848,7 @@ function OhhwellsBridge() {
23028
24848
  };
23029
24849
  const handleDrop = (e) => {
23030
24850
  e.preventDefault();
24851
+ if (buttonCollectionDragRef.current || navDragRef.current || footerDragRef.current) return;
23031
24852
  const el = findImageAtPoint(e.clientX, e.clientY, false);
23032
24853
  if (!el) return;
23033
24854
  const file = e.dataTransfer?.files?.[0];
@@ -23330,6 +25151,7 @@ function OhhwellsBridge() {
23330
25151
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
23331
25152
  }
23332
25153
  applyBrandChrome(content);
25154
+ applyCustomCodeToHead(content);
23333
25155
  initSectionInstancesFromContent(content, window.location.pathname);
23334
25156
  let sectionsJson = null;
23335
25157
  for (const [key, val] of Object.entries(content)) {
@@ -23342,6 +25164,7 @@ function OhhwellsBridge() {
23342
25164
  if (key === STYLE_STORE_KEY) continue;
23343
25165
  if (BRAND_CHROME_KEYS.has(key)) continue;
23344
25166
  if (key === LOGO_PLACEHOLDER_KEY) continue;
25167
+ if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
23345
25168
  if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
23346
25169
  if (applyVideoSettingNode(key, val)) continue;
23347
25170
  if (applyCarouselNode(key, val)) continue;
@@ -23381,6 +25204,7 @@ function OhhwellsBridge() {
23381
25204
  editContentRef.current = { ...editContentRef.current, ...content };
23382
25205
  reconcileNavbarItemsFromContent(editContentRef.current);
23383
25206
  reconcileFooterOrderFromContent(editContentRef.current);
25207
+ syncButtonsFromContent(document, editContentRef.current);
23384
25208
  syncNavigationDragCursorAttrs();
23385
25209
  if (typeof content[STYLE_STORE_KEY] === "string") {
23386
25210
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
@@ -24053,6 +25877,16 @@ function OhhwellsBridge() {
24053
25877
  if (brandKitRef.current) {
24054
25878
  nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
24055
25879
  }
25880
+ nodes.push({
25881
+ key: CUSTOM_CODE_BEFORE_HEAD_KEY,
25882
+ type: "text",
25883
+ text: editContentRef.current[CUSTOM_CODE_BEFORE_HEAD_KEY] ?? ""
25884
+ });
25885
+ nodes.push({
25886
+ key: CUSTOM_CODE_AFTER_HEAD_KEY,
25887
+ type: "text",
25888
+ text: editContentRef.current[CUSTOM_CODE_AFTER_HEAD_KEY] ?? ""
25889
+ });
24056
25890
  document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
24057
25891
  const formKey = formKeyOf(form);
24058
25892
  if (!formKey) return;
@@ -24280,11 +26114,8 @@ function OhhwellsBridge() {
24280
26114
  };
24281
26115
  const handleParentScroll = (e) => {
24282
26116
  if (e.data?.type !== "ow:parent-scroll") return;
24283
- const { iframeOffsetTop, headerH, canvasH } = e.data;
24284
- parentScrollRef.current = { iframeOffsetTop, headerH, canvasH };
24285
- if (floatingPanelOpenRef.current) {
24286
- setParentScrollSnap({ iframeOffsetTop, headerH, canvasH });
24287
- }
26117
+ const { iframeOffsetTop, headerH, canvasH, promptBarH = 0 } = e.data;
26118
+ parentScrollRef.current = { iframeOffsetTop, headerH, canvasH, promptBarH };
24288
26119
  if (visibleViewportRef.current) {
24289
26120
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
24290
26121
  }
@@ -24674,6 +26505,97 @@ function OhhwellsBridge() {
24674
26505
  unlockFooterDragInteraction();
24675
26506
  };
24676
26507
  }, [isEditMode]);
26508
+ (0, import_react19.useEffect)(() => {
26509
+ if (!isEditMode) return;
26510
+ const THRESHOLD = 10;
26511
+ const onPointerDown = (e) => {
26512
+ if (e.button !== 0) return;
26513
+ if (buttonCollectionDragRef.current || navDragRef.current || footerDragRef.current) return;
26514
+ if (buttonPointerDragRef.current) return;
26515
+ const target = e.target;
26516
+ if (!target) return;
26517
+ if (target.closest(
26518
+ '[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]'
26519
+ )) {
26520
+ return;
26521
+ }
26522
+ const fromTarget = target.closest('[data-ohw-role="navbar-button"]');
26523
+ const anchor = (fromTarget && !fromTarget.closest("[data-ohw-bridge]") ? fromTarget : null) ?? findNavbarCtaAtPoint(e.clientX, e.clientY);
26524
+ if (!anchor || !collectionDragEnabled(anchor)) return;
26525
+ disableNativeHrefDrag(anchor);
26526
+ setMediaHover(null);
26527
+ buttonPointerDragRef.current = {
26528
+ el: anchor,
26529
+ startX: e.clientX,
26530
+ startY: e.clientY,
26531
+ pointerId: e.pointerId,
26532
+ started: false
26533
+ };
26534
+ };
26535
+ const onPointerMove = (e) => {
26536
+ const pending = buttonPointerDragRef.current;
26537
+ if (!pending) return;
26538
+ if (pending.started) {
26539
+ e.preventDefault();
26540
+ clearTextSelection();
26541
+ const dragged = buttonCollectionDragRef.current;
26542
+ if (!dragged) return;
26543
+ refreshButtonDragVisuals(dragged, e.clientX, e.clientY);
26544
+ return;
26545
+ }
26546
+ const dx = e.clientX - pending.startX;
26547
+ const dy = e.clientY - pending.startY;
26548
+ if (dx * dx + dy * dy < THRESHOLD * THRESHOLD) return;
26549
+ e.preventDefault();
26550
+ pending.started = true;
26551
+ armFooterPressDrag();
26552
+ clearTextSelection();
26553
+ try {
26554
+ document.body.setPointerCapture(pending.pointerId);
26555
+ } catch {
26556
+ }
26557
+ if (linkPopoverOpenRef.current) setLinkPopoverRef.current(null);
26558
+ if (activeElRef.current) deactivateRef.current();
26559
+ ensureNavbarButtonCollection(pending.el, editContentRef.current);
26560
+ buttonCollectionDragRef.current = pending.el;
26561
+ lockFooterDuringDrag();
26562
+ setIsItemDragging(true);
26563
+ refreshButtonDragVisuals(pending.el, e.clientX, e.clientY);
26564
+ };
26565
+ const endPointerDrag = (e) => {
26566
+ const pending = buttonPointerDragRef.current;
26567
+ buttonPointerDragRef.current = null;
26568
+ try {
26569
+ if (document.body.hasPointerCapture(e.pointerId)) {
26570
+ document.body.releasePointerCapture(e.pointerId);
26571
+ }
26572
+ } catch {
26573
+ }
26574
+ if (!pending) return;
26575
+ if (!pending.started) {
26576
+ unlockFooterDragInteraction();
26577
+ return;
26578
+ }
26579
+ suppressNextClickRef.current = true;
26580
+ suppressClickUntilRef.current = Date.now() + 500;
26581
+ persistButtonCollectionDrag(pending.el, e.clientX);
26582
+ };
26583
+ const blockSelectStart = (e) => {
26584
+ if (buttonCollectionDragRef.current || buttonPointerDragRef.current?.started) e.preventDefault();
26585
+ };
26586
+ document.addEventListener("pointerdown", onPointerDown, true);
26587
+ document.addEventListener("pointermove", onPointerMove, true);
26588
+ document.addEventListener("pointerup", endPointerDrag, true);
26589
+ document.addEventListener("pointercancel", endPointerDrag, true);
26590
+ document.addEventListener("selectstart", blockSelectStart, true);
26591
+ return () => {
26592
+ document.removeEventListener("pointerdown", onPointerDown, true);
26593
+ document.removeEventListener("pointermove", onPointerMove, true);
26594
+ document.removeEventListener("pointerup", endPointerDrag, true);
26595
+ document.removeEventListener("pointercancel", endPointerDrag, true);
26596
+ document.removeEventListener("selectstart", blockSelectStart, true);
26597
+ };
26598
+ }, [isEditMode, persistButtonCollectionDrag, refreshButtonDragVisuals]);
24677
26599
  (0, import_react19.useEffect)(() => {
24678
26600
  const handler = (e) => {
24679
26601
  if (e.data?.type !== "ow:request-schedule-config") return;
@@ -24714,7 +26636,7 @@ function OhhwellsBridge() {
24714
26636
  postToParent2({
24715
26637
  type: "ow:ready",
24716
26638
  version: "1",
24717
- bridgeVersion: "0.1.119",
26639
+ bridgeVersion: "0.1.121",
24718
26640
  path: pathname,
24719
26641
  nodes: collectEditableNodes(editContentRef.current),
24720
26642
  sections
@@ -24793,12 +26715,12 @@ function OhhwellsBridge() {
24793
26715
  return;
24794
26716
  }
24795
26717
  if (type !== "ow:logo-size-set") return;
24796
- const px = Number(e.data.px);
24797
- if (!Number.isFinite(px)) return;
24798
- const next = editorViewport === "mobile" ? { ...logoSizeDraft, mobilePx: px, mobileFollowing: false } : {
26718
+ const px2 = Number(e.data.px);
26719
+ if (!Number.isFinite(px2)) return;
26720
+ const next = editorViewport === "mobile" ? { ...logoSizeDraft, mobilePx: px2, mobileFollowing: false } : {
24799
26721
  ...logoSizeDraft,
24800
- desktopPx: px,
24801
- mobilePx: logoSizeDraft.mobileFollowing ? px : logoSizeDraft.mobilePx
26722
+ desktopPx: px2,
26723
+ mobilePx: logoSizeDraft.mobileFollowing ? px2 : logoSizeDraft.mobilePx
24802
26724
  };
24803
26725
  setLogoSizeDraft(next);
24804
26726
  persistLogoSizeDraft(session.placement, next);
@@ -24896,6 +26818,36 @@ function OhhwellsBridge() {
24896
26818
  const handleDuplicateSelected = (0, import_react19.useCallback)(() => {
24897
26819
  const selected = selectedElRef.current;
24898
26820
  if (!selected || !isNavigationItem2(selected)) return;
26821
+ if (isCtaButton(selected)) {
26822
+ const id = buttonId(selected);
26823
+ if (!id) return;
26824
+ const tokens = tokensForButton(selected, true);
26825
+ if (!readAssignments(editContentRef.current)[id]) {
26826
+ const assignment = assignmentFor(selected, editContentRef.current, tokens);
26827
+ const assignments = withAssignment(editContentRef.current, id, assignment);
26828
+ editContentRef.current = {
26829
+ ...editContentRef.current,
26830
+ [BUTTON_ASSIGNMENTS_KEY]: JSON.stringify(assignments)
26831
+ };
26832
+ applyButtonStyle(selected, assignment, tokens);
26833
+ }
26834
+ const result = duplicateButton(selected, editContentRef.current);
26835
+ if (!result) return;
26836
+ editContentRef.current = result.content;
26837
+ const copyHref = result.copy.getAttribute("data-ohw-href-key");
26838
+ const copyHrefVal = copyHref ? result.content[copyHref] : void 0;
26839
+ if (copyHref && copyHrefVal !== void 0) applyLinkByKey(copyHref, copyHrefVal);
26840
+ postToParent2({ type: "ow:change", nodes: result.nodes });
26841
+ enforceLinkHrefs();
26842
+ requestAnimationFrame(() => {
26843
+ selectRef.current(result.copy);
26844
+ requestAnimationFrame(() => {
26845
+ const el = selectedElRef.current;
26846
+ if (el?.isConnected) setToolbarRect(el.getBoundingClientRect());
26847
+ });
26848
+ });
26849
+ return;
26850
+ }
24899
26851
  const hrefKey = selected.getAttribute("data-ohw-href-key");
24900
26852
  if (!hrefKey) return;
24901
26853
  const social = getSocialItem(selected);
@@ -25025,7 +26977,7 @@ function OhhwellsBridge() {
25025
26977
  selectRef.current(result.anchor);
25026
26978
  });
25027
26979
  }
25028
- }, [postToParent2]);
26980
+ }, [postToParent2, tokensForButton]);
25029
26981
  const runPendingDeleteUndo = (0, import_react19.useCallback)(() => {
25030
26982
  const pending = pendingDeleteUndoRef.current;
25031
26983
  if (!pending) return false;
@@ -25037,6 +26989,38 @@ function OhhwellsBridge() {
25037
26989
  const handleDeleteSelected = (0, import_react19.useCallback)(() => {
25038
26990
  const selected = selectedElRef.current;
25039
26991
  if (!selected) return false;
26992
+ if (isCtaButton(selected)) {
26993
+ if (!canDeleteButton(selected)) return false;
26994
+ const previousContent = editContentRef.current;
26995
+ const result = deleteButton(selected, previousContent);
26996
+ if (!result) return false;
26997
+ editContentRef.current = result.content;
26998
+ postToParent2({ type: "ow:change", nodes: result.nodes });
26999
+ const actionId = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `delete-${Date.now()}`;
27000
+ const restoreNodes = result.nodes.map((node) => ({
27001
+ key: node.key,
27002
+ text: previousContent[node.key] ?? ""
27003
+ }));
27004
+ pendingDeleteUndoRef.current = {
27005
+ actionId,
27006
+ restore: () => {
27007
+ result.undo();
27008
+ editContentRef.current = { ...editContentRef.current, ...Object.fromEntries(restoreNodes.map((node) => [node.key, node.text])) };
27009
+ postToParent2({ type: "ow:change", nodes: restoreNodes });
27010
+ syncButtonsFromContent(document, editContentRef.current);
27011
+ }
27012
+ };
27013
+ deselectRef.current();
27014
+ postToParent2({
27015
+ type: "ow:toast",
27016
+ title: "Button deleted",
27017
+ toastType: "success",
27018
+ actionLabel: "Undo",
27019
+ actionId,
27020
+ duration: 6e3
27021
+ });
27022
+ return true;
27023
+ }
25040
27024
  return deleteSelectedNavFooterItem({
25041
27025
  selected,
25042
27026
  isTextEditing: Boolean(activeElRef.current),
@@ -25174,14 +27158,14 @@ function OhhwellsBridge() {
25174
27158
  },
25175
27159
  [postToParent2]
25176
27160
  );
25177
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
25178
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { id: "ohw-loader", suppressHydrationWarning: true, style: { ...OHW_LOADER_STYLE, display: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(OhwLoaderSpinner, {}) }),
25179
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("script", { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: OHW_LOADER_PREHYDRATE_SCRIPT } }),
25180
- subdomain && !isEditMode && showBranding && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(MadeWithOhhWells, {}),
27161
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
27162
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { id: "ohw-loader", suppressHydrationWarning: true, style: { ...OHW_LOADER_STYLE, display: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(OhwLoaderSpinner, {}) }),
27163
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("script", { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: OHW_LOADER_PREHYDRATE_SCRIPT } }),
27164
+ subdomain && !isEditMode && showBranding && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(MadeWithOhhWells, {}),
25181
27165
  bridgeRoot ? (0, import_react_dom5.createPortal)(
25182
- /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
25183
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
25184
- isEditMode && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27166
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
27167
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
27168
+ isEditMode && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25185
27169
  AiSectionOverlay,
25186
27170
  {
25187
27171
  postToParent: postToParent2,
@@ -25189,7 +27173,7 @@ function OhhwellsBridge() {
25189
27173
  onSelectionChange: handleAiSectionSelectionChange
25190
27174
  }
25191
27175
  ),
25192
- isEditMode && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27176
+ isEditMode && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25193
27177
  AiMediaStripOverlay,
25194
27178
  {
25195
27179
  postToParent: postToParent2,
@@ -25198,7 +27182,7 @@ function OhhwellsBridge() {
25198
27182
  clearInteractionChromeRef
25199
27183
  }
25200
27184
  ),
25201
- isEditMode && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27185
+ isEditMode && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25202
27186
  AiPricingCardOverlay,
25203
27187
  {
25204
27188
  postToParent: postToParent2,
@@ -25206,12 +27190,12 @@ function OhhwellsBridge() {
25206
27190
  applyTreeLocalRef
25207
27191
  }
25208
27192
  ),
25209
- isSectionDragging && sectionDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27193
+ isSectionDragging && sectionDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25210
27194
  "div",
25211
27195
  {
25212
27196
  className: "pointer-events-none fixed z-2147483646",
25213
27197
  style: { left: slot.left, top: slot.y, width: slot.width, height: 3, transform: "translateY(-50%)" },
25214
- children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27198
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25215
27199
  DropIndicator,
25216
27200
  {
25217
27201
  direction: "horizontal",
@@ -25222,7 +27206,7 @@ function OhhwellsBridge() {
25222
27206
  },
25223
27207
  `section-drop-${slot.insertIndex}-${i}`
25224
27208
  )),
25225
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27209
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25226
27210
  MediaOverlay,
25227
27211
  {
25228
27212
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -25233,7 +27217,7 @@ function OhhwellsBridge() {
25233
27217
  },
25234
27218
  `uploading-${key}`
25235
27219
  )),
25236
- mediaHover && !(mediaHover.key in uploadingRects) && mediaHover.key !== selectedMedia?.key && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27220
+ mediaHover && !(mediaHover.key in uploadingRects) && mediaHover.key !== selectedMedia?.key && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25237
27221
  MediaOverlay,
25238
27222
  {
25239
27223
  hover: mediaHover,
@@ -25244,7 +27228,7 @@ function OhhwellsBridge() {
25244
27228
  onVideoSettingsChange: handleVideoSettingsChange
25245
27229
  }
25246
27230
  ),
25247
- selectedMedia && !(selectedMedia.key in uploadingRects) && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27231
+ selectedMedia && !(selectedMedia.key in uploadingRects) && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25248
27232
  MediaOverlay,
25249
27233
  {
25250
27234
  hover: selectedMedia,
@@ -25256,7 +27240,7 @@ function OhhwellsBridge() {
25256
27240
  onVideoSettingsChange: handleVideoSettingsChange
25257
27241
  }
25258
27242
  ),
25259
- carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27243
+ carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25260
27244
  CarouselOverlay,
25261
27245
  {
25262
27246
  hover: carouselHover,
@@ -25264,10 +27248,10 @@ function OhhwellsBridge() {
25264
27248
  cueOnly: !!carouselHover.fullBleedSectionId && carouselHover.fullBleedSectionId !== selectedAiSectionId
25265
27249
  }
25266
27250
  ),
25267
- siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
25268
- siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
25269
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
25270
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27251
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
27252
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
27253
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && selectedElRef.current !== buttonCollectionDragRef.current && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
27254
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25271
27255
  "div",
25272
27256
  {
25273
27257
  className: "pointer-events-none fixed z-2147483646",
@@ -25277,7 +27261,7 @@ function OhhwellsBridge() {
25277
27261
  width: slot.width,
25278
27262
  height: slot.height
25279
27263
  },
25280
- children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27264
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25281
27265
  DropIndicator,
25282
27266
  {
25283
27267
  direction: slot.direction,
@@ -25288,7 +27272,7 @@ function OhhwellsBridge() {
25288
27272
  },
25289
27273
  `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
25290
27274
  )),
25291
- isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27275
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25292
27276
  "div",
25293
27277
  {
25294
27278
  className: "pointer-events-none fixed z-2147483646",
@@ -25298,7 +27282,7 @@ function OhhwellsBridge() {
25298
27282
  width: slot.width,
25299
27283
  height: slot.height
25300
27284
  },
25301
- children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27285
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25302
27286
  DropIndicator,
25303
27287
  {
25304
27288
  direction: slot.direction,
@@ -25309,10 +27293,31 @@ function OhhwellsBridge() {
25309
27293
  },
25310
27294
  `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
25311
27295
  )),
25312
- hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
25313
- hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
25314
- hoveredTextRect && !hoveredNavContainerRect && !hoveredItemRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
25315
- formPickRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27296
+ isItemDragging && buttonDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
27297
+ "div",
27298
+ {
27299
+ className: "pointer-events-none fixed z-2147483646",
27300
+ style: {
27301
+ left: slot.left,
27302
+ top: slot.top,
27303
+ width: slot.width,
27304
+ height: slot.height
27305
+ },
27306
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
27307
+ DropIndicator,
27308
+ {
27309
+ direction: slot.direction,
27310
+ state: activeButtonDropIndex === i ? "dragActive" : "dragIdle",
27311
+ className: "!h-full !w-full"
27312
+ }
27313
+ )
27314
+ },
27315
+ `button-drop-${slot.direction}-${slot.insertIndex}-${i}`
27316
+ )),
27317
+ hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
27318
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
27319
+ hoveredTextRect && !hoveredNavContainerRect && !hoveredItemRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
27320
+ formPickRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25316
27321
  ItemInteractionLayer,
25317
27322
  {
25318
27323
  rect: formPickRect,
@@ -25320,13 +27325,13 @@ function OhhwellsBridge() {
25320
27325
  itemDragSurface: false,
25321
27326
  toolbarAlign: "left",
25322
27327
  chromeGap: 24,
25323
- toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
27328
+ toolbar: fieldPickRect ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
25324
27329
  "div",
25325
27330
  {
25326
27331
  "data-ohw-form-toolbar": "",
25327
27332
  className: "pointer-events-auto flex items-center gap-0.5 whitespace-nowrap rounded-lg border border-border bg-background p-1 shadow-md",
25328
27333
  children: [
25329
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27334
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25330
27335
  "button",
25331
27336
  {
25332
27337
  type: "button",
@@ -25334,11 +27339,11 @@ function OhhwellsBridge() {
25334
27339
  className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
25335
27340
  onClick: () => setFieldTypePickerOpen((open) => !open),
25336
27341
  "data-ohw-add-field": "",
25337
- children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_lucide_react19.Plus, { size: 15, "aria-hidden": true })
27342
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_lucide_react20.Plus, { size: 15, "aria-hidden": true })
25338
27343
  }
25339
27344
  ),
25340
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
25341
- /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
27345
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
27346
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
25342
27347
  "button",
25343
27348
  {
25344
27349
  type: "button",
@@ -25354,11 +27359,11 @@ function OhhwellsBridge() {
25354
27359
  });
25355
27360
  },
25356
27361
  children: [
25357
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_lucide_react19.Settings, { size: 14, "aria-hidden": true }),
27362
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_lucide_react20.Settings, { size: 14, "aria-hidden": true }),
25358
27363
  "Form settings",
25359
27364
  formPickCount ? (
25360
27365
  // Counter pill, per the design — not a text suffix.
25361
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27366
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25362
27367
  "span",
25363
27368
  {
25364
27369
  "data-ohw-form-count": "",
@@ -25370,8 +27375,8 @@ function OhhwellsBridge() {
25370
27375
  ]
25371
27376
  }
25372
27377
  ),
25373
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
25374
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27378
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "mx-0.5 h-5 w-px bg-border" }),
27379
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "flex items-center rounded-md bg-muted/70 p-0.5", children: ["default", "success"].map((state) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25375
27380
  "button",
25376
27381
  {
25377
27382
  type: "button",
@@ -25402,7 +27407,7 @@ function OhhwellsBridge() {
25402
27407
  )
25403
27408
  }
25404
27409
  ),
25405
- formHoverRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27410
+ formHoverRect && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25406
27411
  ItemInteractionLayer,
25407
27412
  {
25408
27413
  rect: formHoverRect,
@@ -25410,7 +27415,7 @@ function OhhwellsBridge() {
25410
27415
  chromeGap: formHoverElRef.current && getFieldWrapper(formHoverElRef.current) ? 8 : 24
25411
27416
  }
25412
27417
  ),
25413
- fieldPickRect && fieldPickState && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27418
+ fieldPickRect && fieldPickState && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25414
27419
  ItemInteractionLayer,
25415
27420
  {
25416
27421
  rect: fieldPickRect,
@@ -25422,7 +27427,7 @@ function OhhwellsBridge() {
25422
27427
  dragHandleLabel: "Reorder field",
25423
27428
  onDragHandleDragStart: handleFieldDragStart,
25424
27429
  onDragHandleDragEnd: handleFieldDragEnd,
25425
- toolbar: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27430
+ toolbar: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25426
27431
  FormFieldToolbar,
25427
27432
  {
25428
27433
  type: fieldPickState.type,
@@ -25435,12 +27440,12 @@ function OhhwellsBridge() {
25435
27440
  )
25436
27441
  }
25437
27442
  ),
25438
- fieldDragging ? fieldDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27443
+ fieldDragging ? fieldDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25439
27444
  "div",
25440
27445
  {
25441
27446
  className: "pointer-events-none fixed z-[2147483644]",
25442
27447
  style: { top: slot.top, left: slot.left, width: slot.width },
25443
- children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27448
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25444
27449
  DropIndicator,
25445
27450
  {
25446
27451
  direction: "horizontal",
@@ -25453,7 +27458,7 @@ function OhhwellsBridge() {
25453
27458
  )) : null,
25454
27459
  fieldTypePickerOpen && formPickRect ? (() => {
25455
27460
  const toolbar = document.querySelector("[data-ohw-form-toolbar]")?.getBoundingClientRect();
25456
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27461
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25457
27462
  "div",
25458
27463
  {
25459
27464
  className: "pointer-events-none fixed z-[2147483645]",
@@ -25461,12 +27466,12 @@ function OhhwellsBridge() {
25461
27466
  top: toolbar ? toolbar.bottom + 6 : formPickRect.top + 16,
25462
27467
  left: toolbar ? toolbar.left : formPickRect.left + 24
25463
27468
  },
25464
- children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(FieldTypePicker, { onPick: handleAddField })
27469
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(FieldTypePicker, { onPick: handleAddField })
25465
27470
  }
25466
27471
  );
25467
27472
  })() : null,
25468
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
25469
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27473
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
27474
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25470
27475
  FooterContainerChrome,
25471
27476
  {
25472
27477
  rect: toolbarRect,
@@ -25474,13 +27479,17 @@ function OhhwellsBridge() {
25474
27479
  addDisabled: !canAddFooterColumn()
25475
27480
  }
25476
27481
  ),
25477
- toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27482
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25478
27483
  ItemInteractionLayer,
25479
27484
  {
25480
- rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
27485
+ rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected || selectedElRef.current === buttonCollectionDragRef.current) ? draggedItemRect : toolbarRect,
25481
27486
  toolbarBelowRect: selectedElRef.current && isNavigationItem2(selectedElRef.current) ? getOpenNavDropdownPanelRect(selectedElRef.current) : null,
25482
27487
  toolbarBelowEl: selectedElRef.current && isNavigationItem2(selectedElRef.current) ? getOpenNavDropdownPanel(selectedElRef.current) : null,
25483
27488
  elRef: glowElRef,
27489
+ hidden: isRectOutsideClip(
27490
+ isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected || selectedElRef.current === buttonCollectionDragRef.current) ? draggedItemRect : toolbarRect,
27491
+ getIframeVisibleClip(parentScrollRef.current)
27492
+ ),
25484
27493
  state: isItemDragging ? "dragging" : selectedElRef.current && isNavigationItem2(selectedElRef.current) && getOpenNavDropdownPanel(selectedElRef.current) ? "active-bottom" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
25485
27494
  showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
25486
27495
  dragDisabled: reorderDragDisabled,
@@ -25490,18 +27499,25 @@ function OhhwellsBridge() {
25490
27499
  onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
25491
27500
  onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
25492
27501
  itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection && !selectedIsSocialsRow,
25493
- toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27502
+ toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25494
27503
  ItemActionToolbar,
25495
27504
  {
25496
27505
  onEditLink: openLinkPopoverForSelected,
25497
27506
  onStyle: () => {
25498
27507
  const row = selectedElRef.current;
25499
27508
  if (!row) return;
25500
- if (floatingPanel?.kind === "socials-display") closeFloatingPanelOnly();
27509
+ suppressNextClickRef.current = true;
27510
+ suppressClickUntilRef.current = Date.now() + 500;
27511
+ if (isCtaButton(row)) {
27512
+ if (floatingPanelRef.current?.kind === "button-style") closeFloatingPanelOnly();
27513
+ else openButtonStylePanel(row);
27514
+ return;
27515
+ }
27516
+ if (floatingPanelRef.current?.kind === "socials-display") closeFloatingPanelOnly();
25501
27517
  else openSocialsDisplayPanel(row);
25502
27518
  },
25503
- showStyle: selectedIsSocialsRow,
25504
- styleActive: floatingPanel?.kind === "socials-display",
27519
+ showStyle: selectedIsSocialsRow || selectedIsCta,
27520
+ styleActive: floatingPanel?.kind === "socials-display" || floatingPanel?.kind === "button-style",
25505
27521
  onAddItem: handleAddChildItem,
25506
27522
  onSelectParent: handleSelectParent,
25507
27523
  onDuplicate: handleDuplicateSelected,
@@ -25512,11 +27528,11 @@ function OhhwellsBridge() {
25512
27528
  })(),
25513
27529
  editLinkDisabled: false,
25514
27530
  moreDisabled: false,
25515
- deleteDisabled: selectedElRef.current !== null && (() => {
27531
+ deleteDisabled: selectedElRef.current !== null && isCtaButton(selectedElRef.current) && !canDeleteButton(selectedElRef.current) || selectedElRef.current !== null && (() => {
25516
27532
  const social = getSocialItem(selectedElRef.current);
25517
27533
  return social ? !canRemoveSocialItem(social) : false;
25518
27534
  })(),
25519
- duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow || selectedElRef.current !== null && (() => {
27535
+ duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow || selectedElRef.current !== null && isCtaButton(selectedElRef.current) && !canDuplicateButton(selectedElRef.current) || selectedElRef.current !== null && (() => {
25520
27536
  const social = getSocialItem(selectedElRef.current);
25521
27537
  const row = social ? findSocialsRow(social) : null;
25522
27538
  return row ? !canAddSocialItem(row) : false;
@@ -25525,7 +27541,7 @@ function OhhwellsBridge() {
25525
27541
  showAddItem: isFooterFrameSelection || selectedIsSocialsRow || !selectedIsCta && Boolean(
25526
27542
  selectedElRef.current && isNavbarHrefKey(selectedElRef.current.getAttribute("data-ohw-href-key")) && !isNestedNavChild(selectedElRef.current)
25527
27543
  ),
25528
- showMore: (!selectedIsCta || isFooterFrameSelection) && !selectedIsSocialsRow,
27544
+ showMore: !selectedIsSocialsRow,
25529
27545
  dropdownOpen: navDropdownPreviewOpen,
25530
27546
  onDropdownOpenChange: handleNavDropdownOpenChange,
25531
27547
  headingVisible: footerHeadingVisible,
@@ -25534,8 +27550,8 @@ function OhhwellsBridge() {
25534
27550
  ) : void 0
25535
27551
  }
25536
27552
  ),
25537
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && !isRectOutsideClip(toolbarRect, getIframeVisibleClip(parentScrollRef.current)) && /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
25538
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27553
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && !isRectOutsideClip(toolbarRect, getIframeVisibleClip(parentScrollRef.current)) && /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
27554
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25539
27555
  EditGlowChrome,
25540
27556
  {
25541
27557
  rect: toolbarRect,
@@ -25545,7 +27561,7 @@ function OhhwellsBridge() {
25545
27561
  hideHandle: isItemDragging
25546
27562
  }
25547
27563
  ),
25548
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27564
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25549
27565
  FloatingToolbar,
25550
27566
  {
25551
27567
  rect: toolbarRect,
@@ -25558,7 +27574,7 @@ function OhhwellsBridge() {
25558
27574
  }
25559
27575
  )
25560
27576
  ] }),
25561
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
27577
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
25562
27578
  "div",
25563
27579
  {
25564
27580
  "data-ohw-max-badge": "",
@@ -25584,7 +27600,7 @@ function OhhwellsBridge() {
25584
27600
  ]
25585
27601
  }
25586
27602
  ),
25587
- toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27603
+ toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25588
27604
  StateToggle,
25589
27605
  {
25590
27606
  rect: toggleState.rect,
@@ -25593,15 +27609,15 @@ function OhhwellsBridge() {
25593
27609
  onStateChange: handleStateChange
25594
27610
  }
25595
27611
  ),
25596
- sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
27612
+ sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
25597
27613
  "div",
25598
27614
  {
25599
27615
  "data-ohw-section-insert-line": "",
25600
27616
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
25601
27617
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
25602
27618
  children: [
25603
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "flex-1 bg-bridge-primary", style: { height: 3 } }),
25604
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27619
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "flex-1 bg-bridge-primary", style: { height: 3 } }),
27620
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25605
27621
  Badge,
25606
27622
  {
25607
27623
  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",
@@ -25618,11 +27634,11 @@ function OhhwellsBridge() {
25618
27634
  children: "Add Section"
25619
27635
  }
25620
27636
  ),
25621
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "flex-1 bg-bridge-primary", style: { height: 3 } })
27637
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "flex-1 bg-bridge-primary", style: { height: 3 } })
25622
27638
  ]
25623
27639
  }
25624
27640
  ),
25625
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27641
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25626
27642
  LinkPopover,
25627
27643
  {
25628
27644
  panelRef: linkPopoverPanelRef,
@@ -25639,7 +27655,7 @@ function OhhwellsBridge() {
25639
27655
  },
25640
27656
  linkPopover.key
25641
27657
  ) : null,
25642
- floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27658
+ floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25643
27659
  FloatingPanel,
25644
27660
  {
25645
27661
  open: true,
@@ -25649,7 +27665,7 @@ function OhhwellsBridge() {
25649
27665
  onPositionChange: setFloatingPanelPos,
25650
27666
  parentScroll: parentScrollSnap ?? parentScrollRef.current,
25651
27667
  onClose: closeFloatingPanelOnly,
25652
- children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
27668
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
25653
27669
  SocialsDisplayPanel,
25654
27670
  {
25655
27671
  display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
@@ -25660,7 +27676,48 @@ function OhhwellsBridge() {
25660
27676
  }
25661
27677
  )
25662
27678
  }
25663
- ) : null
27679
+ ) : null,
27680
+ floatingPanel && floatingPanel.kind === "button-style" ? (() => {
27681
+ const button = document.querySelector(
27682
+ `[data-ohw-href-key="${CSS.escape(floatingPanel.buttonId)}"]`
27683
+ );
27684
+ if (!button) return null;
27685
+ captureButtonOrigin(button);
27686
+ const tokens = tokensFromButton(button);
27687
+ const current = assignmentFor(button, editContentRef.current, tokens);
27688
+ void buttonStyleTick;
27689
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
27690
+ FloatingPanel,
27691
+ {
27692
+ open: true,
27693
+ title: floatingPanel.title,
27694
+ context: floatingPanel.context,
27695
+ icon: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_lucide_react20.Brush, { className: "size-4 shrink-0", "aria-hidden": true }),
27696
+ position: floatingPanelPos,
27697
+ onPositionChange: setFloatingPanelPos,
27698
+ parentScroll: parentScrollSnap ?? parentScrollRef.current,
27699
+ onClose: closeFloatingPanelOnly,
27700
+ bodyClassName: "gap-5",
27701
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
27702
+ ButtonStylePanel,
27703
+ {
27704
+ variants: tokens.variants,
27705
+ sizes: tokens.sizes,
27706
+ variant: current.variant,
27707
+ size: current.size,
27708
+ slotBackground: previewSlotBackground(button),
27709
+ previewLabel: (button.querySelector(
27710
+ '[data-ohw-editable="text"], [data-ohw-editable="plain"]'
27711
+ )?.textContent ?? button.textContent ?? "Book Now").trim() || "Book Now",
27712
+ onVariant: (variant) => applySelectedButtonStyle({ variant }),
27713
+ onSize: (size) => applySelectedButtonStyle({ size })
27714
+ },
27715
+ floatingPanel.key
27716
+ )
27717
+ },
27718
+ floatingPanel.key
27719
+ );
27720
+ })() : null
25664
27721
  ] }),
25665
27722
  bridgeRoot
25666
27723
  ) : null