@ohhwells/bridge 0.1.151-next.406 → 0.1.152

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
@@ -6652,21 +6652,6 @@ function pricingCardFeatureCount(tree, index) {
6652
6652
  const target = findPricingCollection(tree)?.children?.[index];
6653
6653
  return target ? pricingFeatureList(target).length : 0;
6654
6654
  }
6655
- function prevailingFeatureIcon(features) {
6656
- const counts = /* @__PURE__ */ new Map();
6657
- for (const row of features) {
6658
- const key = `${row.icon ?? ""}|${row.iconColor ?? ""}`;
6659
- const entry = counts.get(key) ?? { count: 0, ...row.icon ? { icon: row.icon } : {}, ...row.iconColor ? { iconColor: row.iconColor } : {} };
6660
- entry.count += 1;
6661
- counts.set(key, entry);
6662
- }
6663
- let best = null;
6664
- for (const entry of counts.values()) {
6665
- if (!best || entry.count > best.count) best = entry;
6666
- }
6667
- if (!best) return {};
6668
- return { ...best.icon ? { icon: best.icon } : {}, ...best.iconColor ? { iconColor: best.iconColor } : {} };
6669
- }
6670
6655
  function addPricingCardFeature(tree, index, text) {
6671
6656
  return withPricingCollection(tree, (collection) => {
6672
6657
  const children = collection.children;
@@ -6675,7 +6660,7 @@ function addPricingCardFeature(tree, index, text) {
6675
6660
  const features = pricingFeatureList(target);
6676
6661
  if (features.length >= PRICING_FEATURES_MAX) return false;
6677
6662
  target.slots = target.slots ?? {};
6678
- target.slots.features = [...features, { text, ...prevailingFeatureIcon(features) }];
6663
+ target.slots.features = [...features, { text }];
6679
6664
  return true;
6680
6665
  });
6681
6666
  }
@@ -7859,7 +7844,7 @@ var AI_MOBILE_CSS = [
7859
7844
  // page's first section. Top only (bottom needs no nav clearance); ~96px approximates the
7860
7845
  // template nav's own height (measured ~94px) — there's no shared `--ohw-header-h` var to read
7861
7846
  // an exact figure from (no live measurement exists), so this is a safe approximation, not a
7862
- // pixel-exact fit. Horizontal spacing already comes from `data-ai-section-inner`.
7847
+ // pixel-exact fit. Horizontal spacing already comes from `data-ai-container`.
7863
7848
  '[data-ai-overlay-clearance] [data-ai-overlap-layer="overlay"]{padding-top:96px}',
7864
7849
  // A full-bleed carousel's height is pure arithmetic: it spans the whole window, so 16/9 turns
7865
7850
  // window width straight into height. Measured at 16/9 it came out ~90% of the viewport at EVERY
@@ -7875,10 +7860,8 @@ var AI_MOBILE_CSS = [
7875
7860
  "[data-ai-section]{overflow-x:hidden}",
7876
7861
  // `:not([data-ai-full-bleed])`: this `!important` gutter beat the inline `padding: 0` that a
7877
7862
  // full-bleed section's container sets, so a carousel/media hero that is edge-to-edge on desktop
7878
- // still got a 20px band of section background down both sides on a phone. The footer's own
7879
- // container keeps the unguarded rule below — `data-ai-full-bleed` can never appear on it.
7880
- "[data-ai-section-inner]:not([data-ai-full-bleed]){padding:0 20px !important}",
7881
- "[data-ai-container]{padding:0 20px !important}",
7863
+ // still got a 20px band of section background down both sides on a phone.
7864
+ "[data-ai-container]:not([data-ai-full-bleed]){padding:0 20px !important}",
7882
7865
  "[data-ai-row]{display:flex !important;flex-direction:column !important;align-items:stretch !important}",
7883
7866
  "[data-ai-cell]{width:100%;min-width:0}",
7884
7867
  // The rule above turns every row into a COLUMN flex container on phones, which silently breaks
@@ -8083,31 +8066,6 @@ function pricingCardEmphasis(featured, ctx, hasBg) {
8083
8066
  function textAttrs(ctx, path) {
8084
8067
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
8085
8068
  }
8086
- var AI_RESPONSIVE_CSS = [
8087
- "@media (max-width: 960px) {",
8088
- " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
8089
- ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
8090
- "}",
8091
- "@media (max-width: 640px) {",
8092
- " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
8093
- " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
8094
- // Group containers flatten to a column on phones; span placements come along for free.
8095
- // `:not([data-ai-group="overlap"])` for the same reason as the twin pair in AI_MOBILE_CSS — see
8096
- // the comment there. This sheet's copy is the narrower selector, so without the exemption it
8097
- // flattens an `overlap` hero below 640px even though the other rule stands down: measured a 390px
8098
- // viewport with the heading/CTA in flow *under* the image, 219px of section background where the
8099
- // layered text used to be.
8100
- ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) { display: flex !important; flex-direction: column !important; }',
8101
- ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) > * { grid-column: auto !important; }',
8102
- // `:not([data-ai-full-bleed])` for the same reason as the twin rule in AI_MOBILE_CSS: this one
8103
- // is narrower (0,2,0 vs 0,1,0) and also `!important`, so without the guard it wins outright and
8104
- // a full-bleed hero gets its 20px side band back below 640px only — the one width where the
8105
- // other rule had already been taught to stand down.
8106
- " [data-ai-responsive] [data-ai-section-inner]:not([data-ai-full-bleed]) { padding-left: 20px !important; padding-right: 20px !important; }",
8107
- " [data-ai-responsive] { overflow-x: hidden; }",
8108
- " [data-ai-responsive] img { max-width: 100%; }",
8109
- "}"
8110
- ].join("\n");
8111
8069
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
8112
8070
  function MediaBox({
8113
8071
  refValue,
@@ -8162,7 +8120,7 @@ function MediaBox({
8162
8120
  position: fill ? "absolute" : void 0,
8163
8121
  inset: fill ? 0 : void 0,
8164
8122
  width: fill ? "100%" : fixedSize ? "auto" : "100%",
8165
- height: fill ? "100%" : fixedSize ? height : void 0,
8123
+ height: fill ? "100%" : void 0,
8166
8124
  aspectRatio: fill ? void 0 : aspectRatio,
8167
8125
  minHeight: fill || fixedSize ? void 0 : height ?? (aspectRatio ? void 0 : 180),
8168
8126
  borderRadius: radius,
@@ -9658,7 +9616,7 @@ function CollectionBlock({ node, ctx, path }) {
9658
9616
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
9659
9617
  "div",
9660
9618
  {
9661
- "data-ai-grid": String(itemsPerRow),
9619
+ "data-ai-grid": "",
9662
9620
  "data-ohw-pricing-collection": isPricing ? "" : void 0,
9663
9621
  style: {
9664
9622
  display: "grid",
@@ -9960,7 +9918,7 @@ function renderNode(node, ctx, path, ambientAlign) {
9960
9918
  },
9961
9919
  children: [
9962
9920
  i > 0 && ctx.heroOverlapFullBleed ? (
9963
- // The section's `data-ai-section-inner` gave up its max-width/gutter so the base
9921
+ // The section's `data-ai-container` gave up its max-width/gutter so the base
9964
9922
  // layer (this group's i===0 sibling) can span edge-to-edge — reintroduced here,
9965
9923
  // scoped to just the overlay text, so a heading/copy/CTA stack doesn't stretch
9966
9924
  // the full section width instead of reading at the usual content width.
@@ -10379,7 +10337,6 @@ function AiTreeRenderer({
10379
10337
  ...fillHeight ? { "data-ai-fill-height": "" } : {},
10380
10338
  ...needsOverlayClearance ? { "data-ai-overlay-clearance": "" } : {},
10381
10339
  ...bgAttrs,
10382
- "data-ai-responsive": "",
10383
10340
  style: {
10384
10341
  position: "relative",
10385
10342
  padding: `${padCss} 0`,
@@ -10393,13 +10350,12 @@ function AiTreeRenderer({
10393
10350
  ...fillHeight ? { display: "grid" } : {}
10394
10351
  },
10395
10352
  children: [
10396
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
10397
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_MOBILE_CSS }),
10398
10353
  isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
10354
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_MOBILE_CSS }),
10399
10355
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
10400
10356
  "div",
10401
10357
  {
10402
- "data-ai-section-inner": "",
10358
+ "data-ai-container": "",
10403
10359
  ...fullBleedMedia ? { "data-ai-full-bleed": "" } : {},
10404
10360
  style: {
10405
10361
  position: "relative",
@@ -10416,7 +10372,7 @@ function AiTreeRenderer({
10416
10372
  children: tree.rows.map((row, r2) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
10417
10373
  "div",
10418
10374
  {
10419
- "data-ai-columns": "",
10375
+ "data-ai-row": "",
10420
10376
  style: {
10421
10377
  display: "grid",
10422
10378
  gridTemplateColumns: "repeat(12, 1fr)",
@@ -11078,10 +11034,9 @@ function applyAiSectionsToDom(state, options) {
11078
11034
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
11079
11035
  continue;
11080
11036
  }
11037
+ const isNewMount = !existing || !existing.container.isConnected;
11081
11038
  let container = existing?.container;
11082
- let isNewMount = false;
11083
11039
  if (!container || !container.isConnected) {
11084
- isNewMount = true;
11085
11040
  existing?.root.unmount();
11086
11041
  container?.remove();
11087
11042
  container = document.createElement("div");
@@ -17665,7 +17620,6 @@ function MediaOverlay({
17665
17620
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
17666
17621
  );
17667
17622
  }, [isVideo]);
17668
- const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
17669
17623
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
17670
17624
  const box = {
17671
17625
  position: "fixed",
@@ -17764,10 +17718,11 @@ function MediaOverlay({
17764
17718
  className: "flex items-center justify-center cursor-pointer",
17765
17719
  style: {
17766
17720
  ...box,
17767
- // Visual only. `pointer-events: auto` on this full-bleed rect covered the navbar
17768
- // (hero bg includes the absolute TopNav) and swallowed CTA press-drag. Text overlay
17769
- // and cueOnly also need clicks to fall through — Replace / video controls opt back in.
17770
- pointerEvents: "none",
17721
+ // When text overlays this media, let clicks fall THROUGH to the text underneath. In the
17722
+ // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
17723
+ // in-document, pointer-events does it natively. The button below opts back in, so
17724
+ // Replace still works.
17725
+ pointerEvents: hover.hasTextOverlap || cueOnly ? "none" : "auto",
17771
17726
  // Selected: a firm component ring with no wash, so the image reads as chosen rather
17772
17727
  // than hovered. Hover keeps the existing tinted preview.
17773
17728
  boxShadow: selected ? "inset 0 0 0 2px var(--color-bridge-primary)" : "inset 0 0 0 1.5px var(--color-bridge-primary)",
@@ -17794,17 +17749,17 @@ function MediaOverlay({
17794
17749
  },
17795
17750
  children: [
17796
17751
  isVideo ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
17797
- replaceLabel
17752
+ isVideo ? "Replace video" : "Replace image"
17798
17753
  ]
17799
17754
  }
17800
17755
  ),
17801
- showChrome && replaceMode !== "none" && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
17756
+ replaceMode === "none" || cueOnly ? null : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
17802
17757
  Button,
17803
17758
  {
17804
17759
  "data-ohw-media-overlay": "",
17805
17760
  variant: "outline",
17806
17761
  size: "sm",
17807
- "aria-label": replaceLabel,
17762
+ "aria-label": isVideo ? "Replace video" : "Replace image",
17808
17763
  className: "gap-1.5 cursor-pointer hover:bg-background",
17809
17764
  style: {
17810
17765
  ...OVERLAY_BUTTON_STYLE,
@@ -17827,7 +17782,7 @@ function MediaOverlay({
17827
17782
  },
17828
17783
  children: [
17829
17784
  isVideo ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
17830
- replaceMode === "full" ? replaceLabel : null
17785
+ replaceMode === "full" ? isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image" : null
17831
17786
  ]
17832
17787
  }
17833
17788
  )
@@ -18337,7 +18292,7 @@ function AiSectionOverlay({
18337
18292
 
18338
18293
  // src/ui/ai-section/AiMediaStripOverlay.tsx
18339
18294
  var import_react10 = require("react");
18340
- var import_lucide_react9 = require("lucide-react");
18295
+ var import_lucide_react8 = require("lucide-react");
18341
18296
 
18342
18297
  // src/lib/ai-media-strip-tree.ts
18343
18298
  var MEDIA_STRIP_ITEM_MIN = 1;
@@ -18451,6 +18406,14 @@ function setMediaStripItemAsset(tree, index, assetId) {
18451
18406
  return true;
18452
18407
  });
18453
18408
  }
18409
+ function setMediaStripItemAlt(tree, index, alt) {
18410
+ return withMediaStrip(tree, (block) => {
18411
+ const items = block.items;
18412
+ if (index < 0 || index >= items.length) return false;
18413
+ items[index] = { ...items[index], alt };
18414
+ return true;
18415
+ });
18416
+ }
18454
18417
  function setMediaStripItemLink(tree, index, link) {
18455
18418
  return withMediaStrip(tree, (block) => {
18456
18419
  const items = block.items;
@@ -18468,117 +18431,11 @@ function setMediaStripItemLink(tree, index, link) {
18468
18431
  });
18469
18432
  }
18470
18433
 
18471
- // src/ui/dialog.tsx
18472
- var React9 = __toESM(require("react"), 1);
18473
- var import_radix_ui6 = require("radix-ui");
18474
- var import_lucide_react8 = require("lucide-react");
18475
- var import_jsx_runtime20 = require("react/jsx-runtime");
18476
- function Dialog2({
18477
- ...props
18478
- }) {
18479
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_radix_ui6.Dialog.Root, { "data-slot": "dialog", ...props });
18480
- }
18481
- function DialogPortal({
18482
- ...props
18483
- }) {
18484
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_radix_ui6.Dialog.Portal, { ...props });
18485
- }
18486
- function DialogOverlay({
18487
- className,
18488
- ...props
18489
- }) {
18490
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18491
- import_radix_ui6.Dialog.Overlay,
18492
- {
18493
- "data-slot": "dialog-overlay",
18494
- "data-ohw-link-modal-root": "",
18495
- className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
18496
- ...props
18497
- }
18498
- );
18499
- }
18500
- var DialogContent = React9.forwardRef(
18501
- ({ className, children, showCloseButton = true, container, ...props }, ref) => {
18502
- const positionMode = container ? "absolute" : "fixed";
18503
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(DialogPortal, { container: container ?? void 0, children: [
18504
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
18505
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18506
- import_radix_ui6.Dialog.Content,
18507
- {
18508
- ref,
18509
- "data-slot": "dialog-content",
18510
- className: cn(
18511
- positionMode,
18512
- "left-1/2 top-1/2 z-[2147483647] flex w-full max-w-[448px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-visible",
18513
- "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
18514
- container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
18515
- className
18516
- ),
18517
- onMouseDown: (e) => e.stopPropagation(),
18518
- ...props,
18519
- children: [
18520
- children,
18521
- showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18522
- import_radix_ui6.Dialog.Close,
18523
- {
18524
- type: "button",
18525
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
18526
- "aria-label": "Close",
18527
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react8.X, { size: 16, "aria-hidden": true })
18528
- }
18529
- ) : null
18530
- ]
18531
- }
18532
- )
18533
- ] });
18534
- }
18535
- );
18536
- DialogContent.displayName = import_radix_ui6.Dialog.Content.displayName;
18537
- function DialogHeader({
18538
- className,
18539
- ...props
18540
- }) {
18541
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
18542
- }
18543
- function DialogFooter({
18544
- className,
18545
- ...props
18546
- }) {
18547
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18548
- "div",
18549
- {
18550
- className: cn("flex items-center justify-end gap-2", className),
18551
- ...props
18552
- }
18553
- );
18554
- }
18555
- var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18556
- import_radix_ui6.Dialog.Title,
18557
- {
18558
- ref,
18559
- className: cn(
18560
- "text-lg font-semibold leading-none tracking-tight text-card-foreground",
18561
- className
18562
- ),
18563
- ...props
18564
- }
18565
- ));
18566
- DialogTitle.displayName = import_radix_ui6.Dialog.Title.displayName;
18567
- var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18568
- import_radix_ui6.Dialog.Description,
18569
- {
18570
- ref,
18571
- className: cn("text-sm text-muted-foreground", className),
18572
- ...props
18573
- }
18574
- ));
18575
- DialogDescription.displayName = import_radix_ui6.Dialog.Description.displayName;
18576
- var DialogClose = import_radix_ui6.Dialog.Close;
18577
-
18578
18434
  // src/ui/ai-section/AiMediaStripOverlay.tsx
18579
- var import_jsx_runtime21 = require("react/jsx-runtime");
18580
- var PRIMARY3 = "var(--ohw-primary, #0885FE)";
18581
- var CHROME_SELECTOR = "[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-link-modal-root], [data-ohw-section-picker], [data-ohw-ai-review], [data-ohw-more-menu]";
18435
+ var import_jsx_runtime20 = require("react/jsx-runtime");
18436
+ var PRIMARY3 = "#0885FE";
18437
+ var OHW_BLUE = `var(--ohw-primary, ${PRIMARY3})`;
18438
+ var OHW_BLUE_DARK = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY3}) 60%, #000000)`;
18582
18439
  function findMediaStripItemElement(sectionId, index) {
18583
18440
  const sectionEl = findByInstanceId(sectionId);
18584
18441
  if (!sectionEl) return null;
@@ -18588,35 +18445,14 @@ function findMediaStripItemElement(sectionId, index) {
18588
18445
  }
18589
18446
  return null;
18590
18447
  }
18591
- function itemRefFromTarget(target) {
18592
- if (!(target instanceof Element)) return null;
18593
- if (target.closest(CHROME_SELECTOR)) return null;
18594
- const itemEl = target.closest("[data-ohw-media-strip-item]");
18595
- if (!itemEl) return null;
18596
- const sectionEl = itemEl.closest("[data-ohw-section]");
18597
- const unit = sectionEl ? movableUnit(sectionEl) : null;
18598
- const sectionId = unit ? instanceIdOf(unit) || null : null;
18599
- const index = Number(itemEl.dataset.ohwMediaStripIndex);
18600
- if (!sectionId || Number.isNaN(index)) return null;
18601
- return { sectionId, index };
18602
- }
18603
- function sameRef(a, b) {
18604
- return a?.sectionId === b?.sectionId && a?.index === b?.index;
18605
- }
18606
- function rectOf(el) {
18448
+ function readRect2(ref) {
18449
+ const el = ref && findMediaStripItemElement(ref.sectionId, ref.index);
18607
18450
  if (!el) return null;
18608
18451
  const r2 = el.getBoundingClientRect();
18609
18452
  if (r2.width <= 0 || r2.height <= 0) return null;
18610
18453
  return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
18611
18454
  }
18612
- function readRect2(ref) {
18613
- return rectOf(ref && findMediaStripItemElement(ref.sectionId, ref.index));
18614
- }
18615
- function readStripRect(ref) {
18616
- const item = ref && findMediaStripItemElement(ref.sectionId, ref.index);
18617
- return rectOf(item?.closest("[data-ohw-media-strip]") ?? item);
18618
- }
18619
- function useLiveItemRect(ref, strip = false) {
18455
+ function useLiveItemRect(ref) {
18620
18456
  const [rect, setRect] = (0, import_react10.useState)(null);
18621
18457
  (0, import_react10.useEffect)(() => {
18622
18458
  if (!ref) {
@@ -18624,7 +18460,7 @@ function useLiveItemRect(ref, strip = false) {
18624
18460
  return;
18625
18461
  }
18626
18462
  const update = () => {
18627
- const next = strip ? readStripRect(ref) : readRect2(ref);
18463
+ const next = readRect2(ref);
18628
18464
  setRect(
18629
18465
  (prev) => prev && next && prev.top === next.top && prev.left === next.left && prev.width === next.width && prev.height === next.height ? prev : next
18630
18466
  );
@@ -18639,49 +18475,9 @@ function useLiveItemRect(ref, strip = false) {
18639
18475
  window.removeEventListener("resize", update);
18640
18476
  clearInterval(interval);
18641
18477
  };
18642
- }, [ref?.sectionId, ref?.index, strip]);
18478
+ }, [ref?.sectionId, ref?.index]);
18643
18479
  return rect;
18644
18480
  }
18645
- var TOOLBAR_STRIP_GAP = 52;
18646
- var TOOLBAR_STRIP_GAP_MOBILE = 42;
18647
- var TOOLBAR_EDGE_MARGIN2 = 4;
18648
- function StripToolbarSlot({ stripRect, children }) {
18649
- const slotRef = (0, import_react10.useRef)(null);
18650
- const [shift, setShift] = (0, import_react10.useState)(0);
18651
- const centerX = stripRect.left + stripRect.width / 2;
18652
- (0, import_react10.useLayoutEffect)(() => {
18653
- const slot = slotRef.current;
18654
- if (!slot) return;
18655
- const clamp = () => {
18656
- const w = slot.offsetWidth;
18657
- if (w <= 0) return;
18658
- const half = w / 2;
18659
- const clamped = Math.max(TOOLBAR_EDGE_MARGIN2 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half));
18660
- setShift((prev) => Math.abs(prev - (clamped - centerX)) < 0.5 ? prev : clamped - centerX);
18661
- };
18662
- clamp();
18663
- const ro = new ResizeObserver(clamp);
18664
- ro.observe(slot);
18665
- return () => ro.disconnect();
18666
- }, [centerX, children]);
18667
- const mobile = window.innerWidth <= 480;
18668
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
18669
- "div",
18670
- {
18671
- ref: slotRef,
18672
- "data-ohw-bridge": "",
18673
- "data-ohw-item-interaction": "",
18674
- className: "pointer-events-auto fixed",
18675
- style: {
18676
- top: stripRect.top - (mobile ? TOOLBAR_STRIP_GAP_MOBILE : TOOLBAR_STRIP_GAP),
18677
- left: centerX + shift,
18678
- transform: "translate(-50%, -100%)",
18679
- zIndex: 2147483645
18680
- },
18681
- children
18682
- }
18683
- );
18684
- }
18685
18481
  function requestValidateTree(postToParent2, tree) {
18686
18482
  return new Promise((resolve) => {
18687
18483
  const id = `ai-validate-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
@@ -18718,177 +18514,221 @@ function requestAssetPick(postToParent2) {
18718
18514
  postToParent2({ type: "ow:asset-pick", id });
18719
18515
  });
18720
18516
  }
18721
- function ToolbarAction({
18517
+ function Pill({
18722
18518
  label,
18723
18519
  onClick,
18724
- disabled = false,
18725
- active = false,
18726
- children
18520
+ disabled,
18521
+ active
18727
18522
  }) {
18728
- const button = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, active, onClick, className: "size-8", children });
18729
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
18730
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "inline-flex", children: button }) : button }),
18731
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
18732
- ] });
18733
- }
18734
- function ToolbarSeparator() {
18735
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
18523
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18524
+ "button",
18525
+ {
18526
+ type: "button",
18527
+ onClick,
18528
+ disabled,
18529
+ style: {
18530
+ height: 28,
18531
+ padding: "0 10px",
18532
+ display: "inline-flex",
18533
+ alignItems: "center",
18534
+ gap: 4,
18535
+ borderRadius: 999,
18536
+ border: "none",
18537
+ cursor: disabled ? "default" : "pointer",
18538
+ background: active ? OHW_BLUE_DARK : "#ffffff",
18539
+ color: active ? "#ffffff" : OHW_BLUE_DARK,
18540
+ opacity: disabled ? 0.4 : 1,
18541
+ fontSize: 12,
18542
+ lineHeight: "16px",
18543
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
18544
+ whiteSpace: "nowrap",
18545
+ flex: "0 0 auto"
18546
+ },
18547
+ children: label
18548
+ }
18549
+ );
18736
18550
  }
18737
- function OptionMenu({
18738
- label,
18739
- value,
18740
- options,
18741
- onChange
18742
- }) {
18743
- const current = options.find((o) => o.value === value) ?? options[0];
18744
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
18745
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-xs text-muted-foreground", children: label }),
18746
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DropdownMenu, { modal: false, children: [
18747
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
18748
- "button",
18749
- {
18750
- type: "button",
18751
- "aria-label": label,
18752
- className: "inline-flex h-8 min-w-[76px] items-center gap-[7px] rounded-[6px] bg-transparent px-2 text-[13px] font-medium text-foreground transition-colors hover:bg-muted data-[state=open]:bg-muted",
18753
- children: [
18754
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "flex-1 text-left", children: current.label }),
18755
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
18756
- ]
18757
- }
18758
- ) }),
18759
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[132px] min-w-[132px] p-1", children: options.map((option) => {
18760
- const selected = option.value === value;
18761
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
18762
- DropdownMenuItem,
18551
+ function AltInput({ value, onCommit }) {
18552
+ const [draft, setDraft] = (0, import_react10.useState)(value);
18553
+ (0, import_react10.useEffect)(() => setDraft(value), [value]);
18554
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18555
+ "div",
18556
+ {
18557
+ style: {
18558
+ display: "flex",
18559
+ alignItems: "center",
18560
+ gap: 6,
18561
+ height: 28,
18562
+ padding: "0 12px",
18563
+ borderRadius: 999,
18564
+ background: "#ffffff",
18565
+ width: "100%",
18566
+ maxWidth: 260,
18567
+ boxSizing: "border-box"
18568
+ },
18569
+ children: [
18570
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18571
+ "span",
18763
18572
  {
18764
- role: "menuitemradio",
18765
- "aria-checked": selected,
18766
- onSelect: () => onChange(option.value),
18767
- className: cn("grid grid-cols-[1fr_16px] gap-2 py-2 pl-2 pr-2 text-[13px]", selected && "bg-muted"),
18768
- children: [
18769
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: option.label }),
18770
- selected && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Check, { size: 16, className: "text-bridge-primary" })
18771
- ]
18772
- },
18773
- option.value
18774
- );
18775
- }) })
18776
- ] })
18777
- ] });
18778
- }
18779
- var VARIANTS = [
18780
- { value: "logos", label: "Logos" },
18781
- { value: "reel", label: "Reel" }
18782
- ];
18783
- var ALIGNS = [
18784
- { value: "left", label: "Align left", Icon: import_lucide_react9.AlignLeft },
18785
- { value: "center", label: "Align center", Icon: import_lucide_react9.AlignCenter },
18786
- { value: "right", label: "Align right", Icon: import_lucide_react9.AlignRight }
18787
- ];
18788
- var HOVERS = [
18789
- { value: "none", label: "None" },
18790
- { value: "lift", label: "Lift" },
18791
- { value: "zoom", label: "Zoom" },
18792
- { value: "fade", label: "Fade" }
18793
- ];
18794
- var HTTP_URL_RE = /^https?:\/\/\S+$/i;
18795
- function LinkModal({
18796
- href,
18797
- linkTarget,
18798
- onClose,
18799
- onSave
18800
- }) {
18801
- const [draft, setDraft] = (0, import_react10.useState)(href);
18802
- const [newTab, setNewTab] = (0, import_react10.useState)(linkTarget !== "same-tab");
18573
+ style: {
18574
+ flex: "0 0 auto",
18575
+ color: OHW_BLUE_DARK,
18576
+ fontSize: 10,
18577
+ fontWeight: 700,
18578
+ textTransform: "uppercase",
18579
+ letterSpacing: 0.4
18580
+ },
18581
+ children: "Alt"
18582
+ }
18583
+ ),
18584
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18585
+ "input",
18586
+ {
18587
+ type: "text",
18588
+ value: draft,
18589
+ onChange: (e) => setDraft(e.target.value),
18590
+ onBlur: () => {
18591
+ if (draft !== value) onCommit(draft);
18592
+ },
18593
+ onKeyDown: (e) => {
18594
+ if (e.key === "Enter") e.currentTarget.blur();
18595
+ if (e.key === "Escape") {
18596
+ setDraft(value);
18597
+ e.currentTarget.blur();
18598
+ }
18599
+ },
18600
+ placeholder: "Describe this image",
18601
+ "aria-label": "Image alt text",
18602
+ style: {
18603
+ flex: 1,
18604
+ minWidth: 0,
18605
+ border: "none",
18606
+ outline: "none",
18607
+ background: "transparent",
18608
+ fontSize: 12,
18609
+ lineHeight: "16px",
18610
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
18611
+ color: "#1f2937"
18612
+ }
18613
+ }
18614
+ )
18615
+ ]
18616
+ }
18617
+ );
18618
+ }
18619
+ var VARIANTS = [
18620
+ { value: "logos", label: "Logos" },
18621
+ { value: "reel", label: "Reel" }
18622
+ ];
18623
+ var ALIGNS = [
18624
+ { value: "left", label: "Left" },
18625
+ { value: "center", label: "Center" },
18626
+ { value: "right", label: "Right" }
18627
+ ];
18628
+ var HOVERS = [
18629
+ { value: "none", label: "None" },
18630
+ { value: "lift", label: "Lift" },
18631
+ { value: "zoom", label: "Zoom" },
18632
+ { value: "fade", label: "Fade" }
18633
+ ];
18634
+ var TREATMENTS = [
18635
+ { value: "color", label: "Color" },
18636
+ { value: "grayscale", label: "Grayscale" }
18637
+ ];
18638
+ var HTTP_URL_RE = /^https?:\/\/\S+$/i;
18639
+ function LinkInput({ value, onCommit }) {
18640
+ const [draft, setDraft] = (0, import_react10.useState)(value);
18803
18641
  const [invalid, setInvalid] = (0, import_react10.useState)(false);
18804
- const save = () => {
18642
+ (0, import_react10.useEffect)(() => {
18643
+ setDraft(value);
18644
+ setInvalid(false);
18645
+ }, [value]);
18646
+ const commit = () => {
18805
18647
  const next = draft.trim();
18806
- if (next && !HTTP_URL_RE.test(next)) {
18807
- setInvalid(true);
18808
- return;
18809
- }
18810
- onSave(next, newTab ? "new-tab" : "same-tab");
18811
- onClose();
18648
+ if (next === value) return setInvalid(false);
18649
+ if (next && !HTTP_URL_RE.test(next)) return setInvalid(true);
18650
+ setInvalid(false);
18651
+ onCommit(next);
18812
18652
  };
18813
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Dialog2, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DialogContent, { "data-ohw-link-modal-root": "", className: "max-w-[400px] gap-4 p-5", children: [
18814
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogTitle, { className: "text-base", children: "Image link" }) }),
18815
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col gap-3", children: [
18816
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
18817
- "input",
18818
- {
18819
- autoFocus: true,
18820
- type: "text",
18821
- inputMode: "url",
18822
- value: draft,
18823
- onChange: (e) => {
18824
- setDraft(e.target.value);
18825
- setInvalid(false);
18826
- },
18827
- onKeyDown: (e) => {
18828
- if (e.key === "Enter") save();
18829
- },
18830
- placeholder: "https://example.com",
18831
- "aria-label": "Image link URL",
18832
- "aria-invalid": invalid,
18833
- className: cn(
18834
- "h-9 w-full rounded-[6px] border border-border bg-background px-3 text-[13px] text-foreground outline-none placeholder:text-muted-foreground/70 focus:border-bridge-primary",
18835
- invalid && "border-destructive focus:border-destructive"
18836
- )
18837
- }
18838
- ),
18839
- invalid && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-xs text-destructive", children: "Enter a full URL starting with http:// or https://" }),
18840
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { className: "flex items-center gap-2 text-[13px] text-foreground", children: [
18841
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("input", { type: "checkbox", checked: newTab, onChange: (e) => setNewTab(e.target.checked) }),
18842
- "Open in new tab"
18843
- ] })
18844
- ] }),
18845
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DialogFooter, { children: [
18846
- href && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
18847
- "button",
18848
- {
18849
- type: "button",
18850
- onClick: () => {
18851
- onSave("", "new-tab");
18852
- onClose();
18853
- },
18854
- className: "mr-auto rounded-[6px] px-3 py-2 text-[13px] font-medium text-destructive transition-colors hover:bg-muted",
18855
- children: "Remove link"
18856
- }
18857
- ),
18858
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
18859
- "button",
18860
- {
18861
- type: "button",
18862
- onClick: onClose,
18863
- className: "rounded-[6px] px-3 py-2 text-[13px] font-medium text-foreground transition-colors hover:bg-muted",
18864
- children: "Cancel"
18865
- }
18866
- ),
18867
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
18868
- "button",
18869
- {
18870
- type: "button",
18871
- onClick: save,
18872
- className: "rounded-[6px] bg-bridge-primary px-3 py-2 text-[13px] font-medium text-primary-foreground transition-opacity hover:opacity-90",
18873
- children: "Save"
18874
- }
18875
- )
18876
- ] })
18877
- ] }) });
18653
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18654
+ "div",
18655
+ {
18656
+ style: {
18657
+ display: "flex",
18658
+ alignItems: "center",
18659
+ gap: 6,
18660
+ height: 28,
18661
+ padding: "0 12px",
18662
+ borderRadius: 999,
18663
+ background: "#ffffff",
18664
+ width: "100%",
18665
+ maxWidth: 260,
18666
+ boxSizing: "border-box",
18667
+ boxShadow: invalid ? "inset 0 0 0 1.5px #EF4444" : "none"
18668
+ },
18669
+ children: [
18670
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18671
+ "span",
18672
+ {
18673
+ style: {
18674
+ flex: "0 0 auto",
18675
+ color: OHW_BLUE_DARK,
18676
+ fontSize: 10,
18677
+ fontWeight: 700,
18678
+ textTransform: "uppercase",
18679
+ letterSpacing: 0.4
18680
+ },
18681
+ children: "Link"
18682
+ }
18683
+ ),
18684
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18685
+ "input",
18686
+ {
18687
+ type: "text",
18688
+ inputMode: "url",
18689
+ value: draft,
18690
+ onChange: (e) => setDraft(e.target.value),
18691
+ onBlur: commit,
18692
+ onKeyDown: (e) => {
18693
+ if (e.key === "Enter") e.currentTarget.blur();
18694
+ if (e.key === "Escape") {
18695
+ setDraft(value);
18696
+ setInvalid(false);
18697
+ e.currentTarget.blur();
18698
+ }
18699
+ },
18700
+ placeholder: "https://example.com",
18701
+ "aria-label": "Image link URL",
18702
+ "aria-invalid": invalid,
18703
+ style: {
18704
+ flex: 1,
18705
+ minWidth: 0,
18706
+ border: "none",
18707
+ outline: "none",
18708
+ background: "transparent",
18709
+ fontSize: 12,
18710
+ lineHeight: "16px",
18711
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
18712
+ color: "#1f2937"
18713
+ }
18714
+ }
18715
+ )
18716
+ ]
18717
+ }
18718
+ );
18878
18719
  }
18879
18720
  function MediaStripToolbar({
18880
18721
  entry,
18881
18722
  index,
18882
18723
  pending,
18883
18724
  error,
18884
- itemEditOn,
18885
- onToggleItemEdit,
18886
18725
  onSetVariant,
18887
18726
  onSetAlign,
18888
18727
  onSetHover,
18889
18728
  onSetTreatment,
18890
18729
  onResize,
18891
- onOpenLink,
18730
+ onSetAlt,
18731
+ onSetLink,
18892
18732
  onAddItem,
18893
18733
  onRemoveItem,
18894
18734
  onChangeImage,
@@ -18897,102 +18737,81 @@ function MediaStripToolbar({
18897
18737
  }) {
18898
18738
  const items = mediaStripItems(entry.tree);
18899
18739
  const itemCount = items.length;
18740
+ const currentAlt = items[index]?.alt ?? "";
18741
+ const currentHref = items[index]?.href ?? "";
18742
+ const currentTarget = items[index]?.linkTarget === "same-tab" ? "same-tab" : "new-tab";
18900
18743
  const slots = findMediaStripSlots(entry.tree);
18901
18744
  const variant = slots?.variant === "reel" ? "reel" : "logos";
18902
18745
  const align = slots?.align === "left" || slots?.align === "right" ? slots.align : "center";
18903
18746
  const hover = slots?.hover === "none" || slots?.hover === "lift" || slots?.hover === "zoom" || slots?.hover === "fade" ? slots.hover : "lift";
18904
18747
  const treatment = slots?.treatment === "color" || slots?.treatment === "grayscale" ? slots.treatment : variant === "logos" ? "grayscale" : "color";
18905
18748
  const sizeValue = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
18906
- const mobile = window.innerWidth <= 480;
18907
- const gatedLabel = (label) => itemEditOn ? label : "Turn on the star to edit this image";
18908
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
18749
+ const isMobile = window.innerWidth <= 480;
18750
+ const rowStyle = isMobile ? {
18751
+ display: "flex",
18752
+ gap: 6,
18753
+ flexWrap: "nowrap",
18754
+ justifyContent: "center",
18755
+ overflowX: "auto",
18756
+ WebkitOverflowScrolling: "touch",
18757
+ width: "100%",
18758
+ scrollbarWidth: "none"
18759
+ } : { display: "flex", gap: 6, flexWrap: "nowrap", justifyContent: "center" };
18760
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18909
18761
  "div",
18910
18762
  {
18911
18763
  "data-ohw-bridge": "",
18912
18764
  "data-ohw-toolbar": "",
18913
18765
  onMouseDown: (e) => e.stopPropagation(),
18914
- className: cn("flex flex-col items-center gap-1.5 font-sans", pending && "pointer-events-none opacity-60"),
18915
- style: { maxWidth: mobile ? "calc(100vw - 24px)" : void 0 },
18766
+ style: {
18767
+ display: "flex",
18768
+ flexDirection: "column",
18769
+ gap: 6,
18770
+ alignItems: "center",
18771
+ background: OHW_BLUE,
18772
+ padding: 8,
18773
+ borderRadius: 12,
18774
+ boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
18775
+ pointerEvents: pending ? "none" : "auto",
18776
+ opacity: pending ? 0.6 : 1,
18777
+ maxWidth: isMobile ? "calc(100vw - 24px)" : "none"
18778
+ },
18916
18779
  children: [
18917
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
18918
- "div",
18919
- {
18920
- className: cn(
18921
- "flex items-center gap-1 rounded-[10px] border border-border bg-background p-1 whitespace-nowrap",
18922
- "shadow-[0px_10px_15px_-3px_rgba(0,0,0,0.1),0px_4px_6px_-4px_rgba(0,0,0,0.1)]",
18923
- mobile ? "flex-wrap justify-center" : "flex-nowrap"
18924
- ),
18925
- children: [
18926
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
18927
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
18928
- CustomToolbarButton,
18929
- {
18930
- type: "button",
18931
- "aria-label": itemEditOn ? "Done editing this image" : "Edit this image",
18932
- "aria-pressed": itemEditOn,
18933
- onClick: onToggleItemEdit,
18934
- className: "group size-8",
18935
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
18936
- import_lucide_react9.Star,
18937
- {
18938
- size: 16,
18939
- fill: "currentColor",
18940
- strokeWidth: 0,
18941
- className: cn(
18942
- "transition-colors",
18943
- itemEditOn ? "text-[#f59e0b] group-hover:text-[#d97706]" : "text-[#d6d3d1] group-hover:text-[#fbbf24]"
18944
- ),
18945
- style: { pointerEvents: "none" }
18946
- }
18947
- )
18948
- }
18949
- ) }),
18950
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: itemEditOn ? "Done editing this image" : "Edit this image" })
18951
- ] }),
18952
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
18953
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: gatedLabel("Change image"), onClick: onChangeImage, disabled: !itemEditOn, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Image, { size: 16, style: { pointerEvents: "none" } }) }),
18954
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: gatedLabel("Link"), onClick: onOpenLink, disabled: !itemEditOn, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Link, { size: 16, style: { pointerEvents: "none" } }) }),
18955
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: gatedLabel("Move left"), onClick: onMoveLeft, disabled: !itemEditOn || index <= 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ArrowLeft, { size: 16, style: { pointerEvents: "none" } }) }),
18956
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
18957
- ToolbarAction,
18958
- {
18959
- label: gatedLabel("Move right"),
18960
- onClick: onMoveRight,
18961
- disabled: !itemEditOn || index >= itemCount - 1,
18962
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ArrowRight, { size: 16, style: { pointerEvents: "none" } })
18963
- }
18964
- ),
18965
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: gatedLabel("Remove image"), onClick: onRemoveItem, disabled: !itemEditOn || itemCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ImageMinus, { size: 16, style: { pointerEvents: "none" } }) }),
18966
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
18967
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(OptionMenu, { label: "Layout", value: variant, options: VARIANTS, onChange: onSetVariant }),
18968
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(OptionMenu, { label: "Hover", value: hover, options: HOVERS, onChange: onSetHover }),
18969
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
18970
- ToolbarAction,
18971
- {
18972
- label: treatment === "grayscale" ? "Show in color" : "Show grayscale",
18973
- active: treatment === "grayscale",
18974
- onClick: () => onSetTreatment(treatment === "grayscale" ? "color" : "grayscale"),
18975
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Palette, { size: 16, style: { pointerEvents: "none" } })
18976
- }
18977
- ),
18978
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
18979
- ALIGNS.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label, active: align === value, onClick: () => onSetAlign(value), children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Icon, { size: 16, style: { pointerEvents: "none" } }) }, value)),
18980
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
18981
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Smaller", onClick: () => onResize(-8), children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Minus, { size: 16, style: { pointerEvents: "none" } }) }),
18982
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "min-w-[40px] text-center text-xs tabular-nums text-muted-foreground", children: [
18983
- sizeValue,
18984
- "px"
18985
- ] }),
18986
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Larger", onClick: () => onResize(8), children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Plus, { size: 16, style: { pointerEvents: "none" } }) }),
18987
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
18988
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Add image", onClick: onAddItem, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ImagePlus, { size: 16, style: { pointerEvents: "none" } }) })
18989
- ]
18990
- }
18991
- ),
18992
- error && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
18780
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: rowStyle, children: [
18781
+ VARIANTS.map((v) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: v.label, active: variant === v.value, onClick: () => onSetVariant(v.value) }, v.value)),
18782
+ ALIGNS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: a.label, active: align === a.value, onClick: () => onSetAlign(a.value) }, a.value))
18783
+ ] }),
18784
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: rowStyle, children: [
18785
+ HOVERS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: h.label, active: hover === h.value, onClick: () => onSetHover(h.value) }, h.value)),
18786
+ TREATMENTS.map((t) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: t.label, active: treatment === t.value, onClick: () => onSetTreatment(t.value) }, t.value))
18787
+ ] }),
18788
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
18789
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "\u2212", onClick: () => onResize(-8) }),
18790
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { style: { color: "#ffffff", fontSize: 12, minWidth: 34, textAlign: "center" }, children: [
18791
+ sizeValue,
18792
+ "px"
18793
+ ] }),
18794
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "+", onClick: () => onResize(8) })
18795
+ ] }),
18796
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { display: "flex", justifyContent: "center", width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(AltInput, { value: currentAlt, onCommit: onSetAlt }) }),
18797
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { display: "flex", justifyContent: "center", width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LinkInput, { value: currentHref, onCommit: (href) => onSetLink(href, currentTarget) }) }),
18798
+ currentHref && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: rowStyle, children: [
18799
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "New tab", active: currentTarget === "new-tab", onClick: () => onSetLink(currentHref, "new-tab") }),
18800
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "Same tab", active: currentTarget === "same-tab", onClick: () => onSetLink(currentHref, "same-tab") })
18801
+ ] }),
18802
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: rowStyle, children: [
18803
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "Change Image", onClick: onChangeImage }),
18804
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "+ Item", onClick: onAddItem }),
18805
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "\u2212 Item", onClick: onRemoveItem, disabled: itemCount <= 1 })
18806
+ ] }),
18807
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: rowStyle, children: [
18808
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "\u2190 Move", onClick: onMoveLeft, disabled: index <= 0 }),
18809
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "\u2192 Move", onClick: onMoveRight, disabled: index >= itemCount - 1 })
18810
+ ] }),
18811
+ error && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
18993
18812
  ]
18994
18813
  }
18995
- ) });
18814
+ );
18996
18815
  }
18997
18816
  function findMediaStripSlots(tree) {
18998
18817
  const stack = tree.rows.flatMap((row) => row.blocks);
@@ -19011,23 +18830,31 @@ function AiMediaStripOverlay({
19011
18830
  }) {
19012
18831
  const [hover, setHover] = (0, import_react10.useState)(null);
19013
18832
  const [active, setActive] = (0, import_react10.useState)(null);
19014
- const [itemEditOn, setItemEditOn] = (0, import_react10.useState)(false);
19015
- const [linkOpen, setLinkOpen] = (0, import_react10.useState)(false);
19016
18833
  const [pending, setPending] = (0, import_react10.useState)(false);
19017
18834
  const [error, setError] = (0, import_react10.useState)(null);
19018
18835
  const activeRef = (0, import_react10.useRef)(null);
19019
18836
  activeRef.current = active;
19020
- const linkOpenRef = (0, import_react10.useRef)(false);
19021
- linkOpenRef.current = linkOpen;
19022
18837
  (0, import_react10.useEffect)(() => {
19023
18838
  const onMove = (e) => {
19024
- const ref = itemRefFromTarget(e.target);
19025
18839
  const t = e.target;
19026
- const overEditable = Boolean(t?.closest?.("[data-ohw-editable]"));
19027
- setHover((prev) => {
19028
- const next = ref && !overEditable ? ref : null;
19029
- return sameRef(prev, next) ? prev : next;
19030
- });
18840
+ if (!(t instanceof HTMLElement)) return;
18841
+ if (t.closest("[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-link-modal-root], [data-ohw-section-picker], [data-ohw-ai-review]")) {
18842
+ return;
18843
+ }
18844
+ const itemEl = t.closest("[data-ohw-media-strip-item]");
18845
+ if (!itemEl) {
18846
+ setHover(null);
18847
+ return;
18848
+ }
18849
+ const sectionEl = itemEl.closest("[data-ohw-section]");
18850
+ const unit = sectionEl ? movableUnit(sectionEl) : null;
18851
+ const sectionId = unit ? instanceIdOf(unit) || null : null;
18852
+ const index = Number(itemEl.dataset.ohwMediaStripIndex);
18853
+ if (!sectionId || Number.isNaN(index)) {
18854
+ setHover(null);
18855
+ return;
18856
+ }
18857
+ setHover({ sectionId, index });
19031
18858
  };
19032
18859
  const onLeave = () => setHover(null);
19033
18860
  document.addEventListener("mousemove", onMove, { passive: true });
@@ -19039,25 +18866,23 @@ function AiMediaStripOverlay({
19039
18866
  }, []);
19040
18867
  (0, import_react10.useEffect)(() => {
19041
18868
  const onClick = (e) => {
18869
+ const current = activeRef.current;
18870
+ if (!current) return;
19042
18871
  const t = e.target;
19043
- if (!(t instanceof Element)) return;
19044
- if (t.closest(CHROME_SELECTOR)) return;
19045
- const ref = itemRefFromTarget(t);
19046
- if (sameRef(activeRef.current, ref)) return;
19047
- if (ref) clearInteractionChromeRef.current();
19048
- setItemEditOn(false);
18872
+ if (!(t instanceof HTMLElement)) return;
18873
+ if (t.closest("[data-ohw-item-interaction]")) return;
18874
+ const itemEl = findMediaStripItemElement(current.sectionId, current.index);
18875
+ if (itemEl && itemEl.contains(t)) return;
18876
+ setActive(null);
19049
18877
  setError(null);
19050
- setActive(ref);
19051
18878
  };
19052
18879
  document.addEventListener("click", onClick, true);
19053
18880
  return () => document.removeEventListener("click", onClick, true);
19054
- }, [clearInteractionChromeRef]);
18881
+ }, []);
19055
18882
  (0, import_react10.useEffect)(() => {
19056
18883
  const onBlur = () => {
19057
18884
  if (!activeRef.current) return;
19058
18885
  setActive(null);
19059
- setItemEditOn(false);
19060
- setLinkOpen(false);
19061
18886
  setError(null);
19062
18887
  };
19063
18888
  window.addEventListener("blur", onBlur);
@@ -19067,10 +18892,7 @@ function AiMediaStripOverlay({
19067
18892
  const onKeyDown = (e) => {
19068
18893
  if (e.key !== "Escape") return;
19069
18894
  if (!activeRef.current) return;
19070
- if (linkOpenRef.current) return;
19071
- if (document.querySelector('[data-ohw-more-menu][data-state="open"]')) return;
19072
18895
  setActive(null);
19073
- setItemEditOn(false);
19074
18896
  setError(null);
19075
18897
  };
19076
18898
  document.addEventListener("keydown", onKeyDown);
@@ -19104,133 +18926,137 @@ function AiMediaStripOverlay({
19104
18926
  tree: nextTree,
19105
18927
  label: entry.label,
19106
18928
  media: extraMedia ? { ...entry.media, ...extraMedia } : entry.media,
19107
- ...entry.brand ? { brand: entry.brand } : {},
19108
- // The user is already looking at the strip they just edited — recentering the viewport
19109
- // on every edit reads as the section jumping around.
19110
- skipScrollIntoView: true
18929
+ ...entry.brand ? { brand: entry.brand } : {}
19111
18930
  });
19112
18931
  return true;
19113
18932
  },
19114
18933
  [findEntry, postToParent2, applyTreeLocalRef]
19115
18934
  );
19116
18935
  const activeEntry = active ? findEntry(active.sectionId) : null;
18936
+ const hoverHandleRect = useLiveItemRect(!active && hover ? hover : null);
19117
18937
  const activeItemRect = useLiveItemRect(active);
19118
- const activeStripRect = useLiveItemRect(active, true);
19119
- const hoverRect = useLiveItemRect(hover && !sameRef(hover, active) ? hover : null);
19120
- const activeItems = activeEntry ? mediaStripItems(activeEntry.tree) : [];
19121
- const activeItem = active ? activeItems[active.index] : void 0;
19122
- const activeHref = activeItem?.href ?? "";
19123
- const activeTarget = activeItem?.linkTarget === "same-tab" ? "same-tab" : "new-tab";
19124
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19125
- hoverRect && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
19126
- activeItemRect && activeStripRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19127
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: activeItemRect, state: "active-top" }),
19128
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StripToolbarSlot, { stripRect: activeStripRect, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19129
- MediaStripToolbar,
19130
- {
19131
- entry: activeEntry,
19132
- index: active.index,
19133
- pending,
19134
- error,
19135
- itemEditOn,
19136
- onToggleItemEdit: () => setItemEditOn((on) => !on),
19137
- onSetVariant: (variant) => runMutation(active.sectionId, (tree) => setMediaStripVariant(tree, variant)),
19138
- onSetAlign: (align) => runMutation(active.sectionId, (tree) => setMediaStripAlign(tree, align)),
19139
- onSetHover: (hoverPreset) => runMutation(active.sectionId, (tree) => setMediaStripHover(tree, hoverPreset)),
19140
- onSetTreatment: (treatment) => runMutation(active.sectionId, (tree) => setMediaStripTreatment(tree, treatment)),
19141
- onResize: (delta) => {
19142
- const slots = findMediaStripSlots(activeEntry.tree);
19143
- const variant = slots?.variant === "reel" ? "reel" : "logos";
19144
- const current = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
19145
- void runMutation(active.sectionId, (tree) => setMediaStripSizing(tree, current + delta));
19146
- },
19147
- onOpenLink: () => setLinkOpen(true),
19148
- onAddItem: () => {
19149
- void requestAssetPick(postToParent2).then((picked) => {
19150
- if (!picked) return;
19151
- const item = { assetId: picked.assetId, alt: "Logo" };
18938
+ const TOOLBAR_APPROX_H = 170;
18939
+ const toolbarState = activeItemRect && activeItemRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
18940
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
18941
+ hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18942
+ "button",
18943
+ {
18944
+ type: "button",
18945
+ "data-ohw-toolbar": "",
18946
+ onClick: () => {
18947
+ clearInteractionChromeRef.current();
18948
+ setActive(hover);
18949
+ },
18950
+ "aria-label": "Configure media item",
18951
+ style: {
18952
+ position: "fixed",
18953
+ top: hoverHandleRect.top + 8,
18954
+ left: hoverHandleRect.left + hoverHandleRect.width - 36,
18955
+ width: 28,
18956
+ height: 28,
18957
+ borderRadius: 8,
18958
+ border: `1.5px dashed ${PRIMARY3}`,
18959
+ background: "#ffffff",
18960
+ color: PRIMARY3,
18961
+ display: "flex",
18962
+ alignItems: "center",
18963
+ justifyContent: "center",
18964
+ cursor: "pointer",
18965
+ zIndex: 2147483643,
18966
+ fontSize: 14,
18967
+ fontWeight: 700
18968
+ },
18969
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react8.Star, { size: 14, style: { pointerEvents: "none" } })
18970
+ }
18971
+ ),
18972
+ activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18973
+ ItemInteractionLayer,
18974
+ {
18975
+ rect: activeItemRect,
18976
+ state: toolbarState,
18977
+ toolbar: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18978
+ MediaStripToolbar,
18979
+ {
18980
+ entry: activeEntry,
18981
+ index: active.index,
18982
+ pending,
18983
+ error,
18984
+ onSetVariant: (variant) => runMutation(active.sectionId, (tree) => setMediaStripVariant(tree, variant)),
18985
+ onSetAlign: (align) => runMutation(active.sectionId, (tree) => setMediaStripAlign(tree, align)),
18986
+ onSetHover: (hoverPreset) => runMutation(active.sectionId, (tree) => setMediaStripHover(tree, hoverPreset)),
18987
+ onSetTreatment: (treatment) => runMutation(active.sectionId, (tree) => setMediaStripTreatment(tree, treatment)),
18988
+ onResize: (delta) => {
18989
+ const slots = findMediaStripSlots(activeEntry.tree);
18990
+ const variant = slots?.variant === "reel" ? "reel" : "logos";
18991
+ const current = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
18992
+ void runMutation(active.sectionId, (tree) => setMediaStripSizing(tree, current + delta));
18993
+ },
18994
+ onSetAlt: (alt) => {
18995
+ void runMutation(active.sectionId, (tree) => setMediaStripItemAlt(tree, active.index, alt));
18996
+ },
18997
+ onSetLink: (href, linkTarget) => {
19152
18998
  void runMutation(
19153
18999
  active.sectionId,
19154
- (tree) => addMediaStripItem(tree, item, active.index),
19155
- { [picked.assetId]: picked.url }
19156
- ).then((applied) => {
19157
- if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19000
+ (tree) => setMediaStripItemLink(tree, active.index, href ? { href, linkTarget } : {})
19001
+ );
19002
+ },
19003
+ onAddItem: () => {
19004
+ void requestAssetPick(postToParent2).then((picked) => {
19005
+ if (!picked) return;
19006
+ const item = { assetId: picked.assetId, alt: "Logo" };
19007
+ void runMutation(
19008
+ active.sectionId,
19009
+ (tree) => addMediaStripItem(tree, item, active.index),
19010
+ { [picked.assetId]: picked.url }
19011
+ ).then((applied) => {
19012
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19013
+ });
19158
19014
  });
19159
- });
19160
- },
19161
- onChangeImage: () => {
19162
- void requestAssetPick(postToParent2).then((picked) => {
19163
- if (!picked) return;
19164
- void runMutation(
19165
- active.sectionId,
19166
- (tree) => setMediaStripItemAsset(tree, active.index, picked.assetId),
19167
- { [picked.assetId]: picked.url }
19015
+ },
19016
+ onChangeImage: () => {
19017
+ void requestAssetPick(postToParent2).then((picked) => {
19018
+ if (!picked) return;
19019
+ void runMutation(
19020
+ active.sectionId,
19021
+ (tree) => setMediaStripItemAsset(tree, active.index, picked.assetId),
19022
+ { [picked.assetId]: picked.url }
19023
+ );
19024
+ });
19025
+ },
19026
+ onRemoveItem: () => {
19027
+ void runMutation(active.sectionId, (tree) => removeMediaStripItem(tree, active.index)).then(
19028
+ (applied) => {
19029
+ if (applied) setActive(null);
19030
+ }
19168
19031
  );
19169
- });
19170
- },
19171
- onRemoveItem: () => {
19172
- void runMutation(active.sectionId, (tree) => removeMediaStripItem(tree, active.index)).then(
19173
- (applied) => {
19174
- if (applied) setActive(null);
19175
- }
19176
- );
19177
- },
19178
- onMoveLeft: () => {
19179
- void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, -1)).then(
19180
- (applied) => {
19181
- if (applied) setActive({ sectionId: active.sectionId, index: active.index - 1 });
19182
- }
19183
- );
19184
- },
19185
- onMoveRight: () => {
19186
- void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, 1)).then(
19187
- (applied) => {
19188
- if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19189
- }
19190
- );
19191
- }
19192
- }
19193
- ) }),
19194
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19195
- "div",
19196
- {
19197
- "data-ohw-bridge": "",
19198
- "data-ohw-item-interaction": "",
19199
- "aria-hidden": true,
19200
- className: "pointer-events-none fixed rounded-[4px] px-[7px] py-[3px] font-sans text-[11px] font-medium leading-none text-primary-foreground whitespace-nowrap",
19201
- style: {
19202
- top: activeItemRect.top + activeItemRect.height + 10,
19203
- left: activeItemRect.left - 5,
19204
- background: PRIMARY3,
19205
- zIndex: 2147483644
19206
- },
19207
- children: "Media item"
19208
- }
19209
- ),
19210
- linkOpen && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19211
- LinkModal,
19212
- {
19213
- href: activeHref,
19214
- linkTarget: activeTarget,
19215
- onClose: () => setLinkOpen(false),
19216
- onSave: (href, linkTarget) => {
19217
- void runMutation(
19218
- active.sectionId,
19219
- (tree) => setMediaStripItemLink(tree, active.index, href ? { href, linkTarget } : {})
19220
- );
19032
+ },
19033
+ onMoveLeft: () => {
19034
+ void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, -1)).then(
19035
+ (applied) => {
19036
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index - 1 });
19037
+ }
19038
+ );
19039
+ },
19040
+ onMoveRight: () => {
19041
+ void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, 1)).then(
19042
+ (applied) => {
19043
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19044
+ }
19045
+ );
19046
+ }
19221
19047
  }
19222
- }
19223
- )
19224
- ] })
19048
+ )
19049
+ }
19050
+ )
19225
19051
  ] });
19226
19052
  }
19227
19053
 
19228
19054
  // src/ui/ai-section/AiPricingCardOverlay.tsx
19229
19055
  var import_react11 = require("react");
19230
- var import_lucide_react10 = require("lucide-react");
19231
- var import_jsx_runtime22 = require("react/jsx-runtime");
19056
+ var import_lucide_react9 = require("lucide-react");
19057
+ var import_jsx_runtime21 = require("react/jsx-runtime");
19232
19058
  var PRIMARY4 = "var(--ohw-primary, #0885FE)";
19233
- var CHROME_SELECTOR2 = "[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-link-modal-root], [data-ohw-section-picker], [data-ohw-ai-review], [data-ohw-more-menu]";
19059
+ var CHROME_SELECTOR = "[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-link-modal-root], [data-ohw-section-picker], [data-ohw-ai-review], [data-ohw-more-menu]";
19234
19060
  function findPricingCardElement(sectionId, index) {
19235
19061
  const sectionEl = findByInstanceId(sectionId);
19236
19062
  if (!sectionEl) return null;
@@ -19242,7 +19068,7 @@ function findPricingCardElement(sectionId, index) {
19242
19068
  }
19243
19069
  function cardRefFromTarget(target) {
19244
19070
  if (!(target instanceof Element)) return null;
19245
- if (target.closest(CHROME_SELECTOR2)) return null;
19071
+ if (target.closest(CHROME_SELECTOR)) return null;
19246
19072
  const cardEl = target.closest("[data-ohw-pricing-card]");
19247
19073
  if (!cardEl) return null;
19248
19074
  const sectionEl = cardEl.closest("[data-ohw-section]");
@@ -19252,21 +19078,21 @@ function cardRefFromTarget(target) {
19252
19078
  if (!sectionId || Number.isNaN(index)) return null;
19253
19079
  return { sectionId, index };
19254
19080
  }
19255
- function sameRef2(a, b) {
19081
+ function sameRef(a, b) {
19256
19082
  return a?.sectionId === b?.sectionId && a?.index === b?.index;
19257
19083
  }
19258
- function rectOf2(el) {
19084
+ function rectOf(el) {
19259
19085
  if (!el) return null;
19260
19086
  const r2 = el.getBoundingClientRect();
19261
19087
  if (r2.width <= 0 || r2.height <= 0) return null;
19262
19088
  return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
19263
19089
  }
19264
19090
  function readRect3(ref) {
19265
- return rectOf2(ref && findPricingCardElement(ref.sectionId, ref.index));
19091
+ return rectOf(ref && findPricingCardElement(ref.sectionId, ref.index));
19266
19092
  }
19267
19093
  function readGridRect(ref) {
19268
19094
  const card = ref && findPricingCardElement(ref.sectionId, ref.index);
19269
- return rectOf2(card?.closest("[data-ohw-pricing-collection]") ?? card);
19095
+ return rectOf(card?.closest("[data-ohw-pricing-collection]") ?? card);
19270
19096
  }
19271
19097
  function useLiveCardRect(ref, grid = false) {
19272
19098
  const [rect, setRect] = (0, import_react11.useState)(null);
@@ -19296,7 +19122,7 @@ function useLiveCardRect(ref, grid = false) {
19296
19122
  }
19297
19123
  var TOOLBAR_GRID_GAP = 52;
19298
19124
  var TOOLBAR_GRID_GAP_MOBILE = 42;
19299
- var TOOLBAR_EDGE_MARGIN3 = 4;
19125
+ var TOOLBAR_EDGE_MARGIN2 = 4;
19300
19126
  function GridToolbarSlot({ gridRect, children }) {
19301
19127
  const slotRef = (0, import_react11.useRef)(null);
19302
19128
  const [shift, setShift] = (0, import_react11.useState)(0);
@@ -19308,7 +19134,7 @@ function GridToolbarSlot({ gridRect, children }) {
19308
19134
  const w = slot.offsetWidth;
19309
19135
  if (w <= 0) return;
19310
19136
  const half = w / 2;
19311
- const clamped = Math.max(TOOLBAR_EDGE_MARGIN3 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN3 - half));
19137
+ const clamped = Math.max(TOOLBAR_EDGE_MARGIN2 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half));
19312
19138
  setShift((prev) => Math.abs(prev - (clamped - centerX)) < 0.5 ? prev : clamped - centerX);
19313
19139
  };
19314
19140
  clamp();
@@ -19317,7 +19143,7 @@ function GridToolbarSlot({ gridRect, children }) {
19317
19143
  return () => ro.disconnect();
19318
19144
  }, [centerX, children]);
19319
19145
  const mobile = window.innerWidth <= 480;
19320
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19146
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19321
19147
  "div",
19322
19148
  {
19323
19149
  ref: slotRef,
@@ -19352,7 +19178,7 @@ function requestValidateTree2(postToParent2, tree) {
19352
19178
  });
19353
19179
  }
19354
19180
  function StyleSwatch({ style }) {
19355
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19181
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19356
19182
  "span",
19357
19183
  {
19358
19184
  "aria-hidden": true,
@@ -19366,35 +19192,35 @@ function StyleSwatch({ style }) {
19366
19192
  );
19367
19193
  }
19368
19194
  function ButtonGlyph() {
19369
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("svg", { width: "18", height: "12", viewBox: "0 0 18 12", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
19370
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("rect", { x: "0.75", y: "0.75", width: "16.5", height: "10.5", rx: "3", stroke: "currentColor", strokeWidth: "1.5" }),
19371
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M5.5 6H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
19195
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("svg", { width: "18", height: "12", viewBox: "0 0 18 12", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
19196
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("rect", { x: "0.75", y: "0.75", width: "16.5", height: "10.5", rx: "3", stroke: "currentColor", strokeWidth: "1.5" }),
19197
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M5.5 6H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
19372
19198
  ] });
19373
19199
  }
19374
19200
  function FeatureGlyph({ mode }) {
19375
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
19376
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "8", cy: "8", r: "7.25", stroke: "currentColor", strokeWidth: "1.5" }),
19377
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M5 8.2L7.2 10.3L11 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
19378
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "13.5", cy: "13.5", r: "4.25", fill: "var(--ohw-background, #ffffff)", stroke: "var(--ohw-background, #ffffff)", strokeWidth: "1" }),
19379
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "13.5", cy: "13.5", r: "3.75", fill: "var(--ohw-background, #ffffff)", stroke: PRIMARY4, strokeWidth: "1" }),
19380
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M11.5 13.5H15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" }),
19381
- mode === "add" && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M13.5 11.5V15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" })
19201
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
19202
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("circle", { cx: "8", cy: "8", r: "7.25", stroke: "currentColor", strokeWidth: "1.5" }),
19203
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M5 8.2L7.2 10.3L11 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
19204
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("circle", { cx: "13.5", cy: "13.5", r: "4.25", fill: "var(--ohw-background, #ffffff)", stroke: "var(--ohw-background, #ffffff)", strokeWidth: "1" }),
19205
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("circle", { cx: "13.5", cy: "13.5", r: "3.75", fill: "var(--ohw-background, #ffffff)", stroke: PRIMARY4, strokeWidth: "1" }),
19206
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M11.5 13.5H15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" }),
19207
+ mode === "add" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M13.5 11.5V15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" })
19382
19208
  ] });
19383
19209
  }
19384
- function ToolbarAction2({
19210
+ function ToolbarAction({
19385
19211
  label,
19386
19212
  onClick,
19387
19213
  disabled = false,
19388
19214
  children
19389
19215
  }) {
19390
- const button = /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, onClick, className: "size-8", children });
19391
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Tooltip, { children: [
19392
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "inline-flex", children: button }) : button }),
19393
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
19216
+ const button = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, onClick, className: "size-8", children });
19217
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19218
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "inline-flex", children: button }) : button }),
19219
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
19394
19220
  ] });
19395
19221
  }
19396
- function ToolbarSeparator2() {
19397
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
19222
+ function ToolbarSeparator() {
19223
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
19398
19224
  }
19399
19225
  function CardStyleMenu({
19400
19226
  value,
@@ -19402,7 +19228,7 @@ function CardStyleMenu({
19402
19228
  disabled = false
19403
19229
  }) {
19404
19230
  const current = PRICING_CARD_STYLES.find((s) => s.value === value) ?? PRICING_CARD_STYLES[0];
19405
- const trigger = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19231
+ const trigger = /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19406
19232
  "button",
19407
19233
  {
19408
19234
  type: "button",
@@ -19413,28 +19239,28 @@ function CardStyleMenu({
19413
19239
  disabled && "cursor-not-allowed opacity-40 hover:bg-transparent"
19414
19240
  ),
19415
19241
  children: [
19416
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(StyleSwatch, { style: current.value }),
19417
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "flex-1 text-left", children: current.label }),
19418
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
19242
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StyleSwatch, { style: current.value }),
19243
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "flex-1 text-left", children: current.label }),
19244
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
19419
19245
  ]
19420
19246
  }
19421
19247
  );
19422
19248
  if (disabled) {
19423
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19424
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-xs text-muted-foreground opacity-50", children: "Card style" }),
19425
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Tooltip, { children: [
19426
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "inline-flex", children: trigger }) }),
19427
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: "Mark as featured to edit card style" })
19249
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19250
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-xs text-muted-foreground opacity-50", children: "Card style" }),
19251
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19252
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "inline-flex", children: trigger }) }),
19253
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: "Mark as featured to edit card style" })
19428
19254
  ] })
19429
19255
  ] });
19430
19256
  }
19431
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19432
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-xs text-muted-foreground", children: "Card style" }),
19433
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(DropdownMenu, { modal: false, children: [
19434
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DropdownMenuTrigger, { asChild: true, children: trigger }),
19435
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[152px] min-w-[152px] p-1", children: PRICING_CARD_STYLES.map((style) => {
19257
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19258
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-xs text-muted-foreground", children: "Card style" }),
19259
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DropdownMenu, { modal: false, children: [
19260
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuTrigger, { asChild: true, children: trigger }),
19261
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[152px] min-w-[152px] p-1", children: PRICING_CARD_STYLES.map((style) => {
19436
19262
  const selected = style.value === value;
19437
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19263
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19438
19264
  DropdownMenuItem,
19439
19265
  {
19440
19266
  role: "menuitemradio",
@@ -19442,9 +19268,9 @@ function CardStyleMenu({
19442
19268
  onSelect: () => onChange(style.value),
19443
19269
  className: cn("grid grid-cols-[18px_1fr_16px] gap-2 py-2 pl-2 pr-2 text-[13px]", selected && "bg-muted"),
19444
19270
  children: [
19445
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(StyleSwatch, { style: style.value }),
19446
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: style.label }),
19447
- selected && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Check, { size: 16, className: "text-bridge-primary" })
19271
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StyleSwatch, { style: style.value }),
19272
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: style.label }),
19273
+ selected && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Check, { size: 16, className: "text-bridge-primary" })
19448
19274
  ]
19449
19275
  },
19450
19276
  style.value
@@ -19474,7 +19300,7 @@ function PricingCardToolbar({
19474
19300
  const style = pricingCardStyle(entry.tree, index);
19475
19301
  const featuredTag = pricingCardFeaturedTag(entry.tree, index);
19476
19302
  const mobile = window.innerWidth <= 480;
19477
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19303
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19478
19304
  "div",
19479
19305
  {
19480
19306
  "data-ohw-bridge": "",
@@ -19486,7 +19312,7 @@ function PricingCardToolbar({
19486
19312
  ),
19487
19313
  style: { maxWidth: mobile ? "calc(100vw - 24px)" : void 0 },
19488
19314
  children: [
19489
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19315
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19490
19316
  "div",
19491
19317
  {
19492
19318
  className: cn(
@@ -19495,8 +19321,8 @@ function PricingCardToolbar({
19495
19321
  mobile ? "flex-wrap justify-center" : "flex-nowrap"
19496
19322
  ),
19497
19323
  children: [
19498
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Tooltip, { children: [
19499
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19324
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19325
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19500
19326
  CustomToolbarButton,
19501
19327
  {
19502
19328
  type: "button",
@@ -19504,8 +19330,8 @@ function PricingCardToolbar({
19504
19330
  "aria-pressed": featuredTag,
19505
19331
  onClick: () => onSetFeaturedTag(!featuredTag),
19506
19332
  className: "group size-8",
19507
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19508
- import_lucide_react10.Star,
19333
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19334
+ import_lucide_react9.Star,
19509
19335
  {
19510
19336
  size: 16,
19511
19337
  fill: "currentColor",
@@ -19519,25 +19345,25 @@ function PricingCardToolbar({
19519
19345
  )
19520
19346
  }
19521
19347
  ) }),
19522
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Unmark featured" : "Mark as featured" })
19348
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Unmark featured" : "Mark as featured" })
19523
19349
  ] }),
19524
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarSeparator2, {}),
19525
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CardStyleMenu, { value: style, onChange: onSetStyle, disabled: !featuredTag }),
19526
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarSeparator2, {}),
19527
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Add button", onClick: onAddButton, disabled: buttonCount >= 2, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ButtonGlyph, {}) }),
19528
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Remove button", onClick: onRemoveButton, disabled: buttonCount === 0, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: "relative inline-flex", children: [
19529
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ButtonGlyph, {}),
19530
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Minus, { size: 10, strokeWidth: 2.5, className: "absolute -right-1.5 -top-1.5 rounded-full bg-background text-bridge-primary" })
19350
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19351
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CardStyleMenu, { value: style, onChange: onSetStyle, disabled: !featuredTag }),
19352
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19353
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Add button", onClick: onAddButton, disabled: buttonCount >= 2, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ButtonGlyph, {}) }),
19354
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Remove button", onClick: onRemoveButton, disabled: buttonCount === 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "relative inline-flex", children: [
19355
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ButtonGlyph, {}),
19356
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Minus, { size: 10, strokeWidth: 2.5, className: "absolute -right-1.5 -top-1.5 rounded-full bg-background text-bridge-primary" })
19531
19357
  ] }) }),
19532
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Add feature", onClick: onAddFeature, disabled: featureCount >= 10, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(FeatureGlyph, { mode: "add" }) }),
19533
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Remove feature", onClick: onRemoveFeature, disabled: featureCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(FeatureGlyph, { mode: "remove" }) }),
19534
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarSeparator2, {}),
19535
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Add tier", onClick: onAddTier, disabled: collectionCount >= 4, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Plus, { size: 16, style: { pointerEvents: "none" } }) }),
19536
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Remove tier", onClick: onRemoveTier, disabled: collectionCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Minus, { size: 16, style: { pointerEvents: "none" } }) })
19358
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Add feature", onClick: onAddFeature, disabled: featureCount >= 10, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(FeatureGlyph, { mode: "add" }) }),
19359
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Remove feature", onClick: onRemoveFeature, disabled: featureCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(FeatureGlyph, { mode: "remove" }) }),
19360
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19361
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Add tier", onClick: onAddTier, disabled: collectionCount >= 4, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Plus, { size: 16, style: { pointerEvents: "none" } }) }),
19362
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Remove tier", onClick: onRemoveTier, disabled: collectionCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Minus, { size: 16, style: { pointerEvents: "none" } }) })
19537
19363
  ]
19538
19364
  }
19539
19365
  ),
19540
- error && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
19366
+ error && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
19541
19367
  ]
19542
19368
  }
19543
19369
  ) });
@@ -19571,7 +19397,7 @@ function AiPricingCardOverlay({
19571
19397
  const overEditable = Boolean(t?.closest?.("[data-ohw-editable], [data-ohw-role], a, button"));
19572
19398
  setHover((prev) => {
19573
19399
  const next = ref && !overEditable ? ref : null;
19574
- return sameRef2(prev, next) ? prev : next;
19400
+ return sameRef(prev, next) ? prev : next;
19575
19401
  });
19576
19402
  };
19577
19403
  const onLeave = () => setHover(null);
@@ -19586,10 +19412,10 @@ function AiPricingCardOverlay({
19586
19412
  const onClick = (e) => {
19587
19413
  const t = e.target;
19588
19414
  if (!(t instanceof Element)) return;
19589
- if (t.closest(CHROME_SELECTOR2)) return;
19415
+ if (t.closest(CHROME_SELECTOR)) return;
19590
19416
  const ref = cardRefFromTarget(t);
19591
- setActive((prev) => sameRef2(prev, ref) ? prev : ref);
19592
- if (!ref || !sameRef2(activeRef.current, ref)) setError(null);
19417
+ setActive((prev) => sameRef(prev, ref) ? prev : ref);
19418
+ if (!ref || !sameRef(activeRef.current, ref)) setError(null);
19593
19419
  };
19594
19420
  document.addEventListener("click", onClick, true);
19595
19421
  return () => document.removeEventListener("click", onClick, true);
@@ -19654,12 +19480,12 @@ function AiPricingCardOverlay({
19654
19480
  const activeEntry = active ? findEntry(active.sectionId) : null;
19655
19481
  const activeCardRect = useLiveCardRect(active);
19656
19482
  const activeGridRect = useLiveCardRect(active, true);
19657
- const hoverRect = useLiveCardRect(hover && !sameRef2(hover, active) ? hover : null);
19658
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
19659
- hoverRect && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
19660
- activeCardRect && activeGridRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
19661
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ItemInteractionLayer, { rect: activeCardRect, state: "active-top" }),
19662
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GridToolbarSlot, { gridRect: activeGridRect, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19483
+ const hoverRect = useLiveCardRect(hover && !sameRef(hover, active) ? hover : null);
19484
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19485
+ hoverRect && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
19486
+ activeCardRect && activeGridRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19487
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: activeCardRect, state: "active-top" }),
19488
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(GridToolbarSlot, { gridRect: activeGridRect, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19663
19489
  PricingCardToolbar,
19664
19490
  {
19665
19491
  entry: activeEntry,
@@ -19687,7 +19513,7 @@ function AiPricingCardOverlay({
19687
19513
  onRemoveFeature: () => runMutation(active.sectionId, (tree) => removePricingCardFeature(tree, active.index))
19688
19514
  }
19689
19515
  ) }),
19690
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19516
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19691
19517
  "div",
19692
19518
  {
19693
19519
  "data-ohw-bridge": "",
@@ -19709,7 +19535,7 @@ function AiPricingCardOverlay({
19709
19535
 
19710
19536
  // src/ui/ai-section/AiVideoOverlay.tsx
19711
19537
  var import_react12 = require("react");
19712
- var import_lucide_react11 = require("lucide-react");
19538
+ var import_lucide_react10 = require("lucide-react");
19713
19539
 
19714
19540
  // src/lib/ai-video-tree.ts
19715
19541
  function resolveVideoBlock(tree, path) {
@@ -19780,10 +19606,10 @@ function extractYouTubeId(input) {
19780
19606
  }
19781
19607
 
19782
19608
  // src/ui/ai-section/AiVideoOverlay.tsx
19783
- var import_jsx_runtime23 = require("react/jsx-runtime");
19609
+ var import_jsx_runtime22 = require("react/jsx-runtime");
19784
19610
  var PRIMARY5 = "#0885FE";
19785
- var OHW_BLUE = `var(--ohw-primary, ${PRIMARY5})`;
19786
- var OHW_BLUE_DARK = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY5}) 60%, #000000)`;
19611
+ var OHW_BLUE2 = `var(--ohw-primary, ${PRIMARY5})`;
19612
+ var OHW_BLUE_DARK2 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY5}) 60%, #000000)`;
19787
19613
  function findVideoBlockElement(sectionId, path) {
19788
19614
  const sectionEl = findByInstanceId(sectionId);
19789
19615
  if (!sectionEl) return null;
@@ -19843,12 +19669,12 @@ function requestValidateTree3(postToParent2, tree) {
19843
19669
  postToParent2({ type: "ow:ai-validate-tree-request", id, tree });
19844
19670
  });
19845
19671
  }
19846
- function Pill({
19672
+ function Pill2({
19847
19673
  label,
19848
19674
  onClick,
19849
19675
  disabled
19850
19676
  }) {
19851
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
19677
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19852
19678
  "button",
19853
19679
  {
19854
19680
  type: "button",
@@ -19864,7 +19690,7 @@ function Pill({
19864
19690
  border: "none",
19865
19691
  cursor: disabled ? "default" : "pointer",
19866
19692
  background: "#ffffff",
19867
- color: OHW_BLUE_DARK,
19693
+ color: OHW_BLUE_DARK2,
19868
19694
  opacity: disabled ? 0.4 : 1,
19869
19695
  fontSize: 12,
19870
19696
  lineHeight: "16px",
@@ -19907,8 +19733,8 @@ function VideoUrlInput({ value, onCommit }) {
19907
19733
  onCommit(id);
19908
19734
  }
19909
19735
  };
19910
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 4, width: "100%", maxWidth: 280 }, children: [
19911
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
19736
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 4, width: "100%", maxWidth: 280 }, children: [
19737
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19912
19738
  "div",
19913
19739
  {
19914
19740
  style: {
@@ -19924,12 +19750,12 @@ function VideoUrlInput({ value, onCommit }) {
19924
19750
  boxShadow: invalid ? "inset 0 0 0 1.5px #EF4444" : "none"
19925
19751
  },
19926
19752
  children: [
19927
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
19753
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19928
19754
  "span",
19929
19755
  {
19930
19756
  style: {
19931
19757
  flex: "0 0 auto",
19932
- color: OHW_BLUE_DARK,
19758
+ color: OHW_BLUE_DARK2,
19933
19759
  fontSize: 10,
19934
19760
  lineHeight: "16px",
19935
19761
  fontWeight: 700,
@@ -19939,7 +19765,7 @@ function VideoUrlInput({ value, onCommit }) {
19939
19765
  children: "Video"
19940
19766
  }
19941
19767
  ),
19942
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
19768
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19943
19769
  "input",
19944
19770
  {
19945
19771
  type: "text",
@@ -19976,7 +19802,7 @@ function VideoUrlInput({ value, onCommit }) {
19976
19802
  ]
19977
19803
  }
19978
19804
  ),
19979
- invalid && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, textAlign: "center" }, children: "That doesn't look like a YouTube link \u2014 try pasting the video URL again." })
19805
+ invalid && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, textAlign: "center" }, children: "That doesn't look like a YouTube link \u2014 try pasting the video URL again." })
19980
19806
  ] });
19981
19807
  }
19982
19808
  function VideoToolbar({
@@ -19986,7 +19812,7 @@ function VideoToolbar({
19986
19812
  onSetVideoId,
19987
19813
  onRemove
19988
19814
  }) {
19989
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
19815
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19990
19816
  "div",
19991
19817
  {
19992
19818
  "data-ohw-bridge": "",
@@ -19998,7 +19824,7 @@ function VideoToolbar({
19998
19824
  gap: 6,
19999
19825
  alignItems: "center",
20000
19826
  width: 280,
20001
- background: OHW_BLUE,
19827
+ background: OHW_BLUE2,
20002
19828
  padding: 8,
20003
19829
  borderRadius: 12,
20004
19830
  boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
@@ -20006,9 +19832,9 @@ function VideoToolbar({
20006
19832
  opacity: pending ? 0.6 : 1
20007
19833
  },
20008
19834
  children: [
20009
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(VideoUrlInput, { value: videoId, onCommit: onSetVideoId }),
20010
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Pill, { label: "Remove video", onClick: onRemove, disabled: !videoId }),
20011
- error && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
19835
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VideoUrlInput, { value: videoId, onCommit: onSetVideoId }),
19836
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Pill2, { label: "Remove video", onClick: onRemove, disabled: !videoId }),
19837
+ error && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20012
19838
  ]
20013
19839
  }
20014
19840
  );
@@ -20128,8 +19954,8 @@ function AiVideoOverlay({
20128
19954
  const activeBlockRect = useLiveBlockRect(active);
20129
19955
  const TOOLBAR_APPROX_H = 90;
20130
19956
  const toolbarState = activeBlockRect && activeBlockRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
20131
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
20132
- hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
19957
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
19958
+ hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20133
19959
  "button",
20134
19960
  {
20135
19961
  type: "button",
@@ -20157,15 +19983,15 @@ function AiVideoOverlay({
20157
19983
  fontSize: 14,
20158
19984
  fontWeight: 700
20159
19985
  },
20160
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Star, { size: 14, style: { pointerEvents: "none" } })
19986
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Star, { size: 14, style: { pointerEvents: "none" } })
20161
19987
  }
20162
19988
  ),
20163
- activeBlockRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
19989
+ activeBlockRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20164
19990
  ItemInteractionLayer,
20165
19991
  {
20166
19992
  rect: activeBlockRect,
20167
19993
  state: toolbarState,
20168
- toolbar: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
19994
+ toolbar: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20169
19995
  VideoToolbar,
20170
19996
  {
20171
19997
  videoId: videoIdAt(activeEntry.tree, active.path),
@@ -20189,7 +20015,7 @@ function AiVideoOverlay({
20189
20015
 
20190
20016
  // src/ui/ai-section/AiFadeCarouselOverlay.tsx
20191
20017
  var import_react13 = require("react");
20192
- var import_lucide_react12 = require("lucide-react");
20018
+ var import_lucide_react11 = require("lucide-react");
20193
20019
 
20194
20020
  // src/lib/ai-carousel-tree.ts
20195
20021
  function isCarouselBlock(node) {
@@ -20240,10 +20066,10 @@ function setCarouselAutoplay(tree, on) {
20240
20066
  }
20241
20067
 
20242
20068
  // src/ui/ai-section/AiFadeCarouselOverlay.tsx
20243
- var import_jsx_runtime24 = require("react/jsx-runtime");
20069
+ var import_jsx_runtime23 = require("react/jsx-runtime");
20244
20070
  var PRIMARY6 = "#0885FE";
20245
- var OHW_BLUE2 = `var(--ohw-primary, ${PRIMARY6})`;
20246
- var OHW_BLUE_DARK2 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY6}) 60%, #000000)`;
20071
+ var OHW_BLUE3 = `var(--ohw-primary, ${PRIMARY6})`;
20072
+ var OHW_BLUE_DARK3 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY6}) 60%, #000000)`;
20247
20073
  function findCarouselItemElement(sectionId, index) {
20248
20074
  const sectionEl = findByInstanceId(sectionId);
20249
20075
  if (!sectionEl) return null;
@@ -20322,8 +20148,8 @@ function requestAssetPick2(postToParent2) {
20322
20148
  postToParent2({ type: "ow:asset-pick", id });
20323
20149
  });
20324
20150
  }
20325
- function Pill2({ label, onClick, disabled }) {
20326
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20151
+ function Pill3({ label, onClick, disabled }) {
20152
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20327
20153
  "button",
20328
20154
  {
20329
20155
  type: "button",
@@ -20339,7 +20165,7 @@ function Pill2({ label, onClick, disabled }) {
20339
20165
  border: "none",
20340
20166
  cursor: disabled ? "default" : "pointer",
20341
20167
  background: "#ffffff",
20342
- color: OHW_BLUE_DARK2,
20168
+ color: OHW_BLUE_DARK3,
20343
20169
  opacity: disabled ? 0.4 : 1,
20344
20170
  fontSize: 12,
20345
20171
  lineHeight: "16px",
@@ -20357,7 +20183,7 @@ function TogglePill({
20357
20183
  onToggle,
20358
20184
  disabled
20359
20185
  }) {
20360
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
20186
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
20361
20187
  "button",
20362
20188
  {
20363
20189
  type: "button",
@@ -20376,7 +20202,7 @@ function TogglePill({
20376
20202
  border: "none",
20377
20203
  cursor: disabled ? "default" : "pointer",
20378
20204
  background: "#ffffff",
20379
- color: OHW_BLUE_DARK2,
20205
+ color: OHW_BLUE_DARK3,
20380
20206
  opacity: disabled ? 0.4 : 1,
20381
20207
  fontSize: 12,
20382
20208
  lineHeight: "16px",
@@ -20386,7 +20212,7 @@ function TogglePill({
20386
20212
  },
20387
20213
  children: [
20388
20214
  label,
20389
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20215
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20390
20216
  "span",
20391
20217
  {
20392
20218
  "aria-hidden": true,
@@ -20399,10 +20225,10 @@ function TogglePill({
20399
20225
  // Off is a grey track, not a washed-out blue: the pill's own white background already
20400
20226
  // carries the brand colour in the text, and a pale blue track next to it read as
20401
20227
  // "disabled control" rather than "switch that is off".
20402
- background: on ? OHW_BLUE_DARK2 : "#CBD5E1",
20228
+ background: on ? OHW_BLUE_DARK3 : "#CBD5E1",
20403
20229
  transition: "background 120ms ease"
20404
20230
  },
20405
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20231
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20406
20232
  "span",
20407
20233
  {
20408
20234
  style: {
@@ -20431,7 +20257,7 @@ function CarouselSlideToolbar({
20431
20257
  onChangeImage,
20432
20258
  onToggleAutoplay
20433
20259
  }) {
20434
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
20260
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
20435
20261
  "div",
20436
20262
  {
20437
20263
  "data-ohw-bridge": "",
@@ -20442,7 +20268,7 @@ function CarouselSlideToolbar({
20442
20268
  flexDirection: "column",
20443
20269
  gap: 6,
20444
20270
  alignItems: "center",
20445
- background: OHW_BLUE2,
20271
+ background: OHW_BLUE3,
20446
20272
  padding: 8,
20447
20273
  borderRadius: 12,
20448
20274
  boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
@@ -20454,11 +20280,11 @@ function CarouselSlideToolbar({
20454
20280
  pointerEvents: pending ? "none" : "auto"
20455
20281
  },
20456
20282
  children: [
20457
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
20458
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Pill2, { label: "Change Image", onClick: onChangeImage }),
20459
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(TogglePill, { label: "Autoplay", on: autoplay, onToggle: onToggleAutoplay })
20283
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
20284
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Pill3, { label: "Change Image", onClick: onChangeImage }),
20285
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(TogglePill, { label: "Autoplay", on: autoplay, onToggle: onToggleAutoplay })
20460
20286
  ] }),
20461
- error && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20287
+ error && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20462
20288
  ]
20463
20289
  }
20464
20290
  );
@@ -20598,8 +20424,8 @@ function AiFadeCarouselOverlay({
20598
20424
  const activeItemRect = useLiveItemRect2(active);
20599
20425
  const TOOLBAR_APPROX_H = 90;
20600
20426
  const toolbarState = activeItemRect && activeItemRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
20601
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
20602
- hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20427
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
20428
+ hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20603
20429
  "button",
20604
20430
  {
20605
20431
  type: "button",
@@ -20628,10 +20454,10 @@ function AiFadeCarouselOverlay({
20628
20454
  fontSize: 14,
20629
20455
  fontWeight: 700
20630
20456
  },
20631
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.Star, { size: 14, style: { pointerEvents: "none" } })
20457
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Star, { size: 14, style: { pointerEvents: "none" } })
20632
20458
  }
20633
20459
  ),
20634
- swapping && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20460
+ swapping && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20635
20461
  "div",
20636
20462
  {
20637
20463
  ref: skeletonRef,
@@ -20649,15 +20475,15 @@ function AiFadeCarouselOverlay({
20649
20475
  zIndex: 2147483646,
20650
20476
  pointerEvents: "none"
20651
20477
  },
20652
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("style", { children: MEDIA_SKELETON_CSS })
20478
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("style", { children: MEDIA_SKELETON_CSS })
20653
20479
  }
20654
20480
  ),
20655
- activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20481
+ activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20656
20482
  ItemInteractionLayer,
20657
20483
  {
20658
20484
  rect: activeItemRect,
20659
20485
  state: toolbarState,
20660
- toolbar: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20486
+ toolbar: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20661
20487
  CarouselSlideToolbar,
20662
20488
  {
20663
20489
  pending,
@@ -21010,6 +20836,113 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
21010
20836
  // src/ui/link-modal/LinkPopover.tsx
21011
20837
  var import_react17 = require("react");
21012
20838
 
20839
+ // src/ui/dialog.tsx
20840
+ var React9 = __toESM(require("react"), 1);
20841
+ var import_radix_ui6 = require("radix-ui");
20842
+ var import_lucide_react12 = require("lucide-react");
20843
+ var import_jsx_runtime24 = require("react/jsx-runtime");
20844
+ function Dialog2({
20845
+ ...props
20846
+ }) {
20847
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_radix_ui6.Dialog.Root, { "data-slot": "dialog", ...props });
20848
+ }
20849
+ function DialogPortal({
20850
+ ...props
20851
+ }) {
20852
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_radix_ui6.Dialog.Portal, { ...props });
20853
+ }
20854
+ function DialogOverlay({
20855
+ className,
20856
+ ...props
20857
+ }) {
20858
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20859
+ import_radix_ui6.Dialog.Overlay,
20860
+ {
20861
+ "data-slot": "dialog-overlay",
20862
+ "data-ohw-link-modal-root": "",
20863
+ className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
20864
+ ...props
20865
+ }
20866
+ );
20867
+ }
20868
+ var DialogContent = React9.forwardRef(
20869
+ ({ className, children, showCloseButton = true, container, ...props }, ref) => {
20870
+ const positionMode = container ? "absolute" : "fixed";
20871
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(DialogPortal, { container: container ?? void 0, children: [
20872
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
20873
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
20874
+ import_radix_ui6.Dialog.Content,
20875
+ {
20876
+ ref,
20877
+ "data-slot": "dialog-content",
20878
+ className: cn(
20879
+ positionMode,
20880
+ "left-1/2 top-1/2 z-[2147483647] flex w-full max-w-[448px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-visible",
20881
+ "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
20882
+ container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
20883
+ className
20884
+ ),
20885
+ onMouseDown: (e) => e.stopPropagation(),
20886
+ ...props,
20887
+ children: [
20888
+ children,
20889
+ showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20890
+ import_radix_ui6.Dialog.Close,
20891
+ {
20892
+ type: "button",
20893
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
20894
+ "aria-label": "Close",
20895
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.X, { size: 16, "aria-hidden": true })
20896
+ }
20897
+ ) : null
20898
+ ]
20899
+ }
20900
+ )
20901
+ ] });
20902
+ }
20903
+ );
20904
+ DialogContent.displayName = import_radix_ui6.Dialog.Content.displayName;
20905
+ function DialogHeader({
20906
+ className,
20907
+ ...props
20908
+ }) {
20909
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
20910
+ }
20911
+ function DialogFooter({
20912
+ className,
20913
+ ...props
20914
+ }) {
20915
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20916
+ "div",
20917
+ {
20918
+ className: cn("flex items-center justify-end gap-2", className),
20919
+ ...props
20920
+ }
20921
+ );
20922
+ }
20923
+ var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20924
+ import_radix_ui6.Dialog.Title,
20925
+ {
20926
+ ref,
20927
+ className: cn(
20928
+ "text-lg font-semibold leading-none tracking-tight text-card-foreground",
20929
+ className
20930
+ ),
20931
+ ...props
20932
+ }
20933
+ ));
20934
+ DialogTitle.displayName = import_radix_ui6.Dialog.Title.displayName;
20935
+ var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20936
+ import_radix_ui6.Dialog.Description,
20937
+ {
20938
+ ref,
20939
+ className: cn("text-sm text-muted-foreground", className),
20940
+ ...props
20941
+ }
20942
+ ));
20943
+ DialogDescription.displayName = import_radix_ui6.Dialog.Description.displayName;
20944
+ var DialogClose = import_radix_ui6.Dialog.Close;
20945
+
21013
20946
  // src/ui/link-modal/LinkEditorPanel.tsx
21014
20947
  var import_lucide_react16 = require("lucide-react");
21015
20948
 
@@ -22113,7 +22046,6 @@ function shouldUseDevFixtures() {
22113
22046
  function getLogoElement(el) {
22114
22047
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
22115
22048
  if (marked) return marked;
22116
- if (el.closest('[data-ohw-editable="icon"]')) return null;
22117
22049
  const root = el.closest("nav, [data-ohw-nav-root], footer");
22118
22050
  if (!root) return null;
22119
22051
  const anchor = el.closest("a");
@@ -24694,7 +24626,7 @@ function getNavigationSelectionParent(el) {
24694
24626
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
24695
24627
  return resolveFooterLinksContainerFrom(el);
24696
24628
  }
24697
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isFooterLinksContainer(el) || isInferredFooterGroup2(el)) {
24629
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
24698
24630
  return getNavigationRoot(el);
24699
24631
  }
24700
24632
  return null;
@@ -24916,6 +24848,7 @@ var ICONS = {
24916
24848
  insertUnorderedList: '<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>',
24917
24849
  insertOrderedList: '<line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>'
24918
24850
  };
24851
+ var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
24919
24852
  var SELECTION_CHROME_GAP2 = 4;
24920
24853
  var TOOLBAR_STROKE_GAP2 = 4;
24921
24854
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -25013,7 +24946,7 @@ function applyVisibleViewport(el, parentScroll) {
25013
24946
  el.style.top = `${top}px`;
25014
24947
  el.style.height = `${height}px`;
25015
24948
  }
25016
- var TOOLBAR_EDGE_MARGIN4 = 4;
24949
+ var TOOLBAR_EDGE_MARGIN3 = 4;
25017
24950
  function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
25018
24951
  const isAbove = transform.includes("translateY(-100%)");
25019
24952
  let visualTop = isAbove ? top - approxH : top;
@@ -25040,8 +24973,8 @@ function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
25040
24973
  function clampToolbarLeft(centerX, toolbarWidth) {
25041
24974
  const half = toolbarWidth / 2;
25042
24975
  return Math.max(
25043
- TOOLBAR_EDGE_MARGIN4 + half,
25044
- Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN4 - half)
24976
+ TOOLBAR_EDGE_MARGIN3 + half,
24977
+ Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN3 - half)
25045
24978
  );
25046
24979
  }
25047
24980
  function calcToolbarPos(rect, parentScroll, toolbarW = 306) {
@@ -25893,6 +25826,21 @@ function OhhwellsBridge() {
25893
25826
  const [isItemDragging, setIsItemDragging] = (0, import_react21.useState)(false);
25894
25827
  const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react21.useState)(false);
25895
25828
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
25829
+ const [floatingPanel, setFloatingPanel] = (0, import_react21.useState)(null);
25830
+ const floatingPanelOpenRef = (0, import_react21.useRef)(false);
25831
+ floatingPanelOpenRef.current = floatingPanel !== null;
25832
+ const floatingPanelRef = (0, import_react21.useRef)(null);
25833
+ floatingPanelRef.current = floatingPanel;
25834
+ const [floatingPanelPos, setFloatingPanelPos] = (0, import_react21.useState)(null);
25835
+ const buttonCollectionDragRef = (0, import_react21.useRef)(null);
25836
+ const buttonDropInsertIndexRef = (0, import_react21.useRef)(null);
25837
+ const [buttonDropSlots, setButtonDropSlots] = (0, import_react21.useState)([]);
25838
+ const [activeButtonDropIndex, setActiveButtonDropIndex] = (0, import_react21.useState)(null);
25839
+ const buttonPointerDragRef = (0, import_react21.useRef)(null);
25840
+ const [buttonStyleTick, setButtonStyleTick] = (0, import_react21.useState)(0);
25841
+ const [logoSizeDraft, setLogoSizeDraft] = (0, import_react21.useState)(null);
25842
+ const [editorViewport, setEditorViewport] = (0, import_react21.useState)("desktop");
25843
+ const [parentScrollSnap, setParentScrollSnap] = (0, import_react21.useState)(null);
25896
25844
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react21.useState)(null);
25897
25845
  const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react21.useState)(null);
25898
25846
  const footerDragRef = (0, import_react21.useRef)(null);
@@ -25918,21 +25866,6 @@ function OhhwellsBridge() {
25918
25866
  const stylesRef = (0, import_react21.useRef)("");
25919
25867
  const appliedDrawerRef = (0, import_react21.useRef)(null);
25920
25868
  const pendingDeleteUndoRef = (0, import_react21.useRef)(null);
25921
- const [floatingPanel, setFloatingPanel] = (0, import_react21.useState)(null);
25922
- const floatingPanelOpenRef = (0, import_react21.useRef)(false);
25923
- const floatingPanelRef = (0, import_react21.useRef)(null);
25924
- floatingPanelRef.current = floatingPanel;
25925
- const setFloatingPanelRef = (0, import_react21.useRef)(setFloatingPanel);
25926
- const [floatingPanelPos, setFloatingPanelPos] = (0, import_react21.useState)(null);
25927
- const buttonCollectionDragRef = (0, import_react21.useRef)(null);
25928
- const buttonDropInsertIndexRef = (0, import_react21.useRef)(null);
25929
- const [buttonDropSlots, setButtonDropSlots] = (0, import_react21.useState)([]);
25930
- const [activeButtonDropIndex, setActiveButtonDropIndex] = (0, import_react21.useState)(null);
25931
- const buttonPointerDragRef = (0, import_react21.useRef)(null);
25932
- const [buttonStyleTick, setButtonStyleTick] = (0, import_react21.useState)(0);
25933
- const [logoSizeDraft, setLogoSizeDraft] = (0, import_react21.useState)(null);
25934
- const [editorViewport, setEditorViewport] = (0, import_react21.useState)("desktop");
25935
- const [parentScrollSnap, setParentScrollSnap] = (0, import_react21.useState)(null);
25936
25869
  const [sitePages, setSitePages] = (0, import_react21.useState)([]);
25937
25870
  const [sectionsByPath, setSectionsByPath] = (0, import_react21.useState)({});
25938
25871
  const sectionsPrefetchGenRef = (0, import_react21.useRef)(0);
@@ -25941,18 +25874,7 @@ function OhhwellsBridge() {
25941
25874
  const linkPopoverOpenRef = (0, import_react21.useRef)(false);
25942
25875
  const linkPopoverGraceUntilRef = (0, import_react21.useRef)(0);
25943
25876
  setLinkPopoverRef.current = setLinkPopover;
25944
- setFloatingPanelRef.current = setFloatingPanel;
25945
25877
  linkPopoverSessionRef.current = linkPopover;
25946
- floatingPanelOpenRef.current = Boolean(floatingPanel);
25947
- (0, import_react21.useEffect)(() => {
25948
- const syncViewport = () => {
25949
- const next = window.innerWidth <= 480 ? "mobile" : "desktop";
25950
- setEditorViewport((prev) => prev === next ? prev : next);
25951
- };
25952
- syncViewport();
25953
- window.addEventListener("resize", syncViewport);
25954
- return () => window.removeEventListener("resize", syncViewport);
25955
- }, []);
25956
25878
  (0, import_react21.useEffect)(() => {
25957
25879
  setSectionOrderPersistHandler((entries) => {
25958
25880
  const currentPath = window.location.pathname;
@@ -27647,7 +27569,6 @@ function OhhwellsBridge() {
27647
27569
  applyAiFooterFromContent(content);
27648
27570
  if (typeof content[STYLE_STORE_KEY] === "string") {
27649
27571
  stylesRef.current = content[STYLE_STORE_KEY];
27650
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
27651
27572
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
27652
27573
  }
27653
27574
  applyBrandChrome(content);
@@ -27658,12 +27579,12 @@ function OhhwellsBridge() {
27658
27579
  if (key === "__ohw_sections") continue;
27659
27580
  if (key === AI_SECTIONS_KEY) continue;
27660
27581
  if (key === AI_FOOTER_KEY) continue;
27661
- if (key === LOGO_PLACEHOLDER_KEY) continue;
27662
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
27663
27582
  if (key === BRAND_KIT_KEY) continue;
27664
27583
  if (key === STYLE_STORE_KEY) continue;
27665
27584
  if (BRAND_CHROME_KEYS.has(key)) continue;
27585
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
27666
27586
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
27587
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
27667
27588
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
27668
27589
  continue;
27669
27590
  }
@@ -27879,18 +27800,17 @@ function OhhwellsBridge() {
27879
27800
  }
27880
27801
  applyAiFooterFromContent(content);
27881
27802
  if (typeof content[STYLE_STORE_KEY] === "string") {
27882
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
27883
27803
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
27884
27804
  }
27885
27805
  for (const [key, val] of Object.entries(content)) {
27886
27806
  if (key === "__ohw_sections") continue;
27887
27807
  if (key === AI_SECTIONS_KEY) continue;
27888
27808
  if (key === AI_FOOTER_KEY) continue;
27889
- if (key === LOGO_PLACEHOLDER_KEY) continue;
27890
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
27891
27809
  if (key === BRAND_KIT_KEY) continue;
27892
27810
  if (key === STYLE_STORE_KEY) continue;
27893
27811
  if (BRAND_CHROME_KEYS.has(key)) continue;
27812
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
27813
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
27894
27814
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
27895
27815
  continue;
27896
27816
  }
@@ -28157,11 +28077,26 @@ function OhhwellsBridge() {
28157
28077
  const t2 = setTimeout(measure, 500);
28158
28078
  const ro = new ResizeObserver(schedule);
28159
28079
  ro.observe(document.body);
28080
+ let lastWidth = window.innerWidth;
28081
+ let resizeTimers = [];
28082
+ const clearResizeTimers = () => {
28083
+ resizeTimers.forEach(clearTimeout);
28084
+ resizeTimers = [];
28085
+ };
28086
+ const handleResize = () => {
28087
+ if (window.innerWidth === lastWidth) return;
28088
+ lastWidth = window.innerWidth;
28089
+ clearResizeTimers();
28090
+ resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
28091
+ };
28092
+ window.addEventListener("resize", handleResize);
28160
28093
  return () => {
28161
28094
  clearTimeout(t1);
28162
28095
  clearTimeout(t2);
28163
28096
  if (raf != null) cancelAnimationFrame(raf);
28164
28097
  ro.disconnect();
28098
+ clearResizeTimers();
28099
+ window.removeEventListener("resize", handleResize);
28165
28100
  };
28166
28101
  }, [pathname, isEditMode, postToParent2]);
28167
28102
  (0, import_react21.useEffect)(() => {
@@ -28458,13 +28393,6 @@ function OhhwellsBridge() {
28458
28393
  if (target.closest("[data-ohw-state-toggle]")) return;
28459
28394
  if (target.closest("[data-ohw-max-badge]")) return;
28460
28395
  if (isInsideLinkEditor(target)) return;
28461
- if (isInsideFloatingPanel(target)) return;
28462
- if (target.closest("[data-ohw-form-toolbar]")) return;
28463
- if (target.closest(
28464
- '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
28465
- )) {
28466
- return;
28467
- }
28468
28396
  if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
28469
28397
  clearMediaSelectionRef.current();
28470
28398
  }
@@ -28488,6 +28416,13 @@ function OhhwellsBridge() {
28488
28416
  return;
28489
28417
  }
28490
28418
  }
28419
+ if (isInsideFloatingPanel(target)) return;
28420
+ if (target.closest("[data-ohw-form-toolbar]")) return;
28421
+ if (target.closest(
28422
+ '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
28423
+ )) {
28424
+ return;
28425
+ }
28491
28426
  {
28492
28427
  const formEl = getFormElement(target);
28493
28428
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -28641,13 +28576,9 @@ function OhhwellsBridge() {
28641
28576
  if (isIconEditable(editable) && !getSocialItem(editable)) {
28642
28577
  e.preventDefault();
28643
28578
  e.stopPropagation();
28579
+ selectIconElementRef.current(editable);
28644
28580
  const aiIcon = editable.hasAttribute("data-ai-icon");
28645
28581
  const featureIcon = aiIcon && /\.features\.\d+\.icon$/.test(editable.dataset.ohwKey ?? "");
28646
- if (aiIcon) {
28647
- aiSectionApiRef.current?.selectFromElement(editable);
28648
- } else {
28649
- selectIconElementRef.current(editable);
28650
- }
28651
28582
  postToParentRef.current({
28652
28583
  type: "ow:icon-pick",
28653
28584
  key: editable.dataset.ohwKey ?? "",
@@ -28875,9 +28806,6 @@ function OhhwellsBridge() {
28875
28806
  setHoveredItemRect(null);
28876
28807
  hoveredNavContainerRef.current = null;
28877
28808
  setHoveredNavContainerRect(null);
28878
- siblingHintElRef.current = null;
28879
- setSiblingHintRect(null);
28880
- setSiblingHintRects([]);
28881
28809
  return;
28882
28810
  }
28883
28811
  {
@@ -28998,6 +28926,7 @@ function OhhwellsBridge() {
28998
28926
  hoveredNavContainerRef.current = null;
28999
28927
  setHoveredNavContainerRect(null);
29000
28928
  hoveredItemElRef.current = editable;
28929
+ setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
29001
28930
  }
29002
28931
  }
29003
28932
  }
@@ -29707,11 +29636,7 @@ function OhhwellsBridge() {
29707
29636
  };
29708
29637
  const handleMouseMove = (e) => {
29709
29638
  const { clientX, clientY } = e;
29710
- if (pointOwnedByFloatingPanel(clientX, clientY)) {
29711
- dismissImageHover();
29712
- clearImageHover();
29713
- return;
29714
- }
29639
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
29715
29640
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) {
29716
29641
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
29717
29642
  formHoverElRef.current = null;
@@ -29740,12 +29665,7 @@ function OhhwellsBridge() {
29740
29665
  setHoveredItemRect(null);
29741
29666
  hoveredNavContainerRef.current = null;
29742
29667
  setHoveredNavContainerRect(null);
29743
- siblingHintElRef.current = null;
29744
- setSiblingHintRect(null);
29745
- setSiblingHintRects([]);
29746
29668
  dismissImageHover();
29747
- clearImageHover();
29748
- setSectionGap(null);
29749
29669
  return;
29750
29670
  }
29751
29671
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -29757,11 +29677,7 @@ function OhhwellsBridge() {
29757
29677
  if (e.data?.type !== "ow:pointer-sync") return;
29758
29678
  const { clientX, clientY } = e.data;
29759
29679
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
29760
- if (pointOwnedByFloatingPanel(clientX, clientY)) {
29761
- dismissImageHover();
29762
- clearImageHover();
29763
- return;
29764
- }
29680
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
29765
29681
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) return;
29766
29682
  if (probeSocialsRowAt(clientX, clientY)) return;
29767
29683
  probeSectionGapAt(clientX, clientY);
@@ -30141,7 +30057,6 @@ function OhhwellsBridge() {
30141
30057
  applyAiFooterFromContent(content);
30142
30058
  if (typeof content[STYLE_STORE_KEY] === "string") {
30143
30059
  stylesRef.current = content[STYLE_STORE_KEY];
30144
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
30145
30060
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
30146
30061
  }
30147
30062
  applyBrandChrome(content);
@@ -30156,12 +30071,12 @@ function OhhwellsBridge() {
30156
30071
  }
30157
30072
  if (key === AI_SECTIONS_KEY) continue;
30158
30073
  if (key === AI_FOOTER_KEY) continue;
30159
- if (key === LOGO_PLACEHOLDER_KEY) continue;
30160
- if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
30161
30074
  if (key === BRAND_KIT_KEY) continue;
30162
30075
  if (key === STYLE_STORE_KEY) continue;
30163
30076
  if (BRAND_CHROME_KEYS.has(key)) continue;
30077
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
30164
30078
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
30079
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
30165
30080
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
30166
30081
  continue;
30167
30082
  }
@@ -30797,12 +30712,6 @@ function OhhwellsBridge() {
30797
30712
  closeLinkPopoverRef.current();
30798
30713
  return;
30799
30714
  }
30800
- if (floatingPanelOpenRef.current) {
30801
- setFloatingPanelRef.current(null);
30802
- deselectRef.current();
30803
- deactivateRef.current();
30804
- return;
30805
- }
30806
30715
  deselectRef.current();
30807
30716
  deactivateRef.current();
30808
30717
  clearMediaSelectionRef.current();
@@ -31625,10 +31534,10 @@ function OhhwellsBridge() {
31625
31534
  window.removeEventListener("message", handleDeleteSection);
31626
31535
  window.removeEventListener("message", handleDuplicateSection);
31627
31536
  window.removeEventListener("message", handleDeactivate);
31537
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
31628
31538
  window.removeEventListener("message", handleToastAction);
31629
31539
  window.removeEventListener("message", handleFormCount);
31630
31540
  window.removeEventListener("message", handleUiEscape);
31631
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
31632
31541
  autoSaveTimers.current.forEach(clearTimeout);
31633
31542
  autoSaveTimers.current.clear();
31634
31543
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
@@ -31922,7 +31831,7 @@ function OhhwellsBridge() {
31922
31831
  postToParent2({
31923
31832
  type: "ow:ready",
31924
31833
  version: "1",
31925
- bridgeVersion: "0.1.151-next.406",
31834
+ bridgeVersion: "0.1.152",
31926
31835
  path: pathname,
31927
31836
  nodes: collectEditableNodes(editContentRef.current),
31928
31837
  sections