@ohhwells/bridge 0.1.108-next.282 → 0.1.109

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
@@ -46,7 +46,6 @@ __export(index_exports, {
46
46
  DropdownMenuItem: () => DropdownMenuItem,
47
47
  DropdownMenuSeparator: () => DropdownMenuSeparator,
48
48
  DropdownMenuTrigger: () => DropdownMenuTrigger,
49
- EmptySection: () => EmptySection,
50
49
  ItemActionToolbar: () => ItemActionToolbar,
51
50
  ItemInteractionLayer: () => ItemInteractionLayer,
52
51
  LinkEditorPanel: () => LinkEditorPanel,
@@ -77,7 +76,7 @@ module.exports = __toCommonJS(index_exports);
77
76
  // src/OhhwellsBridge.tsx
78
77
  var import_react17 = __toESM(require("react"), 1);
79
78
  var import_client2 = require("react-dom/client");
80
- var import_react_dom3 = require("react-dom");
79
+ var import_react_dom4 = require("react-dom");
81
80
 
82
81
  // src/linkHrefStore.ts
83
82
  var linkHrefStore = /* @__PURE__ */ new Map();
@@ -353,17 +352,16 @@ var LEGACY_BRAND_VAR_NAMES = [
353
352
  "text-muted",
354
353
  "surface",
355
354
  "border",
356
- "on-primary"
355
+ "on-primary",
356
+ "navbar-background"
357
357
  ].map((role) => `--brand-${role}`);
358
358
  var FONT_VARS = {
359
359
  heading: ["--font-heading", "--font-display", "--brand-font-heading"],
360
360
  body: ["--font-body", "--brand-font-body"]
361
361
  };
362
362
  var BRAND_FONT_LINK_ID = "ohw-brand-fonts";
363
- var CUSTOM_FONT_STYLE_ID = "ohw-brand-custom-fonts";
364
- var CUSTOM_FONT_FORMATS = /* @__PURE__ */ new Set(["woff2", "woff", "truetype", "opentype"]);
365
- function brandColorVars(palette) {
366
- const { dark, primary, accent, light } = palette;
363
+ function brandColorVars(kit) {
364
+ const { dark, primary, accent, light } = kit.palette;
367
365
  const mix = (a, aPct, b) => `color-mix(in srgb, ${a} ${aPct}%, ${b})`;
368
366
  const surface = mix(light, 95, dark);
369
367
  const border = mix(light, 85, dark);
@@ -386,42 +384,22 @@ function brandColorVars(palette) {
386
384
  "--brand-border": border,
387
385
  // Buttons/bands painted in the primary colour assume it's dark/saturated enough to need
388
386
  // light text on top — the same assumption LOGO_IMAGE's light-on-dark navbar mark makes.
389
- "--brand-on-primary": light
387
+ "--brand-on-primary": light,
388
+ "--brand-navbar-background": dark
390
389
  };
391
390
  }
392
- function parseCustomFontWeight(raw) {
393
- if (typeof raw !== "object" || raw === null) return null;
394
- const w = raw;
395
- if (typeof w.weight !== "number" || !Number.isFinite(w.weight)) return null;
396
- if (typeof w.url !== "string" || !w.url) return null;
397
- if (typeof w.format !== "string" || !CUSTOM_FONT_FORMATS.has(w.format)) return null;
398
- const weightEnd = typeof w.weightEnd === "number" && Number.isFinite(w.weightEnd) && w.weightEnd > w.weight ? w.weightEnd : void 0;
399
- return { weight: w.weight, ...weightEnd !== void 0 ? { weightEnd } : {}, url: w.url, format: w.format };
400
- }
401
- function parseCustomFont(raw) {
402
- if (typeof raw !== "object" || raw === null) return null;
403
- const f = raw;
404
- if (typeof f.family !== "string" || !f.family) return null;
405
- if (typeof f.label !== "string" || !f.label) return null;
406
- if (!Array.isArray(f.weights)) return null;
407
- const weights = f.weights.map(parseCustomFontWeight).filter((w) => w !== null);
408
- if (weights.length === 0) return null;
409
- return { family: f.family, label: f.label, weights };
410
- }
411
391
  function parseBrandKit(raw) {
412
392
  if (!raw) return null;
413
393
  try {
414
394
  const parsed = JSON.parse(raw);
415
395
  const p = parsed?.palette;
416
396
  const f = parsed?.fonts;
417
- const palette = p && typeof p.dark === "string" && typeof p.primary === "string" && typeof p.accent === "string" && typeof p.light === "string" ? { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light } : void 0;
418
- const fonts = f && typeof f.heading === "string" && typeof f.body === "string" ? { heading: f.heading, body: f.body } : void 0;
419
- const customFonts = Array.isArray(parsed?.customFonts) ? parsed.customFonts.map(parseCustomFont).filter((c) => c !== null) : void 0;
420
- if (!palette && !fonts && !customFonts) return null;
397
+ if (!p || !f || typeof p.dark !== "string" || typeof p.primary !== "string" || typeof p.accent !== "string" || typeof p.light !== "string" || typeof f.heading !== "string" || typeof f.body !== "string") {
398
+ return null;
399
+ }
421
400
  return {
422
- ...palette ? { palette } : {},
423
- ...fonts ? { fonts } : {},
424
- ...customFonts ? { customFonts } : {}
401
+ palette: { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light },
402
+ fonts: { heading: f.heading, body: f.body }
425
403
  };
426
404
  } catch {
427
405
  return null;
@@ -431,16 +409,11 @@ function familyOf(stack) {
431
409
  const first = stack.split(",")[0]?.trim() ?? "";
432
410
  return first.replace(/^['"]|['"]$/g, "");
433
411
  }
434
- function quoteFamily(family) {
435
- return `'${family.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
436
- }
437
- var WEIGHT_SCALE = [100, 200, 300, 400, 500, 600, 700, 800, 900];
438
412
  function loadBrandFonts(families) {
439
413
  const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
440
414
  if (unique.length === 0) return;
441
- const weights = WEIGHT_SCALE.join(";");
442
- const spec = unique.map((f) => `family=${f.replace(/ /g, "+")}:wght@${weights}`).join("&");
443
- const href = `https://fonts.googleapis.com/css2?${spec}&display=swap`;
415
+ const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
416
+ const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
444
417
  let link = document.getElementById(BRAND_FONT_LINK_ID);
445
418
  if (!link) {
446
419
  link = document.createElement("link");
@@ -450,54 +423,18 @@ function loadBrandFonts(families) {
450
423
  }
451
424
  if (link.href !== href) link.href = href;
452
425
  }
453
- function escapeCssString(value) {
454
- return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
455
- }
456
- function loadCustomFonts(fonts) {
457
- const list = fonts ?? [];
458
- let style = document.getElementById(CUSTOM_FONT_STYLE_ID);
459
- if (list.length === 0) {
460
- style?.remove();
461
- return;
462
- }
463
- if (!style) {
464
- style = document.createElement("style");
465
- style.id = CUSTOM_FONT_STYLE_ID;
466
- document.head.appendChild(style);
467
- }
468
- style.textContent = list.flatMap(
469
- (font) => font.weights.map((w) => {
470
- const weightDescriptor = w.weightEnd ? `${w.weight} ${w.weightEnd}` : `${w.weight}`;
471
- return `@font-face { font-family: '${escapeCssString(font.family)}'; src: url('${escapeCssString(w.url)}') format('${w.format}'); font-weight: ${weightDescriptor}; font-display: swap; }`;
472
- })
473
- ).join("\n");
474
- }
475
426
  function applyBrandToDom(kit) {
476
427
  const root = document.documentElement;
477
428
  if (!kit) {
478
429
  for (const name of [...BRAND_VAR_NAMES, ...LEGACY_BRAND_VAR_NAMES]) root.style.removeProperty(name);
479
430
  for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
480
431
  document.getElementById(BRAND_FONT_LINK_ID)?.remove();
481
- document.getElementById(CUSTOM_FONT_STYLE_ID)?.remove();
482
432
  return;
483
433
  }
484
- if (kit.palette) {
485
- for (const [name, value] of Object.entries(brandColorVars(kit.palette))) root.style.setProperty(name, value);
486
- }
487
- if (kit.customFonts) {
488
- loadCustomFonts(kit.customFonts);
489
- }
490
- if (kit.fonts) {
491
- const heading = quoteFamily(kit.fonts.heading);
492
- const body = quoteFamily(kit.fonts.body);
493
- for (const name of FONT_VARS.heading) root.style.setProperty(name, heading);
494
- for (const name of FONT_VARS.body) root.style.setProperty(name, body);
495
- const customFamilies = new Set((kit.customFonts ?? []).map((f) => f.family));
496
- const googleFamilies = [familyOf(kit.fonts.heading), familyOf(kit.fonts.body)].filter(
497
- (f) => !customFamilies.has(f)
498
- );
499
- loadBrandFonts(googleFamilies);
500
- }
434
+ for (const [name, value] of Object.entries(brandColorVars(kit))) root.style.setProperty(name, value);
435
+ for (const name of FONT_VARS.heading) root.style.setProperty(name, kit.fonts.heading);
436
+ for (const name of FONT_VARS.body) root.style.setProperty(name, kit.fonts.body);
437
+ loadBrandFonts([familyOf(kit.fonts.heading), familyOf(kit.fonts.body)]);
501
438
  }
502
439
 
503
440
  // src/lib/section-styles.ts
@@ -1418,22 +1355,6 @@ function accentBandContext(brand) {
1418
1355
  function textAttrs(ctx, path) {
1419
1356
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
1420
1357
  }
1421
- var AI_RESPONSIVE_CSS = [
1422
- "@media (max-width: 960px) {",
1423
- " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
1424
- ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
1425
- "}",
1426
- "@media (max-width: 640px) {",
1427
- " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
1428
- " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
1429
- // Group containers flatten to a column on phones; span placements come along for free.
1430
- " [data-ai-responsive] [data-ai-group] { display: flex !important; flex-direction: column !important; }",
1431
- " [data-ai-responsive] [data-ai-group] > * { grid-column: auto !important; }",
1432
- " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
1433
- " [data-ai-responsive] { overflow-x: hidden; }",
1434
- " [data-ai-responsive] img { max-width: 100%; }",
1435
- "}"
1436
- ].join("\n");
1437
1358
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
1438
1359
  function MediaBox({
1439
1360
  refValue,
@@ -1446,17 +1367,13 @@ function MediaBox({
1446
1367
  const url = refValue ? ctx.resolveMedia(refValue) : null;
1447
1368
  const isIcon = /^(lucide|simple):/.test(refValue);
1448
1369
  const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
1449
- const editAttrs = ctx.keyFor && editPath ? {
1450
- "data-ohw-key": ctx.keyFor(editPath),
1451
- "data-ohw-editable": isIcon ? "icon" : "image"
1452
- } : {};
1370
+ const editAttrs = ctx.keyFor && editPath && !isIcon ? { "data-ohw-key": ctx.keyFor(editPath), "data-ohw-editable": "image" } : {};
1453
1371
  if (isIcon) {
1454
1372
  const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
1455
1373
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1456
1374
  "span",
1457
1375
  {
1458
1376
  "data-ai-icon": refValue,
1459
- ...editAttrs,
1460
1377
  style: {
1461
1378
  display: "inline-flex",
1462
1379
  width: 48,
@@ -2349,7 +2266,7 @@ function CollectionBlock({ node, ctx, path }) {
2349
2266
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2350
2267
  "div",
2351
2268
  {
2352
- "data-ai-grid": String(itemsPerRow),
2269
+ "data-ai-grid": "",
2353
2270
  style: {
2354
2271
  display: "grid",
2355
2272
  gridTemplateColumns: `repeat(${itemsPerRow}, 1fr)`,
@@ -2702,7 +2619,6 @@ function AiTreeRenderer({
2702
2619
  {
2703
2620
  "data-ai-section": tree.tag ?? "",
2704
2621
  ...bgAttrs,
2705
- "data-ai-responsive": "",
2706
2622
  style: {
2707
2623
  position: "relative",
2708
2624
  padding: `${pad}px 0`,
@@ -2713,13 +2629,12 @@ function AiTreeRenderer({
2713
2629
  color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
2714
2630
  },
2715
2631
  children: [
2716
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
2717
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_MOBILE_CSS }),
2718
2632
  isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
2633
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_MOBILE_CSS }),
2719
2634
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2720
2635
  "div",
2721
2636
  {
2722
- "data-ai-section-inner": "",
2637
+ "data-ai-container": "",
2723
2638
  style: {
2724
2639
  position: "relative",
2725
2640
  maxWidth: AI_TREE_TOKENS.sectionMaxWidth,
@@ -2730,7 +2645,7 @@ function AiTreeRenderer({
2730
2645
  children: tree.rows.map((row, r2) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2731
2646
  "div",
2732
2647
  {
2733
- "data-ai-columns": "",
2648
+ "data-ai-row": "",
2734
2649
  style: {
2735
2650
  display: "grid",
2736
2651
  gridTemplateColumns: "repeat(12, 1fr)",
@@ -2780,52 +2695,14 @@ function readRootVar(name) {
2780
2695
  if (typeof document === "undefined") return "";
2781
2696
  return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
2782
2697
  }
2783
- function normalizeColorToHex(value) {
2784
- if (!value || typeof document === "undefined") return value;
2785
- const canvas = document.createElement("canvas");
2786
- canvas.width = 1;
2787
- canvas.height = 1;
2788
- const ctx = canvas.getContext("2d");
2789
- if (!ctx) return value;
2790
- ctx.fillStyle = value;
2791
- ctx.fillRect(0, 0, 1, 1);
2792
- const [r2, g, b] = ctx.getImageData(0, 0, 1, 1).data;
2793
- const toHex = (n) => n.toString(16).padStart(2, "0");
2794
- return `#${toHex(r2)}${toHex(g)}${toHex(b)}`;
2795
- }
2796
- var GENERIC_FONT_KEYWORDS = /* @__PURE__ */ new Set([
2797
- "serif",
2798
- "sans-serif",
2799
- "monospace",
2800
- "cursive",
2801
- "fantasy",
2802
- "system-ui",
2803
- "ui-serif",
2804
- "ui-sans-serif",
2805
- "ui-monospace",
2806
- "ui-rounded",
2807
- "math",
2808
- "emoji",
2809
- "fangsong"
2810
- ]);
2811
- function primaryFontFamily(stack) {
2812
- const first = stack.split(",").map((part) => part.trim().replace(/^["']|["']$/g, "")).find((part) => part && !GENERIC_FONT_KEYWORDS.has(part.toLowerCase()));
2813
- return first ?? "";
2814
- }
2815
- function humanizeFontFamily(name) {
2816
- return name.replace(/^__/, "").replace(/_[0-9a-f]{6}$/i, "").replace(/_/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").trim().replace(/\s+/g, " ").replace(/(^|\s)([a-z])/g, (_, sep, c) => sep + c.toUpperCase());
2817
- }
2818
- function readFontVar(name) {
2819
- return humanizeFontFamily(primaryFontFamily(readRootVar(name)));
2820
- }
2821
2698
  function deriveBrandOverride() {
2822
2699
  const dark = readRootVar("--ohw-brand-dark");
2823
2700
  const primary = readRootVar("--ohw-brand-primary");
2824
2701
  const light = readRootVar("--ohw-brand-light");
2825
2702
  if (!dark || !primary || !light) return null;
2826
2703
  const accent = readRootVar("--ohw-brand-accent");
2827
- const heading = readFontVar("--font-heading") || readFontVar("--font-display");
2828
- const body = readFontVar("--font-body");
2704
+ const heading = readRootVar("--font-heading") || readRootVar("--font-display");
2705
+ const body = readRootVar("--font-body");
2829
2706
  return {
2830
2707
  palette: { dark, primary, accent: accent || dark, light },
2831
2708
  fonts: {
@@ -2834,55 +2711,22 @@ function deriveBrandOverride() {
2834
2711
  }
2835
2712
  };
2836
2713
  }
2837
- function deriveTemplateBrandLive() {
2838
- const dark = readRootVar("--color-dark") || readRootVar("--brand-text");
2839
- const primary = readRootVar("--color-primary") || readRootVar("--brand-primary");
2840
- const light = readRootVar("--color-light") || readRootVar("--brand-background");
2714
+ function deriveTemplateBrand() {
2715
+ const primary = readRootVar("--brand-primary") || readRootVar("--color-primary");
2716
+ const dark = readRootVar("--brand-text") || readRootVar("--color-dark");
2717
+ const light = readRootVar("--brand-background") || readRootVar("--color-light");
2841
2718
  if (!dark || !primary || !light) return null;
2842
- const accent = readRootVar("--color-accent") || readRootVar("--brand-accent");
2843
- const heading = readFontVar("--brand-font-heading") || readFontVar("--font-heading") || readFontVar("--font-display");
2844
- const body = readFontVar("--brand-font-body") || readFontVar("--font-body");
2719
+ const accent = readRootVar("--brand-accent") || readRootVar("--color-accent");
2720
+ const heading = readRootVar("--brand-font-heading") || readRootVar("--font-heading") || readRootVar("--font-display");
2721
+ const body = readRootVar("--brand-font-body") || readRootVar("--font-body");
2845
2722
  return {
2846
- palette: {
2847
- dark: normalizeColorToHex(dark),
2848
- primary: normalizeColorToHex(primary),
2849
- accent: normalizeColorToHex(accent || dark),
2850
- light: normalizeColorToHex(light)
2851
- },
2723
+ palette: { dark, primary, accent: accent || dark, light },
2852
2724
  fonts: {
2853
2725
  heading: heading || AI_DEFAULT_BRAND.fonts.heading,
2854
2726
  body: body || AI_DEFAULT_BRAND.fonts.body
2855
2727
  }
2856
2728
  };
2857
2729
  }
2858
- function deriveTemplateFontsLive() {
2859
- const heading = readFontVar("--brand-font-heading") || readFontVar("--font-heading") || readFontVar("--font-display");
2860
- const body = readFontVar("--brand-font-body") || readFontVar("--font-body");
2861
- if (!heading && !body) return null;
2862
- return {
2863
- heading: heading || AI_DEFAULT_BRAND.fonts.heading,
2864
- body: body || AI_DEFAULT_BRAND.fonts.body
2865
- };
2866
- }
2867
- var OVERRIDE_VAR_NAMES = [...BRAND_VAR_NAMES, ...LEGACY_BRAND_VAR_NAMES, ...FONT_VARS.heading, ...FONT_VARS.body];
2868
- function withOverrideStripped(read) {
2869
- const root = document.documentElement;
2870
- const restore = OVERRIDE_VAR_NAMES.map((name) => [name, root.style.getPropertyValue(name)]);
2871
- for (const name of OVERRIDE_VAR_NAMES) root.style.removeProperty(name);
2872
- try {
2873
- return read();
2874
- } finally {
2875
- for (const [name, value] of restore) if (value) root.style.setProperty(name, value);
2876
- }
2877
- }
2878
- var TEMPLATE_BRAND_SNAPSHOT = typeof document === "undefined" ? null : withOverrideStripped(deriveTemplateBrandLive);
2879
- var TEMPLATE_FONTS_SNAPSHOT = typeof document === "undefined" ? null : withOverrideStripped(deriveTemplateFontsLive);
2880
- function deriveTemplateBrand() {
2881
- return TEMPLATE_BRAND_SNAPSHOT;
2882
- }
2883
- function deriveTemplateFonts() {
2884
- return TEMPLATE_FONTS_SNAPSHOT;
2885
- }
2886
2730
  function deriveTemplateButtonStyle() {
2887
2731
  if (typeof document === "undefined") return null;
2888
2732
  const candidates = Array.from(
@@ -7380,12 +7224,12 @@ var cva = (base, config) => (props) => {
7380
7224
  var import_radix_ui2 = require("radix-ui");
7381
7225
  var import_jsx_runtime5 = require("react/jsx-runtime");
7382
7226
  var toggleVariants = cva(
7383
- "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-bridge-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
7227
+ "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
7384
7228
  {
7385
7229
  variants: {
7386
7230
  variant: {
7387
7231
  default: "bg-transparent border-0",
7388
- outline: "border border-input bg-transparent shadow-xs hover:bg-bridge-accent hover:text-accent-foreground"
7232
+ outline: "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground"
7389
7233
  },
7390
7234
  size: {
7391
7235
  default: "px-2 py-1",
@@ -7478,10 +7322,10 @@ var DragHandle = React4.forwardRef(
7478
7322
  type,
7479
7323
  "data-slot": "drag-handle",
7480
7324
  className: cn(
7481
- "inline-flex h-7 w-4 shrink-0 items-center justify-center rounded-md transition-all duration-200 max-h-7.5",
7325
+ "inline-flex h-7 w-4 shrink-0 items-center justify-center rounded-md transition-all duration-200 max-h-[30px]",
7482
7326
  "bg-white border border-transparent text-stone-500 shadow-md cursor-grab",
7483
7327
  "enabled:hover:border enabled:hover:border-stone-200 enabled:hover:text-stone-950",
7484
- "enabled:active:border enabled:active:border-bridge-primary enabled:active:bg-primary-50 enabled:active:text-stone-950 enabled:active:shadow enabled:active:cursor-grabbing",
7328
+ "enabled:active:border enabled:active:border-primary enabled:active:bg-primary-50 enabled:active:text-stone-950 enabled:active:shadow enabled:active:cursor-grabbing",
7485
7329
  "disabled:cursor-not-allowed disabled:opacity-40 disabled:text-stone-950 disabled:pointer-events-none",
7486
7330
  className
7487
7331
  ),
@@ -7503,7 +7347,7 @@ var CustomToolbar = React5.forwardRef(({ className, onMouseDown, ...props }, ref
7503
7347
  "data-ohw-toolbar": "",
7504
7348
  className: cn(
7505
7349
  // Figma: bg background, radius 8, gap-1, p-0.5, shadow-md — no border
7506
- "inline-flex h-8 items-center gap-1 rounded-(--radius,0.5rem) bg-background p-0.5 font-sans whitespace-nowrap shadow-md",
7350
+ "inline-flex h-8 items-center gap-1 rounded-[var(--radius,0.5rem)] bg-background p-0.5 font-sans whitespace-nowrap shadow-md",
7507
7351
  className
7508
7352
  ),
7509
7353
  onMouseDown: (e) => {
@@ -7532,7 +7376,7 @@ var CustomToolbarButton = React5.forwardRef(
7532
7376
  type,
7533
7377
  className: cn(
7534
7378
  "inline-flex size-7 shrink-0 items-center justify-center rounded-[calc(var(--radius,0.5rem)-2px)] text-foreground transition-colors",
7535
- active ? "bg-bridge-primary text-primary-foreground" : "bg-transparent hover:bg-muted disabled:cursor-not-allowed disabled:text-muted-foreground disabled:opacity-60",
7379
+ active ? "bg-primary text-primary-foreground" : "bg-transparent hover:bg-muted disabled:cursor-not-allowed disabled:text-muted-foreground disabled:opacity-60",
7536
7380
  className
7537
7381
  ),
7538
7382
  ...props
@@ -7928,6 +7772,7 @@ function ItemActionToolbar({
7928
7772
 
7929
7773
  // src/ui/item-interaction-layer.tsx
7930
7774
  var import_react7 = require("react");
7775
+ var import_react_dom2 = require("react-dom");
7931
7776
  var import_jsx_runtime12 = require("react/jsx-runtime");
7932
7777
  var PRIMARY = "var(--ohw-primary, #0885FE)";
7933
7778
  var CHROME_RADIUS = 6;
@@ -7937,6 +7782,7 @@ var TOOLBAR_EDGE_MARGIN = 4;
7937
7782
  var SELECTION_CHROME_GAP = 4;
7938
7783
  var HOVER_CHROME_GAP = 2;
7939
7784
  var TOOLBAR_STROKE_GAP = 4;
7785
+ var DETACHED_TOOLBAR_Z = 2147483647;
7940
7786
  function getChromeZIndex(state) {
7941
7787
  switch (state) {
7942
7788
  case "active-top":
@@ -7994,6 +7840,14 @@ function ClampedToolbarSlot({
7994
7840
  const parent = slot.offsetParent;
7995
7841
  if (!parent) return;
7996
7842
  const parentRect = parent.getBoundingClientRect();
7843
+ if (align === "left") {
7844
+ const leftEdge = parentRect.left;
7845
+ const maxLeft = window.innerWidth - TOOLBAR_EDGE_MARGIN - w;
7846
+ const minLeft = TOOLBAR_EDGE_MARGIN;
7847
+ const clampedLeft = Math.max(minLeft, Math.min(leftEdge, maxLeft));
7848
+ slot.style.transform = `translateX(${clampedLeft - leftEdge}px)`;
7849
+ return;
7850
+ }
7997
7851
  const centerX = parentRect.left + parentRect.width / 2;
7998
7852
  const half = w / 2;
7999
7853
  const clampedCenter = Math.max(
@@ -8001,7 +7855,7 @@ function ClampedToolbarSlot({
8001
7855
  Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN - half)
8002
7856
  );
8003
7857
  const offsetX = clampedCenter - centerX;
8004
- slot.style.transform = align === "left" ? "none" : `translateX(calc(-50% + ${offsetX}px))`;
7858
+ slot.style.transform = `translateX(calc(-50% + ${offsetX}px))`;
8005
7859
  };
8006
7860
  clamp();
8007
7861
  const ro = new ResizeObserver(clamp);
@@ -8035,13 +7889,25 @@ function ClampedToolbarSlot({
8035
7889
  function DetachedBelowToolbarSlot({
8036
7890
  centerX,
8037
7891
  belowRect,
7892
+ belowEl,
8038
7893
  children
8039
7894
  }) {
8040
7895
  const slotRef = (0, import_react7.useRef)(null);
7896
+ const [anchorBottom, setAnchorBottom] = (0, import_react7.useState)(belowRect.bottom);
7897
+ const [portalTarget] = (0, import_react7.useState)(
7898
+ () => typeof document === "undefined" ? null : document.querySelector("[data-ohw-bridge-root]") ?? document.body
7899
+ );
7900
+ (0, import_react7.useLayoutEffect)(() => {
7901
+ setAnchorBottom(belowRect.bottom);
7902
+ }, [belowRect.bottom, belowRect.height, belowRect.top]);
8041
7903
  (0, import_react7.useLayoutEffect)(() => {
8042
7904
  const slot = slotRef.current;
8043
7905
  if (!slot) return;
8044
- const clamp = () => {
7906
+ const sync = () => {
7907
+ if (belowEl?.isConnected) {
7908
+ const nextBottom = belowEl.getBoundingClientRect().bottom;
7909
+ setAnchorBottom((prev) => Math.abs(prev - nextBottom) < 0.5 ? prev : nextBottom);
7910
+ }
8045
7911
  const toolbar = slot.firstElementChild;
8046
7912
  if (!toolbar) return;
8047
7913
  const w = toolbar.offsetWidth;
@@ -8053,30 +7919,46 @@ function DetachedBelowToolbarSlot({
8053
7919
  );
8054
7920
  slot.style.left = `${clampedCenter}px`;
8055
7921
  };
8056
- clamp();
8057
- const ro = new ResizeObserver(clamp);
7922
+ sync();
7923
+ const ro = new ResizeObserver(sync);
8058
7924
  ro.observe(slot);
8059
7925
  if (slot.firstElementChild) ro.observe(slot.firstElementChild);
8060
- window.addEventListener("resize", clamp);
7926
+ if (belowEl) {
7927
+ ro.observe(belowEl);
7928
+ const mo = new MutationObserver(sync);
7929
+ mo.observe(belowEl, { childList: true, subtree: true });
7930
+ window.addEventListener("resize", sync);
7931
+ return () => {
7932
+ ro.disconnect();
7933
+ mo.disconnect();
7934
+ window.removeEventListener("resize", sync);
7935
+ };
7936
+ }
7937
+ window.addEventListener("resize", sync);
8061
7938
  return () => {
8062
7939
  ro.disconnect();
8063
- window.removeEventListener("resize", clamp);
7940
+ window.removeEventListener("resize", sync);
8064
7941
  };
8065
- }, [centerX, belowRect, children]);
8066
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
8067
- "div",
8068
- {
8069
- ref: slotRef,
8070
- className: "pointer-events-auto fixed",
8071
- style: {
8072
- top: belowRect.bottom + TOOLBAR_STROKE_GAP,
8073
- left: centerX,
8074
- transform: "translateX(-50%)",
8075
- zIndex: getChromeZIndex("active-bottom")
8076
- },
8077
- "data-ohw-item-toolbar-anchor": "below-external",
8078
- children
8079
- }
7942
+ }, [centerX, belowRect, belowEl, children]);
7943
+ if (!portalTarget) return null;
7944
+ return (0, import_react_dom2.createPortal)(
7945
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7946
+ "div",
7947
+ {
7948
+ ref: slotRef,
7949
+ className: "pointer-events-auto fixed",
7950
+ style: {
7951
+ top: anchorBottom + TOOLBAR_STROKE_GAP,
7952
+ left: centerX,
7953
+ transform: "translateX(-50%)",
7954
+ zIndex: DETACHED_TOOLBAR_Z
7955
+ },
7956
+ "data-ohw-bridge": "",
7957
+ "data-ohw-item-toolbar-anchor": "below-external",
7958
+ children
7959
+ }
7960
+ ),
7961
+ portalTarget
8080
7962
  );
8081
7963
  }
8082
7964
  function ItemInteractionLayer({
@@ -8085,6 +7967,7 @@ function ItemInteractionLayer({
8085
7967
  elRef,
8086
7968
  toolbar,
8087
7969
  toolbarBelowRect = null,
7970
+ toolbarBelowEl = null,
8088
7971
  showHandle = false,
8089
7972
  dragDisabled = false,
8090
7973
  dragHandleLabel = "Reorder item",
@@ -8190,6 +8073,7 @@ function ItemInteractionLayer({
8190
8073
  {
8191
8074
  centerX: rect.left + rect.width / 2,
8192
8075
  belowRect: toolbarBelowRect,
8076
+ belowEl: toolbarBelowEl,
8193
8077
  children: toolbar
8194
8078
  }
8195
8079
  ) : null
@@ -8560,6 +8444,7 @@ function setFieldRequired(wrapper, required) {
8560
8444
  function beginPlaceholderEdit(wrapper) {
8561
8445
  const input = fieldInputOf(wrapper);
8562
8446
  if (!input || input.hasAttribute(PLACEHOLDER_EDIT_ATTR)) return;
8447
+ input.style.setProperty("--ohw-placeholder-edit-color", getComputedStyle(input).color);
8563
8448
  input.setAttribute(PLACEHOLDER_EDIT_ATTR, "");
8564
8449
  input.value = input.getAttribute("placeholder") ?? "";
8565
8450
  input.setAttribute("placeholder", "");
@@ -8570,6 +8455,7 @@ function commitPlaceholderEdit(wrapper) {
8570
8455
  if (!input || !input.hasAttribute(PLACEHOLDER_EDIT_ATTR)) return false;
8571
8456
  const typed = input.value;
8572
8457
  input.removeAttribute(PLACEHOLDER_EDIT_ATTR);
8458
+ input.style.removeProperty("--ohw-placeholder-edit-color");
8573
8459
  input.value = "";
8574
8460
  const previous = input.getAttribute("placeholder") ?? "";
8575
8461
  input.setAttribute("placeholder", typed);
@@ -8734,13 +8620,13 @@ function FormFieldToolbar({
8734
8620
  ]
8735
8621
  }
8736
8622
  ) }),
8737
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-47.5 p-1", children: FIELD_TYPES.map((entry) => {
8623
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-[190px] p-1", children: FIELD_TYPES.map((entry) => {
8738
8624
  const Icon = TYPE_ICONS[entry.type];
8739
8625
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
8740
8626
  DropdownMenuItem,
8741
8627
  {
8742
8628
  onSelect: () => onTypeChange(entry.type),
8743
- className: "rounded-md py-2 text-[13px] " + (entry.type === type ? "bg-bridge-primary/10" : ""),
8629
+ className: "rounded-md py-2 text-[13px] " + (entry.type === type ? "bg-primary/10" : ""),
8744
8630
  children: [
8745
8631
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Icon, { size: 14, strokeWidth: 1.75, className: "shrink-0", "aria-hidden": true }),
8746
8632
  entry.label
@@ -8757,7 +8643,7 @@ function FormFieldToolbar({
8757
8643
  type: "button",
8758
8644
  "aria-pressed": required,
8759
8645
  onClick: onRequiredToggle,
8760
- className: "flex h-7 items-center gap-1.5 whitespace-nowrap rounded-md px-2 text-[13px] font-medium transition-colors " + (required ? "bg-bridge-primary/10 text-bridge-primary" : "text-foreground hover:bg-muted/70"),
8646
+ className: "flex h-7 items-center gap-1.5 whitespace-nowrap rounded-md px-2 text-[13px] font-medium transition-colors " + (required ? "bg-primary/10 text-primary" : "text-foreground hover:bg-muted/70"),
8761
8647
  "data-ohw-field-required-toggle": "",
8762
8648
  children: [
8763
8649
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react4.Asterisk, { size: 14, strokeWidth: 2, "aria-hidden": true }),
@@ -8777,7 +8663,7 @@ function FormFieldToolbar({
8777
8663
  children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react4.MoreHorizontal, { size: 15, "aria-hidden": true })
8778
8664
  }
8779
8665
  ) }),
8780
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-42.5 p-1", children: [
8666
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-[170px] p-1", children: [
8781
8667
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DropdownMenuItem, { onSelect: onDuplicate, className: "rounded-md py-2 text-[13px]", children: [
8782
8668
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react4.Copy, { size: 14, strokeWidth: 1.75, className: "shrink-0", "aria-hidden": true }),
8783
8669
  "Duplicate"
@@ -8797,7 +8683,7 @@ function FieldTypePicker({ onPick }) {
8797
8683
  "div",
8798
8684
  {
8799
8685
  "data-ohw-field-type-picker": "",
8800
- className: "pointer-events-auto grid w-105 grid-cols-3 gap-3 rounded-xl border border-border bg-background p-4 shadow-lg",
8686
+ className: "pointer-events-auto grid w-[280px] grid-cols-2 gap-2 rounded-xl border border-border bg-background p-3 shadow-lg",
8801
8687
  children: FIELD_TYPES.map((entry) => {
8802
8688
  const Icon = TYPE_ICONS[entry.type];
8803
8689
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
@@ -8805,7 +8691,7 @@ function FieldTypePicker({ onPick }) {
8805
8691
  {
8806
8692
  type: "button",
8807
8693
  onClick: () => onPick(entry.type),
8808
- className: "flex h-26 flex-col items-center justify-center gap-3 rounded-xl border border-border text-[15px] font-medium text-foreground transition-colors hover:border-bridge-primary hover:bg-bridge-primary/5",
8694
+ className: "flex h-[76px] flex-col items-center justify-center gap-2 rounded-xl border border-border text-[15px] font-medium text-foreground transition-colors hover:border-primary hover:bg-primary/5",
8809
8695
  children: [
8810
8696
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Icon, { size: 26, strokeWidth: 1.5, "aria-hidden": true }),
8811
8697
  entry.label
@@ -8831,7 +8717,7 @@ var buttonVariants = cva(
8831
8717
  {
8832
8718
  variants: {
8833
8719
  variant: {
8834
- default: "bg-bridge-primary text-primary-foreground hover:opacity-90",
8720
+ default: "bg-primary text-primary-foreground hover:opacity-90",
8835
8721
  outline: "border border-border bg-background text-foreground shadow-sm hover:bg-muted/80",
8836
8722
  ghost: "min-w-0 px-3 py-2 text-foreground hover:bg-muted/50"
8837
8723
  },
@@ -8926,7 +8812,6 @@ function MediaOverlay({
8926
8812
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
8927
8813
  );
8928
8814
  }, [isVideo]);
8929
- const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
8930
8815
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
8931
8816
  const box = {
8932
8817
  position: "fixed",
@@ -9032,8 +8917,8 @@ function MediaOverlay({
9032
8917
  pointerEvents: hover.hasTextOverlap ? "none" : "auto",
9033
8918
  // Selected: a firm component ring with no wash, so the image reads as chosen rather
9034
8919
  // than hovered. Hover keeps the existing tinted preview.
9035
- boxShadow: selected ? "inset 0 0 0 2px var(--color-bridge-primary)" : "inset 0 0 0 1.5px var(--color-bridge-primary)",
9036
- background: selected ? "transparent" : "color-mix(in srgb, var(--color-bridge-primary) 20%, transparent)"
8920
+ boxShadow: selected ? "inset 0 0 0 2px var(--color-primary)" : "inset 0 0 0 1.5px var(--color-primary)",
8921
+ background: selected ? "transparent" : "color-mix(in srgb, var(--color-primary) 20%, transparent)"
9037
8922
  },
9038
8923
  onClick: () => onSelect && !selected ? onSelect(hover.key) : onReplace(hover.key),
9039
8924
  children: [
@@ -9056,17 +8941,17 @@ function MediaOverlay({
9056
8941
  },
9057
8942
  children: [
9058
8943
  isVideo ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
9059
- replaceLabel
8944
+ isVideo ? "Replace video" : "Replace image"
9060
8945
  ]
9061
8946
  }
9062
8947
  ),
9063
- showChrome && replaceMode !== "none" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
8948
+ replaceMode === "none" ? null : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
9064
8949
  Button,
9065
8950
  {
9066
8951
  "data-ohw-media-overlay": "",
9067
8952
  variant: "outline",
9068
8953
  size: "sm",
9069
- "aria-label": replaceLabel,
8954
+ "aria-label": isVideo ? "Replace video" : "Replace image",
9070
8955
  className: "gap-1.5 cursor-pointer hover:bg-background",
9071
8956
  style: {
9072
8957
  ...OVERLAY_BUTTON_STYLE,
@@ -9089,7 +8974,7 @@ function MediaOverlay({
9089
8974
  },
9090
8975
  children: [
9091
8976
  isVideo ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
9092
- replaceMode === "full" ? replaceLabel : null
8977
+ replaceMode === "full" ? isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image" : null
9093
8978
  ]
9094
8979
  }
9095
8980
  )
@@ -9127,8 +9012,8 @@ function CarouselOverlay({
9127
9012
  height: rect.height,
9128
9013
  zIndex: 2147483646,
9129
9014
  pointerEvents: "auto",
9130
- boxShadow: "inset 0 0 0 1.5px var(--color-bridge-primary)",
9131
- background: "color-mix(in srgb, var(--color-bridge-primary) 20%, transparent)"
9015
+ boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
9016
+ background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
9132
9017
  },
9133
9018
  onClick: () => onEdit(hover.key),
9134
9019
  children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
@@ -9394,10 +9279,25 @@ function AiSectionOverlay({
9394
9279
  },
9395
9280
  [postToParent2]
9396
9281
  );
9282
+ const [navDropdownForceOpen, setNavDropdownForceOpen] = (0, import_react8.useState)(false);
9283
+ (0, import_react8.useEffect)(() => {
9284
+ const update = () => {
9285
+ setNavDropdownForceOpen(Boolean(document.querySelector("[data-ohw-nav-force-open]")));
9286
+ };
9287
+ update();
9288
+ const mo = new MutationObserver(update);
9289
+ mo.observe(document.documentElement, {
9290
+ attributes: true,
9291
+ attributeFilter: ["data-ohw-nav-force-open"],
9292
+ subtree: true
9293
+ });
9294
+ return () => mo.disconnect();
9295
+ }, []);
9397
9296
  const activeSelectionId = reviewId ? null : selectedId;
9398
9297
  const selectionRect = useLiveSectionRect(activeSelectionId);
9399
9298
  const reviewRect = useLiveSectionRect(reviewId);
9400
9299
  const hoverRect = useLiveSectionRect(reviewId || hoveredId === selectedId ? null : hoveredId);
9300
+ const showSelectionBorder = Boolean(selectionRect) && !navDropdownForceOpen;
9401
9301
  (0, import_react8.useEffect)(() => {
9402
9302
  const selectedEl = activeSelectionId ? findSectionElement(activeSelectionId) : null;
9403
9303
  if (!activeSelectionId || !selectionRect || selectedEl && isChromeSection(selectedEl)) {
@@ -9437,7 +9337,7 @@ function AiSectionOverlay({
9437
9337
  }
9438
9338
  }
9439
9339
  ),
9440
- selectionRect && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
9340
+ showSelectionBorder && selectionRect && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
9441
9341
  "div",
9442
9342
  {
9443
9343
  "data-ohw-ai-section-selected": "",
@@ -9501,7 +9401,7 @@ function AiSectionOverlay({
9501
9401
  }
9502
9402
 
9503
9403
  // src/OhhwellsBridge.tsx
9504
- var import_react_dom4 = require("react-dom");
9404
+ var import_react_dom5 = require("react-dom");
9505
9405
  var import_navigation3 = require("next/navigation");
9506
9406
 
9507
9407
  // src/lib/cta-button.ts
@@ -9966,7 +9866,7 @@ function SectionTreeItem({
9966
9866
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
9967
9867
  "div",
9968
9868
  {
9969
- className: "-mr-px h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
9869
+ className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
9970
9870
  "aria-hidden": true
9971
9871
  }
9972
9872
  ),
@@ -9985,7 +9885,7 @@ function SectionTreeItem({
9985
9885
  className: cn(
9986
9886
  "flex h-9 min-w-0 flex-1 items-center gap-2 rounded-md border border-border bg-background p-3",
9987
9887
  interactive && "cursor-pointer hover:bg-muted/30",
9988
- interactive && selected && "border-bridge-primary"
9888
+ interactive && selected && "border-primary"
9989
9889
  ),
9990
9890
  children: [
9991
9891
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
@@ -10115,7 +10015,7 @@ function UrlOrPageInput({
10115
10015
  };
10116
10016
  const fieldClassName = cn(
10117
10017
  "data-ohw-link-field flex h-[36px] w-full items-center overflow-hidden rounded-md border bg-background pl-3 pr-3 py-2 outline-none transition-[border-color,box-shadow]",
10118
- urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-bridge-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
10018
+ urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
10119
10019
  );
10120
10020
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex w-full flex-col gap-2 p-0", children: [
10121
10021
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
@@ -10285,7 +10185,7 @@ function LinkEditorPanel({ state, onClose }) {
10285
10185
 
10286
10186
  // src/ui/link-modal/SectionPickerOverlay.tsx
10287
10187
  var import_react10 = require("react");
10288
- var import_react_dom2 = require("react-dom");
10188
+ var import_react_dom3 = require("react-dom");
10289
10189
  var import_lucide_react13 = require("lucide-react");
10290
10190
  var import_navigation2 = require("next/navigation");
10291
10191
  var import_jsx_runtime25 = require("react/jsx-runtime");
@@ -10516,7 +10416,7 @@ function SectionPickerOverlay({
10516
10416
  }, [onBack]);
10517
10417
  const portalRoot = typeof document !== "undefined" ? document.querySelector("[data-ohw-bridge-root]") ?? document.body : null;
10518
10418
  if (!portalRoot) return null;
10519
- return (0, import_react_dom2.createPortal)(
10419
+ return (0, import_react_dom3.createPortal)(
10520
10420
  /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
10521
10421
  "div",
10522
10422
  {
@@ -11539,7 +11439,11 @@ function isNestedNavChild(anchor) {
11539
11439
  function navItemOwnsDropdownPanel(anchor) {
11540
11440
  if (isNestedNavChild(anchor)) return true;
11541
11441
  const group = anchor.closest("[data-ohw-nav-group]");
11542
- return Boolean(group?.querySelector(":scope > [data-ohw-nav-children]"));
11442
+ const childrenRoot = group?.querySelector(":scope > [data-ohw-nav-children]");
11443
+ if (!childrenRoot) return false;
11444
+ return Array.from(childrenRoot.querySelectorAll(":scope > [data-ohw-href-key]")).some(
11445
+ isNavbarLinkItem
11446
+ );
11543
11447
  }
11544
11448
  function setNavGroupForceOpen(anchor, open) {
11545
11449
  document.querySelectorAll("[data-ohw-nav-force-open]").forEach((el) => {
@@ -11679,6 +11583,12 @@ function addNavbarSubitem(parentAnchor, href = "/", label = "New") {
11679
11583
  };
11680
11584
  }
11681
11585
  function reconcileNavbarItemsFromContent(content) {
11586
+ const openParentKeys = Array.from(
11587
+ document.querySelectorAll("[data-ohw-nav-group][data-ohw-nav-force-open]")
11588
+ ).map((group) => {
11589
+ const trigger = group.querySelector(":scope > [data-ohw-href-key]");
11590
+ return trigger?.getAttribute("data-ohw-href-key") ?? null;
11591
+ }).filter((key) => Boolean(key));
11682
11592
  const orderForest = parseNavOrderJson(content[NAV_ORDER_KEY]);
11683
11593
  if (orderForest?.length) {
11684
11594
  const allowed = new Set(flattenNavOrder(orderForest));
@@ -11734,6 +11644,12 @@ function reconcileNavbarItemsFromContent(content) {
11734
11644
  }
11735
11645
  pruneNavbarItemsNotInOrder(allowed);
11736
11646
  applyNavForest(orderForest);
11647
+ for (const parentKey of openParentKeys) {
11648
+ const trigger = document.querySelector(
11649
+ `[data-ohw-href-key="${CSS.escape(parentKey)}"]`
11650
+ );
11651
+ if (trigger) setNavGroupForceOpen(trigger, true);
11652
+ }
11737
11653
  return;
11738
11654
  }
11739
11655
  const indices = collectNavbarIndicesFromContent(content);
@@ -11744,6 +11660,12 @@ function reconcileNavbarItemsFromContent(content) {
11744
11660
  if (!href && !label) continue;
11745
11661
  insertNavbarItemDom(index, href ?? "/", label ?? "Untitled", null);
11746
11662
  }
11663
+ for (const parentKey of openParentKeys) {
11664
+ const trigger = document.querySelector(
11665
+ `[data-ohw-href-key="${CSS.escape(parentKey)}"]`
11666
+ );
11667
+ if (trigger) setNavGroupForceOpen(trigger, true);
11668
+ }
11747
11669
  }
11748
11670
  function pruneNavbarItemsNotInOrder(allowed) {
11749
11671
  const roots = [getNavbarDesktopContainer(), getNavbarDrawerContainer()].filter(
@@ -12092,19 +12014,31 @@ function deleteNavbarItem(sourceAnchor) {
12092
12014
 
12093
12015
  // src/lib/icon-markup.ts
12094
12016
  var GLYPH_SELECTOR = "svg, img";
12017
+ function squareIconSide(width, height) {
12018
+ if (!(width > 0) || !(height > 0)) return Math.max(width, height, 0);
12019
+ const ratio = width / height;
12020
+ if (ratio > 1.35 || ratio < 1 / 1.35) return Math.min(width, height);
12021
+ return (width + height) / 2;
12022
+ }
12095
12023
  function referenceBox(slot) {
12096
12024
  const row = slot.closest("[data-ohw-socials-row]") ?? slot.closest("a")?.parentElement ?? null;
12097
12025
  const neighbour = row ? Array.from(row.querySelectorAll('[data-ohw-editable="icon"]')).find(
12098
12026
  (el) => el !== slot && !el.hasAttribute("data-ohw-social-icon-placeholder")
12099
12027
  ) : null;
12028
+ const fromRect = (box) => {
12029
+ if (!box?.width || !box.height) return null;
12030
+ const side = Math.round(squareIconSide(box.width, box.height));
12031
+ return side > 0 ? { width: side, height: side } : null;
12032
+ };
12100
12033
  if (neighbour) {
12101
- const box2 = neighbour.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect();
12102
- if (box2?.width && box2.height) return box2;
12034
+ const sized = fromRect(
12035
+ neighbour.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect()
12036
+ );
12037
+ if (sized) return sized;
12103
12038
  }
12104
- const own = slot.getBoundingClientRect();
12105
- if (own.width && own.height) return own;
12106
- const box = slot.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect() ?? null;
12107
- return box?.width && box.height ? box : null;
12039
+ const own = fromRect(slot.getBoundingClientRect());
12040
+ if (own) return own;
12041
+ return fromRect(slot.querySelector(GLYPH_SELECTOR)?.getBoundingClientRect());
12108
12042
  }
12109
12043
  function iconMarkupSizedFor(slot, markup) {
12110
12044
  const box = referenceBox(slot);
@@ -12118,8 +12052,8 @@ function iconMarkupSizedFor(slot, markup) {
12118
12052
  glyph.style.height = "1.5em";
12119
12053
  return holder.innerHTML;
12120
12054
  }
12121
- glyph.style.width = `${Math.round(box.width)}px`;
12122
- glyph.style.height = `${Math.round(box.height)}px`;
12055
+ glyph.style.width = `${box.width}px`;
12056
+ glyph.style.height = `${box.height}px`;
12123
12057
  return holder.innerHTML;
12124
12058
  }
12125
12059
  var ICON_SOURCE_ATTR = "data-ohw-icon-source";
@@ -12138,16 +12072,6 @@ function applyIconMarkup(slot, markup) {
12138
12072
  });
12139
12073
  }
12140
12074
  }
12141
- function detectIconStyle(el) {
12142
- const row = el.closest("[data-ohw-socials-row]");
12143
- const glyphs = Array.from((row ?? el).querySelectorAll("svg"));
12144
- const outlined = glyphs.some((svg) => {
12145
- return Array.from(svg.querySelectorAll("*")).some((node) => {
12146
- return node.getAttribute("stroke") !== null && node.getAttribute("stroke") !== "none";
12147
- });
12148
- });
12149
- return outlined ? "outline" : "fill";
12150
- }
12151
12075
  function iconMarkupInheritingColour(markup) {
12152
12076
  const holder = document.createElement("div");
12153
12077
  holder.innerHTML = markup;
@@ -12170,6 +12094,9 @@ var SOCIAL_KEY_RE = /(^|-)social(s)?(-|$)/i;
12170
12094
  var SOCIAL_PLATFORM_KEY_RE = /(^|-)(instagram|facebook|fb|linkedin|twitter|tiktok|youtube|threads|whatsapp|telegram|pinterest|snapchat|discord|behance|dribbble|vimeo|spotify|github|reddit|medium|twitch)(-|$)/i;
12171
12095
  var SOCIALS_ROW_ATTR = "data-ohw-socials-row";
12172
12096
  var SOCIALS_ITEM_ATTR = "data-ohw-social-item";
12097
+ var SOCIALS_ICONS_ATTR = "data-ohw-socials-icons";
12098
+ var SOCIALS_TEXT_ATTR = "data-ohw-socials-text";
12099
+ var SOCIALS_DISPLAY_STYLE_ID = "ohw-socials-display-style";
12173
12100
  function isSocialItem(el) {
12174
12101
  if (!el) return false;
12175
12102
  const anchor = el instanceof HTMLAnchorElement ? el : el.closest("a");
@@ -12276,9 +12203,11 @@ var SOCIALS_EMPTY_LABEL_ATTR = "data-ohw-empty-label";
12276
12203
  var EMPTY_LABEL_WIDTH = "8ch";
12277
12204
  function markEmptySocialLabels(root = document, skip) {
12278
12205
  root.querySelectorAll(`[${SOCIALS_ROW_ATTR}]`).forEach((row) => {
12206
+ if (row.getAttribute(SOCIALS_TEXT_ATTR) === "off") return;
12279
12207
  listSocialItems(row).forEach((item) => {
12280
12208
  const label = socialLabelElement(item);
12281
12209
  if (!label || label === skip) return;
12210
+ if (label.hasAttribute("data-ohw-hidden")) return;
12282
12211
  const empty = !label.textContent?.trim();
12283
12212
  if (empty === label.hasAttribute(SOCIALS_EMPTY_LABEL_ATTR)) return;
12284
12213
  if (empty) {
@@ -12605,6 +12534,11 @@ function planSocialMove(hrefKey, insertIndex, root = document) {
12605
12534
  }
12606
12535
  var SOCIALS_DISPLAY_KEY = "__ohw_socials_display";
12607
12536
  function readSocialsDisplay(row) {
12537
+ const iconsAttr = row.getAttribute(SOCIALS_ICONS_ATTR);
12538
+ const textAttr = row.getAttribute(SOCIALS_TEXT_ATTR);
12539
+ if ((iconsAttr === "on" || iconsAttr === "off") && (textAttr === "on" || textAttr === "off")) {
12540
+ return { text: textAttr === "on", icon: iconsAttr === "on" };
12541
+ }
12608
12542
  const items = listSocialItems(row);
12609
12543
  const visible = (el) => Boolean(el) && el.style.display !== "none" && el.getAttribute("data-ohw-hidden") === null;
12610
12544
  const drawn = (el) => Boolean(el && visible(el) && el.innerHTML.trim());
@@ -12629,15 +12563,48 @@ function socialsDisplayWith(row, display, content) {
12629
12563
  return { ...parseSocialsDisplay(content[SOCIALS_DISPLAY_KEY]) ?? {}, [rowKeyOf(row)]: display };
12630
12564
  }
12631
12565
  function applySocialsDisplayToRow(row, display) {
12566
+ ensureSocialsDisplayStyles(row.ownerDocument);
12567
+ row.setAttribute(SOCIALS_ICONS_ATTR, display.icon ? "on" : "off");
12568
+ row.setAttribute(SOCIALS_TEXT_ATTR, display.text ? "on" : "off");
12632
12569
  listSocialItems(row).forEach((item) => {
12633
12570
  const label = socialLabelElement(item);
12634
12571
  const icon = item.querySelector(ICON_SELECTOR);
12635
- if (label) label.style.display = display.text ? "" : "none";
12636
- if (icon) icon.style.display = display.icon ? "" : "none";
12572
+ setSocialSlotVisible(label, display.text);
12573
+ setSocialSlotVisible(icon, display.icon);
12637
12574
  layOutIconAndLabel(item, Boolean(display.text));
12638
12575
  });
12639
12576
  allowRowToWrap(row);
12640
12577
  }
12578
+ function setSocialSlotVisible(el, visible) {
12579
+ if (!el) return;
12580
+ if (visible) {
12581
+ el.removeAttribute("data-ohw-hidden");
12582
+ if (el.style.display === "none") el.style.display = "";
12583
+ } else {
12584
+ el.setAttribute("data-ohw-hidden", "");
12585
+ el.style.display = "none";
12586
+ }
12587
+ }
12588
+ function ensureSocialsDisplayStyles(doc) {
12589
+ if (!doc?.head) return;
12590
+ if (doc.getElementById(SOCIALS_DISPLAY_STYLE_ID)) return;
12591
+ const style = doc.createElement("style");
12592
+ style.id = SOCIALS_DISPLAY_STYLE_ID;
12593
+ style.textContent = `
12594
+ [${SOCIALS_ROW_ATTR}][${SOCIALS_ICONS_ATTR}="off"] [data-ohw-editable="icon"],
12595
+ [${SOCIALS_ROW_ATTR}][${SOCIALS_ICONS_ATTR}="off"] [data-ohw-editable="icon"] * {
12596
+ display: none !important;
12597
+ }
12598
+ [${SOCIALS_ROW_ATTR}][${SOCIALS_TEXT_ATTR}="off"] [${SOCIALS_LABEL_ATTR}],
12599
+ [${SOCIALS_ROW_ATTR}][${SOCIALS_TEXT_ATTR}="off"] [data-ohw-href-key] > [data-ohw-editable="text"],
12600
+ [${SOCIALS_ROW_ATTR}][${SOCIALS_TEXT_ATTR}="off"] [data-ohw-href-key] > [data-ohw-editable="plain"],
12601
+ [${SOCIALS_ROW_ATTR}][${SOCIALS_TEXT_ATTR}="off"] [data-ohw-social-item] [data-ohw-editable="text"],
12602
+ [${SOCIALS_ROW_ATTR}][${SOCIALS_TEXT_ATTR}="off"] [data-ohw-social-item] [data-ohw-editable="plain"] {
12603
+ display: none !important;
12604
+ }
12605
+ `;
12606
+ doc.head.appendChild(style);
12607
+ }
12641
12608
  function layOutIconAndLabel(item, on) {
12642
12609
  const hasBoth = Boolean(item.querySelector(ICON_SELECTOR)) && Boolean(socialLabelElement(item));
12643
12610
  if (!hasBoth) {
@@ -13057,15 +13024,6 @@ function setFooterColumnHeadingVisible(column, visible) {
13057
13024
  else column.prepend(heading);
13058
13025
  return { headingKey, text, visible: true };
13059
13026
  }
13060
- function ensureFooterColumnDefaultHeading(column, content) {
13061
- if (isFooterColumnHeadingVisible(column)) return null;
13062
- const headingKey = getFooterColumnHeadingKey(column);
13063
- if (!headingKey) return null;
13064
- if (content && content[headingKey] === "") return null;
13065
- const result = setFooterColumnHeadingVisible(column, true);
13066
- if (!result?.visible) return null;
13067
- return { headingKey: result.headingKey, text: result.text };
13068
- }
13069
13027
  function reconcileFooterHeadingVisibility(content) {
13070
13028
  for (const column of listFooterColumns()) {
13071
13029
  const headingKey = getFooterColumnHeadingKey(column);
@@ -13964,7 +13922,6 @@ function readLogoSizeState(content, placement) {
13964
13922
  function getLogoElement(el) {
13965
13923
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
13966
13924
  if (marked) return marked;
13967
- if (el.closest('[data-ohw-editable="icon"]')) return null;
13968
13925
  const root = el.closest("nav, [data-ohw-nav-root], footer");
13969
13926
  if (!root) return null;
13970
13927
  const anchor = el.closest("a");
@@ -14252,7 +14209,7 @@ function DisplaySwitch({
14252
14209
  onClick: () => onChange(!checked),
14253
14210
  className: cn(
14254
14211
  "relative h-5 w-9 shrink-0 rounded-full transition-colors",
14255
- checked ? "bg-bridge-primary" : "bg-primary-50",
14212
+ checked ? "bg-primary" : "bg-primary-50",
14256
14213
  disabled ? "cursor-default opacity-50" : "cursor-pointer"
14257
14214
  ),
14258
14215
  children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
@@ -14260,7 +14217,7 @@ function DisplaySwitch({
14260
14217
  {
14261
14218
  className: cn(
14262
14219
  "absolute top-0.5 h-4 w-4 rounded-full bg-white transition-all",
14263
- checked ? "left-4.5" : "left-0.5"
14220
+ checked ? "left-[1.125rem]" : "left-0.5"
14264
14221
  )
14265
14222
  }
14266
14223
  )
@@ -14584,20 +14541,30 @@ function useNavItemDrag({
14584
14541
  const navPointerDragRef = (0, import_react14.useRef)(null);
14585
14542
  const clearNavDragVisuals = (0, import_react14.useCallback)(() => {
14586
14543
  const session = navDragRef.current;
14587
- const keepOpenEl = session?.draggedEl?.closest("[data-ohw-nav-children]") != null ? session.draggedEl : null;
14544
+ const nestedHrefKey = session?.draggedEl?.closest("[data-ohw-nav-children]") != null ? session.hrefKey : null;
14588
14545
  navDragRef.current = null;
14589
14546
  setNavDropSlots([]);
14590
14547
  setActiveNavDropIndex(null);
14591
14548
  setDraggedItemRect(null);
14592
14549
  setSiblingHintRects([]);
14593
14550
  setIsItemDragging(false);
14594
- if (keepOpenEl?.isConnected) {
14595
- setNavGroupForceOpen(keepOpenEl, true);
14551
+ if (nestedHrefKey) {
14552
+ const reopen = () => {
14553
+ const desktop = document.querySelector("[data-ohw-nav-container]");
14554
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
14555
+ const link = desktop?.querySelector(
14556
+ `[data-ohw-href-key="${CSS.escape(nestedHrefKey)}"]`
14557
+ ) ?? drawer?.querySelector(
14558
+ `[data-ohw-href-key="${CSS.escape(nestedHrefKey)}"]`
14559
+ ) ?? document.querySelector(
14560
+ `[data-ohw-href-key="${CSS.escape(nestedHrefKey)}"]`
14561
+ );
14562
+ if (link) setNavGroupForceOpen(link, true);
14563
+ };
14564
+ reopen();
14596
14565
  requestAnimationFrame(() => {
14597
- if (keepOpenEl.isConnected) setNavGroupForceOpen(keepOpenEl, true);
14598
- requestAnimationFrame(() => {
14599
- if (keepOpenEl.isConnected) setNavGroupForceOpen(keepOpenEl, true);
14600
- });
14566
+ reopen();
14567
+ requestAnimationFrame(reopen);
14601
14568
  });
14602
14569
  } else {
14603
14570
  setNavGroupForceOpen(null, false);
@@ -14682,19 +14649,24 @@ function useNavItemDrag({
14682
14649
  const wasSelected = session.wasSelected;
14683
14650
  const hrefKey = session.hrefKey;
14684
14651
  const keepDropdownOpen = Boolean(session.draggedEl.closest("[data-ohw-nav-children]"));
14685
- const applySelectionAfterDrop = () => {
14686
- if (!wasSelected) {
14687
- deselectRef.current();
14688
- return;
14689
- }
14652
+ const findDroppedLink = () => {
14690
14653
  const desktop = document.querySelector("[data-ohw-nav-container]");
14691
14654
  const drawer = document.querySelector("[data-ohw-nav-drawer]");
14692
- const link = desktop?.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`) ?? drawer?.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`) ?? document.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
14693
- if (link) {
14655
+ return desktop?.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`) ?? drawer?.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`) ?? document.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
14656
+ };
14657
+ const reopenDropdown = () => {
14658
+ if (!keepDropdownOpen) return;
14659
+ const link = findDroppedLink();
14660
+ if (link) setNavGroupForceOpen(link, true);
14661
+ };
14662
+ const applySelectionAfterDrop = () => {
14663
+ const link = findDroppedLink();
14664
+ if (wasSelected && link) {
14694
14665
  selectRef.current(link);
14695
- return;
14666
+ } else {
14667
+ deselectRef.current();
14696
14668
  }
14697
- deselectRef.current();
14669
+ reopenDropdown();
14698
14670
  };
14699
14671
  if (planned) {
14700
14672
  editContentRef.current = {
@@ -14713,27 +14685,31 @@ function useNavItemDrag({
14713
14685
  type: "ow:change",
14714
14686
  nodes: [{ key: NAV_ORDER_KEY, text: planned.orderJson }]
14715
14687
  });
14688
+ reopenDropdown();
14716
14689
  applySelectionAfterDrop();
14717
14690
  clearNavDragVisuals();
14718
- if (keepDropdownOpen) {
14719
- requestAnimationFrame(() => {
14720
- const desktop = document.querySelector("[data-ohw-nav-container]");
14721
- const drawer = document.querySelector("[data-ohw-nav-drawer]");
14722
- const link = desktop?.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`) ?? drawer?.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`) ?? document.querySelector(`[data-ohw-href-key="${CSS.escape(hrefKey)}"]`);
14723
- if (link) setNavGroupForceOpen(link, true);
14724
- });
14725
- }
14691
+ reopenDropdown();
14726
14692
  requestAnimationFrame(() => {
14727
14693
  if (editContentRef.current[NAV_ORDER_KEY] === planned.orderJson) {
14728
14694
  applyNavForest(planned.forest);
14729
14695
  }
14696
+ reopenDropdown();
14730
14697
  applySelectionAfterDrop();
14731
- requestAnimationFrame(applySelectionAfterDrop);
14698
+ requestAnimationFrame(() => {
14699
+ reopenDropdown();
14700
+ applySelectionAfterDrop();
14701
+ });
14702
+ window.setTimeout(reopenDropdown, 0);
14703
+ window.setTimeout(reopenDropdown, 50);
14704
+ window.setTimeout(reopenDropdown, 150);
14732
14705
  });
14733
14706
  return;
14734
14707
  }
14735
14708
  applySelectionAfterDrop();
14736
14709
  clearNavDragVisuals();
14710
+ reopenDropdown();
14711
+ window.setTimeout(reopenDropdown, 0);
14712
+ window.setTimeout(reopenDropdown, 50);
14737
14713
  },
14738
14714
  [clearNavDragVisuals, deselectRef, editContentRef, postToParentRef, selectRef]
14739
14715
  );
@@ -14825,7 +14801,7 @@ function useNavItemDrag({
14825
14801
  }
14826
14802
  if (linkPopoverOpenRef.current) setLinkPopover(null);
14827
14803
  const isNestedDrag = Boolean(pending.el.closest("[data-ohw-nav-children]"));
14828
- if (activeElRef.current && !isNestedDrag) deactivateRef.current();
14804
+ if (activeElRef.current) deactivateRef.current();
14829
14805
  const key = pending.el.getAttribute("data-ohw-href-key");
14830
14806
  if (!key) return;
14831
14807
  beginNavDragRef.current({
@@ -14836,9 +14812,7 @@ function useNavItemDrag({
14836
14812
  lastClientY: e.clientY,
14837
14813
  activeSlot: null
14838
14814
  });
14839
- if (activeElRef.current && isNestedDrag) {
14840
- deactivateRef.current();
14841
- }
14815
+ if (isNestedDrag) setNavGroupForceOpen(pending.el, true);
14842
14816
  };
14843
14817
  const endPointerDrag = (e) => {
14844
14818
  const pending = navPointerDragRef.current;
@@ -15878,7 +15852,7 @@ var badgeVariants = cva(
15878
15852
  {
15879
15853
  variants: {
15880
15854
  variant: {
15881
- default: "border-transparent bg-bridge-primary text-primary-foreground",
15855
+ default: "border-transparent bg-primary text-primary-foreground",
15882
15856
  secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15883
15857
  destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
15884
15858
  outline: "text-foreground"
@@ -16015,7 +15989,7 @@ function duplicateSchedulingWidget(instanceId, scheduleId) {
16015
15989
  try {
16016
15990
  const root = (0, import_client2.createRoot)(container);
16017
15991
  schedulingRoots.set(container, root);
16018
- (0, import_react_dom3.flushSync)(() => {
15992
+ (0, import_react_dom4.flushSync)(() => {
16019
15993
  root.render(
16020
15994
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
16021
15995
  SchedulingWidget,
@@ -16060,10 +16034,21 @@ function parseSchedulingInsertAfter(insertAfter) {
16060
16034
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
16061
16035
  };
16062
16036
  }
16063
- function resolveEntryAnchor(entry) {
16064
- if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
16065
- const parsed = parseSchedulingInsertAfter(entry.insertAfter);
16066
- return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
16037
+ function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
16038
+ const parsed = parseSchedulingInsertAfter(insertAfter);
16039
+ const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
16040
+ const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
16041
+ return { effectiveInsertAfter, insertBefore };
16042
+ }
16043
+ function getSchedulingMountPoint(insertAfter) {
16044
+ const { anchor } = parseSchedulingInsertAfter(insertAfter);
16045
+ let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
16046
+ if (!anchorEl && anchor === "scheduling") {
16047
+ const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
16048
+ anchorEl = widgets.at(-1) ?? null;
16049
+ }
16050
+ if (!anchorEl) return null;
16051
+ return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
16067
16052
  }
16068
16053
  function schedulingMountDepth(insertAfter) {
16069
16054
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -16080,7 +16065,8 @@ function getPageSchedulingEntries(raw) {
16080
16065
  }
16081
16066
  }
16082
16067
  function isSchedulingWidgetMissing(entry) {
16083
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
16068
+ const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
16069
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
16084
16070
  }
16085
16071
  function hasMissingSchedulingWidgets(entries) {
16086
16072
  return entries.some(isSchedulingWidgetMissing);
@@ -16118,18 +16104,17 @@ function initSectionsFromContent(content, removeExisting = false, currentPath =
16118
16104
  } catch {
16119
16105
  }
16120
16106
  }
16121
- function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
16122
- const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
16123
- const sectionId = schedulingSectionId(widgetId);
16107
+ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
16108
+ const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
16109
+ const sectionId = schedulingSectionId(effectiveInsertAfter);
16124
16110
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
16125
- const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
16126
- if (!anchorEl) return false;
16127
- const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
16111
+ const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
16112
+ if (!mountPoint) return false;
16128
16113
  const container = document.createElement("div");
16129
16114
  container.dataset.ohwSectionContainer = "scheduling";
16130
16115
  container.dataset.ohwInstance = sectionId;
16131
- if (beforeId) {
16132
- const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
16116
+ if (insertBefore) {
16117
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
16133
16118
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
16134
16119
  if (!beforePoint) return false;
16135
16120
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -16140,26 +16125,20 @@ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, be
16140
16125
  }
16141
16126
  tail.insertAdjacentElement("afterend", container);
16142
16127
  }
16143
- try {
16144
- const root = (0, import_client2.createRoot)(container);
16145
- schedulingRoots.set(container, root);
16146
- (0, import_react_dom3.flushSync)(() => {
16147
- root.render(
16148
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
16149
- SchedulingWidget,
16150
- {
16151
- notifyOnConnect,
16152
- initialScheduleId: scheduleId,
16153
- insertAfter: widgetId
16154
- }
16155
- )
16156
- );
16157
- });
16158
- } catch (err) {
16159
- console.error("[ow:scheduling] render threw", err);
16160
- container.remove();
16161
- return false;
16162
- }
16128
+ const root = (0, import_client2.createRoot)(container);
16129
+ schedulingRoots.set(container, root);
16130
+ (0, import_react_dom4.flushSync)(() => {
16131
+ root.render(
16132
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
16133
+ SchedulingWidget,
16134
+ {
16135
+ notifyOnConnect,
16136
+ initialScheduleId: scheduleId,
16137
+ insertAfter: effectiveInsertAfter
16138
+ }
16139
+ )
16140
+ );
16141
+ });
16163
16142
  const tracker = getSectionsTracker();
16164
16143
  let sections = [];
16165
16144
  try {
@@ -16167,12 +16146,10 @@ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, be
16167
16146
  } catch {
16168
16147
  }
16169
16148
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
16170
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
16149
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
16171
16150
  sections.push({
16172
16151
  type: "scheduling",
16173
- insertAfter: widgetId,
16174
- anchorId,
16175
- beforeId: beforeId ?? null,
16152
+ insertAfter: effectiveInsertAfter,
16176
16153
  pagePath: window.location.pathname,
16177
16154
  ...scheduleId ? { scheduleId } : {}
16178
16155
  });
@@ -16186,8 +16163,7 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
16186
16163
  for (let i = pending.length - 1; i >= 0; i--) {
16187
16164
  const entry = pending[i];
16188
16165
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
16189
- const { anchorId, beforeId } = resolveEntryAnchor(entry);
16190
- if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
16166
+ if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId ?? null)) {
16191
16167
  pending.splice(i, 1);
16192
16168
  }
16193
16169
  }
@@ -16351,11 +16327,6 @@ function applyLinkByKey(key, val) {
16351
16327
  hrefAnchors.forEach((el) => applyLinkHref(el, val));
16352
16328
  }
16353
16329
  }
16354
- function isInsideLinkEditor(target) {
16355
- return Boolean(
16356
- target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest("[data-ohw-section-picker]") || target.closest("[data-ohw-navbar-container-chrome]") || target.closest("[data-ohw-navbar-add-button]") || target.closest("[data-ohw-footer-container-chrome]") || target.closest("[data-ohw-footer-add-button]") || target.closest("[data-ohw-more-menu]") || target.closest('[data-slot="dropdown-menu-content"]') || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
16357
- );
16358
- }
16359
16330
  function isInsideFloatingPanel(target) {
16360
16331
  return Boolean(target.closest("[data-ohw-floating-panel]"));
16361
16332
  }
@@ -16363,6 +16334,11 @@ function isPointOverFloatingPanel(clientX, clientY) {
16363
16334
  const el = document.elementFromPoint(clientX, clientY);
16364
16335
  return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
16365
16336
  }
16337
+ function isInsideLinkEditor(target) {
16338
+ return Boolean(
16339
+ target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest("[data-ohw-section-picker]") || target.closest("[data-ohw-navbar-container-chrome]") || target.closest("[data-ohw-navbar-add-button]") || target.closest("[data-ohw-footer-container-chrome]") || target.closest("[data-ohw-footer-add-button]") || target.closest("[data-ohw-more-menu]") || target.closest('[data-slot="dropdown-menu-content"]') || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
16340
+ );
16341
+ }
16366
16342
  function getHrefKeyFromElement(el) {
16367
16343
  if (!el) return null;
16368
16344
  const anchor = el.closest("[data-ohw-href-key]");
@@ -16394,14 +16370,18 @@ function isNavDropdownPanelOpen(childrenRoot) {
16394
16370
  }
16395
16371
  return true;
16396
16372
  }
16397
- function getOpenNavDropdownPanelRect(anchor) {
16373
+ function getOpenNavDropdownPanel(anchor) {
16398
16374
  const group = anchor.closest("[data-ohw-nav-group]");
16399
16375
  if (!group?.hasAttribute("data-ohw-nav-force-open")) return null;
16400
16376
  const panel = group.querySelector(":scope > [data-ohw-nav-children]");
16401
16377
  if (!panel || !isNavDropdownPanelOpen(panel)) return null;
16402
16378
  const panelRect = panel.getBoundingClientRect();
16403
16379
  if (panelRect.height < 2 || panelRect.width < 2) return null;
16404
- return panelRect;
16380
+ return panel;
16381
+ }
16382
+ function getOpenNavDropdownPanelRect(anchor) {
16383
+ const panel = getOpenNavDropdownPanel(anchor);
16384
+ return panel ? panel.getBoundingClientRect() : null;
16405
16385
  }
16406
16386
  function isNavItemPointerTarget(el) {
16407
16387
  const childrenRoot = el.closest("[data-ohw-nav-children]");
@@ -16437,7 +16417,7 @@ function requestSocialDialog(anchor, post, content) {
16437
16417
  hrefKey: item.getAttribute("data-ohw-href-key") ?? "",
16438
16418
  iconKey,
16439
16419
  url: getLinkHref4(item),
16440
- iconStyle: detectIconStyle(item),
16420
+ iconStyle: "outline",
16441
16421
  // What was chosen last time. Guessing from the address instead reads as "Website" for anything
16442
16422
  // unrecognised, and for an item with no address at all — so a deliberate choice looked lost.
16443
16423
  platformId: content[socialPlatformKey(iconKey)] ?? ""
@@ -16627,7 +16607,7 @@ function getNavigationSelectionParent(el) {
16627
16607
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
16628
16608
  return getFooterLinksContainer();
16629
16609
  }
16630
- 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)) {
16610
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
16631
16611
  return getNavigationRoot(el);
16632
16612
  }
16633
16613
  return null;
@@ -16843,6 +16823,7 @@ var ICONS = {
16843
16823
  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"/>',
16844
16824
  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"/>'
16845
16825
  };
16826
+ var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
16846
16827
  var SELECTION_CHROME_GAP2 = 4;
16847
16828
  var TOOLBAR_STROKE_GAP2 = 4;
16848
16829
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -16939,10 +16920,6 @@ function getIframeVisibleClip(parentScroll) {
16939
16920
  );
16940
16921
  return { top: clipTop, bottom: Math.max(clipTop, clipBottom) };
16941
16922
  }
16942
- function isRectOutsideClip(rect, clip) {
16943
- if (!clip) return false;
16944
- return rect.bottom < clip.top || rect.top > clip.bottom;
16945
- }
16946
16923
  function applyVisibleViewport(el, parentScroll) {
16947
16924
  const clip = getIframeVisibleClip(parentScroll);
16948
16925
  const top = clip?.top ?? 0;
@@ -17226,7 +17203,6 @@ function StateToggle({
17226
17203
  );
17227
17204
  }
17228
17205
  var contentCache = /* @__PURE__ */ new Map();
17229
- var fetchedContentPaths = /* @__PURE__ */ new Set();
17230
17206
  var brandingCache = /* @__PURE__ */ new Map();
17231
17207
  var OHW_LOADER_STYLE = {
17232
17208
  position: "fixed",
@@ -17775,6 +17751,13 @@ function OhhwellsBridge() {
17775
17751
  const [isItemDragging, setIsItemDragging] = (0, import_react17.useState)(false);
17776
17752
  const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react17.useState)(false);
17777
17753
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
17754
+ const [floatingPanel, setFloatingPanel] = (0, import_react17.useState)(null);
17755
+ const floatingPanelOpenRef = (0, import_react17.useRef)(false);
17756
+ floatingPanelOpenRef.current = floatingPanel !== null;
17757
+ const [floatingPanelPos, setFloatingPanelPos] = (0, import_react17.useState)(null);
17758
+ const [logoSizeDraft, setLogoSizeDraft] = (0, import_react17.useState)(null);
17759
+ const [editorViewport, setEditorViewport] = (0, import_react17.useState)("desktop");
17760
+ const [parentScrollSnap, setParentScrollSnap] = (0, import_react17.useState)(null);
17778
17761
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react17.useState)(null);
17779
17762
  const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react17.useState)(null);
17780
17763
  const footerDragRef = (0, import_react17.useRef)(null);
@@ -17792,13 +17775,6 @@ function OhhwellsBridge() {
17792
17775
  const brandKitRef = (0, import_react17.useRef)("");
17793
17776
  const stylesRef = (0, import_react17.useRef)("");
17794
17777
  const pendingDeleteUndoRef = (0, import_react17.useRef)(null);
17795
- const [floatingPanel, setFloatingPanel] = (0, import_react17.useState)(null);
17796
- const floatingPanelOpenRef = (0, import_react17.useRef)(false);
17797
- const setFloatingPanelRef = (0, import_react17.useRef)(setFloatingPanel);
17798
- const [floatingPanelPos, setFloatingPanelPos] = (0, import_react17.useState)(null);
17799
- const [logoSizeDraft, setLogoSizeDraft] = (0, import_react17.useState)(null);
17800
- const [editorViewport, setEditorViewport] = (0, import_react17.useState)("desktop");
17801
- const [parentScrollSnap, setParentScrollSnap] = (0, import_react17.useState)(null);
17802
17778
  const [sitePages, setSitePages] = (0, import_react17.useState)([]);
17803
17779
  const [sectionsByPath, setSectionsByPath] = (0, import_react17.useState)({});
17804
17780
  const sectionsPrefetchGenRef = (0, import_react17.useRef)(0);
@@ -17807,18 +17783,7 @@ function OhhwellsBridge() {
17807
17783
  const linkPopoverOpenRef = (0, import_react17.useRef)(false);
17808
17784
  const linkPopoverGraceUntilRef = (0, import_react17.useRef)(0);
17809
17785
  setLinkPopoverRef.current = setLinkPopover;
17810
- setFloatingPanelRef.current = setFloatingPanel;
17811
17786
  linkPopoverSessionRef.current = linkPopover;
17812
- floatingPanelOpenRef.current = Boolean(floatingPanel);
17813
- (0, import_react17.useEffect)(() => {
17814
- const syncViewport = () => {
17815
- const next = window.innerWidth <= 480 ? "mobile" : "desktop";
17816
- setEditorViewport((prev) => prev === next ? prev : next);
17817
- };
17818
- syncViewport();
17819
- window.addEventListener("resize", syncViewport);
17820
- return () => window.removeEventListener("resize", syncViewport);
17821
- }, []);
17822
17787
  const {
17823
17788
  navDragRef,
17824
17789
  navPointerDragRef,
@@ -18024,11 +17989,16 @@ function OhhwellsBridge() {
18024
17989
  if (owner) persistFieldsRef.current(owner);
18025
17990
  }
18026
17991
  activeElRef.current = null;
18027
- const preserveNavDropdown = Boolean(navPointerDragRef.current?.el?.closest("[data-ohw-nav-children]")) || Boolean(navDragRef.current?.draggedEl?.closest("[data-ohw-nav-children]")) || Boolean(
17992
+ const preserveNavDropdown = Boolean(navPointerDragRef.current?.el?.closest("[data-ohw-nav-children]")) || Boolean(navDragRef.current?.draggedEl?.closest("[data-ohw-nav-children]")) || Boolean(el.closest("[data-ohw-nav-children]")) || Boolean(
18028
17993
  selectedElRef.current?.closest("[data-ohw-nav-children]") && selectedElRef.current?.closest("[data-ohw-nav-group]")?.hasAttribute("data-ohw-nav-force-open")
18029
17994
  );
18030
17995
  if (!preserveNavDropdown) {
18031
17996
  setNavGroupForceOpen(null, false);
17997
+ } else {
17998
+ const keepAnchor = selectedElRef.current?.closest("[data-ohw-nav-children]") != null ? selectedElRef.current : el.closest("[data-ohw-href-key]") ?? el;
17999
+ if (keepAnchor.closest("[data-ohw-nav-children]")) {
18000
+ setNavGroupForceOpen(keepAnchor, true);
18001
+ }
18032
18002
  }
18033
18003
  setReorderHrefKey(null);
18034
18004
  setReorderDragDisabled(false);
@@ -18047,6 +18017,7 @@ function OhhwellsBridge() {
18047
18017
  });
18048
18018
  }, []);
18049
18019
  const deselect = (0, import_react17.useCallback)(() => {
18020
+ const nestedGroup = selectedElRef.current?.closest("[data-ohw-nav-children]") != null ? selectedElRef.current.closest("[data-ohw-nav-group]") : null;
18050
18021
  clearSelectedAttr();
18051
18022
  selectedElRef.current = null;
18052
18023
  selectedHrefKeyRef.current = null;
@@ -18070,7 +18041,11 @@ function OhhwellsBridge() {
18070
18041
  setFloatingPanel(null);
18071
18042
  setLogoSizeDraft(null);
18072
18043
  if (!activeElRef.current) {
18073
- setNavGroupForceOpen(null, false);
18044
+ if (nestedGroup?.isConnected) {
18045
+ nestedGroup.setAttribute("data-ohw-nav-force-open", "");
18046
+ } else {
18047
+ setNavGroupForceOpen(null, false);
18048
+ }
18074
18049
  setToolbarRect(null);
18075
18050
  setToolbarVariant("none");
18076
18051
  }
@@ -18151,10 +18126,7 @@ function OhhwellsBridge() {
18151
18126
  setSelectedIsSocialsRow(false);
18152
18127
  const isDropdownTrigger = !isNestedNavChild(navAnchor) && (navItemHasDropdownChildren(navAnchor) || navItemOwnsDropdownPanel(navAnchor));
18153
18128
  if (isNestedNavChild(navAnchor)) {
18154
- const group = navAnchor.closest("[data-ohw-nav-group]");
18155
- if (group?.hasAttribute("data-ohw-nav-force-open")) {
18156
- setNavGroupForceOpen(navAnchor, true);
18157
- }
18129
+ setNavGroupForceOpen(navAnchor, true);
18158
18130
  setNavDropdownPreviewOpen(null);
18159
18131
  } else if (isDropdownTrigger) {
18160
18132
  const group = navAnchor.closest("[data-ohw-nav-group]");
@@ -18306,7 +18278,7 @@ function OhhwellsBridge() {
18306
18278
  hrefKey: planned.hrefKey,
18307
18279
  iconKey: planned.iconKey,
18308
18280
  url: "",
18309
- iconStyle: detectIconStyle(getSocialItem(selected) ?? socialsRow),
18281
+ iconStyle: "outline",
18310
18282
  platformId: "",
18311
18283
  // Tells the editor this one is not on the page yet, so dismissing means "never mind".
18312
18284
  isNew: true
@@ -18388,8 +18360,10 @@ function OhhwellsBridge() {
18388
18360
  setNavDropdownPreviewOpen(true);
18389
18361
  setToolbarRect(selected.getBoundingClientRect());
18390
18362
  requestAnimationFrame(() => {
18391
- if (selected.isConnected) setToolbarRect(selected.getBoundingClientRect());
18392
- enterEditOnNewItem(result.anchor);
18363
+ requestAnimationFrame(() => {
18364
+ if (selected.isConnected) setToolbarRect(selected.getBoundingClientRect());
18365
+ enterEditOnNewItem(result.anchor);
18366
+ });
18393
18367
  });
18394
18368
  }, [enterEditOnNewItem, isFooterFrameSelection, maybeWarnNavLinkDropdownConflict, postToParent2]);
18395
18369
  const handleAddFooterColumn = (0, import_react17.useCallback)(() => {
@@ -18769,10 +18743,7 @@ function OhhwellsBridge() {
18769
18743
  clearHrefKeyHover(anchor);
18770
18744
  const isDropdownTrigger = !isNestedNavChild(anchor) && (navItemHasDropdownChildren(anchor) || navItemOwnsDropdownPanel(anchor));
18771
18745
  if (isNestedNavChild(anchor)) {
18772
- const group = anchor.closest("[data-ohw-nav-group]");
18773
- if (group?.hasAttribute("data-ohw-nav-force-open")) {
18774
- setNavGroupForceOpen(anchor, true);
18775
- }
18746
+ setNavGroupForceOpen(anchor, true);
18776
18747
  setNavDropdownPreviewOpen(null);
18777
18748
  } else if (isDropdownTrigger) {
18778
18749
  setNavGroupForceOpen(null, false);
@@ -18830,17 +18801,6 @@ function OhhwellsBridge() {
18830
18801
  setIsFooterFrameSelection(isFooterColumn);
18831
18802
  setNavDropdownPreviewOpen(null);
18832
18803
  if (isFooterColumn) {
18833
- const ensured = ensureFooterColumnDefaultHeading(el, editContentRef.current);
18834
- if (ensured) {
18835
- editContentRef.current = {
18836
- ...editContentRef.current,
18837
- [ensured.headingKey]: ensured.text
18838
- };
18839
- postToParent2({
18840
- type: "ow:change",
18841
- nodes: [{ key: ensured.headingKey, text: ensured.text }]
18842
- });
18843
- }
18844
18804
  setFooterHeadingVisible(isFooterColumnHeadingVisible(el));
18845
18805
  } else {
18846
18806
  setFooterHeadingVisible(null);
@@ -19159,7 +19119,6 @@ function OhhwellsBridge() {
19159
19119
  }
19160
19120
  if (typeof content[STYLE_STORE_KEY] === "string") {
19161
19121
  stylesRef.current = content[STYLE_STORE_KEY];
19162
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
19163
19122
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
19164
19123
  }
19165
19124
  applyBrandChrome(content);
@@ -19167,11 +19126,11 @@ function OhhwellsBridge() {
19167
19126
  for (const [key, val] of Object.entries(content)) {
19168
19127
  if (key === "__ohw_sections") continue;
19169
19128
  if (key === AI_SECTIONS_KEY) continue;
19170
- if (key === LOGO_PLACEHOLDER_KEY) continue;
19171
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
19172
19129
  if (key === BRAND_KIT_KEY) continue;
19173
19130
  if (key === STYLE_STORE_KEY) continue;
19174
19131
  if (BRAND_CHROME_KEYS.has(key)) continue;
19132
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
19133
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
19175
19134
  if (applyVideoSettingNode(key, val)) continue;
19176
19135
  if (applyCarouselNode(key, val)) continue;
19177
19136
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -19242,9 +19201,7 @@ function OhhwellsBridge() {
19242
19201
  let cancelled = false;
19243
19202
  setFetchState("loading");
19244
19203
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
19245
- const initialPath = pathname;
19246
- fetchedContentPaths.add(`${subdomain}::${initialPath}`);
19247
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
19204
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
19248
19205
  if (cancelled) return;
19249
19206
  const content = data?.content ?? {};
19250
19207
  const branding = Boolean(data?.showBranding);
@@ -19353,9 +19310,7 @@ function OhhwellsBridge() {
19353
19310
  }, [isEditMode]);
19354
19311
  (0, import_react17.useEffect)(() => {
19355
19312
  if (isEditMode || fetchState !== "done") return;
19356
- console.log("env", process.env.NEXT_PUBLIC_FLOWOPS_API_URL);
19357
19313
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
19358
- console.log({ apiUrl, subdomain });
19359
19314
  bindPublishedForms(apiUrl, subdomain, contentCache.get(subdomain) ?? {});
19360
19315
  }, [isEditMode, fetchState, subdomain]);
19361
19316
  (0, import_react17.useEffect)(() => {
@@ -19380,17 +19335,16 @@ function OhhwellsBridge() {
19380
19335
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
19381
19336
  }
19382
19337
  if (typeof content[STYLE_STORE_KEY] === "string") {
19383
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
19384
19338
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
19385
19339
  }
19386
19340
  for (const [key, val] of Object.entries(content)) {
19387
19341
  if (key === "__ohw_sections") continue;
19388
19342
  if (key === AI_SECTIONS_KEY) continue;
19389
- if (key === LOGO_PLACEHOLDER_KEY) continue;
19390
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
19391
19343
  if (key === BRAND_KIT_KEY) continue;
19392
19344
  if (key === STYLE_STORE_KEY) continue;
19393
19345
  if (BRAND_CHROME_KEYS.has(key)) continue;
19346
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
19347
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
19394
19348
  if (applyVideoSettingNode(key, val)) continue;
19395
19349
  if (applyCarouselNode(key, val)) continue;
19396
19350
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -19441,17 +19395,6 @@ function OhhwellsBridge() {
19441
19395
  debounceTimer = setTimeout(applyFromCache, 150);
19442
19396
  };
19443
19397
  applyFromCache();
19444
- const pathCacheKey = `${subdomain}::${pathname}`;
19445
- if (!fetchedContentPaths.has(pathCacheKey)) {
19446
- fetchedContentPaths.add(pathCacheKey);
19447
- const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
19448
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
19449
- if (!data?.content) return;
19450
- contentCache.set(subdomain, data.content);
19451
- applyFromCache();
19452
- }).catch(() => {
19453
- });
19454
- }
19455
19398
  observer = new MutationObserver(scheduleApply);
19456
19399
  observer.observe(document.body, { childList: true, subtree: true });
19457
19400
  return () => {
@@ -19587,11 +19530,26 @@ function OhhwellsBridge() {
19587
19530
  const t2 = setTimeout(measure, 500);
19588
19531
  const ro = new ResizeObserver(schedule);
19589
19532
  ro.observe(document.body);
19533
+ let lastWidth = window.innerWidth;
19534
+ let resizeTimers = [];
19535
+ const clearResizeTimers = () => {
19536
+ resizeTimers.forEach(clearTimeout);
19537
+ resizeTimers = [];
19538
+ };
19539
+ const handleResize = () => {
19540
+ if (window.innerWidth === lastWidth) return;
19541
+ lastWidth = window.innerWidth;
19542
+ clearResizeTimers();
19543
+ resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
19544
+ };
19545
+ window.addEventListener("resize", handleResize);
19590
19546
  return () => {
19591
19547
  clearTimeout(t1);
19592
19548
  clearTimeout(t2);
19593
19549
  if (raf != null) cancelAnimationFrame(raf);
19594
19550
  ro.disconnect();
19551
+ clearResizeTimers();
19552
+ window.removeEventListener("resize", handleResize);
19595
19553
  };
19596
19554
  }, [pathname, isEditMode, postToParent2]);
19597
19555
  (0, import_react17.useEffect)(() => {
@@ -19686,8 +19644,10 @@ function OhhwellsBridge() {
19686
19644
  /* The glyph too, and this one mattered most: as a block it filled the whole width of its
19687
19645
  link and sat over the words, so every click meant for the text landed on the icon. */
19688
19646
  [data-ohw-socials-row] [data-ohw-editable="icon"] {
19689
- display: inline-flex;
19690
- flex: 0 0 auto;
19647
+ display: inline-flex !important;
19648
+ flex: 0 0 auto !important;
19649
+ width: auto !important;
19650
+ max-width: max-content !important;
19691
19651
  align-items: center;
19692
19652
  }
19693
19653
  [data-ohw-socials-row] a {
@@ -19766,9 +19726,24 @@ function OhhwellsBridge() {
19766
19726
  /* Somewhere to click the block itself, on every side (OHH-642) \u2014 editor only. */
19767
19727
  [data-ohw-editable="form"] {
19768
19728
  padding: 18px !important;
19729
+ /* Horizontal newsletter rows collapse added fields into pill-circles without wrap. */
19730
+ flex-wrap: wrap !important;
19731
+ align-items: flex-end !important;
19732
+ }
19733
+ [data-ohw-editable="form"] [data-ohw-form-field] {
19734
+ flex: 1 1 12rem !important;
19735
+ min-width: min(12rem, 100%) !important;
19736
+ max-width: 100% !important;
19769
19737
  }
19738
+ /* Placeholder text lives in the value while selected. currentColor inside color-mix
19739
+ resolves to the *inherited* color, so reverse-print footers washed it out on white
19740
+ inputs \u2014 pin to the control's own color captured in beginPlaceholderEdit. */
19770
19741
  [data-ohw-placeholder-edit] {
19771
- color: color-mix(in srgb, currentColor 55%, transparent) !important;
19742
+ color: color-mix(
19743
+ in srgb,
19744
+ var(--ohw-placeholder-edit-color, CanvasText) 55%,
19745
+ transparent
19746
+ ) !important;
19772
19747
  cursor: text !important;
19773
19748
  }
19774
19749
  /* Text hover chrome is drawn by the overlay (see hoveredTextRect) \u2014 the CSS outline
@@ -19864,6 +19839,9 @@ function OhhwellsBridge() {
19864
19839
  if (target.closest("[data-ohw-state-toggle]")) return;
19865
19840
  if (target.closest("[data-ohw-max-badge]")) return;
19866
19841
  if (isInsideLinkEditor(target)) return;
19842
+ if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
19843
+ clearMediaSelectionRef.current();
19844
+ }
19867
19845
  if (isInsideFloatingPanel(target)) return;
19868
19846
  if (target.closest("[data-ohw-form-toolbar]")) return;
19869
19847
  if (target.closest(
@@ -19871,9 +19849,6 @@ function OhhwellsBridge() {
19871
19849
  )) {
19872
19850
  return;
19873
19851
  }
19874
- if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
19875
- clearMediaSelectionRef.current();
19876
- }
19877
19852
  {
19878
19853
  const formEl = getFormElement(target);
19879
19854
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -20037,6 +20012,14 @@ function OhhwellsBridge() {
20037
20012
  }
20038
20013
  const clickedButton = findClosestButtonLike(target);
20039
20014
  const buttonOnMedia = Boolean(clickedButton && isMediaEditable(editable) && editable.contains(clickedButton));
20015
+ console.log("[click-debug]", {
20016
+ editableType: editable.dataset.ohwEditable,
20017
+ editableTag: editable.tagName,
20018
+ targetTag: target.tagName,
20019
+ clickedButtonTag: clickedButton?.tagName ?? null,
20020
+ buttonOnMedia,
20021
+ isMediaEditableEditable: isMediaEditable(editable)
20022
+ });
20040
20023
  if (isMediaEditable(editable) && !buttonOnMedia) {
20041
20024
  e.preventDefault();
20042
20025
  e.stopPropagation();
@@ -20063,6 +20046,11 @@ function OhhwellsBridge() {
20063
20046
  const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
20064
20047
  const hrefCtx = getHrefKeyFromElement(hrefLookupTarget);
20065
20048
  const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
20049
+ console.log("[click-debug 2]", {
20050
+ hrefLookupTargetTag: hrefLookupTarget.tagName,
20051
+ hrefCtx: hrefCtx ? { key: hrefCtx.key } : null,
20052
+ navAnchorTag: navAnchor?.tagName ?? null
20053
+ });
20066
20054
  if (navAnchor) {
20067
20055
  e.preventDefault();
20068
20056
  e.stopPropagation();
@@ -20232,9 +20220,6 @@ function OhhwellsBridge() {
20232
20220
  setHoveredItemRect(null);
20233
20221
  hoveredNavContainerRef.current = null;
20234
20222
  setHoveredNavContainerRect(null);
20235
- siblingHintElRef.current = null;
20236
- setSiblingHintRect(null);
20237
- setSiblingHintRects([]);
20238
20223
  return;
20239
20224
  }
20240
20225
  {
@@ -20355,6 +20340,7 @@ function OhhwellsBridge() {
20355
20340
  hoveredNavContainerRef.current = null;
20356
20341
  setHoveredNavContainerRect(null);
20357
20342
  hoveredItemElRef.current = editable;
20343
+ setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
20358
20344
  }
20359
20345
  }
20360
20346
  }
@@ -20651,7 +20637,7 @@ function OhhwellsBridge() {
20651
20637
  }
20652
20638
  };
20653
20639
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
20654
- if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
20640
+ if (linkPopoverOpenRef.current) {
20655
20641
  if (hoveredImageRef.current) {
20656
20642
  hoveredImageRef.current = null;
20657
20643
  hoveredImageHasTextOverlapRef.current = false;
@@ -21024,7 +21010,8 @@ function OhhwellsBridge() {
21024
21010
  };
21025
21011
  const handleMouseMove = (e) => {
21026
21012
  const { clientX, clientY } = e;
21027
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
21013
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
21014
+ if (isOverEditorChrome(clientX, clientY)) {
21028
21015
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
21029
21016
  formHoverElRef.current = null;
21030
21017
  setFormHoverRect(null);
@@ -21032,12 +21019,7 @@ function OhhwellsBridge() {
21032
21019
  setHoveredItemRect(null);
21033
21020
  hoveredNavContainerRef.current = null;
21034
21021
  setHoveredNavContainerRect(null);
21035
- siblingHintElRef.current = null;
21036
- setSiblingHintRect(null);
21037
- setSiblingHintRects([]);
21038
21022
  dismissImageHover();
21039
- clearImageHover();
21040
- setSectionGap(null);
21041
21023
  return;
21042
21024
  }
21043
21025
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -21049,11 +21031,7 @@ function OhhwellsBridge() {
21049
21031
  if (e.data?.type !== "ow:pointer-sync") return;
21050
21032
  const { clientX, clientY } = e.data;
21051
21033
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
21052
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
21053
- dismissImageHover();
21054
- clearImageHover();
21055
- return;
21056
- }
21034
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
21057
21035
  if (probeSocialsRowAt(clientX, clientY)) return;
21058
21036
  probeSectionGapAt(clientX, clientY);
21059
21037
  probeImageAt(clientX, clientY);
@@ -21392,7 +21370,6 @@ function OhhwellsBridge() {
21392
21370
  }
21393
21371
  if (typeof content[STYLE_STORE_KEY] === "string") {
21394
21372
  stylesRef.current = content[STYLE_STORE_KEY];
21395
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
21396
21373
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
21397
21374
  }
21398
21375
  applyBrandChrome(content);
@@ -21404,11 +21381,11 @@ function OhhwellsBridge() {
21404
21381
  continue;
21405
21382
  }
21406
21383
  if (key === AI_SECTIONS_KEY) continue;
21407
- if (key === LOGO_PLACEHOLDER_KEY) continue;
21408
- if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
21409
21384
  if (key === BRAND_KIT_KEY) continue;
21410
21385
  if (key === STYLE_STORE_KEY) continue;
21411
21386
  if (BRAND_CHROME_KEYS.has(key)) continue;
21387
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
21388
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
21412
21389
  if (applyVideoSettingNode(key, val)) continue;
21413
21390
  if (applyCarouselNode(key, val)) continue;
21414
21391
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -21629,26 +21606,10 @@ function OhhwellsBridge() {
21629
21606
  const handleGetBrand = (e) => {
21630
21607
  if (e.data?.type !== "ow:get-brand") return;
21631
21608
  const template = deriveTemplateBrand();
21632
- const fallback = template ?? (() => {
21633
- const fonts = deriveTemplateFonts();
21634
- return fonts ? { palette: AI_DEFAULT_BRAND.palette, fonts } : null;
21635
- })();
21636
- const value = brandKitRef.current || (fallback ? JSON.stringify(fallback) : "");
21609
+ const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
21637
21610
  postToParentRef.current({ type: "ow:brand-value", value });
21638
21611
  };
21639
21612
  window.addEventListener("message", handleGetBrand);
21640
- const handleGetTemplateFonts = (e) => {
21641
- if (e.data?.type !== "ow:get-template-fonts") return;
21642
- const fonts = deriveTemplateFonts();
21643
- postToParentRef.current({ type: "ow:template-fonts-value", value: fonts ? JSON.stringify(fonts) : "" });
21644
- };
21645
- window.addEventListener("message", handleGetTemplateFonts);
21646
- const handleGetTemplateBrand = (e) => {
21647
- if (e.data?.type !== "ow:get-template-brand") return;
21648
- const brand = deriveTemplateBrand();
21649
- postToParentRef.current({ type: "ow:template-brand-value", value: brand ? JSON.stringify(brand) : "" });
21650
- };
21651
- window.addEventListener("message", handleGetTemplateBrand);
21652
21613
  const handleMoveSection = (e) => {
21653
21614
  if (e.data?.type !== "ow:move-section") return;
21654
21615
  const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
@@ -21841,12 +21802,6 @@ function OhhwellsBridge() {
21841
21802
  closeLinkPopoverRef.current();
21842
21803
  return;
21843
21804
  }
21844
- if (floatingPanelOpenRef.current) {
21845
- setFloatingPanelRef.current(null);
21846
- deselectRef.current();
21847
- deactivateRef.current();
21848
- return;
21849
- }
21850
21805
  deselectRef.current();
21851
21806
  deactivateRef.current();
21852
21807
  clearMediaSelectionRef.current();
@@ -22135,12 +22090,8 @@ function OhhwellsBridge() {
22135
22090
  if (inserted) {
22136
22091
  const tracker = getSectionsTracker();
22137
22092
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
22138
- const reportHeight = () => {
22139
- const h = document.body.scrollHeight;
22140
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
22141
- };
22142
- reportHeight();
22143
- setTimeout(reportHeight, 500);
22093
+ const h = document.body.scrollHeight;
22094
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
22144
22095
  }
22145
22096
  };
22146
22097
  const handleSwitchSchedule = (e) => {
@@ -22317,20 +22268,14 @@ function OhhwellsBridge() {
22317
22268
  };
22318
22269
  const applyToolbarPos = (rect) => {
22319
22270
  const ps = parentScrollRef.current;
22320
- const hidden = isRectOutsideClip(rect, getIframeVisibleClip(ps));
22321
22271
  const measuredW = toolbarElRef.current?.offsetWidth || 330;
22322
22272
  if (toolbarElRef.current) {
22323
- toolbarElRef.current.style.display = hidden ? "none" : "";
22324
- if (!hidden) {
22325
- const { top, left, transform } = calcToolbarPos(rect, ps, measuredW);
22326
- toolbarElRef.current.style.top = `${top}px`;
22327
- toolbarElRef.current.style.left = `${left}px`;
22328
- toolbarElRef.current.style.transform = transform;
22329
- }
22273
+ const { top, left, transform } = calcToolbarPos(rect, ps, measuredW);
22274
+ toolbarElRef.current.style.top = `${top}px`;
22275
+ toolbarElRef.current.style.left = `${left}px`;
22276
+ toolbarElRef.current.style.transform = transform;
22330
22277
  }
22331
22278
  if (glowElRef.current) {
22332
- glowElRef.current.style.display = hidden ? "none" : "";
22333
- if (hidden) return;
22334
22279
  const GAP = SELECTION_CHROME_GAP2;
22335
22280
  glowElRef.current.style.top = `${rect.top - GAP}px`;
22336
22281
  glowElRef.current.style.left = `${rect.left - GAP}px`;
@@ -22561,17 +22506,15 @@ function OhhwellsBridge() {
22561
22506
  window.removeEventListener("message", handleAiSetBrand);
22562
22507
  window.removeEventListener("message", handleAiSetStyles);
22563
22508
  window.removeEventListener("message", handleGetBrand);
22564
- window.removeEventListener("message", handleGetTemplateFonts);
22565
- window.removeEventListener("message", handleGetTemplateBrand);
22566
22509
  window.removeEventListener("message", handleMoveSection);
22567
22510
  window.removeEventListener("message", handlePanelDragging);
22568
22511
  window.removeEventListener("message", handleDeleteSection);
22569
22512
  window.removeEventListener("message", handleDuplicateSection);
22570
22513
  window.removeEventListener("message", handleDeactivate);
22514
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
22571
22515
  window.removeEventListener("message", handleToastAction);
22572
22516
  window.removeEventListener("message", handleFormCount);
22573
22517
  window.removeEventListener("message", handleUiEscape);
22574
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
22575
22518
  autoSaveTimers.current.forEach(clearTimeout);
22576
22519
  autoSaveTimers.current.clear();
22577
22520
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
@@ -23238,7 +23181,7 @@ function OhhwellsBridge() {
23238
23181
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { id: "ohw-loader", suppressHydrationWarning: true, style: { ...OHW_LOADER_STYLE, display: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(OhwLoaderSpinner, {}) }),
23239
23182
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("script", { suppressHydrationWarning: true, dangerouslySetInnerHTML: { __html: OHW_LOADER_PREHYDRATE_SCRIPT } }),
23240
23183
  subdomain && !isEditMode && showBranding && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(MadeWithOhhWells, {}),
23241
- bridgeRoot ? (0, import_react_dom4.createPortal)(
23184
+ bridgeRoot ? (0, import_react_dom5.createPortal)(
23242
23185
  /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
23243
23186
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
23244
23187
  isEditMode && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
@@ -23390,7 +23333,7 @@ function OhhwellsBridge() {
23390
23333
  "span",
23391
23334
  {
23392
23335
  "data-ohw-form-count": "",
23393
- className: "ml-0.5 inline-flex h-[18px] min-w-[18px] items-center justify-center rounded-full bg-bridge-primary px-1.5 text-[11px] font-semibold text-primary-foreground",
23336
+ className: "ml-0.5 inline-flex h-[18px] min-w-[18px] items-center justify-center rounded-full bg-primary px-1.5 text-[11px] font-semibold text-primary-foreground",
23394
23337
  children: formPickCount
23395
23338
  }
23396
23339
  )
@@ -23507,8 +23450,9 @@ function OhhwellsBridge() {
23507
23450
  {
23508
23451
  rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
23509
23452
  toolbarBelowRect: selectedElRef.current && isNavigationItem2(selectedElRef.current) ? getOpenNavDropdownPanelRect(selectedElRef.current) : null,
23453
+ toolbarBelowEl: selectedElRef.current && isNavigationItem2(selectedElRef.current) ? getOpenNavDropdownPanel(selectedElRef.current) : null,
23510
23454
  elRef: glowElRef,
23511
- state: isItemDragging ? "dragging" : selectedElRef.current && isNavigationItem2(selectedElRef.current) && getOpenNavDropdownPanelRect(selectedElRef.current) ? "active-bottom" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
23455
+ state: isItemDragging ? "dragging" : selectedElRef.current && isNavigationItem2(selectedElRef.current) && getOpenNavDropdownPanel(selectedElRef.current) ? "active-bottom" : resolveItemInteractionState(toolbarRect, parentScrollRef.current),
23512
23456
  showHandle: toolbarVariant === "link-action" && Boolean(reorderHrefKey) || toolbarVariant === "select-frame" && isFooterFrameSelection,
23513
23457
  dragDisabled: reorderDragDisabled,
23514
23458
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
@@ -23561,7 +23505,7 @@ function OhhwellsBridge() {
23561
23505
  ) : void 0
23562
23506
  }
23563
23507
  ),
23564
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && !isRectOutsideClip(toolbarRect, getIframeVisibleClip(parentScrollRef.current)) && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
23508
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
23565
23509
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
23566
23510
  EditGlowChrome,
23567
23511
  {
@@ -23627,11 +23571,11 @@ function OhhwellsBridge() {
23627
23571
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
23628
23572
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
23629
23573
  children: [
23630
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex-1 bg-bridge-primary", style: { height: 3 } }),
23574
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
23631
23575
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
23632
23576
  Badge,
23633
23577
  {
23634
- className: "px-8 py-1 bg-bridge-primary hover:bg-bridge-primary text-primary-foreground text-xs font-medium shrink-0 rounded-full cursor-pointer pointer-events-auto",
23578
+ className: "px-8 py-1 bg-primary hover:bg-primary text-primary-foreground text-xs font-medium shrink-0 rounded-full cursor-pointer pointer-events-auto",
23635
23579
  onClick: () => {
23636
23580
  window.parent.postMessage(
23637
23581
  {
@@ -23645,7 +23589,7 @@ function OhhwellsBridge() {
23645
23589
  children: "Add Section"
23646
23590
  }
23647
23591
  ),
23648
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex-1 bg-bridge-primary", style: { height: 3 } })
23592
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
23649
23593
  ]
23650
23594
  }
23651
23595
  ),
@@ -23693,59 +23637,6 @@ function OhhwellsBridge() {
23693
23637
  ) : null
23694
23638
  ] });
23695
23639
  }
23696
-
23697
- // src/ui/EmptySection.tsx
23698
- var import_link = __toESM(require("next/link"), 1);
23699
- var import_jsx_runtime34 = require("react/jsx-runtime");
23700
- function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
23701
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
23702
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
23703
- "p",
23704
- {
23705
- style: {
23706
- fontFamily: "var(--brand-font-body)",
23707
- fontSize: "0.75rem",
23708
- fontWeight: 500,
23709
- letterSpacing: "0.15em",
23710
- textTransform: "uppercase",
23711
- color: "var(--brand-accent)",
23712
- marginBottom: "1.5rem"
23713
- },
23714
- children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_link.default, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
23715
- }
23716
- ),
23717
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
23718
- "h1",
23719
- {
23720
- style: {
23721
- fontFamily: "var(--brand-font-heading)",
23722
- fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
23723
- lineHeight: 1.1,
23724
- letterSpacing: "-0.025em",
23725
- color: "var(--brand-text)",
23726
- marginBottom: "1rem"
23727
- },
23728
- ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
23729
- children: title
23730
- }
23731
- ),
23732
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
23733
- "p",
23734
- {
23735
- style: {
23736
- fontFamily: "var(--brand-font-body)",
23737
- fontSize: "1rem",
23738
- lineHeight: 1.7,
23739
- fontWeight: 300,
23740
- color: "var(--brand-text-muted)",
23741
- maxWidth: "340px"
23742
- },
23743
- ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
23744
- children: "This page doesn't have any content yet."
23745
- }
23746
- )
23747
- ] });
23748
- }
23749
23640
  // Annotate the CommonJS export names for ESM import in node:
23750
23641
  0 && (module.exports = {
23751
23642
  AI_DEFAULT_BRAND,
@@ -23763,7 +23654,6 @@ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey
23763
23654
  DropdownMenuItem,
23764
23655
  DropdownMenuSeparator,
23765
23656
  DropdownMenuTrigger,
23766
- EmptySection,
23767
23657
  ItemActionToolbar,
23768
23658
  ItemInteractionLayer,
23769
23659
  LinkEditorPanel,