@ohhwells/bridge 0.1.149-next.405 → 0.1.150

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
@@ -6637,21 +6637,6 @@ function pricingCardFeatureCount(tree, index) {
6637
6637
  const target = findPricingCollection(tree)?.children?.[index];
6638
6638
  return target ? pricingFeatureList(target).length : 0;
6639
6639
  }
6640
- function prevailingFeatureIcon(features) {
6641
- const counts = /* @__PURE__ */ new Map();
6642
- for (const row of features) {
6643
- const key = `${row.icon ?? ""}|${row.iconColor ?? ""}`;
6644
- const entry = counts.get(key) ?? { count: 0, ...row.icon ? { icon: row.icon } : {}, ...row.iconColor ? { iconColor: row.iconColor } : {} };
6645
- entry.count += 1;
6646
- counts.set(key, entry);
6647
- }
6648
- let best = null;
6649
- for (const entry of counts.values()) {
6650
- if (!best || entry.count > best.count) best = entry;
6651
- }
6652
- if (!best) return {};
6653
- return { ...best.icon ? { icon: best.icon } : {}, ...best.iconColor ? { iconColor: best.iconColor } : {} };
6654
- }
6655
6640
  function addPricingCardFeature(tree, index, text) {
6656
6641
  return withPricingCollection(tree, (collection) => {
6657
6642
  const children = collection.children;
@@ -6660,7 +6645,7 @@ function addPricingCardFeature(tree, index, text) {
6660
6645
  const features = pricingFeatureList(target);
6661
6646
  if (features.length >= PRICING_FEATURES_MAX) return false;
6662
6647
  target.slots = target.slots ?? {};
6663
- target.slots.features = [...features, { text, ...prevailingFeatureIcon(features) }];
6648
+ target.slots.features = [...features, { text }];
6664
6649
  return true;
6665
6650
  });
6666
6651
  }
@@ -7843,7 +7828,7 @@ var AI_MOBILE_CSS = [
7843
7828
  // page's first section. Top only (bottom needs no nav clearance); ~96px approximates the
7844
7829
  // template nav's own height (measured ~94px) — there's no shared `--ohw-header-h` var to read
7845
7830
  // an exact figure from (no live measurement exists), so this is a safe approximation, not a
7846
- // pixel-exact fit. Horizontal spacing already comes from `data-ai-section-inner`.
7831
+ // pixel-exact fit. Horizontal spacing already comes from `data-ai-container`.
7847
7832
  '[data-ai-overlay-clearance] [data-ai-overlap-layer="overlay"]{padding-top:96px}',
7848
7833
  // A full-bleed carousel's height is pure arithmetic: it spans the whole window, so 16/9 turns
7849
7834
  // window width straight into height. Measured at 16/9 it came out ~90% of the viewport at EVERY
@@ -7859,10 +7844,8 @@ var AI_MOBILE_CSS = [
7859
7844
  "[data-ai-section]{overflow-x:hidden}",
7860
7845
  // `:not([data-ai-full-bleed])`: this `!important` gutter beat the inline `padding: 0` that a
7861
7846
  // full-bleed section's container sets, so a carousel/media hero that is edge-to-edge on desktop
7862
- // still got a 20px band of section background down both sides on a phone. The footer's own
7863
- // container keeps the unguarded rule below — `data-ai-full-bleed` can never appear on it.
7864
- "[data-ai-section-inner]:not([data-ai-full-bleed]){padding:0 20px !important}",
7865
- "[data-ai-container]{padding:0 20px !important}",
7847
+ // still got a 20px band of section background down both sides on a phone.
7848
+ "[data-ai-container]:not([data-ai-full-bleed]){padding:0 20px !important}",
7866
7849
  "[data-ai-row]{display:flex !important;flex-direction:column !important;align-items:stretch !important}",
7867
7850
  "[data-ai-cell]{width:100%;min-width:0}",
7868
7851
  // The rule above turns every row into a COLUMN flex container on phones, which silently breaks
@@ -8067,31 +8050,6 @@ function pricingCardEmphasis(featured, ctx, hasBg) {
8067
8050
  function textAttrs(ctx, path) {
8068
8051
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
8069
8052
  }
8070
- var AI_RESPONSIVE_CSS = [
8071
- "@media (max-width: 960px) {",
8072
- " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
8073
- ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
8074
- "}",
8075
- "@media (max-width: 640px) {",
8076
- " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
8077
- " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
8078
- // Group containers flatten to a column on phones; span placements come along for free.
8079
- // `:not([data-ai-group="overlap"])` for the same reason as the twin pair in AI_MOBILE_CSS — see
8080
- // the comment there. This sheet's copy is the narrower selector, so without the exemption it
8081
- // flattens an `overlap` hero below 640px even though the other rule stands down: measured a 390px
8082
- // viewport with the heading/CTA in flow *under* the image, 219px of section background where the
8083
- // layered text used to be.
8084
- ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) { display: flex !important; flex-direction: column !important; }',
8085
- ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) > * { grid-column: auto !important; }',
8086
- // `:not([data-ai-full-bleed])` for the same reason as the twin rule in AI_MOBILE_CSS: this one
8087
- // is narrower (0,2,0 vs 0,1,0) and also `!important`, so without the guard it wins outright and
8088
- // a full-bleed hero gets its 20px side band back below 640px only — the one width where the
8089
- // other rule had already been taught to stand down.
8090
- " [data-ai-responsive] [data-ai-section-inner]:not([data-ai-full-bleed]) { padding-left: 20px !important; padding-right: 20px !important; }",
8091
- " [data-ai-responsive] { overflow-x: hidden; }",
8092
- " [data-ai-responsive] img { max-width: 100%; }",
8093
- "}"
8094
- ].join("\n");
8095
8053
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
8096
8054
  function MediaBox({
8097
8055
  refValue,
@@ -8146,7 +8104,7 @@ function MediaBox({
8146
8104
  position: fill ? "absolute" : void 0,
8147
8105
  inset: fill ? 0 : void 0,
8148
8106
  width: fill ? "100%" : fixedSize ? "auto" : "100%",
8149
- height: fill ? "100%" : fixedSize ? height : void 0,
8107
+ height: fill ? "100%" : void 0,
8150
8108
  aspectRatio: fill ? void 0 : aspectRatio,
8151
8109
  minHeight: fill || fixedSize ? void 0 : height ?? (aspectRatio ? void 0 : 180),
8152
8110
  borderRadius: radius,
@@ -9642,7 +9600,7 @@ function CollectionBlock({ node, ctx, path }) {
9642
9600
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
9643
9601
  "div",
9644
9602
  {
9645
- "data-ai-grid": String(itemsPerRow),
9603
+ "data-ai-grid": "",
9646
9604
  "data-ohw-pricing-collection": isPricing ? "" : void 0,
9647
9605
  style: {
9648
9606
  display: "grid",
@@ -9944,7 +9902,7 @@ function renderNode(node, ctx, path, ambientAlign) {
9944
9902
  },
9945
9903
  children: [
9946
9904
  i > 0 && ctx.heroOverlapFullBleed ? (
9947
- // The section's `data-ai-section-inner` gave up its max-width/gutter so the base
9905
+ // The section's `data-ai-container` gave up its max-width/gutter so the base
9948
9906
  // layer (this group's i===0 sibling) can span edge-to-edge — reintroduced here,
9949
9907
  // scoped to just the overlay text, so a heading/copy/CTA stack doesn't stretch
9950
9908
  // the full section width instead of reading at the usual content width.
@@ -10363,7 +10321,6 @@ function AiTreeRenderer({
10363
10321
  ...fillHeight ? { "data-ai-fill-height": "" } : {},
10364
10322
  ...needsOverlayClearance ? { "data-ai-overlay-clearance": "" } : {},
10365
10323
  ...bgAttrs,
10366
- "data-ai-responsive": "",
10367
10324
  style: {
10368
10325
  position: "relative",
10369
10326
  padding: `${padCss} 0`,
@@ -10377,13 +10334,12 @@ function AiTreeRenderer({
10377
10334
  ...fillHeight ? { display: "grid" } : {}
10378
10335
  },
10379
10336
  children: [
10380
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
10381
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_MOBILE_CSS }),
10382
10337
  isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
10338
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_MOBILE_CSS }),
10383
10339
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
10384
10340
  "div",
10385
10341
  {
10386
- "data-ai-section-inner": "",
10342
+ "data-ai-container": "",
10387
10343
  ...fullBleedMedia ? { "data-ai-full-bleed": "" } : {},
10388
10344
  style: {
10389
10345
  position: "relative",
@@ -10400,7 +10356,7 @@ function AiTreeRenderer({
10400
10356
  children: tree.rows.map((row, r2) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
10401
10357
  "div",
10402
10358
  {
10403
- "data-ai-columns": "",
10359
+ "data-ai-row": "",
10404
10360
  style: {
10405
10361
  display: "grid",
10406
10362
  gridTemplateColumns: "repeat(12, 1fr)",
@@ -11062,10 +11018,9 @@ function applyAiSectionsToDom(state, options) {
11062
11018
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
11063
11019
  continue;
11064
11020
  }
11021
+ const isNewMount = !existing || !existing.container.isConnected;
11065
11022
  let container = existing?.container;
11066
- let isNewMount = false;
11067
11023
  if (!container || !container.isConnected) {
11068
- isNewMount = true;
11069
11024
  existing?.root.unmount();
11070
11025
  container?.remove();
11071
11026
  container = document.createElement("div");
@@ -17507,7 +17462,6 @@ function MediaOverlay({
17507
17462
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
17508
17463
  );
17509
17464
  }, [isVideo]);
17510
- const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
17511
17465
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
17512
17466
  const box = {
17513
17467
  position: "fixed",
@@ -17606,10 +17560,11 @@ function MediaOverlay({
17606
17560
  className: "flex items-center justify-center cursor-pointer",
17607
17561
  style: {
17608
17562
  ...box,
17609
- // Visual only. `pointer-events: auto` on this full-bleed rect covered the navbar
17610
- // (hero bg includes the absolute TopNav) and swallowed CTA press-drag. Text overlay
17611
- // and cueOnly also need clicks to fall through — Replace / video controls opt back in.
17612
- pointerEvents: "none",
17563
+ // When text overlays this media, let clicks fall THROUGH to the text underneath. In the
17564
+ // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
17565
+ // in-document, pointer-events does it natively. The button below opts back in, so
17566
+ // Replace still works.
17567
+ pointerEvents: hover.hasTextOverlap || cueOnly ? "none" : "auto",
17613
17568
  // Selected: a firm component ring with no wash, so the image reads as chosen rather
17614
17569
  // than hovered. Hover keeps the existing tinted preview.
17615
17570
  boxShadow: selected ? "inset 0 0 0 2px var(--color-bridge-primary)" : "inset 0 0 0 1.5px var(--color-bridge-primary)",
@@ -17636,17 +17591,17 @@ function MediaOverlay({
17636
17591
  },
17637
17592
  children: [
17638
17593
  isVideo ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
17639
- replaceLabel
17594
+ isVideo ? "Replace video" : "Replace image"
17640
17595
  ]
17641
17596
  }
17642
17597
  ),
17643
- showChrome && replaceMode !== "none" && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
17598
+ replaceMode === "none" || cueOnly ? null : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
17644
17599
  Button,
17645
17600
  {
17646
17601
  "data-ohw-media-overlay": "",
17647
17602
  variant: "outline",
17648
17603
  size: "sm",
17649
- "aria-label": replaceLabel,
17604
+ "aria-label": isVideo ? "Replace video" : "Replace image",
17650
17605
  className: "gap-1.5 cursor-pointer hover:bg-background",
17651
17606
  style: {
17652
17607
  ...OVERLAY_BUTTON_STYLE,
@@ -17669,7 +17624,7 @@ function MediaOverlay({
17669
17624
  },
17670
17625
  children: [
17671
17626
  isVideo ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
17672
- replaceMode === "full" ? replaceLabel : null
17627
+ replaceMode === "full" ? isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image" : null
17673
17628
  ]
17674
17629
  }
17675
17630
  )
@@ -18179,7 +18134,7 @@ function AiSectionOverlay({
18179
18134
 
18180
18135
  // src/ui/ai-section/AiMediaStripOverlay.tsx
18181
18136
  var import_react10 = require("react");
18182
- var import_lucide_react9 = require("lucide-react");
18137
+ var import_lucide_react8 = require("lucide-react");
18183
18138
 
18184
18139
  // src/lib/ai-media-strip-tree.ts
18185
18140
  var MEDIA_STRIP_ITEM_MIN = 1;
@@ -18293,6 +18248,14 @@ function setMediaStripItemAsset(tree, index, assetId) {
18293
18248
  return true;
18294
18249
  });
18295
18250
  }
18251
+ function setMediaStripItemAlt(tree, index, alt) {
18252
+ return withMediaStrip(tree, (block) => {
18253
+ const items = block.items;
18254
+ if (index < 0 || index >= items.length) return false;
18255
+ items[index] = { ...items[index], alt };
18256
+ return true;
18257
+ });
18258
+ }
18296
18259
  function setMediaStripItemLink(tree, index, link) {
18297
18260
  return withMediaStrip(tree, (block) => {
18298
18261
  const items = block.items;
@@ -18310,117 +18273,11 @@ function setMediaStripItemLink(tree, index, link) {
18310
18273
  });
18311
18274
  }
18312
18275
 
18313
- // src/ui/dialog.tsx
18314
- var React9 = __toESM(require("react"), 1);
18315
- var import_radix_ui6 = require("radix-ui");
18316
- var import_lucide_react8 = require("lucide-react");
18317
- var import_jsx_runtime19 = require("react/jsx-runtime");
18318
- function Dialog2({
18319
- ...props
18320
- }) {
18321
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_radix_ui6.Dialog.Root, { "data-slot": "dialog", ...props });
18322
- }
18323
- function DialogPortal({
18324
- ...props
18325
- }) {
18326
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_radix_ui6.Dialog.Portal, { ...props });
18327
- }
18328
- function DialogOverlay({
18329
- className,
18330
- ...props
18331
- }) {
18332
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18333
- import_radix_ui6.Dialog.Overlay,
18334
- {
18335
- "data-slot": "dialog-overlay",
18336
- "data-ohw-link-modal-root": "",
18337
- className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
18338
- ...props
18339
- }
18340
- );
18341
- }
18342
- var DialogContent = React9.forwardRef(
18343
- ({ className, children, showCloseButton = true, container, ...props }, ref) => {
18344
- const positionMode = container ? "absolute" : "fixed";
18345
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(DialogPortal, { container: container ?? void 0, children: [
18346
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
18347
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
18348
- import_radix_ui6.Dialog.Content,
18349
- {
18350
- ref,
18351
- "data-slot": "dialog-content",
18352
- className: cn(
18353
- positionMode,
18354
- "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",
18355
- "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
18356
- container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
18357
- className
18358
- ),
18359
- onMouseDown: (e) => e.stopPropagation(),
18360
- ...props,
18361
- children: [
18362
- children,
18363
- showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18364
- import_radix_ui6.Dialog.Close,
18365
- {
18366
- type: "button",
18367
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
18368
- "aria-label": "Close",
18369
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.X, { size: 16, "aria-hidden": true })
18370
- }
18371
- ) : null
18372
- ]
18373
- }
18374
- )
18375
- ] });
18376
- }
18377
- );
18378
- DialogContent.displayName = import_radix_ui6.Dialog.Content.displayName;
18379
- function DialogHeader({
18380
- className,
18381
- ...props
18382
- }) {
18383
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
18384
- }
18385
- function DialogFooter({
18386
- className,
18387
- ...props
18388
- }) {
18389
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18390
- "div",
18391
- {
18392
- className: cn("flex items-center justify-end gap-2", className),
18393
- ...props
18394
- }
18395
- );
18396
- }
18397
- var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18398
- import_radix_ui6.Dialog.Title,
18399
- {
18400
- ref,
18401
- className: cn(
18402
- "text-lg font-semibold leading-none tracking-tight text-card-foreground",
18403
- className
18404
- ),
18405
- ...props
18406
- }
18407
- ));
18408
- DialogTitle.displayName = import_radix_ui6.Dialog.Title.displayName;
18409
- var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18410
- import_radix_ui6.Dialog.Description,
18411
- {
18412
- ref,
18413
- className: cn("text-sm text-muted-foreground", className),
18414
- ...props
18415
- }
18416
- ));
18417
- DialogDescription.displayName = import_radix_ui6.Dialog.Description.displayName;
18418
- var DialogClose = import_radix_ui6.Dialog.Close;
18419
-
18420
18276
  // src/ui/ai-section/AiMediaStripOverlay.tsx
18421
- var import_jsx_runtime20 = require("react/jsx-runtime");
18422
- var PRIMARY3 = "var(--ohw-primary, #0885FE)";
18423
- 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]";
18277
+ var import_jsx_runtime19 = require("react/jsx-runtime");
18278
+ var PRIMARY3 = "#0885FE";
18279
+ var OHW_BLUE = `var(--ohw-primary, ${PRIMARY3})`;
18280
+ var OHW_BLUE_DARK = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY3}) 60%, #000000)`;
18424
18281
  function findMediaStripItemElement(sectionId, index) {
18425
18282
  const sectionEl = findByInstanceId(sectionId);
18426
18283
  if (!sectionEl) return null;
@@ -18430,35 +18287,14 @@ function findMediaStripItemElement(sectionId, index) {
18430
18287
  }
18431
18288
  return null;
18432
18289
  }
18433
- function itemRefFromTarget(target) {
18434
- if (!(target instanceof Element)) return null;
18435
- if (target.closest(CHROME_SELECTOR)) return null;
18436
- const itemEl = target.closest("[data-ohw-media-strip-item]");
18437
- if (!itemEl) return null;
18438
- const sectionEl = itemEl.closest("[data-ohw-section]");
18439
- const unit = sectionEl ? movableUnit(sectionEl) : null;
18440
- const sectionId = unit ? instanceIdOf(unit) || null : null;
18441
- const index = Number(itemEl.dataset.ohwMediaStripIndex);
18442
- if (!sectionId || Number.isNaN(index)) return null;
18443
- return { sectionId, index };
18444
- }
18445
- function sameRef(a, b) {
18446
- return a?.sectionId === b?.sectionId && a?.index === b?.index;
18447
- }
18448
- function rectOf(el) {
18290
+ function readRect2(ref) {
18291
+ const el = ref && findMediaStripItemElement(ref.sectionId, ref.index);
18449
18292
  if (!el) return null;
18450
18293
  const r2 = el.getBoundingClientRect();
18451
18294
  if (r2.width <= 0 || r2.height <= 0) return null;
18452
18295
  return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
18453
18296
  }
18454
- function readRect2(ref) {
18455
- return rectOf(ref && findMediaStripItemElement(ref.sectionId, ref.index));
18456
- }
18457
- function readStripRect(ref) {
18458
- const item = ref && findMediaStripItemElement(ref.sectionId, ref.index);
18459
- return rectOf(item?.closest("[data-ohw-media-strip]") ?? item);
18460
- }
18461
- function useLiveItemRect(ref, strip = false) {
18297
+ function useLiveItemRect(ref) {
18462
18298
  const [rect, setRect] = (0, import_react10.useState)(null);
18463
18299
  (0, import_react10.useEffect)(() => {
18464
18300
  if (!ref) {
@@ -18466,7 +18302,7 @@ function useLiveItemRect(ref, strip = false) {
18466
18302
  return;
18467
18303
  }
18468
18304
  const update = () => {
18469
- const next = strip ? readStripRect(ref) : readRect2(ref);
18305
+ const next = readRect2(ref);
18470
18306
  setRect(
18471
18307
  (prev) => prev && next && prev.top === next.top && prev.left === next.left && prev.width === next.width && prev.height === next.height ? prev : next
18472
18308
  );
@@ -18481,49 +18317,9 @@ function useLiveItemRect(ref, strip = false) {
18481
18317
  window.removeEventListener("resize", update);
18482
18318
  clearInterval(interval);
18483
18319
  };
18484
- }, [ref?.sectionId, ref?.index, strip]);
18320
+ }, [ref?.sectionId, ref?.index]);
18485
18321
  return rect;
18486
18322
  }
18487
- var TOOLBAR_STRIP_GAP = 52;
18488
- var TOOLBAR_STRIP_GAP_MOBILE = 42;
18489
- var TOOLBAR_EDGE_MARGIN2 = 4;
18490
- function StripToolbarSlot({ stripRect, children }) {
18491
- const slotRef = (0, import_react10.useRef)(null);
18492
- const [shift, setShift] = (0, import_react10.useState)(0);
18493
- const centerX = stripRect.left + stripRect.width / 2;
18494
- (0, import_react10.useLayoutEffect)(() => {
18495
- const slot = slotRef.current;
18496
- if (!slot) return;
18497
- const clamp = () => {
18498
- const w = slot.offsetWidth;
18499
- if (w <= 0) return;
18500
- const half = w / 2;
18501
- const clamped = Math.max(TOOLBAR_EDGE_MARGIN2 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half));
18502
- setShift((prev) => Math.abs(prev - (clamped - centerX)) < 0.5 ? prev : clamped - centerX);
18503
- };
18504
- clamp();
18505
- const ro = new ResizeObserver(clamp);
18506
- ro.observe(slot);
18507
- return () => ro.disconnect();
18508
- }, [centerX, children]);
18509
- const mobile = window.innerWidth <= 480;
18510
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18511
- "div",
18512
- {
18513
- ref: slotRef,
18514
- "data-ohw-bridge": "",
18515
- "data-ohw-item-interaction": "",
18516
- className: "pointer-events-auto fixed",
18517
- style: {
18518
- top: stripRect.top - (mobile ? TOOLBAR_STRIP_GAP_MOBILE : TOOLBAR_STRIP_GAP),
18519
- left: centerX + shift,
18520
- transform: "translate(-50%, -100%)",
18521
- zIndex: 2147483645
18522
- },
18523
- children
18524
- }
18525
- );
18526
- }
18527
18323
  function requestValidateTree(postToParent2, tree) {
18528
18324
  return new Promise((resolve) => {
18529
18325
  const id = `ai-validate-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
@@ -18560,177 +18356,221 @@ function requestAssetPick(postToParent2) {
18560
18356
  postToParent2({ type: "ow:asset-pick", id });
18561
18357
  });
18562
18358
  }
18563
- function ToolbarAction({
18359
+ function Pill({
18564
18360
  label,
18565
18361
  onClick,
18566
- disabled = false,
18567
- active = false,
18568
- children
18362
+ disabled,
18363
+ active
18569
18364
  }) {
18570
- const button = /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, active, onClick, className: "size-8", children });
18571
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Tooltip, { children: [
18572
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "inline-flex", children: button }) : button }),
18573
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
18574
- ] });
18575
- }
18576
- function ToolbarSeparator() {
18577
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
18365
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18366
+ "button",
18367
+ {
18368
+ type: "button",
18369
+ onClick,
18370
+ disabled,
18371
+ style: {
18372
+ height: 28,
18373
+ padding: "0 10px",
18374
+ display: "inline-flex",
18375
+ alignItems: "center",
18376
+ gap: 4,
18377
+ borderRadius: 999,
18378
+ border: "none",
18379
+ cursor: disabled ? "default" : "pointer",
18380
+ background: active ? OHW_BLUE_DARK : "#ffffff",
18381
+ color: active ? "#ffffff" : OHW_BLUE_DARK,
18382
+ opacity: disabled ? 0.4 : 1,
18383
+ fontSize: 12,
18384
+ lineHeight: "16px",
18385
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
18386
+ whiteSpace: "nowrap",
18387
+ flex: "0 0 auto"
18388
+ },
18389
+ children: label
18390
+ }
18391
+ );
18578
18392
  }
18579
- function OptionMenu({
18580
- label,
18581
- value,
18582
- options,
18583
- onChange
18584
- }) {
18585
- const current = options.find((o) => o.value === value) ?? options[0];
18586
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
18587
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-xs text-muted-foreground", children: label }),
18588
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(DropdownMenu, { modal: false, children: [
18589
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18590
- "button",
18591
- {
18592
- type: "button",
18593
- "aria-label": label,
18594
- 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",
18595
- children: [
18596
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "flex-1 text-left", children: current.label }),
18597
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
18598
- ]
18599
- }
18600
- ) }),
18601
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[132px] min-w-[132px] p-1", children: options.map((option) => {
18602
- const selected = option.value === value;
18603
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18604
- DropdownMenuItem,
18393
+ function AltInput({ value, onCommit }) {
18394
+ const [draft, setDraft] = (0, import_react10.useState)(value);
18395
+ (0, import_react10.useEffect)(() => setDraft(value), [value]);
18396
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
18397
+ "div",
18398
+ {
18399
+ style: {
18400
+ display: "flex",
18401
+ alignItems: "center",
18402
+ gap: 6,
18403
+ height: 28,
18404
+ padding: "0 12px",
18405
+ borderRadius: 999,
18406
+ background: "#ffffff",
18407
+ width: "100%",
18408
+ maxWidth: 260,
18409
+ boxSizing: "border-box"
18410
+ },
18411
+ children: [
18412
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18413
+ "span",
18605
18414
  {
18606
- role: "menuitemradio",
18607
- "aria-checked": selected,
18608
- onSelect: () => onChange(option.value),
18609
- className: cn("grid grid-cols-[1fr_16px] gap-2 py-2 pl-2 pr-2 text-[13px]", selected && "bg-muted"),
18610
- children: [
18611
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: option.label }),
18612
- selected && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Check, { size: 16, className: "text-bridge-primary" })
18613
- ]
18614
- },
18615
- option.value
18616
- );
18617
- }) })
18618
- ] })
18619
- ] });
18620
- }
18621
- var VARIANTS = [
18622
- { value: "logos", label: "Logos" },
18623
- { value: "reel", label: "Reel" }
18624
- ];
18625
- var ALIGNS = [
18626
- { value: "left", label: "Align left", Icon: import_lucide_react9.AlignLeft },
18627
- { value: "center", label: "Align center", Icon: import_lucide_react9.AlignCenter },
18628
- { value: "right", label: "Align right", Icon: import_lucide_react9.AlignRight }
18629
- ];
18630
- var HOVERS = [
18631
- { value: "none", label: "None" },
18632
- { value: "lift", label: "Lift" },
18633
- { value: "zoom", label: "Zoom" },
18634
- { value: "fade", label: "Fade" }
18635
- ];
18636
- var HTTP_URL_RE = /^https?:\/\/\S+$/i;
18637
- function LinkModal({
18638
- href,
18639
- linkTarget,
18640
- onClose,
18641
- onSave
18642
- }) {
18643
- const [draft, setDraft] = (0, import_react10.useState)(href);
18644
- const [newTab, setNewTab] = (0, import_react10.useState)(linkTarget !== "same-tab");
18415
+ style: {
18416
+ flex: "0 0 auto",
18417
+ color: OHW_BLUE_DARK,
18418
+ fontSize: 10,
18419
+ fontWeight: 700,
18420
+ textTransform: "uppercase",
18421
+ letterSpacing: 0.4
18422
+ },
18423
+ children: "Alt"
18424
+ }
18425
+ ),
18426
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18427
+ "input",
18428
+ {
18429
+ type: "text",
18430
+ value: draft,
18431
+ onChange: (e) => setDraft(e.target.value),
18432
+ onBlur: () => {
18433
+ if (draft !== value) onCommit(draft);
18434
+ },
18435
+ onKeyDown: (e) => {
18436
+ if (e.key === "Enter") e.currentTarget.blur();
18437
+ if (e.key === "Escape") {
18438
+ setDraft(value);
18439
+ e.currentTarget.blur();
18440
+ }
18441
+ },
18442
+ placeholder: "Describe this image",
18443
+ "aria-label": "Image alt text",
18444
+ style: {
18445
+ flex: 1,
18446
+ minWidth: 0,
18447
+ border: "none",
18448
+ outline: "none",
18449
+ background: "transparent",
18450
+ fontSize: 12,
18451
+ lineHeight: "16px",
18452
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
18453
+ color: "#1f2937"
18454
+ }
18455
+ }
18456
+ )
18457
+ ]
18458
+ }
18459
+ );
18460
+ }
18461
+ var VARIANTS = [
18462
+ { value: "logos", label: "Logos" },
18463
+ { value: "reel", label: "Reel" }
18464
+ ];
18465
+ var ALIGNS = [
18466
+ { value: "left", label: "Left" },
18467
+ { value: "center", label: "Center" },
18468
+ { value: "right", label: "Right" }
18469
+ ];
18470
+ var HOVERS = [
18471
+ { value: "none", label: "None" },
18472
+ { value: "lift", label: "Lift" },
18473
+ { value: "zoom", label: "Zoom" },
18474
+ { value: "fade", label: "Fade" }
18475
+ ];
18476
+ var TREATMENTS = [
18477
+ { value: "color", label: "Color" },
18478
+ { value: "grayscale", label: "Grayscale" }
18479
+ ];
18480
+ var HTTP_URL_RE = /^https?:\/\/\S+$/i;
18481
+ function LinkInput({ value, onCommit }) {
18482
+ const [draft, setDraft] = (0, import_react10.useState)(value);
18645
18483
  const [invalid, setInvalid] = (0, import_react10.useState)(false);
18646
- const save = () => {
18484
+ (0, import_react10.useEffect)(() => {
18485
+ setDraft(value);
18486
+ setInvalid(false);
18487
+ }, [value]);
18488
+ const commit = () => {
18647
18489
  const next = draft.trim();
18648
- if (next && !HTTP_URL_RE.test(next)) {
18649
- setInvalid(true);
18650
- return;
18651
- }
18652
- onSave(next, newTab ? "new-tab" : "same-tab");
18653
- onClose();
18490
+ if (next === value) return setInvalid(false);
18491
+ if (next && !HTTP_URL_RE.test(next)) return setInvalid(true);
18492
+ setInvalid(false);
18493
+ onCommit(next);
18654
18494
  };
18655
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Dialog2, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(DialogContent, { "data-ohw-link-modal-root": "", className: "max-w-[400px] gap-4 p-5", children: [
18656
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DialogHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DialogTitle, { className: "text-base", children: "Image link" }) }),
18657
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col gap-3", children: [
18658
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18659
- "input",
18660
- {
18661
- autoFocus: true,
18662
- type: "text",
18663
- inputMode: "url",
18664
- value: draft,
18665
- onChange: (e) => {
18666
- setDraft(e.target.value);
18667
- setInvalid(false);
18668
- },
18669
- onKeyDown: (e) => {
18670
- if (e.key === "Enter") save();
18671
- },
18672
- placeholder: "https://example.com",
18673
- "aria-label": "Image link URL",
18674
- "aria-invalid": invalid,
18675
- className: cn(
18676
- "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",
18677
- invalid && "border-destructive focus:border-destructive"
18678
- )
18679
- }
18680
- ),
18681
- invalid && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "text-xs text-destructive", children: "Enter a full URL starting with http:// or https://" }),
18682
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("label", { className: "flex items-center gap-2 text-[13px] text-foreground", children: [
18683
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("input", { type: "checkbox", checked: newTab, onChange: (e) => setNewTab(e.target.checked) }),
18684
- "Open in new tab"
18685
- ] })
18686
- ] }),
18687
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(DialogFooter, { children: [
18688
- href && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18689
- "button",
18690
- {
18691
- type: "button",
18692
- onClick: () => {
18693
- onSave("", "new-tab");
18694
- onClose();
18695
- },
18696
- className: "mr-auto rounded-[6px] px-3 py-2 text-[13px] font-medium text-destructive transition-colors hover:bg-muted",
18697
- children: "Remove link"
18698
- }
18699
- ),
18700
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18701
- "button",
18702
- {
18703
- type: "button",
18704
- onClick: onClose,
18705
- className: "rounded-[6px] px-3 py-2 text-[13px] font-medium text-foreground transition-colors hover:bg-muted",
18706
- children: "Cancel"
18707
- }
18708
- ),
18709
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18710
- "button",
18711
- {
18712
- type: "button",
18713
- onClick: save,
18714
- className: "rounded-[6px] bg-bridge-primary px-3 py-2 text-[13px] font-medium text-primary-foreground transition-opacity hover:opacity-90",
18715
- children: "Save"
18716
- }
18717
- )
18718
- ] })
18719
- ] }) });
18495
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
18496
+ "div",
18497
+ {
18498
+ style: {
18499
+ display: "flex",
18500
+ alignItems: "center",
18501
+ gap: 6,
18502
+ height: 28,
18503
+ padding: "0 12px",
18504
+ borderRadius: 999,
18505
+ background: "#ffffff",
18506
+ width: "100%",
18507
+ maxWidth: 260,
18508
+ boxSizing: "border-box",
18509
+ boxShadow: invalid ? "inset 0 0 0 1.5px #EF4444" : "none"
18510
+ },
18511
+ children: [
18512
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18513
+ "span",
18514
+ {
18515
+ style: {
18516
+ flex: "0 0 auto",
18517
+ color: OHW_BLUE_DARK,
18518
+ fontSize: 10,
18519
+ fontWeight: 700,
18520
+ textTransform: "uppercase",
18521
+ letterSpacing: 0.4
18522
+ },
18523
+ children: "Link"
18524
+ }
18525
+ ),
18526
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18527
+ "input",
18528
+ {
18529
+ type: "text",
18530
+ inputMode: "url",
18531
+ value: draft,
18532
+ onChange: (e) => setDraft(e.target.value),
18533
+ onBlur: commit,
18534
+ onKeyDown: (e) => {
18535
+ if (e.key === "Enter") e.currentTarget.blur();
18536
+ if (e.key === "Escape") {
18537
+ setDraft(value);
18538
+ setInvalid(false);
18539
+ e.currentTarget.blur();
18540
+ }
18541
+ },
18542
+ placeholder: "https://example.com",
18543
+ "aria-label": "Image link URL",
18544
+ "aria-invalid": invalid,
18545
+ style: {
18546
+ flex: 1,
18547
+ minWidth: 0,
18548
+ border: "none",
18549
+ outline: "none",
18550
+ background: "transparent",
18551
+ fontSize: 12,
18552
+ lineHeight: "16px",
18553
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
18554
+ color: "#1f2937"
18555
+ }
18556
+ }
18557
+ )
18558
+ ]
18559
+ }
18560
+ );
18720
18561
  }
18721
18562
  function MediaStripToolbar({
18722
18563
  entry,
18723
18564
  index,
18724
18565
  pending,
18725
18566
  error,
18726
- itemEditOn,
18727
- onToggleItemEdit,
18728
18567
  onSetVariant,
18729
18568
  onSetAlign,
18730
18569
  onSetHover,
18731
18570
  onSetTreatment,
18732
18571
  onResize,
18733
- onOpenLink,
18572
+ onSetAlt,
18573
+ onSetLink,
18734
18574
  onAddItem,
18735
18575
  onRemoveItem,
18736
18576
  onChangeImage,
@@ -18739,102 +18579,81 @@ function MediaStripToolbar({
18739
18579
  }) {
18740
18580
  const items = mediaStripItems(entry.tree);
18741
18581
  const itemCount = items.length;
18582
+ const currentAlt = items[index]?.alt ?? "";
18583
+ const currentHref = items[index]?.href ?? "";
18584
+ const currentTarget = items[index]?.linkTarget === "same-tab" ? "same-tab" : "new-tab";
18742
18585
  const slots = findMediaStripSlots(entry.tree);
18743
18586
  const variant = slots?.variant === "reel" ? "reel" : "logos";
18744
18587
  const align = slots?.align === "left" || slots?.align === "right" ? slots.align : "center";
18745
18588
  const hover = slots?.hover === "none" || slots?.hover === "lift" || slots?.hover === "zoom" || slots?.hover === "fade" ? slots.hover : "lift";
18746
18589
  const treatment = slots?.treatment === "color" || slots?.treatment === "grayscale" ? slots.treatment : variant === "logos" ? "grayscale" : "color";
18747
18590
  const sizeValue = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
18748
- const mobile = window.innerWidth <= 480;
18749
- const gatedLabel = (label) => itemEditOn ? label : "Turn on the star to edit this image";
18750
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18591
+ const isMobile = window.innerWidth <= 480;
18592
+ const rowStyle = isMobile ? {
18593
+ display: "flex",
18594
+ gap: 6,
18595
+ flexWrap: "nowrap",
18596
+ justifyContent: "center",
18597
+ overflowX: "auto",
18598
+ WebkitOverflowScrolling: "touch",
18599
+ width: "100%",
18600
+ scrollbarWidth: "none"
18601
+ } : { display: "flex", gap: 6, flexWrap: "nowrap", justifyContent: "center" };
18602
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
18751
18603
  "div",
18752
18604
  {
18753
18605
  "data-ohw-bridge": "",
18754
18606
  "data-ohw-toolbar": "",
18755
18607
  onMouseDown: (e) => e.stopPropagation(),
18756
- className: cn("flex flex-col items-center gap-1.5 font-sans", pending && "pointer-events-none opacity-60"),
18757
- style: { maxWidth: mobile ? "calc(100vw - 24px)" : void 0 },
18608
+ style: {
18609
+ display: "flex",
18610
+ flexDirection: "column",
18611
+ gap: 6,
18612
+ alignItems: "center",
18613
+ background: OHW_BLUE,
18614
+ padding: 8,
18615
+ borderRadius: 12,
18616
+ boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
18617
+ pointerEvents: pending ? "none" : "auto",
18618
+ opacity: pending ? 0.6 : 1,
18619
+ maxWidth: isMobile ? "calc(100vw - 24px)" : "none"
18620
+ },
18758
18621
  children: [
18759
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18760
- "div",
18761
- {
18762
- className: cn(
18763
- "flex items-center gap-1 rounded-[10px] border border-border bg-background p-1 whitespace-nowrap",
18764
- "shadow-[0px_10px_15px_-3px_rgba(0,0,0,0.1),0px_4px_6px_-4px_rgba(0,0,0,0.1)]",
18765
- mobile ? "flex-wrap justify-center" : "flex-nowrap"
18766
- ),
18767
- children: [
18768
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Tooltip, { children: [
18769
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18770
- CustomToolbarButton,
18771
- {
18772
- type: "button",
18773
- "aria-label": itemEditOn ? "Done editing this image" : "Edit this image",
18774
- "aria-pressed": itemEditOn,
18775
- onClick: onToggleItemEdit,
18776
- className: "group size-8",
18777
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18778
- import_lucide_react9.Star,
18779
- {
18780
- size: 16,
18781
- fill: "currentColor",
18782
- strokeWidth: 0,
18783
- className: cn(
18784
- "transition-colors",
18785
- itemEditOn ? "text-[#f59e0b] group-hover:text-[#d97706]" : "text-[#d6d3d1] group-hover:text-[#fbbf24]"
18786
- ),
18787
- style: { pointerEvents: "none" }
18788
- }
18789
- )
18790
- }
18791
- ) }),
18792
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: itemEditOn ? "Done editing this image" : "Edit this image" })
18793
- ] }),
18794
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarSeparator, {}),
18795
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: gatedLabel("Change image"), onClick: onChangeImage, disabled: !itemEditOn, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Image, { size: 16, style: { pointerEvents: "none" } }) }),
18796
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: gatedLabel("Link"), onClick: onOpenLink, disabled: !itemEditOn, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Link, { size: 16, style: { pointerEvents: "none" } }) }),
18797
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: gatedLabel("Move left"), onClick: onMoveLeft, disabled: !itemEditOn || index <= 0, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ArrowLeft, { size: 16, style: { pointerEvents: "none" } }) }),
18798
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18799
- ToolbarAction,
18800
- {
18801
- label: gatedLabel("Move right"),
18802
- onClick: onMoveRight,
18803
- disabled: !itemEditOn || index >= itemCount - 1,
18804
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ArrowRight, { size: 16, style: { pointerEvents: "none" } })
18805
- }
18806
- ),
18807
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: gatedLabel("Remove image"), onClick: onRemoveItem, disabled: !itemEditOn || itemCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ImageMinus, { size: 16, style: { pointerEvents: "none" } }) }),
18808
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarSeparator, {}),
18809
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(OptionMenu, { label: "Layout", value: variant, options: VARIANTS, onChange: onSetVariant }),
18810
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(OptionMenu, { label: "Hover", value: hover, options: HOVERS, onChange: onSetHover }),
18811
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18812
- ToolbarAction,
18813
- {
18814
- label: treatment === "grayscale" ? "Show in color" : "Show grayscale",
18815
- active: treatment === "grayscale",
18816
- onClick: () => onSetTreatment(treatment === "grayscale" ? "color" : "grayscale"),
18817
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Palette, { size: 16, style: { pointerEvents: "none" } })
18818
- }
18819
- ),
18820
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarSeparator, {}),
18821
- ALIGNS.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label, active: align === value, onClick: () => onSetAlign(value), children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Icon, { size: 16, style: { pointerEvents: "none" } }) }, value)),
18822
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarSeparator, {}),
18823
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Smaller", onClick: () => onResize(-8), children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Minus, { size: 16, style: { pointerEvents: "none" } }) }),
18824
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "min-w-[40px] text-center text-xs tabular-nums text-muted-foreground", children: [
18825
- sizeValue,
18826
- "px"
18827
- ] }),
18828
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Larger", onClick: () => onResize(8), children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Plus, { size: 16, style: { pointerEvents: "none" } }) }),
18829
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarSeparator, {}),
18830
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Add image", onClick: onAddItem, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ImagePlus, { size: 16, style: { pointerEvents: "none" } }) })
18831
- ]
18832
- }
18833
- ),
18834
- error && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
18622
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: rowStyle, children: [
18623
+ VARIANTS.map((v) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: v.label, active: variant === v.value, onClick: () => onSetVariant(v.value) }, v.value)),
18624
+ ALIGNS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: a.label, active: align === a.value, onClick: () => onSetAlign(a.value) }, a.value))
18625
+ ] }),
18626
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: rowStyle, children: [
18627
+ HOVERS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: h.label, active: hover === h.value, onClick: () => onSetHover(h.value) }, h.value)),
18628
+ TREATMENTS.map((t) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: t.label, active: treatment === t.value, onClick: () => onSetTreatment(t.value) }, t.value))
18629
+ ] }),
18630
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
18631
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: "\u2212", onClick: () => onResize(-8) }),
18632
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { style: { color: "#ffffff", fontSize: 12, minWidth: 34, textAlign: "center" }, children: [
18633
+ sizeValue,
18634
+ "px"
18635
+ ] }),
18636
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: "+", onClick: () => onResize(8) })
18637
+ ] }),
18638
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: { display: "flex", justifyContent: "center", width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(AltInput, { value: currentAlt, onCommit: onSetAlt }) }),
18639
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: { display: "flex", justifyContent: "center", width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(LinkInput, { value: currentHref, onCommit: (href) => onSetLink(href, currentTarget) }) }),
18640
+ currentHref && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: rowStyle, children: [
18641
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: "New tab", active: currentTarget === "new-tab", onClick: () => onSetLink(currentHref, "new-tab") }),
18642
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: "Same tab", active: currentTarget === "same-tab", onClick: () => onSetLink(currentHref, "same-tab") })
18643
+ ] }),
18644
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: rowStyle, children: [
18645
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: "Change Image", onClick: onChangeImage }),
18646
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: "+ Item", onClick: onAddItem }),
18647
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: "\u2212 Item", onClick: onRemoveItem, disabled: itemCount <= 1 })
18648
+ ] }),
18649
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: rowStyle, children: [
18650
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: "\u2190 Move", onClick: onMoveLeft, disabled: index <= 0 }),
18651
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Pill, { label: "\u2192 Move", onClick: onMoveRight, disabled: index >= itemCount - 1 })
18652
+ ] }),
18653
+ error && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
18835
18654
  ]
18836
18655
  }
18837
- ) });
18656
+ );
18838
18657
  }
18839
18658
  function findMediaStripSlots(tree) {
18840
18659
  const stack = tree.rows.flatMap((row) => row.blocks);
@@ -18853,23 +18672,31 @@ function AiMediaStripOverlay({
18853
18672
  }) {
18854
18673
  const [hover, setHover] = (0, import_react10.useState)(null);
18855
18674
  const [active, setActive] = (0, import_react10.useState)(null);
18856
- const [itemEditOn, setItemEditOn] = (0, import_react10.useState)(false);
18857
- const [linkOpen, setLinkOpen] = (0, import_react10.useState)(false);
18858
18675
  const [pending, setPending] = (0, import_react10.useState)(false);
18859
18676
  const [error, setError] = (0, import_react10.useState)(null);
18860
18677
  const activeRef = (0, import_react10.useRef)(null);
18861
18678
  activeRef.current = active;
18862
- const linkOpenRef = (0, import_react10.useRef)(false);
18863
- linkOpenRef.current = linkOpen;
18864
18679
  (0, import_react10.useEffect)(() => {
18865
18680
  const onMove = (e) => {
18866
- const ref = itemRefFromTarget(e.target);
18867
18681
  const t = e.target;
18868
- const overEditable = Boolean(t?.closest?.("[data-ohw-editable]"));
18869
- setHover((prev) => {
18870
- const next = ref && !overEditable ? ref : null;
18871
- return sameRef(prev, next) ? prev : next;
18872
- });
18682
+ if (!(t instanceof HTMLElement)) return;
18683
+ 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]")) {
18684
+ return;
18685
+ }
18686
+ const itemEl = t.closest("[data-ohw-media-strip-item]");
18687
+ if (!itemEl) {
18688
+ setHover(null);
18689
+ return;
18690
+ }
18691
+ const sectionEl = itemEl.closest("[data-ohw-section]");
18692
+ const unit = sectionEl ? movableUnit(sectionEl) : null;
18693
+ const sectionId = unit ? instanceIdOf(unit) || null : null;
18694
+ const index = Number(itemEl.dataset.ohwMediaStripIndex);
18695
+ if (!sectionId || Number.isNaN(index)) {
18696
+ setHover(null);
18697
+ return;
18698
+ }
18699
+ setHover({ sectionId, index });
18873
18700
  };
18874
18701
  const onLeave = () => setHover(null);
18875
18702
  document.addEventListener("mousemove", onMove, { passive: true });
@@ -18881,25 +18708,23 @@ function AiMediaStripOverlay({
18881
18708
  }, []);
18882
18709
  (0, import_react10.useEffect)(() => {
18883
18710
  const onClick = (e) => {
18711
+ const current = activeRef.current;
18712
+ if (!current) return;
18884
18713
  const t = e.target;
18885
- if (!(t instanceof Element)) return;
18886
- if (t.closest(CHROME_SELECTOR)) return;
18887
- const ref = itemRefFromTarget(t);
18888
- if (sameRef(activeRef.current, ref)) return;
18889
- if (ref) clearInteractionChromeRef.current();
18890
- setItemEditOn(false);
18714
+ if (!(t instanceof HTMLElement)) return;
18715
+ if (t.closest("[data-ohw-item-interaction]")) return;
18716
+ const itemEl = findMediaStripItemElement(current.sectionId, current.index);
18717
+ if (itemEl && itemEl.contains(t)) return;
18718
+ setActive(null);
18891
18719
  setError(null);
18892
- setActive(ref);
18893
18720
  };
18894
18721
  document.addEventListener("click", onClick, true);
18895
18722
  return () => document.removeEventListener("click", onClick, true);
18896
- }, [clearInteractionChromeRef]);
18723
+ }, []);
18897
18724
  (0, import_react10.useEffect)(() => {
18898
18725
  const onBlur = () => {
18899
18726
  if (!activeRef.current) return;
18900
18727
  setActive(null);
18901
- setItemEditOn(false);
18902
- setLinkOpen(false);
18903
18728
  setError(null);
18904
18729
  };
18905
18730
  window.addEventListener("blur", onBlur);
@@ -18909,10 +18734,7 @@ function AiMediaStripOverlay({
18909
18734
  const onKeyDown = (e) => {
18910
18735
  if (e.key !== "Escape") return;
18911
18736
  if (!activeRef.current) return;
18912
- if (linkOpenRef.current) return;
18913
- if (document.querySelector('[data-ohw-more-menu][data-state="open"]')) return;
18914
18737
  setActive(null);
18915
- setItemEditOn(false);
18916
18738
  setError(null);
18917
18739
  };
18918
18740
  document.addEventListener("keydown", onKeyDown);
@@ -18946,133 +18768,137 @@ function AiMediaStripOverlay({
18946
18768
  tree: nextTree,
18947
18769
  label: entry.label,
18948
18770
  media: extraMedia ? { ...entry.media, ...extraMedia } : entry.media,
18949
- ...entry.brand ? { brand: entry.brand } : {},
18950
- // The user is already looking at the strip they just edited — recentering the viewport
18951
- // on every edit reads as the section jumping around.
18952
- skipScrollIntoView: true
18771
+ ...entry.brand ? { brand: entry.brand } : {}
18953
18772
  });
18954
18773
  return true;
18955
18774
  },
18956
18775
  [findEntry, postToParent2, applyTreeLocalRef]
18957
18776
  );
18958
18777
  const activeEntry = active ? findEntry(active.sectionId) : null;
18778
+ const hoverHandleRect = useLiveItemRect(!active && hover ? hover : null);
18959
18779
  const activeItemRect = useLiveItemRect(active);
18960
- const activeStripRect = useLiveItemRect(active, true);
18961
- const hoverRect = useLiveItemRect(hover && !sameRef(hover, active) ? hover : null);
18962
- const activeItems = activeEntry ? mediaStripItems(activeEntry.tree) : [];
18963
- const activeItem = active ? activeItems[active.index] : void 0;
18964
- const activeHref = activeItem?.href ?? "";
18965
- const activeTarget = activeItem?.linkTarget === "same-tab" ? "same-tab" : "new-tab";
18966
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
18967
- hoverRect && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
18968
- activeItemRect && activeStripRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
18969
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ItemInteractionLayer, { rect: activeItemRect, state: "active-top" }),
18970
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(StripToolbarSlot, { stripRect: activeStripRect, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18971
- MediaStripToolbar,
18972
- {
18973
- entry: activeEntry,
18974
- index: active.index,
18975
- pending,
18976
- error,
18977
- itemEditOn,
18978
- onToggleItemEdit: () => setItemEditOn((on) => !on),
18979
- onSetVariant: (variant) => runMutation(active.sectionId, (tree) => setMediaStripVariant(tree, variant)),
18980
- onSetAlign: (align) => runMutation(active.sectionId, (tree) => setMediaStripAlign(tree, align)),
18981
- onSetHover: (hoverPreset) => runMutation(active.sectionId, (tree) => setMediaStripHover(tree, hoverPreset)),
18982
- onSetTreatment: (treatment) => runMutation(active.sectionId, (tree) => setMediaStripTreatment(tree, treatment)),
18983
- onResize: (delta) => {
18984
- const slots = findMediaStripSlots(activeEntry.tree);
18985
- const variant = slots?.variant === "reel" ? "reel" : "logos";
18986
- const current = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
18987
- void runMutation(active.sectionId, (tree) => setMediaStripSizing(tree, current + delta));
18988
- },
18989
- onOpenLink: () => setLinkOpen(true),
18990
- onAddItem: () => {
18991
- void requestAssetPick(postToParent2).then((picked) => {
18992
- if (!picked) return;
18993
- const item = { assetId: picked.assetId, alt: "Logo" };
18780
+ const TOOLBAR_APPROX_H = 170;
18781
+ const toolbarState = activeItemRect && activeItemRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
18782
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
18783
+ hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18784
+ "button",
18785
+ {
18786
+ type: "button",
18787
+ "data-ohw-toolbar": "",
18788
+ onClick: () => {
18789
+ clearInteractionChromeRef.current();
18790
+ setActive(hover);
18791
+ },
18792
+ "aria-label": "Configure media item",
18793
+ style: {
18794
+ position: "fixed",
18795
+ top: hoverHandleRect.top + 8,
18796
+ left: hoverHandleRect.left + hoverHandleRect.width - 36,
18797
+ width: 28,
18798
+ height: 28,
18799
+ borderRadius: 8,
18800
+ border: `1.5px dashed ${PRIMARY3}`,
18801
+ background: "#ffffff",
18802
+ color: PRIMARY3,
18803
+ display: "flex",
18804
+ alignItems: "center",
18805
+ justifyContent: "center",
18806
+ cursor: "pointer",
18807
+ zIndex: 2147483643,
18808
+ fontSize: 14,
18809
+ fontWeight: 700
18810
+ },
18811
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react8.Star, { size: 14, style: { pointerEvents: "none" } })
18812
+ }
18813
+ ),
18814
+ activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18815
+ ItemInteractionLayer,
18816
+ {
18817
+ rect: activeItemRect,
18818
+ state: toolbarState,
18819
+ toolbar: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
18820
+ MediaStripToolbar,
18821
+ {
18822
+ entry: activeEntry,
18823
+ index: active.index,
18824
+ pending,
18825
+ error,
18826
+ onSetVariant: (variant) => runMutation(active.sectionId, (tree) => setMediaStripVariant(tree, variant)),
18827
+ onSetAlign: (align) => runMutation(active.sectionId, (tree) => setMediaStripAlign(tree, align)),
18828
+ onSetHover: (hoverPreset) => runMutation(active.sectionId, (tree) => setMediaStripHover(tree, hoverPreset)),
18829
+ onSetTreatment: (treatment) => runMutation(active.sectionId, (tree) => setMediaStripTreatment(tree, treatment)),
18830
+ onResize: (delta) => {
18831
+ const slots = findMediaStripSlots(activeEntry.tree);
18832
+ const variant = slots?.variant === "reel" ? "reel" : "logos";
18833
+ const current = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
18834
+ void runMutation(active.sectionId, (tree) => setMediaStripSizing(tree, current + delta));
18835
+ },
18836
+ onSetAlt: (alt) => {
18837
+ void runMutation(active.sectionId, (tree) => setMediaStripItemAlt(tree, active.index, alt));
18838
+ },
18839
+ onSetLink: (href, linkTarget) => {
18994
18840
  void runMutation(
18995
18841
  active.sectionId,
18996
- (tree) => addMediaStripItem(tree, item, active.index),
18997
- { [picked.assetId]: picked.url }
18998
- ).then((applied) => {
18999
- if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
18842
+ (tree) => setMediaStripItemLink(tree, active.index, href ? { href, linkTarget } : {})
18843
+ );
18844
+ },
18845
+ onAddItem: () => {
18846
+ void requestAssetPick(postToParent2).then((picked) => {
18847
+ if (!picked) return;
18848
+ const item = { assetId: picked.assetId, alt: "Logo" };
18849
+ void runMutation(
18850
+ active.sectionId,
18851
+ (tree) => addMediaStripItem(tree, item, active.index),
18852
+ { [picked.assetId]: picked.url }
18853
+ ).then((applied) => {
18854
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
18855
+ });
19000
18856
  });
19001
- });
19002
- },
19003
- onChangeImage: () => {
19004
- void requestAssetPick(postToParent2).then((picked) => {
19005
- if (!picked) return;
19006
- void runMutation(
19007
- active.sectionId,
19008
- (tree) => setMediaStripItemAsset(tree, active.index, picked.assetId),
19009
- { [picked.assetId]: picked.url }
18857
+ },
18858
+ onChangeImage: () => {
18859
+ void requestAssetPick(postToParent2).then((picked) => {
18860
+ if (!picked) return;
18861
+ void runMutation(
18862
+ active.sectionId,
18863
+ (tree) => setMediaStripItemAsset(tree, active.index, picked.assetId),
18864
+ { [picked.assetId]: picked.url }
18865
+ );
18866
+ });
18867
+ },
18868
+ onRemoveItem: () => {
18869
+ void runMutation(active.sectionId, (tree) => removeMediaStripItem(tree, active.index)).then(
18870
+ (applied) => {
18871
+ if (applied) setActive(null);
18872
+ }
19010
18873
  );
19011
- });
19012
- },
19013
- onRemoveItem: () => {
19014
- void runMutation(active.sectionId, (tree) => removeMediaStripItem(tree, active.index)).then(
19015
- (applied) => {
19016
- if (applied) setActive(null);
19017
- }
19018
- );
19019
- },
19020
- onMoveLeft: () => {
19021
- void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, -1)).then(
19022
- (applied) => {
19023
- if (applied) setActive({ sectionId: active.sectionId, index: active.index - 1 });
19024
- }
19025
- );
19026
- },
19027
- onMoveRight: () => {
19028
- void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, 1)).then(
19029
- (applied) => {
19030
- if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19031
- }
19032
- );
19033
- }
19034
- }
19035
- ) }),
19036
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19037
- "div",
19038
- {
19039
- "data-ohw-bridge": "",
19040
- "data-ohw-item-interaction": "",
19041
- "aria-hidden": true,
19042
- className: "pointer-events-none fixed rounded-[4px] px-[7px] py-[3px] font-sans text-[11px] font-medium leading-none text-primary-foreground whitespace-nowrap",
19043
- style: {
19044
- top: activeItemRect.top + activeItemRect.height + 10,
19045
- left: activeItemRect.left - 5,
19046
- background: PRIMARY3,
19047
- zIndex: 2147483644
19048
- },
19049
- children: "Media item"
19050
- }
19051
- ),
19052
- linkOpen && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19053
- LinkModal,
19054
- {
19055
- href: activeHref,
19056
- linkTarget: activeTarget,
19057
- onClose: () => setLinkOpen(false),
19058
- onSave: (href, linkTarget) => {
19059
- void runMutation(
19060
- active.sectionId,
19061
- (tree) => setMediaStripItemLink(tree, active.index, href ? { href, linkTarget } : {})
19062
- );
18874
+ },
18875
+ onMoveLeft: () => {
18876
+ void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, -1)).then(
18877
+ (applied) => {
18878
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index - 1 });
18879
+ }
18880
+ );
18881
+ },
18882
+ onMoveRight: () => {
18883
+ void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, 1)).then(
18884
+ (applied) => {
18885
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
18886
+ }
18887
+ );
18888
+ }
19063
18889
  }
19064
- }
19065
- )
19066
- ] })
18890
+ )
18891
+ }
18892
+ )
19067
18893
  ] });
19068
18894
  }
19069
18895
 
19070
18896
  // src/ui/ai-section/AiPricingCardOverlay.tsx
19071
18897
  var import_react11 = require("react");
19072
- var import_lucide_react10 = require("lucide-react");
19073
- var import_jsx_runtime21 = require("react/jsx-runtime");
18898
+ var import_lucide_react9 = require("lucide-react");
18899
+ var import_jsx_runtime20 = require("react/jsx-runtime");
19074
18900
  var PRIMARY4 = "var(--ohw-primary, #0885FE)";
19075
- 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]";
18901
+ 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]";
19076
18902
  function findPricingCardElement(sectionId, index) {
19077
18903
  const sectionEl = findByInstanceId(sectionId);
19078
18904
  if (!sectionEl) return null;
@@ -19084,7 +18910,7 @@ function findPricingCardElement(sectionId, index) {
19084
18910
  }
19085
18911
  function cardRefFromTarget(target) {
19086
18912
  if (!(target instanceof Element)) return null;
19087
- if (target.closest(CHROME_SELECTOR2)) return null;
18913
+ if (target.closest(CHROME_SELECTOR)) return null;
19088
18914
  const cardEl = target.closest("[data-ohw-pricing-card]");
19089
18915
  if (!cardEl) return null;
19090
18916
  const sectionEl = cardEl.closest("[data-ohw-section]");
@@ -19094,21 +18920,21 @@ function cardRefFromTarget(target) {
19094
18920
  if (!sectionId || Number.isNaN(index)) return null;
19095
18921
  return { sectionId, index };
19096
18922
  }
19097
- function sameRef2(a, b) {
18923
+ function sameRef(a, b) {
19098
18924
  return a?.sectionId === b?.sectionId && a?.index === b?.index;
19099
18925
  }
19100
- function rectOf2(el) {
18926
+ function rectOf(el) {
19101
18927
  if (!el) return null;
19102
18928
  const r2 = el.getBoundingClientRect();
19103
18929
  if (r2.width <= 0 || r2.height <= 0) return null;
19104
18930
  return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
19105
18931
  }
19106
18932
  function readRect3(ref) {
19107
- return rectOf2(ref && findPricingCardElement(ref.sectionId, ref.index));
18933
+ return rectOf(ref && findPricingCardElement(ref.sectionId, ref.index));
19108
18934
  }
19109
18935
  function readGridRect(ref) {
19110
18936
  const card = ref && findPricingCardElement(ref.sectionId, ref.index);
19111
- return rectOf2(card?.closest("[data-ohw-pricing-collection]") ?? card);
18937
+ return rectOf(card?.closest("[data-ohw-pricing-collection]") ?? card);
19112
18938
  }
19113
18939
  function useLiveCardRect(ref, grid = false) {
19114
18940
  const [rect, setRect] = (0, import_react11.useState)(null);
@@ -19138,7 +18964,7 @@ function useLiveCardRect(ref, grid = false) {
19138
18964
  }
19139
18965
  var TOOLBAR_GRID_GAP = 52;
19140
18966
  var TOOLBAR_GRID_GAP_MOBILE = 42;
19141
- var TOOLBAR_EDGE_MARGIN3 = 4;
18967
+ var TOOLBAR_EDGE_MARGIN2 = 4;
19142
18968
  function GridToolbarSlot({ gridRect, children }) {
19143
18969
  const slotRef = (0, import_react11.useRef)(null);
19144
18970
  const [shift, setShift] = (0, import_react11.useState)(0);
@@ -19150,7 +18976,7 @@ function GridToolbarSlot({ gridRect, children }) {
19150
18976
  const w = slot.offsetWidth;
19151
18977
  if (w <= 0) return;
19152
18978
  const half = w / 2;
19153
- const clamped = Math.max(TOOLBAR_EDGE_MARGIN3 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN3 - half));
18979
+ const clamped = Math.max(TOOLBAR_EDGE_MARGIN2 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half));
19154
18980
  setShift((prev) => Math.abs(prev - (clamped - centerX)) < 0.5 ? prev : clamped - centerX);
19155
18981
  };
19156
18982
  clamp();
@@ -19159,7 +18985,7 @@ function GridToolbarSlot({ gridRect, children }) {
19159
18985
  return () => ro.disconnect();
19160
18986
  }, [centerX, children]);
19161
18987
  const mobile = window.innerWidth <= 480;
19162
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
18988
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19163
18989
  "div",
19164
18990
  {
19165
18991
  ref: slotRef,
@@ -19194,7 +19020,7 @@ function requestValidateTree2(postToParent2, tree) {
19194
19020
  });
19195
19021
  }
19196
19022
  function StyleSwatch({ style }) {
19197
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19023
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19198
19024
  "span",
19199
19025
  {
19200
19026
  "aria-hidden": true,
@@ -19208,35 +19034,35 @@ function StyleSwatch({ style }) {
19208
19034
  );
19209
19035
  }
19210
19036
  function ButtonGlyph() {
19211
- 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: [
19212
- /* @__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" }),
19213
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M5.5 6H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
19037
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("svg", { width: "18", height: "12", viewBox: "0 0 18 12", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
19038
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("rect", { x: "0.75", y: "0.75", width: "16.5", height: "10.5", rx: "3", stroke: "currentColor", strokeWidth: "1.5" }),
19039
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("path", { d: "M5.5 6H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
19214
19040
  ] });
19215
19041
  }
19216
19042
  function FeatureGlyph({ mode }) {
19217
- 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: [
19218
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("circle", { cx: "8", cy: "8", r: "7.25", stroke: "currentColor", strokeWidth: "1.5" }),
19219
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M5 8.2L7.2 10.3L11 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
19220
- /* @__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" }),
19221
- /* @__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" }),
19222
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M11.5 13.5H15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" }),
19223
- mode === "add" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M13.5 11.5V15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" })
19043
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
19044
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("circle", { cx: "8", cy: "8", r: "7.25", stroke: "currentColor", strokeWidth: "1.5" }),
19045
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("path", { d: "M5 8.2L7.2 10.3L11 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
19046
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("circle", { cx: "13.5", cy: "13.5", r: "4.25", fill: "var(--ohw-background, #ffffff)", stroke: "var(--ohw-background, #ffffff)", strokeWidth: "1" }),
19047
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("circle", { cx: "13.5", cy: "13.5", r: "3.75", fill: "var(--ohw-background, #ffffff)", stroke: PRIMARY4, strokeWidth: "1" }),
19048
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("path", { d: "M11.5 13.5H15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" }),
19049
+ mode === "add" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("path", { d: "M13.5 11.5V15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" })
19224
19050
  ] });
19225
19051
  }
19226
- function ToolbarAction2({
19052
+ function ToolbarAction({
19227
19053
  label,
19228
19054
  onClick,
19229
19055
  disabled = false,
19230
19056
  children
19231
19057
  }) {
19232
- const button = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, onClick, className: "size-8", children });
19233
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19234
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "inline-flex", children: button }) : button }),
19235
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
19058
+ const button = /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, onClick, className: "size-8", children });
19059
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Tooltip, { children: [
19060
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "inline-flex", children: button }) : button }),
19061
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
19236
19062
  ] });
19237
19063
  }
19238
- function ToolbarSeparator2() {
19239
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
19064
+ function ToolbarSeparator() {
19065
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
19240
19066
  }
19241
19067
  function CardStyleMenu({
19242
19068
  value,
@@ -19244,7 +19070,7 @@ function CardStyleMenu({
19244
19070
  disabled = false
19245
19071
  }) {
19246
19072
  const current = PRICING_CARD_STYLES.find((s) => s.value === value) ?? PRICING_CARD_STYLES[0];
19247
- const trigger = /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19073
+ const trigger = /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
19248
19074
  "button",
19249
19075
  {
19250
19076
  type: "button",
@@ -19255,28 +19081,28 @@ function CardStyleMenu({
19255
19081
  disabled && "cursor-not-allowed opacity-40 hover:bg-transparent"
19256
19082
  ),
19257
19083
  children: [
19258
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StyleSwatch, { style: current.value }),
19259
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "flex-1 text-left", children: current.label }),
19260
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
19084
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(StyleSwatch, { style: current.value }),
19085
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "flex-1 text-left", children: current.label }),
19086
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
19261
19087
  ]
19262
19088
  }
19263
19089
  );
19264
19090
  if (disabled) {
19265
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19266
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-xs text-muted-foreground opacity-50", children: "Card style" }),
19267
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19268
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "inline-flex", children: trigger }) }),
19269
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: "Mark as featured to edit card style" })
19091
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19092
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-xs text-muted-foreground opacity-50", children: "Card style" }),
19093
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Tooltip, { children: [
19094
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "inline-flex", children: trigger }) }),
19095
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: "Mark as featured to edit card style" })
19270
19096
  ] })
19271
19097
  ] });
19272
19098
  }
19273
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19274
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-xs text-muted-foreground", children: "Card style" }),
19275
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DropdownMenu, { modal: false, children: [
19276
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuTrigger, { asChild: true, children: trigger }),
19277
- /* @__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) => {
19099
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19100
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-xs text-muted-foreground", children: "Card style" }),
19101
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(DropdownMenu, { modal: false, children: [
19102
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DropdownMenuTrigger, { asChild: true, children: trigger }),
19103
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[152px] min-w-[152px] p-1", children: PRICING_CARD_STYLES.map((style) => {
19278
19104
  const selected = style.value === value;
19279
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19105
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
19280
19106
  DropdownMenuItem,
19281
19107
  {
19282
19108
  role: "menuitemradio",
@@ -19284,9 +19110,9 @@ function CardStyleMenu({
19284
19110
  onSelect: () => onChange(style.value),
19285
19111
  className: cn("grid grid-cols-[18px_1fr_16px] gap-2 py-2 pl-2 pr-2 text-[13px]", selected && "bg-muted"),
19286
19112
  children: [
19287
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StyleSwatch, { style: style.value }),
19288
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: style.label }),
19289
- selected && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Check, { size: 16, className: "text-bridge-primary" })
19113
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(StyleSwatch, { style: style.value }),
19114
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: style.label }),
19115
+ selected && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Check, { size: 16, className: "text-bridge-primary" })
19290
19116
  ]
19291
19117
  },
19292
19118
  style.value
@@ -19316,7 +19142,7 @@ function PricingCardToolbar({
19316
19142
  const style = pricingCardStyle(entry.tree, index);
19317
19143
  const featuredTag = pricingCardFeaturedTag(entry.tree, index);
19318
19144
  const mobile = window.innerWidth <= 480;
19319
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19145
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
19320
19146
  "div",
19321
19147
  {
19322
19148
  "data-ohw-bridge": "",
@@ -19328,7 +19154,7 @@ function PricingCardToolbar({
19328
19154
  ),
19329
19155
  style: { maxWidth: mobile ? "calc(100vw - 24px)" : void 0 },
19330
19156
  children: [
19331
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19157
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
19332
19158
  "div",
19333
19159
  {
19334
19160
  className: cn(
@@ -19337,8 +19163,8 @@ function PricingCardToolbar({
19337
19163
  mobile ? "flex-wrap justify-center" : "flex-nowrap"
19338
19164
  ),
19339
19165
  children: [
19340
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19341
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19166
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Tooltip, { children: [
19167
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19342
19168
  CustomToolbarButton,
19343
19169
  {
19344
19170
  type: "button",
@@ -19346,8 +19172,8 @@ function PricingCardToolbar({
19346
19172
  "aria-pressed": featuredTag,
19347
19173
  onClick: () => onSetFeaturedTag(!featuredTag),
19348
19174
  className: "group size-8",
19349
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19350
- import_lucide_react10.Star,
19175
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19176
+ import_lucide_react9.Star,
19351
19177
  {
19352
19178
  size: 16,
19353
19179
  fill: "currentColor",
@@ -19361,25 +19187,25 @@ function PricingCardToolbar({
19361
19187
  )
19362
19188
  }
19363
19189
  ) }),
19364
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Unmark featured" : "Mark as featured" })
19190
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Unmark featured" : "Mark as featured" })
19365
19191
  ] }),
19366
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator2, {}),
19367
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CardStyleMenu, { value: style, onChange: onSetStyle, disabled: !featuredTag }),
19368
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator2, {}),
19369
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction2, { label: "Add button", onClick: onAddButton, disabled: buttonCount >= 2, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ButtonGlyph, {}) }),
19370
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction2, { label: "Remove button", onClick: onRemoveButton, disabled: buttonCount === 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "relative inline-flex", children: [
19371
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ButtonGlyph, {}),
19372
- /* @__PURE__ */ (0, import_jsx_runtime21.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" })
19192
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarSeparator, {}),
19193
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CardStyleMenu, { value: style, onChange: onSetStyle, disabled: !featuredTag }),
19194
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarSeparator, {}),
19195
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Add button", onClick: onAddButton, disabled: buttonCount >= 2, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ButtonGlyph, {}) }),
19196
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Remove button", onClick: onRemoveButton, disabled: buttonCount === 0, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "relative inline-flex", children: [
19197
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ButtonGlyph, {}),
19198
+ /* @__PURE__ */ (0, import_jsx_runtime20.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" })
19373
19199
  ] }) }),
19374
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction2, { label: "Add feature", onClick: onAddFeature, disabled: featureCount >= 10, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(FeatureGlyph, { mode: "add" }) }),
19375
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction2, { label: "Remove feature", onClick: onRemoveFeature, disabled: featureCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(FeatureGlyph, { mode: "remove" }) }),
19376
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator2, {}),
19377
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction2, { label: "Add tier", onClick: onAddTier, disabled: collectionCount >= 4, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Plus, { size: 16, style: { pointerEvents: "none" } }) }),
19378
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction2, { label: "Remove tier", onClick: onRemoveTier, disabled: collectionCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Minus, { size: 16, style: { pointerEvents: "none" } }) })
19200
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Add feature", onClick: onAddFeature, disabled: featureCount >= 10, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(FeatureGlyph, { mode: "add" }) }),
19201
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Remove feature", onClick: onRemoveFeature, disabled: featureCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(FeatureGlyph, { mode: "remove" }) }),
19202
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarSeparator, {}),
19203
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Add tier", onClick: onAddTier, disabled: collectionCount >= 4, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Plus, { size: 16, style: { pointerEvents: "none" } }) }),
19204
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ToolbarAction, { label: "Remove tier", onClick: onRemoveTier, disabled: collectionCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react9.Minus, { size: 16, style: { pointerEvents: "none" } }) })
19379
19205
  ]
19380
19206
  }
19381
19207
  ),
19382
- 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 })
19208
+ error && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
19383
19209
  ]
19384
19210
  }
19385
19211
  ) });
@@ -19413,7 +19239,7 @@ function AiPricingCardOverlay({
19413
19239
  const overEditable = Boolean(t?.closest?.("[data-ohw-editable], [data-ohw-role], a, button"));
19414
19240
  setHover((prev) => {
19415
19241
  const next = ref && !overEditable ? ref : null;
19416
- return sameRef2(prev, next) ? prev : next;
19242
+ return sameRef(prev, next) ? prev : next;
19417
19243
  });
19418
19244
  };
19419
19245
  const onLeave = () => setHover(null);
@@ -19428,10 +19254,10 @@ function AiPricingCardOverlay({
19428
19254
  const onClick = (e) => {
19429
19255
  const t = e.target;
19430
19256
  if (!(t instanceof Element)) return;
19431
- if (t.closest(CHROME_SELECTOR2)) return;
19257
+ if (t.closest(CHROME_SELECTOR)) return;
19432
19258
  const ref = cardRefFromTarget(t);
19433
- setActive((prev) => sameRef2(prev, ref) ? prev : ref);
19434
- if (!ref || !sameRef2(activeRef.current, ref)) setError(null);
19259
+ setActive((prev) => sameRef(prev, ref) ? prev : ref);
19260
+ if (!ref || !sameRef(activeRef.current, ref)) setError(null);
19435
19261
  };
19436
19262
  document.addEventListener("click", onClick, true);
19437
19263
  return () => document.removeEventListener("click", onClick, true);
@@ -19496,12 +19322,12 @@ function AiPricingCardOverlay({
19496
19322
  const activeEntry = active ? findEntry(active.sectionId) : null;
19497
19323
  const activeCardRect = useLiveCardRect(active);
19498
19324
  const activeGridRect = useLiveCardRect(active, true);
19499
- const hoverRect = useLiveCardRect(hover && !sameRef2(hover, active) ? hover : null);
19500
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19501
- hoverRect && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
19502
- activeCardRect && activeGridRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19503
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: activeCardRect, state: "active-top" }),
19504
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(GridToolbarSlot, { gridRect: activeGridRect, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19325
+ const hoverRect = useLiveCardRect(hover && !sameRef(hover, active) ? hover : null);
19326
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
19327
+ hoverRect && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
19328
+ activeCardRect && activeGridRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
19329
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ItemInteractionLayer, { rect: activeCardRect, state: "active-top" }),
19330
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(GridToolbarSlot, { gridRect: activeGridRect, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19505
19331
  PricingCardToolbar,
19506
19332
  {
19507
19333
  entry: activeEntry,
@@ -19529,7 +19355,7 @@ function AiPricingCardOverlay({
19529
19355
  onRemoveFeature: () => runMutation(active.sectionId, (tree) => removePricingCardFeature(tree, active.index))
19530
19356
  }
19531
19357
  ) }),
19532
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19358
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19533
19359
  "div",
19534
19360
  {
19535
19361
  "data-ohw-bridge": "",
@@ -19551,7 +19377,7 @@ function AiPricingCardOverlay({
19551
19377
 
19552
19378
  // src/ui/ai-section/AiVideoOverlay.tsx
19553
19379
  var import_react12 = require("react");
19554
- var import_lucide_react11 = require("lucide-react");
19380
+ var import_lucide_react10 = require("lucide-react");
19555
19381
 
19556
19382
  // src/lib/ai-video-tree.ts
19557
19383
  function resolveVideoBlock(tree, path) {
@@ -19622,10 +19448,10 @@ function extractYouTubeId(input) {
19622
19448
  }
19623
19449
 
19624
19450
  // src/ui/ai-section/AiVideoOverlay.tsx
19625
- var import_jsx_runtime22 = require("react/jsx-runtime");
19451
+ var import_jsx_runtime21 = require("react/jsx-runtime");
19626
19452
  var PRIMARY5 = "#0885FE";
19627
- var OHW_BLUE = `var(--ohw-primary, ${PRIMARY5})`;
19628
- var OHW_BLUE_DARK = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY5}) 60%, #000000)`;
19453
+ var OHW_BLUE2 = `var(--ohw-primary, ${PRIMARY5})`;
19454
+ var OHW_BLUE_DARK2 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY5}) 60%, #000000)`;
19629
19455
  function findVideoBlockElement(sectionId, path) {
19630
19456
  const sectionEl = findByInstanceId(sectionId);
19631
19457
  if (!sectionEl) return null;
@@ -19685,12 +19511,12 @@ function requestValidateTree3(postToParent2, tree) {
19685
19511
  postToParent2({ type: "ow:ai-validate-tree-request", id, tree });
19686
19512
  });
19687
19513
  }
19688
- function Pill({
19514
+ function Pill2({
19689
19515
  label,
19690
19516
  onClick,
19691
19517
  disabled
19692
19518
  }) {
19693
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19519
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19694
19520
  "button",
19695
19521
  {
19696
19522
  type: "button",
@@ -19706,7 +19532,7 @@ function Pill({
19706
19532
  border: "none",
19707
19533
  cursor: disabled ? "default" : "pointer",
19708
19534
  background: "#ffffff",
19709
- color: OHW_BLUE_DARK,
19535
+ color: OHW_BLUE_DARK2,
19710
19536
  opacity: disabled ? 0.4 : 1,
19711
19537
  fontSize: 12,
19712
19538
  lineHeight: "16px",
@@ -19749,8 +19575,8 @@ function VideoUrlInput({ value, onCommit }) {
19749
19575
  onCommit(id);
19750
19576
  }
19751
19577
  };
19752
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 4, width: "100%", maxWidth: 280 }, children: [
19753
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19578
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 4, width: "100%", maxWidth: 280 }, children: [
19579
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19754
19580
  "div",
19755
19581
  {
19756
19582
  style: {
@@ -19766,12 +19592,12 @@ function VideoUrlInput({ value, onCommit }) {
19766
19592
  boxShadow: invalid ? "inset 0 0 0 1.5px #EF4444" : "none"
19767
19593
  },
19768
19594
  children: [
19769
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19595
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19770
19596
  "span",
19771
19597
  {
19772
19598
  style: {
19773
19599
  flex: "0 0 auto",
19774
- color: OHW_BLUE_DARK,
19600
+ color: OHW_BLUE_DARK2,
19775
19601
  fontSize: 10,
19776
19602
  lineHeight: "16px",
19777
19603
  fontWeight: 700,
@@ -19781,7 +19607,7 @@ function VideoUrlInput({ value, onCommit }) {
19781
19607
  children: "Video"
19782
19608
  }
19783
19609
  ),
19784
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19610
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19785
19611
  "input",
19786
19612
  {
19787
19613
  type: "text",
@@ -19818,7 +19644,7 @@ function VideoUrlInput({ value, onCommit }) {
19818
19644
  ]
19819
19645
  }
19820
19646
  ),
19821
- 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." })
19647
+ invalid && /* @__PURE__ */ (0, import_jsx_runtime21.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." })
19822
19648
  ] });
19823
19649
  }
19824
19650
  function VideoToolbar({
@@ -19828,7 +19654,7 @@ function VideoToolbar({
19828
19654
  onSetVideoId,
19829
19655
  onRemove
19830
19656
  }) {
19831
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19657
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19832
19658
  "div",
19833
19659
  {
19834
19660
  "data-ohw-bridge": "",
@@ -19840,7 +19666,7 @@ function VideoToolbar({
19840
19666
  gap: 6,
19841
19667
  alignItems: "center",
19842
19668
  width: 280,
19843
- background: OHW_BLUE,
19669
+ background: OHW_BLUE2,
19844
19670
  padding: 8,
19845
19671
  borderRadius: 12,
19846
19672
  boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
@@ -19848,9 +19674,9 @@ function VideoToolbar({
19848
19674
  opacity: pending ? 0.6 : 1
19849
19675
  },
19850
19676
  children: [
19851
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VideoUrlInput, { value: videoId, onCommit: onSetVideoId }),
19852
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Pill, { label: "Remove video", onClick: onRemove, disabled: !videoId }),
19853
- error && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
19677
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(VideoUrlInput, { value: videoId, onCommit: onSetVideoId }),
19678
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Pill2, { label: "Remove video", onClick: onRemove, disabled: !videoId }),
19679
+ error && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
19854
19680
  ]
19855
19681
  }
19856
19682
  );
@@ -19970,8 +19796,8 @@ function AiVideoOverlay({
19970
19796
  const activeBlockRect = useLiveBlockRect(active);
19971
19797
  const TOOLBAR_APPROX_H = 90;
19972
19798
  const toolbarState = activeBlockRect && activeBlockRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
19973
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
19974
- hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19799
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19800
+ hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19975
19801
  "button",
19976
19802
  {
19977
19803
  type: "button",
@@ -19999,15 +19825,15 @@ function AiVideoOverlay({
19999
19825
  fontSize: 14,
20000
19826
  fontWeight: 700
20001
19827
  },
20002
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react11.Star, { size: 14, style: { pointerEvents: "none" } })
19828
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react10.Star, { size: 14, style: { pointerEvents: "none" } })
20003
19829
  }
20004
19830
  ),
20005
- activeBlockRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19831
+ activeBlockRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
20006
19832
  ItemInteractionLayer,
20007
19833
  {
20008
19834
  rect: activeBlockRect,
20009
19835
  state: toolbarState,
20010
- toolbar: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19836
+ toolbar: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
20011
19837
  VideoToolbar,
20012
19838
  {
20013
19839
  videoId: videoIdAt(activeEntry.tree, active.path),
@@ -20031,7 +19857,7 @@ function AiVideoOverlay({
20031
19857
 
20032
19858
  // src/ui/ai-section/AiFadeCarouselOverlay.tsx
20033
19859
  var import_react13 = require("react");
20034
- var import_lucide_react12 = require("lucide-react");
19860
+ var import_lucide_react11 = require("lucide-react");
20035
19861
 
20036
19862
  // src/lib/ai-carousel-tree.ts
20037
19863
  function isCarouselBlock(node) {
@@ -20082,10 +19908,10 @@ function setCarouselAutoplay(tree, on) {
20082
19908
  }
20083
19909
 
20084
19910
  // src/ui/ai-section/AiFadeCarouselOverlay.tsx
20085
- var import_jsx_runtime23 = require("react/jsx-runtime");
19911
+ var import_jsx_runtime22 = require("react/jsx-runtime");
20086
19912
  var PRIMARY6 = "#0885FE";
20087
- var OHW_BLUE2 = `var(--ohw-primary, ${PRIMARY6})`;
20088
- var OHW_BLUE_DARK2 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY6}) 60%, #000000)`;
19913
+ var OHW_BLUE3 = `var(--ohw-primary, ${PRIMARY6})`;
19914
+ var OHW_BLUE_DARK3 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY6}) 60%, #000000)`;
20089
19915
  function findCarouselItemElement(sectionId, index) {
20090
19916
  const sectionEl = findByInstanceId(sectionId);
20091
19917
  if (!sectionEl) return null;
@@ -20164,8 +19990,8 @@ function requestAssetPick2(postToParent2) {
20164
19990
  postToParent2({ type: "ow:asset-pick", id });
20165
19991
  });
20166
19992
  }
20167
- function Pill2({ label, onClick, disabled }) {
20168
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
19993
+ function Pill3({ label, onClick, disabled }) {
19994
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20169
19995
  "button",
20170
19996
  {
20171
19997
  type: "button",
@@ -20181,7 +20007,7 @@ function Pill2({ label, onClick, disabled }) {
20181
20007
  border: "none",
20182
20008
  cursor: disabled ? "default" : "pointer",
20183
20009
  background: "#ffffff",
20184
- color: OHW_BLUE_DARK2,
20010
+ color: OHW_BLUE_DARK3,
20185
20011
  opacity: disabled ? 0.4 : 1,
20186
20012
  fontSize: 12,
20187
20013
  lineHeight: "16px",
@@ -20199,7 +20025,7 @@ function TogglePill({
20199
20025
  onToggle,
20200
20026
  disabled
20201
20027
  }) {
20202
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
20028
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
20203
20029
  "button",
20204
20030
  {
20205
20031
  type: "button",
@@ -20218,7 +20044,7 @@ function TogglePill({
20218
20044
  border: "none",
20219
20045
  cursor: disabled ? "default" : "pointer",
20220
20046
  background: "#ffffff",
20221
- color: OHW_BLUE_DARK2,
20047
+ color: OHW_BLUE_DARK3,
20222
20048
  opacity: disabled ? 0.4 : 1,
20223
20049
  fontSize: 12,
20224
20050
  lineHeight: "16px",
@@ -20228,7 +20054,7 @@ function TogglePill({
20228
20054
  },
20229
20055
  children: [
20230
20056
  label,
20231
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20057
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20232
20058
  "span",
20233
20059
  {
20234
20060
  "aria-hidden": true,
@@ -20241,10 +20067,10 @@ function TogglePill({
20241
20067
  // Off is a grey track, not a washed-out blue: the pill's own white background already
20242
20068
  // carries the brand colour in the text, and a pale blue track next to it read as
20243
20069
  // "disabled control" rather than "switch that is off".
20244
- background: on ? OHW_BLUE_DARK2 : "#CBD5E1",
20070
+ background: on ? OHW_BLUE_DARK3 : "#CBD5E1",
20245
20071
  transition: "background 120ms ease"
20246
20072
  },
20247
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20073
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20248
20074
  "span",
20249
20075
  {
20250
20076
  style: {
@@ -20273,7 +20099,7 @@ function CarouselSlideToolbar({
20273
20099
  onChangeImage,
20274
20100
  onToggleAutoplay
20275
20101
  }) {
20276
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
20102
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
20277
20103
  "div",
20278
20104
  {
20279
20105
  "data-ohw-bridge": "",
@@ -20284,7 +20110,7 @@ function CarouselSlideToolbar({
20284
20110
  flexDirection: "column",
20285
20111
  gap: 6,
20286
20112
  alignItems: "center",
20287
- background: OHW_BLUE2,
20113
+ background: OHW_BLUE3,
20288
20114
  padding: 8,
20289
20115
  borderRadius: 12,
20290
20116
  boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
@@ -20296,11 +20122,11 @@ function CarouselSlideToolbar({
20296
20122
  pointerEvents: pending ? "none" : "auto"
20297
20123
  },
20298
20124
  children: [
20299
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
20300
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Pill2, { label: "Change Image", onClick: onChangeImage }),
20301
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(TogglePill, { label: "Autoplay", on: autoplay, onToggle: onToggleAutoplay })
20125
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
20126
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Pill3, { label: "Change Image", onClick: onChangeImage }),
20127
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TogglePill, { label: "Autoplay", on: autoplay, onToggle: onToggleAutoplay })
20302
20128
  ] }),
20303
- error && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20129
+ error && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20304
20130
  ]
20305
20131
  }
20306
20132
  );
@@ -20440,8 +20266,8 @@ function AiFadeCarouselOverlay({
20440
20266
  const activeItemRect = useLiveItemRect2(active);
20441
20267
  const TOOLBAR_APPROX_H = 90;
20442
20268
  const toolbarState = activeItemRect && activeItemRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
20443
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
20444
- hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20269
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
20270
+ hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20445
20271
  "button",
20446
20272
  {
20447
20273
  type: "button",
@@ -20470,10 +20296,10 @@ function AiFadeCarouselOverlay({
20470
20296
  fontSize: 14,
20471
20297
  fontWeight: 700
20472
20298
  },
20473
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react12.Star, { size: 14, style: { pointerEvents: "none" } })
20299
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react11.Star, { size: 14, style: { pointerEvents: "none" } })
20474
20300
  }
20475
20301
  ),
20476
- swapping && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20302
+ swapping && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20477
20303
  "div",
20478
20304
  {
20479
20305
  ref: skeletonRef,
@@ -20491,15 +20317,15 @@ function AiFadeCarouselOverlay({
20491
20317
  zIndex: 2147483646,
20492
20318
  pointerEvents: "none"
20493
20319
  },
20494
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("style", { children: MEDIA_SKELETON_CSS })
20320
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("style", { children: MEDIA_SKELETON_CSS })
20495
20321
  }
20496
20322
  ),
20497
- activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20323
+ activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20498
20324
  ItemInteractionLayer,
20499
20325
  {
20500
20326
  rect: activeItemRect,
20501
20327
  state: toolbarState,
20502
- toolbar: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20328
+ toolbar: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20503
20329
  CarouselSlideToolbar,
20504
20330
  {
20505
20331
  pending,
@@ -20852,6 +20678,113 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
20852
20678
  // src/ui/link-modal/LinkPopover.tsx
20853
20679
  var import_react17 = require("react");
20854
20680
 
20681
+ // src/ui/dialog.tsx
20682
+ var React9 = __toESM(require("react"), 1);
20683
+ var import_radix_ui6 = require("radix-ui");
20684
+ var import_lucide_react12 = require("lucide-react");
20685
+ var import_jsx_runtime23 = require("react/jsx-runtime");
20686
+ function Dialog2({
20687
+ ...props
20688
+ }) {
20689
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_radix_ui6.Dialog.Root, { "data-slot": "dialog", ...props });
20690
+ }
20691
+ function DialogPortal({
20692
+ ...props
20693
+ }) {
20694
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_radix_ui6.Dialog.Portal, { ...props });
20695
+ }
20696
+ function DialogOverlay({
20697
+ className,
20698
+ ...props
20699
+ }) {
20700
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20701
+ import_radix_ui6.Dialog.Overlay,
20702
+ {
20703
+ "data-slot": "dialog-overlay",
20704
+ "data-ohw-link-modal-root": "",
20705
+ className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
20706
+ ...props
20707
+ }
20708
+ );
20709
+ }
20710
+ var DialogContent = React9.forwardRef(
20711
+ ({ className, children, showCloseButton = true, container, ...props }, ref) => {
20712
+ const positionMode = container ? "absolute" : "fixed";
20713
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(DialogPortal, { container: container ?? void 0, children: [
20714
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
20715
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
20716
+ import_radix_ui6.Dialog.Content,
20717
+ {
20718
+ ref,
20719
+ "data-slot": "dialog-content",
20720
+ className: cn(
20721
+ positionMode,
20722
+ "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",
20723
+ "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
20724
+ container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
20725
+ className
20726
+ ),
20727
+ onMouseDown: (e) => e.stopPropagation(),
20728
+ ...props,
20729
+ children: [
20730
+ children,
20731
+ showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20732
+ import_radix_ui6.Dialog.Close,
20733
+ {
20734
+ type: "button",
20735
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
20736
+ "aria-label": "Close",
20737
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react12.X, { size: 16, "aria-hidden": true })
20738
+ }
20739
+ ) : null
20740
+ ]
20741
+ }
20742
+ )
20743
+ ] });
20744
+ }
20745
+ );
20746
+ DialogContent.displayName = import_radix_ui6.Dialog.Content.displayName;
20747
+ function DialogHeader({
20748
+ className,
20749
+ ...props
20750
+ }) {
20751
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
20752
+ }
20753
+ function DialogFooter({
20754
+ className,
20755
+ ...props
20756
+ }) {
20757
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20758
+ "div",
20759
+ {
20760
+ className: cn("flex items-center justify-end gap-2", className),
20761
+ ...props
20762
+ }
20763
+ );
20764
+ }
20765
+ var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20766
+ import_radix_ui6.Dialog.Title,
20767
+ {
20768
+ ref,
20769
+ className: cn(
20770
+ "text-lg font-semibold leading-none tracking-tight text-card-foreground",
20771
+ className
20772
+ ),
20773
+ ...props
20774
+ }
20775
+ ));
20776
+ DialogTitle.displayName = import_radix_ui6.Dialog.Title.displayName;
20777
+ var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20778
+ import_radix_ui6.Dialog.Description,
20779
+ {
20780
+ ref,
20781
+ className: cn("text-sm text-muted-foreground", className),
20782
+ ...props
20783
+ }
20784
+ ));
20785
+ DialogDescription.displayName = import_radix_ui6.Dialog.Description.displayName;
20786
+ var DialogClose = import_radix_ui6.Dialog.Close;
20787
+
20855
20788
  // src/ui/link-modal/LinkEditorPanel.tsx
20856
20789
  var import_lucide_react16 = require("lucide-react");
20857
20790
 
@@ -21955,7 +21888,6 @@ function shouldUseDevFixtures() {
21955
21888
  function getLogoElement(el) {
21956
21889
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
21957
21890
  if (marked) return marked;
21958
- if (el.closest('[data-ohw-editable="icon"]')) return null;
21959
21891
  const root = el.closest("nav, [data-ohw-nav-root], footer");
21960
21892
  if (!root) return null;
21961
21893
  const anchor = el.closest("a");
@@ -24536,7 +24468,7 @@ function getNavigationSelectionParent(el) {
24536
24468
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
24537
24469
  return resolveFooterLinksContainerFrom(el);
24538
24470
  }
24539
- 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)) {
24471
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
24540
24472
  return getNavigationRoot(el);
24541
24473
  }
24542
24474
  return null;
@@ -24758,6 +24690,7 @@ var ICONS = {
24758
24690
  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"/>',
24759
24691
  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"/>'
24760
24692
  };
24693
+ var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
24761
24694
  var SELECTION_CHROME_GAP2 = 4;
24762
24695
  var TOOLBAR_STROKE_GAP2 = 4;
24763
24696
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -24855,7 +24788,7 @@ function applyVisibleViewport(el, parentScroll) {
24855
24788
  el.style.top = `${top}px`;
24856
24789
  el.style.height = `${height}px`;
24857
24790
  }
24858
- var TOOLBAR_EDGE_MARGIN4 = 4;
24791
+ var TOOLBAR_EDGE_MARGIN3 = 4;
24859
24792
  function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
24860
24793
  const isAbove = transform.includes("translateY(-100%)");
24861
24794
  let visualTop = isAbove ? top - approxH : top;
@@ -24882,8 +24815,8 @@ function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
24882
24815
  function clampToolbarLeft(centerX, toolbarWidth) {
24883
24816
  const half = toolbarWidth / 2;
24884
24817
  return Math.max(
24885
- TOOLBAR_EDGE_MARGIN4 + half,
24886
- Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN4 - half)
24818
+ TOOLBAR_EDGE_MARGIN3 + half,
24819
+ Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN3 - half)
24887
24820
  );
24888
24821
  }
24889
24822
  function calcToolbarPos(rect, parentScroll, toolbarW = 306) {
@@ -25773,6 +25706,21 @@ function OhhwellsBridge() {
25773
25706
  const [isItemDragging, setIsItemDragging] = (0, import_react21.useState)(false);
25774
25707
  const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react21.useState)(false);
25775
25708
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
25709
+ const [floatingPanel, setFloatingPanel] = (0, import_react21.useState)(null);
25710
+ const floatingPanelOpenRef = (0, import_react21.useRef)(false);
25711
+ floatingPanelOpenRef.current = floatingPanel !== null;
25712
+ const floatingPanelRef = (0, import_react21.useRef)(null);
25713
+ floatingPanelRef.current = floatingPanel;
25714
+ const [floatingPanelPos, setFloatingPanelPos] = (0, import_react21.useState)(null);
25715
+ const buttonCollectionDragRef = (0, import_react21.useRef)(null);
25716
+ const buttonDropInsertIndexRef = (0, import_react21.useRef)(null);
25717
+ const [buttonDropSlots, setButtonDropSlots] = (0, import_react21.useState)([]);
25718
+ const [activeButtonDropIndex, setActiveButtonDropIndex] = (0, import_react21.useState)(null);
25719
+ const buttonPointerDragRef = (0, import_react21.useRef)(null);
25720
+ const [buttonStyleTick, setButtonStyleTick] = (0, import_react21.useState)(0);
25721
+ const [logoSizeDraft, setLogoSizeDraft] = (0, import_react21.useState)(null);
25722
+ const [editorViewport, setEditorViewport] = (0, import_react21.useState)("desktop");
25723
+ const [parentScrollSnap, setParentScrollSnap] = (0, import_react21.useState)(null);
25776
25724
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react21.useState)(null);
25777
25725
  const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react21.useState)(null);
25778
25726
  const footerDragRef = (0, import_react21.useRef)(null);
@@ -25798,21 +25746,6 @@ function OhhwellsBridge() {
25798
25746
  const stylesRef = (0, import_react21.useRef)("");
25799
25747
  const appliedDrawerRef = (0, import_react21.useRef)(null);
25800
25748
  const pendingDeleteUndoRef = (0, import_react21.useRef)(null);
25801
- const [floatingPanel, setFloatingPanel] = (0, import_react21.useState)(null);
25802
- const floatingPanelOpenRef = (0, import_react21.useRef)(false);
25803
- const floatingPanelRef = (0, import_react21.useRef)(null);
25804
- floatingPanelRef.current = floatingPanel;
25805
- const setFloatingPanelRef = (0, import_react21.useRef)(setFloatingPanel);
25806
- const [floatingPanelPos, setFloatingPanelPos] = (0, import_react21.useState)(null);
25807
- const buttonCollectionDragRef = (0, import_react21.useRef)(null);
25808
- const buttonDropInsertIndexRef = (0, import_react21.useRef)(null);
25809
- const [buttonDropSlots, setButtonDropSlots] = (0, import_react21.useState)([]);
25810
- const [activeButtonDropIndex, setActiveButtonDropIndex] = (0, import_react21.useState)(null);
25811
- const buttonPointerDragRef = (0, import_react21.useRef)(null);
25812
- const [buttonStyleTick, setButtonStyleTick] = (0, import_react21.useState)(0);
25813
- const [logoSizeDraft, setLogoSizeDraft] = (0, import_react21.useState)(null);
25814
- const [editorViewport, setEditorViewport] = (0, import_react21.useState)("desktop");
25815
- const [parentScrollSnap, setParentScrollSnap] = (0, import_react21.useState)(null);
25816
25749
  const [sitePages, setSitePages] = (0, import_react21.useState)([]);
25817
25750
  const [sectionsByPath, setSectionsByPath] = (0, import_react21.useState)({});
25818
25751
  const sectionsPrefetchGenRef = (0, import_react21.useRef)(0);
@@ -25821,18 +25754,7 @@ function OhhwellsBridge() {
25821
25754
  const linkPopoverOpenRef = (0, import_react21.useRef)(false);
25822
25755
  const linkPopoverGraceUntilRef = (0, import_react21.useRef)(0);
25823
25756
  setLinkPopoverRef.current = setLinkPopover;
25824
- setFloatingPanelRef.current = setFloatingPanel;
25825
25757
  linkPopoverSessionRef.current = linkPopover;
25826
- floatingPanelOpenRef.current = Boolean(floatingPanel);
25827
- (0, import_react21.useEffect)(() => {
25828
- const syncViewport = () => {
25829
- const next = window.innerWidth <= 480 ? "mobile" : "desktop";
25830
- setEditorViewport((prev) => prev === next ? prev : next);
25831
- };
25832
- syncViewport();
25833
- window.addEventListener("resize", syncViewport);
25834
- return () => window.removeEventListener("resize", syncViewport);
25835
- }, []);
25836
25758
  (0, import_react21.useEffect)(() => {
25837
25759
  setSectionOrderPersistHandler((entries) => {
25838
25760
  const currentPath = window.location.pathname;
@@ -27527,7 +27449,6 @@ function OhhwellsBridge() {
27527
27449
  applyAiFooterFromContent(content);
27528
27450
  if (typeof content[STYLE_STORE_KEY] === "string") {
27529
27451
  stylesRef.current = content[STYLE_STORE_KEY];
27530
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
27531
27452
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
27532
27453
  }
27533
27454
  applyBrandChrome(content);
@@ -27538,12 +27459,12 @@ function OhhwellsBridge() {
27538
27459
  if (key === "__ohw_sections") continue;
27539
27460
  if (key === AI_SECTIONS_KEY) continue;
27540
27461
  if (key === AI_FOOTER_KEY) continue;
27541
- if (key === LOGO_PLACEHOLDER_KEY) continue;
27542
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
27543
27462
  if (key === BRAND_KIT_KEY) continue;
27544
27463
  if (key === STYLE_STORE_KEY) continue;
27545
27464
  if (BRAND_CHROME_KEYS.has(key)) continue;
27465
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
27546
27466
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
27467
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
27547
27468
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
27548
27469
  continue;
27549
27470
  }
@@ -27758,18 +27679,17 @@ function OhhwellsBridge() {
27758
27679
  }
27759
27680
  applyAiFooterFromContent(content);
27760
27681
  if (typeof content[STYLE_STORE_KEY] === "string") {
27761
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
27762
27682
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
27763
27683
  }
27764
27684
  for (const [key, val] of Object.entries(content)) {
27765
27685
  if (key === "__ohw_sections") continue;
27766
27686
  if (key === AI_SECTIONS_KEY) continue;
27767
27687
  if (key === AI_FOOTER_KEY) continue;
27768
- if (key === LOGO_PLACEHOLDER_KEY) continue;
27769
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
27770
27688
  if (key === BRAND_KIT_KEY) continue;
27771
27689
  if (key === STYLE_STORE_KEY) continue;
27772
27690
  if (BRAND_CHROME_KEYS.has(key)) continue;
27691
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
27692
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
27773
27693
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
27774
27694
  continue;
27775
27695
  }
@@ -28026,11 +27946,26 @@ function OhhwellsBridge() {
28026
27946
  const t2 = setTimeout(measure, 500);
28027
27947
  const ro = new ResizeObserver(schedule);
28028
27948
  ro.observe(document.body);
27949
+ let lastWidth = window.innerWidth;
27950
+ let resizeTimers = [];
27951
+ const clearResizeTimers = () => {
27952
+ resizeTimers.forEach(clearTimeout);
27953
+ resizeTimers = [];
27954
+ };
27955
+ const handleResize = () => {
27956
+ if (window.innerWidth === lastWidth) return;
27957
+ lastWidth = window.innerWidth;
27958
+ clearResizeTimers();
27959
+ resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
27960
+ };
27961
+ window.addEventListener("resize", handleResize);
28029
27962
  return () => {
28030
27963
  clearTimeout(t1);
28031
27964
  clearTimeout(t2);
28032
27965
  if (raf != null) cancelAnimationFrame(raf);
28033
27966
  ro.disconnect();
27967
+ clearResizeTimers();
27968
+ window.removeEventListener("resize", handleResize);
28034
27969
  };
28035
27970
  }, [pathname, isEditMode, postToParent2]);
28036
27971
  (0, import_react21.useEffect)(() => {
@@ -28329,13 +28264,6 @@ function OhhwellsBridge() {
28329
28264
  if (target.closest("[data-ohw-state-toggle]")) return;
28330
28265
  if (target.closest("[data-ohw-max-badge]")) return;
28331
28266
  if (isInsideLinkEditor(target)) return;
28332
- if (isInsideFloatingPanel(target)) return;
28333
- if (target.closest("[data-ohw-form-toolbar]")) return;
28334
- if (target.closest(
28335
- '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
28336
- )) {
28337
- return;
28338
- }
28339
28267
  if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
28340
28268
  clearMediaSelectionRef.current();
28341
28269
  }
@@ -28359,6 +28287,13 @@ function OhhwellsBridge() {
28359
28287
  return;
28360
28288
  }
28361
28289
  }
28290
+ if (isInsideFloatingPanel(target)) return;
28291
+ if (target.closest("[data-ohw-form-toolbar]")) return;
28292
+ if (target.closest(
28293
+ '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
28294
+ )) {
28295
+ return;
28296
+ }
28362
28297
  {
28363
28298
  const formEl = getFormElement(target);
28364
28299
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -28512,13 +28447,9 @@ function OhhwellsBridge() {
28512
28447
  if (isIconEditable(editable) && !getSocialItem(editable)) {
28513
28448
  e.preventDefault();
28514
28449
  e.stopPropagation();
28450
+ selectIconElementRef.current(editable);
28515
28451
  const aiIcon = editable.hasAttribute("data-ai-icon");
28516
28452
  const featureIcon = aiIcon && /\.features\.\d+\.icon$/.test(editable.dataset.ohwKey ?? "");
28517
- if (aiIcon) {
28518
- aiSectionApiRef.current?.selectFromElement(editable);
28519
- } else {
28520
- selectIconElementRef.current(editable);
28521
- }
28522
28453
  postToParentRef.current({
28523
28454
  type: "ow:icon-pick",
28524
28455
  key: editable.dataset.ohwKey ?? "",
@@ -28746,9 +28677,6 @@ function OhhwellsBridge() {
28746
28677
  setHoveredItemRect(null);
28747
28678
  hoveredNavContainerRef.current = null;
28748
28679
  setHoveredNavContainerRect(null);
28749
- siblingHintElRef.current = null;
28750
- setSiblingHintRect(null);
28751
- setSiblingHintRects([]);
28752
28680
  return;
28753
28681
  }
28754
28682
  {
@@ -28869,6 +28797,7 @@ function OhhwellsBridge() {
28869
28797
  hoveredNavContainerRef.current = null;
28870
28798
  setHoveredNavContainerRect(null);
28871
28799
  hoveredItemElRef.current = editable;
28800
+ setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
28872
28801
  }
28873
28802
  }
28874
28803
  }
@@ -29578,11 +29507,7 @@ function OhhwellsBridge() {
29578
29507
  };
29579
29508
  const handleMouseMove = (e) => {
29580
29509
  const { clientX, clientY } = e;
29581
- if (pointOwnedByFloatingPanel(clientX, clientY)) {
29582
- dismissImageHover();
29583
- clearImageHover();
29584
- return;
29585
- }
29510
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
29586
29511
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) {
29587
29512
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
29588
29513
  formHoverElRef.current = null;
@@ -29611,12 +29536,7 @@ function OhhwellsBridge() {
29611
29536
  setHoveredItemRect(null);
29612
29537
  hoveredNavContainerRef.current = null;
29613
29538
  setHoveredNavContainerRect(null);
29614
- siblingHintElRef.current = null;
29615
- setSiblingHintRect(null);
29616
- setSiblingHintRects([]);
29617
29539
  dismissImageHover();
29618
- clearImageHover();
29619
- setSectionGap(null);
29620
29540
  return;
29621
29541
  }
29622
29542
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -29628,11 +29548,7 @@ function OhhwellsBridge() {
29628
29548
  if (e.data?.type !== "ow:pointer-sync") return;
29629
29549
  const { clientX, clientY } = e.data;
29630
29550
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
29631
- if (pointOwnedByFloatingPanel(clientX, clientY)) {
29632
- dismissImageHover();
29633
- clearImageHover();
29634
- return;
29635
- }
29551
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
29636
29552
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) return;
29637
29553
  if (probeSocialsRowAt(clientX, clientY)) return;
29638
29554
  probeSectionGapAt(clientX, clientY);
@@ -30012,7 +29928,6 @@ function OhhwellsBridge() {
30012
29928
  applyAiFooterFromContent(content);
30013
29929
  if (typeof content[STYLE_STORE_KEY] === "string") {
30014
29930
  stylesRef.current = content[STYLE_STORE_KEY];
30015
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
30016
29931
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
30017
29932
  }
30018
29933
  applyBrandChrome(content);
@@ -30027,12 +29942,12 @@ function OhhwellsBridge() {
30027
29942
  }
30028
29943
  if (key === AI_SECTIONS_KEY) continue;
30029
29944
  if (key === AI_FOOTER_KEY) continue;
30030
- if (key === LOGO_PLACEHOLDER_KEY) continue;
30031
- if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
30032
29945
  if (key === BRAND_KIT_KEY) continue;
30033
29946
  if (key === STYLE_STORE_KEY) continue;
30034
29947
  if (BRAND_CHROME_KEYS.has(key)) continue;
29948
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
30035
29949
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
29950
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
30036
29951
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
30037
29952
  continue;
30038
29953
  }
@@ -30668,12 +30583,6 @@ function OhhwellsBridge() {
30668
30583
  closeLinkPopoverRef.current();
30669
30584
  return;
30670
30585
  }
30671
- if (floatingPanelOpenRef.current) {
30672
- setFloatingPanelRef.current(null);
30673
- deselectRef.current();
30674
- deactivateRef.current();
30675
- return;
30676
- }
30677
30586
  deselectRef.current();
30678
30587
  deactivateRef.current();
30679
30588
  clearMediaSelectionRef.current();
@@ -31496,10 +31405,10 @@ function OhhwellsBridge() {
31496
31405
  window.removeEventListener("message", handleDeleteSection);
31497
31406
  window.removeEventListener("message", handleDuplicateSection);
31498
31407
  window.removeEventListener("message", handleDeactivate);
31408
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
31499
31409
  window.removeEventListener("message", handleToastAction);
31500
31410
  window.removeEventListener("message", handleFormCount);
31501
31411
  window.removeEventListener("message", handleUiEscape);
31502
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
31503
31412
  autoSaveTimers.current.forEach(clearTimeout);
31504
31413
  autoSaveTimers.current.clear();
31505
31414
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
@@ -31793,7 +31702,7 @@ function OhhwellsBridge() {
31793
31702
  postToParent2({
31794
31703
  type: "ow:ready",
31795
31704
  version: "1",
31796
- bridgeVersion: "0.1.149-next.405",
31705
+ bridgeVersion: "0.1.150",
31797
31706
  path: pathname,
31798
31707
  nodes: collectEditableNodes(editContentRef.current),
31799
31708
  sections