@ohhwells/bridge 0.1.112 → 0.1.113-next.293

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -279,16 +279,17 @@ var LEGACY_BRAND_VAR_NAMES = [
279
279
  "text-muted",
280
280
  "surface",
281
281
  "border",
282
- "on-primary",
283
- "navbar-background"
282
+ "on-primary"
284
283
  ].map((role) => `--brand-${role}`);
285
284
  var FONT_VARS = {
286
285
  heading: ["--font-heading", "--font-display", "--brand-font-heading"],
287
286
  body: ["--font-body", "--brand-font-body"]
288
287
  };
289
288
  var BRAND_FONT_LINK_ID = "ohw-brand-fonts";
290
- function brandColorVars(kit) {
291
- const { dark, primary, accent, light } = kit.palette;
289
+ var CUSTOM_FONT_STYLE_ID = "ohw-brand-custom-fonts";
290
+ var CUSTOM_FONT_FORMATS = /* @__PURE__ */ new Set(["woff2", "woff", "truetype", "opentype"]);
291
+ function brandColorVars(palette) {
292
+ const { dark, primary, accent, light } = palette;
292
293
  const mix = (a, aPct, b) => `color-mix(in srgb, ${a} ${aPct}%, ${b})`;
293
294
  const surface = mix(light, 95, dark);
294
295
  const border = mix(light, 85, dark);
@@ -311,22 +312,42 @@ function brandColorVars(kit) {
311
312
  "--brand-border": border,
312
313
  // Buttons/bands painted in the primary colour assume it's dark/saturated enough to need
313
314
  // light text on top — the same assumption LOGO_IMAGE's light-on-dark navbar mark makes.
314
- "--brand-on-primary": light,
315
- "--brand-navbar-background": dark
315
+ "--brand-on-primary": light
316
316
  };
317
317
  }
318
+ function parseCustomFontWeight(raw) {
319
+ if (typeof raw !== "object" || raw === null) return null;
320
+ const w = raw;
321
+ if (typeof w.weight !== "number" || !Number.isFinite(w.weight)) return null;
322
+ if (typeof w.url !== "string" || !w.url) return null;
323
+ if (typeof w.format !== "string" || !CUSTOM_FONT_FORMATS.has(w.format)) return null;
324
+ const weightEnd = typeof w.weightEnd === "number" && Number.isFinite(w.weightEnd) && w.weightEnd > w.weight ? w.weightEnd : void 0;
325
+ return { weight: w.weight, ...weightEnd !== void 0 ? { weightEnd } : {}, url: w.url, format: w.format };
326
+ }
327
+ function parseCustomFont(raw) {
328
+ if (typeof raw !== "object" || raw === null) return null;
329
+ const f = raw;
330
+ if (typeof f.family !== "string" || !f.family) return null;
331
+ if (typeof f.label !== "string" || !f.label) return null;
332
+ if (!Array.isArray(f.weights)) return null;
333
+ const weights = f.weights.map(parseCustomFontWeight).filter((w) => w !== null);
334
+ if (weights.length === 0) return null;
335
+ return { family: f.family, label: f.label, weights };
336
+ }
318
337
  function parseBrandKit(raw) {
319
338
  if (!raw) return null;
320
339
  try {
321
340
  const parsed = JSON.parse(raw);
322
341
  const p = parsed?.palette;
323
342
  const f = parsed?.fonts;
324
- 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") {
325
- return null;
326
- }
343
+ 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;
344
+ const fonts = f && typeof f.heading === "string" && typeof f.body === "string" ? { heading: f.heading, body: f.body } : void 0;
345
+ const customFonts = Array.isArray(parsed?.customFonts) ? parsed.customFonts.map(parseCustomFont).filter((c) => c !== null) : void 0;
346
+ if (!palette && !fonts && !customFonts) return null;
327
347
  return {
328
- palette: { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light },
329
- fonts: { heading: f.heading, body: f.body }
348
+ ...palette ? { palette } : {},
349
+ ...fonts ? { fonts } : {},
350
+ ...customFonts ? { customFonts } : {}
330
351
  };
331
352
  } catch {
332
353
  return null;
@@ -336,11 +357,16 @@ function familyOf(stack) {
336
357
  const first = stack.split(",")[0]?.trim() ?? "";
337
358
  return first.replace(/^['"]|['"]$/g, "");
338
359
  }
360
+ function quoteFamily(family) {
361
+ return `'${family.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
362
+ }
363
+ var WEIGHT_SCALE = [100, 200, 300, 400, 500, 600, 700, 800, 900];
339
364
  function loadBrandFonts(families) {
340
365
  const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
341
366
  if (unique.length === 0) return;
342
- const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
343
- const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
367
+ const weights = WEIGHT_SCALE.join(";");
368
+ const spec = unique.map((f) => `family=${f.replace(/ /g, "+")}:wght@${weights}`).join("&");
369
+ const href = `https://fonts.googleapis.com/css2?${spec}&display=swap`;
344
370
  let link = document.getElementById(BRAND_FONT_LINK_ID);
345
371
  if (!link) {
346
372
  link = document.createElement("link");
@@ -350,18 +376,54 @@ function loadBrandFonts(families) {
350
376
  }
351
377
  if (link.href !== href) link.href = href;
352
378
  }
379
+ function escapeCssString(value) {
380
+ return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
381
+ }
382
+ function loadCustomFonts(fonts) {
383
+ const list = fonts ?? [];
384
+ let style = document.getElementById(CUSTOM_FONT_STYLE_ID);
385
+ if (list.length === 0) {
386
+ style?.remove();
387
+ return;
388
+ }
389
+ if (!style) {
390
+ style = document.createElement("style");
391
+ style.id = CUSTOM_FONT_STYLE_ID;
392
+ document.head.appendChild(style);
393
+ }
394
+ style.textContent = list.flatMap(
395
+ (font) => font.weights.map((w) => {
396
+ const weightDescriptor = w.weightEnd ? `${w.weight} ${w.weightEnd}` : `${w.weight}`;
397
+ return `@font-face { font-family: '${escapeCssString(font.family)}'; src: url('${escapeCssString(w.url)}') format('${w.format}'); font-weight: ${weightDescriptor}; font-display: swap; }`;
398
+ })
399
+ ).join("\n");
400
+ }
353
401
  function applyBrandToDom(kit) {
354
402
  const root = document.documentElement;
355
403
  if (!kit) {
356
404
  for (const name of [...BRAND_VAR_NAMES, ...LEGACY_BRAND_VAR_NAMES]) root.style.removeProperty(name);
357
405
  for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
358
406
  document.getElementById(BRAND_FONT_LINK_ID)?.remove();
407
+ document.getElementById(CUSTOM_FONT_STYLE_ID)?.remove();
359
408
  return;
360
409
  }
361
- for (const [name, value] of Object.entries(brandColorVars(kit))) root.style.setProperty(name, value);
362
- for (const name of FONT_VARS.heading) root.style.setProperty(name, kit.fonts.heading);
363
- for (const name of FONT_VARS.body) root.style.setProperty(name, kit.fonts.body);
364
- loadBrandFonts([familyOf(kit.fonts.heading), familyOf(kit.fonts.body)]);
410
+ if (kit.palette) {
411
+ for (const [name, value] of Object.entries(brandColorVars(kit.palette))) root.style.setProperty(name, value);
412
+ }
413
+ if (kit.customFonts) {
414
+ loadCustomFonts(kit.customFonts);
415
+ }
416
+ if (kit.fonts) {
417
+ const heading = quoteFamily(kit.fonts.heading);
418
+ const body = quoteFamily(kit.fonts.body);
419
+ for (const name of FONT_VARS.heading) root.style.setProperty(name, heading);
420
+ for (const name of FONT_VARS.body) root.style.setProperty(name, body);
421
+ const customFamilies = new Set((kit.customFonts ?? []).map((f) => f.family));
422
+ const googleFamilies = [familyOf(kit.fonts.heading), familyOf(kit.fonts.body)].filter(
423
+ (f) => !customFamilies.has(f)
424
+ );
425
+ loadBrandFonts(googleFamilies);
426
+ }
365
427
  }
366
428
 
367
429
  // src/lib/section-styles.ts
@@ -386,6 +448,20 @@ var BG_VALUES = {
386
448
  accent: "var(--ohw-brand-primary, var(--color-primary, #0078E5))",
387
449
  "accent-soft": "color-mix(in srgb, var(--ohw-brand-primary, var(--color-primary, #0078E5)) 12%, var(--ohw-brand-light, var(--color-light, #FFFFFF)))"
388
450
  };
451
+ var NAVBAR_COLOR_TOKENS = ["dark", "primary", "accent", "light"];
452
+ function navbarColorVar(token) {
453
+ const fallback = {
454
+ dark: "#0C0A09",
455
+ primary: "#0078E5",
456
+ accent: "#0078E5",
457
+ light: "#FFFFFF"
458
+ };
459
+ return `var(--ohw-brand-${token}, var(--color-${token}, ${fallback[token]}))`;
460
+ }
461
+ function resolveNavbarValue(value) {
462
+ if (value === void 0) return void 0;
463
+ return NAVBAR_COLOR_TOKENS.includes(value) ? navbarColorVar(value) : value;
464
+ }
389
465
  var HEADLINE_SIZES = { md: 24, lg: 32, xl: 40, display: 56 };
390
466
  function styleSheetCss() {
391
467
  const rules = [];
@@ -555,8 +631,16 @@ function applyStylesToDom(store) {
555
631
  for (const [sectionId, override] of Object.entries(store.sections)) {
556
632
  const escaped = CSS.escape(sectionId);
557
633
  const byInstance = document.querySelectorAll(`[data-ohw-instance="${escaped}"]`);
558
- const sections = byInstance.length ? byInstance : document.querySelectorAll(`[data-ohw-section="${escaped}"]`);
634
+ const sections = Array.from(
635
+ byInstance.length ? byInstance : document.querySelectorAll(`[data-ohw-section="${escaped}"]`)
636
+ );
637
+ const isNavbar = sectionId === "navbar";
638
+ if (isNavbar) {
639
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
640
+ if (drawer && !sections.includes(drawer)) sections.push(drawer);
641
+ }
559
642
  for (const marker of Array.from(sections)) {
643
+ const isDrawerMarker = isNavbar && marker.hasAttribute("data-ohw-nav-drawer");
560
644
  const section = marker.querySelector(":scope > [data-ai-section]") ?? marker;
561
645
  for (const [prop, attr] of Object.entries(SECTION_ATTRS)) {
562
646
  const value = override[prop];
@@ -564,19 +648,23 @@ function applyStylesToDom(store) {
564
648
  if (prop === "sectionBackground" && override.sectionBackgroundColor !== void 0) continue;
565
649
  section.setAttribute(attr, String(value).replace(":", "-"));
566
650
  }
567
- if (override.sectionBackgroundColor !== void 0) {
651
+ const bgOverride = isDrawerMarker ? override.navbarMobileBackground : override.sectionBackgroundColor;
652
+ const textOverride = isDrawerMarker ? override.navbarMobileTextColor : override.textColor;
653
+ const bgValue = isNavbar ? resolveNavbarValue(bgOverride) : bgOverride;
654
+ const textValue = isNavbar ? resolveNavbarValue(textOverride) : textOverride;
655
+ if (bgValue !== void 0) {
568
656
  saveInline(section, "background");
569
- section.style.setProperty("background", override.sectionBackgroundColor, "important");
657
+ section.style.setProperty("background", bgValue, "important");
570
658
  section.setAttribute("data-ohw-style-bgcolor", "");
571
659
  }
572
- if (override.textColor !== void 0 || override.fontFamily !== void 0) {
660
+ if (textValue !== void 0 || override.fontFamily !== void 0) {
573
661
  const textEls = marker.querySelectorAll(
574
662
  '[data-ohw-editable="text"], [data-ohw-editable="plain"], [data-ohw-field-label]'
575
663
  );
576
664
  for (const el of Array.from(textEls)) {
577
- if (override.textColor !== void 0) {
665
+ if (textValue !== void 0) {
578
666
  saveInline(el, "color");
579
- el.style.setProperty("color", override.textColor, "important");
667
+ el.style.setProperty("color", textValue, "important");
580
668
  el.setAttribute(NODE_WROTE_ATTR, "");
581
669
  }
582
670
  if (override.fontFamily !== void 0) {
@@ -1282,6 +1370,22 @@ function accentBandContext(brand) {
1282
1370
  function textAttrs(ctx, path) {
1283
1371
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
1284
1372
  }
1373
+ var AI_RESPONSIVE_CSS = [
1374
+ "@media (max-width: 960px) {",
1375
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
1376
+ ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
1377
+ "}",
1378
+ "@media (max-width: 640px) {",
1379
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
1380
+ " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
1381
+ // Group containers flatten to a column on phones; span placements come along for free.
1382
+ " [data-ai-responsive] [data-ai-group] { display: flex !important; flex-direction: column !important; }",
1383
+ " [data-ai-responsive] [data-ai-group] > * { grid-column: auto !important; }",
1384
+ " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
1385
+ " [data-ai-responsive] { overflow-x: hidden; }",
1386
+ " [data-ai-responsive] img { max-width: 100%; }",
1387
+ "}"
1388
+ ].join("\n");
1285
1389
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
1286
1390
  function MediaBox({
1287
1391
  refValue,
@@ -1294,13 +1398,17 @@ function MediaBox({
1294
1398
  const url = refValue ? ctx.resolveMedia(refValue) : null;
1295
1399
  const isIcon = /^(lucide|simple):/.test(refValue);
1296
1400
  const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
1297
- const editAttrs = ctx.keyFor && editPath && !isIcon ? { "data-ohw-key": ctx.keyFor(editPath), "data-ohw-editable": "image" } : {};
1401
+ const editAttrs = ctx.keyFor && editPath ? {
1402
+ "data-ohw-key": ctx.keyFor(editPath),
1403
+ "data-ohw-editable": isIcon ? "icon" : "image"
1404
+ } : {};
1298
1405
  if (isIcon) {
1299
1406
  const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
1300
1407
  return /* @__PURE__ */ jsx(
1301
1408
  "span",
1302
1409
  {
1303
1410
  "data-ai-icon": refValue,
1411
+ ...editAttrs,
1304
1412
  style: {
1305
1413
  display: "inline-flex",
1306
1414
  width: 48,
@@ -2193,7 +2301,7 @@ function CollectionBlock({ node, ctx, path }) {
2193
2301
  return /* @__PURE__ */ jsx(
2194
2302
  "div",
2195
2303
  {
2196
- "data-ai-grid": "",
2304
+ "data-ai-grid": String(itemsPerRow),
2197
2305
  style: {
2198
2306
  display: "grid",
2199
2307
  gridTemplateColumns: `repeat(${itemsPerRow}, 1fr)`,
@@ -2546,6 +2654,7 @@ function AiTreeRenderer({
2546
2654
  {
2547
2655
  "data-ai-section": tree.tag ?? "",
2548
2656
  ...bgAttrs,
2657
+ "data-ai-responsive": "",
2549
2658
  style: {
2550
2659
  position: "relative",
2551
2660
  padding: `${pad}px 0`,
@@ -2556,12 +2665,13 @@ function AiTreeRenderer({
2556
2665
  color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
2557
2666
  },
2558
2667
  children: [
2559
- isOverlay && backgroundUrl && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
2668
+ /* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
2560
2669
  /* @__PURE__ */ jsx("style", { children: AI_MOBILE_CSS }),
2670
+ isOverlay && backgroundUrl && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
2561
2671
  /* @__PURE__ */ jsx(
2562
2672
  "div",
2563
2673
  {
2564
- "data-ai-container": "",
2674
+ "data-ai-section-inner": "",
2565
2675
  style: {
2566
2676
  position: "relative",
2567
2677
  maxWidth: AI_TREE_TOKENS.sectionMaxWidth,
@@ -2572,7 +2682,7 @@ function AiTreeRenderer({
2572
2682
  children: tree.rows.map((row, r2) => /* @__PURE__ */ jsx(
2573
2683
  "div",
2574
2684
  {
2575
- "data-ai-row": "",
2685
+ "data-ai-columns": "",
2576
2686
  style: {
2577
2687
  display: "grid",
2578
2688
  gridTemplateColumns: "repeat(12, 1fr)",
@@ -2622,14 +2732,52 @@ function readRootVar(name) {
2622
2732
  if (typeof document === "undefined") return "";
2623
2733
  return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
2624
2734
  }
2735
+ function normalizeColorToHex(value) {
2736
+ if (!value || typeof document === "undefined") return value;
2737
+ const canvas = document.createElement("canvas");
2738
+ canvas.width = 1;
2739
+ canvas.height = 1;
2740
+ const ctx = canvas.getContext("2d");
2741
+ if (!ctx) return value;
2742
+ ctx.fillStyle = value;
2743
+ ctx.fillRect(0, 0, 1, 1);
2744
+ const [r2, g, b] = ctx.getImageData(0, 0, 1, 1).data;
2745
+ const toHex = (n) => n.toString(16).padStart(2, "0");
2746
+ return `#${toHex(r2)}${toHex(g)}${toHex(b)}`;
2747
+ }
2748
+ var GENERIC_FONT_KEYWORDS = /* @__PURE__ */ new Set([
2749
+ "serif",
2750
+ "sans-serif",
2751
+ "monospace",
2752
+ "cursive",
2753
+ "fantasy",
2754
+ "system-ui",
2755
+ "ui-serif",
2756
+ "ui-sans-serif",
2757
+ "ui-monospace",
2758
+ "ui-rounded",
2759
+ "math",
2760
+ "emoji",
2761
+ "fangsong"
2762
+ ]);
2763
+ function primaryFontFamily(stack) {
2764
+ const first = stack.split(",").map((part) => part.trim().replace(/^["']|["']$/g, "")).find((part) => part && !GENERIC_FONT_KEYWORDS.has(part.toLowerCase()));
2765
+ return first ?? "";
2766
+ }
2767
+ function humanizeFontFamily(name) {
2768
+ 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());
2769
+ }
2770
+ function readFontVar(name) {
2771
+ return humanizeFontFamily(primaryFontFamily(readRootVar(name)));
2772
+ }
2625
2773
  function deriveBrandOverride() {
2626
2774
  const dark = readRootVar("--ohw-brand-dark");
2627
2775
  const primary = readRootVar("--ohw-brand-primary");
2628
2776
  const light = readRootVar("--ohw-brand-light");
2629
2777
  if (!dark || !primary || !light) return null;
2630
2778
  const accent = readRootVar("--ohw-brand-accent");
2631
- const heading = readRootVar("--font-heading") || readRootVar("--font-display");
2632
- const body = readRootVar("--font-body");
2779
+ const heading = readFontVar("--font-heading") || readFontVar("--font-display");
2780
+ const body = readFontVar("--font-body");
2633
2781
  return {
2634
2782
  palette: { dark, primary, accent: accent || dark, light },
2635
2783
  fonts: {
@@ -2638,22 +2786,55 @@ function deriveBrandOverride() {
2638
2786
  }
2639
2787
  };
2640
2788
  }
2641
- function deriveTemplateBrand() {
2789
+ function deriveTemplateBrandLive() {
2642
2790
  const primary = readRootVar("--brand-primary") || readRootVar("--color-primary");
2643
2791
  const dark = readRootVar("--brand-text") || readRootVar("--color-dark");
2644
2792
  const light = readRootVar("--brand-background") || readRootVar("--color-light");
2645
2793
  if (!dark || !primary || !light) return null;
2646
2794
  const accent = readRootVar("--brand-accent") || readRootVar("--color-accent");
2647
- const heading = readRootVar("--brand-font-heading") || readRootVar("--font-heading") || readRootVar("--font-display");
2648
- const body = readRootVar("--brand-font-body") || readRootVar("--font-body");
2795
+ const heading = readFontVar("--brand-font-heading") || readFontVar("--font-heading") || readFontVar("--font-display");
2796
+ const body = readFontVar("--brand-font-body") || readFontVar("--font-body");
2649
2797
  return {
2650
- palette: { dark, primary, accent: accent || dark, light },
2798
+ palette: {
2799
+ dark: normalizeColorToHex(dark),
2800
+ primary: normalizeColorToHex(primary),
2801
+ accent: normalizeColorToHex(accent || dark),
2802
+ light: normalizeColorToHex(light)
2803
+ },
2651
2804
  fonts: {
2652
2805
  heading: heading || AI_DEFAULT_BRAND.fonts.heading,
2653
2806
  body: body || AI_DEFAULT_BRAND.fonts.body
2654
2807
  }
2655
2808
  };
2656
2809
  }
2810
+ function deriveTemplateFontsLive() {
2811
+ const heading = readFontVar("--brand-font-heading") || readFontVar("--font-heading") || readFontVar("--font-display");
2812
+ const body = readFontVar("--brand-font-body") || readFontVar("--font-body");
2813
+ if (!heading && !body) return null;
2814
+ return {
2815
+ heading: heading || AI_DEFAULT_BRAND.fonts.heading,
2816
+ body: body || AI_DEFAULT_BRAND.fonts.body
2817
+ };
2818
+ }
2819
+ var OVERRIDE_VAR_NAMES = [...BRAND_VAR_NAMES, ...LEGACY_BRAND_VAR_NAMES, ...FONT_VARS.heading, ...FONT_VARS.body];
2820
+ function withOverrideStripped(read) {
2821
+ const root = document.documentElement;
2822
+ const restore = OVERRIDE_VAR_NAMES.map((name) => [name, root.style.getPropertyValue(name)]);
2823
+ for (const name of OVERRIDE_VAR_NAMES) root.style.removeProperty(name);
2824
+ try {
2825
+ return read();
2826
+ } finally {
2827
+ for (const [name, value] of restore) if (value) root.style.setProperty(name, value);
2828
+ }
2829
+ }
2830
+ var TEMPLATE_BRAND_SNAPSHOT = typeof document === "undefined" ? null : withOverrideStripped(deriveTemplateBrandLive);
2831
+ var TEMPLATE_FONTS_SNAPSHOT = typeof document === "undefined" ? null : withOverrideStripped(deriveTemplateFontsLive);
2832
+ function deriveTemplateBrand() {
2833
+ return TEMPLATE_BRAND_SNAPSHOT;
2834
+ }
2835
+ function deriveTemplateFonts() {
2836
+ return TEMPLATE_FONTS_SNAPSHOT;
2837
+ }
2657
2838
  function deriveTemplateButtonStyle() {
2658
2839
  if (typeof document === "undefined") return null;
2659
2840
  const candidates = Array.from(
@@ -7151,12 +7332,12 @@ var cva = (base, config) => (props) => {
7151
7332
  import { Toggle as TogglePrimitive } from "radix-ui";
7152
7333
  import { jsx as jsx5 } from "react/jsx-runtime";
7153
7334
  var toggleVariants = cva(
7154
- "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",
7335
+ "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",
7155
7336
  {
7156
7337
  variants: {
7157
7338
  variant: {
7158
7339
  default: "bg-transparent border-0",
7159
- outline: "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground"
7340
+ outline: "border border-input bg-transparent shadow-xs hover:bg-bridge-accent hover:text-accent-foreground"
7160
7341
  },
7161
7342
  size: {
7162
7343
  default: "px-2 py-1",
@@ -7249,10 +7430,10 @@ var DragHandle = React4.forwardRef(
7249
7430
  type,
7250
7431
  "data-slot": "drag-handle",
7251
7432
  className: cn(
7252
- "inline-flex h-7 w-4 shrink-0 items-center justify-center rounded-md transition-all duration-200 max-h-[30px]",
7433
+ "inline-flex h-7 w-4 shrink-0 items-center justify-center rounded-md transition-all duration-200 max-h-7.5",
7253
7434
  "bg-white border border-transparent text-stone-500 shadow-md cursor-grab",
7254
7435
  "enabled:hover:border enabled:hover:border-stone-200 enabled:hover:text-stone-950",
7255
- "enabled:active:border enabled:active:border-primary enabled:active:bg-primary-50 enabled:active:text-stone-950 enabled:active:shadow enabled:active:cursor-grabbing",
7436
+ "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",
7256
7437
  "disabled:cursor-not-allowed disabled:opacity-40 disabled:text-stone-950 disabled:pointer-events-none",
7257
7438
  className
7258
7439
  ),
@@ -7274,7 +7455,7 @@ var CustomToolbar = React5.forwardRef(({ className, onMouseDown, ...props }, ref
7274
7455
  "data-ohw-toolbar": "",
7275
7456
  className: cn(
7276
7457
  // Figma: bg background, radius 8, gap-1, p-0.5, shadow-md — no border
7277
- "inline-flex h-8 items-center gap-1 rounded-[var(--radius,0.5rem)] bg-background p-0.5 font-sans whitespace-nowrap shadow-md",
7458
+ "inline-flex h-8 items-center gap-1 rounded-(--radius,0.5rem) bg-background p-0.5 font-sans whitespace-nowrap shadow-md",
7278
7459
  className
7279
7460
  ),
7280
7461
  onMouseDown: (e) => {
@@ -7303,7 +7484,7 @@ var CustomToolbarButton = React5.forwardRef(
7303
7484
  type,
7304
7485
  className: cn(
7305
7486
  "inline-flex size-7 shrink-0 items-center justify-center rounded-[calc(var(--radius,0.5rem)-2px)] text-foreground transition-colors",
7306
- active ? "bg-primary text-primary-foreground" : "bg-transparent hover:bg-muted disabled:cursor-not-allowed disabled:text-muted-foreground disabled:opacity-60",
7487
+ active ? "bg-bridge-primary text-primary-foreground" : "bg-transparent hover:bg-muted disabled:cursor-not-allowed disabled:text-muted-foreground disabled:opacity-60",
7307
7488
  className
7308
7489
  ),
7309
7490
  ...props
@@ -8567,13 +8748,13 @@ function FormFieldToolbar({
8567
8748
  ]
8568
8749
  }
8569
8750
  ) }),
8570
- /* @__PURE__ */ jsx13(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-[190px] p-1", children: FIELD_TYPES.map((entry) => {
8751
+ /* @__PURE__ */ jsx13(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-47.5 p-1", children: FIELD_TYPES.map((entry) => {
8571
8752
  const Icon = TYPE_ICONS[entry.type];
8572
8753
  return /* @__PURE__ */ jsxs6(
8573
8754
  DropdownMenuItem,
8574
8755
  {
8575
8756
  onSelect: () => onTypeChange(entry.type),
8576
- className: "rounded-md py-2 text-[13px] " + (entry.type === type ? "bg-primary/10" : ""),
8757
+ className: "rounded-md py-2 text-[13px] " + (entry.type === type ? "bg-bridge-primary/10" : ""),
8577
8758
  children: [
8578
8759
  /* @__PURE__ */ jsx13(Icon, { size: 14, strokeWidth: 1.75, className: "shrink-0", "aria-hidden": true }),
8579
8760
  entry.label
@@ -8590,7 +8771,7 @@ function FormFieldToolbar({
8590
8771
  type: "button",
8591
8772
  "aria-pressed": required,
8592
8773
  onClick: onRequiredToggle,
8593
- 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"),
8774
+ 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"),
8594
8775
  "data-ohw-field-required-toggle": "",
8595
8776
  children: [
8596
8777
  /* @__PURE__ */ jsx13(Asterisk, { size: 14, strokeWidth: 2, "aria-hidden": true }),
@@ -8610,7 +8791,7 @@ function FormFieldToolbar({
8610
8791
  children: /* @__PURE__ */ jsx13(MoreHorizontal2, { size: 15, "aria-hidden": true })
8611
8792
  }
8612
8793
  ) }),
8613
- /* @__PURE__ */ jsxs6(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-[170px] p-1", children: [
8794
+ /* @__PURE__ */ jsxs6(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-42.5 p-1", children: [
8614
8795
  /* @__PURE__ */ jsxs6(DropdownMenuItem, { onSelect: onDuplicate, className: "rounded-md py-2 text-[13px]", children: [
8615
8796
  /* @__PURE__ */ jsx13(Copy2, { size: 14, strokeWidth: 1.75, className: "shrink-0", "aria-hidden": true }),
8616
8797
  "Duplicate"
@@ -8638,7 +8819,7 @@ function FieldTypePicker({ onPick }) {
8638
8819
  {
8639
8820
  type: "button",
8640
8821
  onClick: () => onPick(entry.type),
8641
- 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",
8822
+ 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-bridge-primary hover:bg-bridge-primary/5",
8642
8823
  children: [
8643
8824
  /* @__PURE__ */ jsx13(Icon, { size: 26, strokeWidth: 1.5, "aria-hidden": true }),
8644
8825
  entry.label
@@ -8664,7 +8845,7 @@ var buttonVariants = cva(
8664
8845
  {
8665
8846
  variants: {
8666
8847
  variant: {
8667
- default: "bg-primary text-primary-foreground hover:opacity-90",
8848
+ default: "bg-bridge-primary text-primary-foreground hover:opacity-90",
8668
8849
  outline: "border border-border bg-background text-foreground shadow-sm hover:bg-muted/80",
8669
8850
  ghost: "min-w-0 px-3 py-2 text-foreground hover:bg-muted/50"
8670
8851
  },
@@ -8759,6 +8940,7 @@ function MediaOverlay({
8759
8940
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
8760
8941
  );
8761
8942
  }, [isVideo]);
8943
+ const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
8762
8944
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
8763
8945
  const box = {
8764
8946
  position: "fixed",
@@ -8864,8 +9046,8 @@ function MediaOverlay({
8864
9046
  pointerEvents: hover.hasTextOverlap ? "none" : "auto",
8865
9047
  // Selected: a firm component ring with no wash, so the image reads as chosen rather
8866
9048
  // than hovered. Hover keeps the existing tinted preview.
8867
- boxShadow: selected ? "inset 0 0 0 2px var(--color-primary)" : "inset 0 0 0 1.5px var(--color-primary)",
8868
- background: selected ? "transparent" : "color-mix(in srgb, var(--color-primary) 20%, transparent)"
9049
+ boxShadow: selected ? "inset 0 0 0 2px var(--color-bridge-primary)" : "inset 0 0 0 1.5px var(--color-bridge-primary)",
9050
+ background: selected ? "transparent" : "color-mix(in srgb, var(--color-bridge-primary) 20%, transparent)"
8869
9051
  },
8870
9052
  onClick: () => onSelect && !selected ? onSelect(hover.key) : onReplace(hover.key),
8871
9053
  children: [
@@ -8888,17 +9070,17 @@ function MediaOverlay({
8888
9070
  },
8889
9071
  children: [
8890
9072
  isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
8891
- isVideo ? "Replace video" : "Replace image"
9073
+ replaceLabel
8892
9074
  ]
8893
9075
  }
8894
9076
  ),
8895
- replaceMode === "none" ? null : /* @__PURE__ */ jsxs7(
9077
+ showChrome && replaceMode !== "none" && /* @__PURE__ */ jsxs7(
8896
9078
  Button,
8897
9079
  {
8898
9080
  "data-ohw-media-overlay": "",
8899
9081
  variant: "outline",
8900
9082
  size: "sm",
8901
- "aria-label": isVideo ? "Replace video" : "Replace image",
9083
+ "aria-label": replaceLabel,
8902
9084
  className: "gap-1.5 cursor-pointer hover:bg-background",
8903
9085
  style: {
8904
9086
  ...OVERLAY_BUTTON_STYLE,
@@ -8921,7 +9103,7 @@ function MediaOverlay({
8921
9103
  },
8922
9104
  children: [
8923
9105
  isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
8924
- replaceMode === "full" ? isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image" : null
9106
+ replaceMode === "full" ? replaceLabel : null
8925
9107
  ]
8926
9108
  }
8927
9109
  )
@@ -8959,8 +9141,8 @@ function CarouselOverlay({
8959
9141
  height: rect.height,
8960
9142
  zIndex: 2147483646,
8961
9143
  pointerEvents: "auto",
8962
- boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
8963
- background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
9144
+ boxShadow: "inset 0 0 0 1.5px var(--color-bridge-primary)",
9145
+ background: "color-mix(in srgb, var(--color-bridge-primary) 20%, transparent)"
8964
9146
  },
8965
9147
  onClick: () => onEdit(hover.key),
8966
9148
  children: /* @__PURE__ */ jsxs8(
@@ -9028,6 +9210,57 @@ function useLiveSectionRect(sectionId) {
9028
9210
  }, [sectionId]);
9029
9211
  return rect;
9030
9212
  }
9213
+ var NAV_TOGGLE_LABEL_RE = /^(toggle menu|open menu|close menu)$/i;
9214
+ function readNavToggleRect() {
9215
+ const el = Array.from(document.querySelectorAll("button[aria-label]")).find(
9216
+ (e) => NAV_TOGGLE_LABEL_RE.test(e.getAttribute("aria-label") ?? "")
9217
+ );
9218
+ if (!el) return null;
9219
+ const r2 = el.getBoundingClientRect();
9220
+ if (r2.width <= 0 || r2.height <= 0) return null;
9221
+ return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
9222
+ }
9223
+ function useLiveNavToggleHole(active) {
9224
+ const [rect, setRect] = useState6(null);
9225
+ useEffect4(() => {
9226
+ if (!active) {
9227
+ setRect(null);
9228
+ return;
9229
+ }
9230
+ const update = () => {
9231
+ const next = readNavToggleRect();
9232
+ setRect(
9233
+ (prev) => prev && next && prev.top === next.top && prev.left === next.left && prev.width === next.width && prev.height === next.height ? prev : next
9234
+ );
9235
+ };
9236
+ update();
9237
+ const opts = { capture: true, passive: true };
9238
+ window.addEventListener("scroll", update, opts);
9239
+ window.addEventListener("resize", update);
9240
+ const interval = setInterval(update, 500);
9241
+ return () => {
9242
+ window.removeEventListener("scroll", update, opts);
9243
+ window.removeEventListener("resize", update);
9244
+ clearInterval(interval);
9245
+ };
9246
+ }, [active]);
9247
+ return rect;
9248
+ }
9249
+ function subtractRect(outer, hole) {
9250
+ if (!hole) return [outer];
9251
+ const l = Math.max(outer.left, hole.left);
9252
+ const t = Math.max(outer.top, hole.top);
9253
+ const r2 = Math.min(outer.left + outer.width, hole.left + hole.width);
9254
+ const b = Math.min(outer.top + outer.height, hole.top + hole.height);
9255
+ if (l >= r2 || t >= b) return [outer];
9256
+ const rects = [];
9257
+ if (t > outer.top) rects.push({ left: outer.left, top: outer.top, width: outer.width, height: t - outer.top });
9258
+ const midHeight = b - t;
9259
+ if (l > outer.left) rects.push({ left: outer.left, top: t, width: l - outer.left, height: midHeight });
9260
+ if (r2 < outer.left + outer.width) rects.push({ left: r2, top: t, width: outer.left + outer.width - r2, height: midHeight });
9261
+ if (b < outer.top + outer.height) rects.push({ left: outer.left, top: b, width: outer.width, height: outer.top + outer.height - b });
9262
+ return rects;
9263
+ }
9031
9264
  function computeSectionBoundaryFlags(instanceId) {
9032
9265
  const topLevel = topLevelSections();
9033
9266
  const index = topLevel.findIndex((el) => instanceIdOf(el) === instanceId);
@@ -9243,6 +9476,7 @@ function AiSectionOverlay({
9243
9476
  const activeSelectionId = reviewId ? null : selectedId;
9244
9477
  const selectionRect = useLiveSectionRect(activeSelectionId);
9245
9478
  const reviewRect = useLiveSectionRect(reviewId);
9479
+ const navToggleHole = useLiveNavToggleHole(Boolean(reviewId));
9246
9480
  const hoverRect = useLiveSectionRect(reviewId || hoveredId === selectedId ? null : hoveredId);
9247
9481
  const showSelectionBorder = Boolean(selectionRect) && !navDropdownForceOpen;
9248
9482
  useEffect4(() => {
@@ -9302,48 +9536,65 @@ function AiSectionOverlay({
9302
9536
  }
9303
9537
  }
9304
9538
  ),
9305
- reviewRect && /* @__PURE__ */ jsx17(
9306
- "div",
9307
- {
9308
- "data-ohw-ai-review": "",
9309
- style: {
9310
- position: "fixed",
9311
- top: reviewRect.top,
9312
- left: reviewRect.left,
9313
- width: reviewRect.width,
9314
- height: reviewRect.height,
9315
- border: `2px solid ${PRIMARY2}`,
9316
- borderRadius: edgeAwareRadius(reviewRect),
9317
- zIndex: 2147483200,
9318
- // The veil itself: swallows clicks so the section stays locked until decided. This
9319
- // stopPropagation only guards the bubble phase; the bridge's capture-phase click
9320
- // handlers additionally bail on `[data-ohw-ai-review]`, without which a click on
9321
- // Accept/Discard resolves to the media beneath and opens the file picker.
9322
- background: "rgba(8, 133, 254, 0.04)",
9323
- pointerEvents: "auto",
9324
- cursor: "default"
9539
+ reviewRect && /* @__PURE__ */ jsxs9(Fragment5, { children: [
9540
+ subtractRect(reviewRect, navToggleHole).map((r2, i) => /* @__PURE__ */ jsx17(
9541
+ "div",
9542
+ {
9543
+ "data-ohw-ai-review": "",
9544
+ "aria-hidden": true,
9545
+ style: {
9546
+ position: "fixed",
9547
+ top: r2.top,
9548
+ left: r2.left,
9549
+ width: r2.width,
9550
+ height: r2.height,
9551
+ background: "rgba(8, 133, 254, 0.04)",
9552
+ pointerEvents: "auto",
9553
+ cursor: "default",
9554
+ zIndex: 2147483200
9555
+ },
9556
+ onClick: (e) => e.stopPropagation()
9325
9557
  },
9326
- onClick: (e) => e.stopPropagation(),
9327
- children: !reviewButtonsHidden && /* @__PURE__ */ jsxs9(
9328
- "div",
9329
- {
9330
- style: {
9331
- position: "sticky",
9332
- top: 12,
9333
- display: "flex",
9334
- gap: 8,
9335
- justifyContent: "flex-end",
9336
- paddingRight: 12,
9337
- paddingTop: 12
9338
- },
9339
- children: [
9340
- /* @__PURE__ */ jsx17(ReviewButton, { label: "Accept", onClick: () => decide("accept"), background: PRIMARY2, color: "#ffffff", children: /* @__PURE__ */ jsx17(Check, { size: 16, strokeWidth: 2.5, "aria-hidden": true }) }),
9341
- /* @__PURE__ */ jsx17(ReviewButton, { label: "Discard", onClick: () => decide("discard"), background: "#EFF6FF", color: "#0c0a09", children: /* @__PURE__ */ jsx17(X, { size: 16, strokeWidth: 2, "aria-hidden": true }) })
9342
- ]
9558
+ i
9559
+ )),
9560
+ /* @__PURE__ */ jsx17(
9561
+ "div",
9562
+ {
9563
+ "aria-hidden": true,
9564
+ style: {
9565
+ position: "fixed",
9566
+ top: reviewRect.top,
9567
+ left: reviewRect.left,
9568
+ width: reviewRect.width,
9569
+ height: reviewRect.height,
9570
+ border: `2px solid ${PRIMARY2}`,
9571
+ borderRadius: edgeAwareRadius(reviewRect),
9572
+ pointerEvents: "none",
9573
+ zIndex: 2147483200
9343
9574
  }
9344
- )
9345
- }
9346
- )
9575
+ }
9576
+ ),
9577
+ !reviewButtonsHidden && /* @__PURE__ */ jsxs9(
9578
+ "div",
9579
+ {
9580
+ style: {
9581
+ position: "fixed",
9582
+ top: reviewRect.top + 12,
9583
+ left: reviewRect.left,
9584
+ width: reviewRect.width,
9585
+ display: "flex",
9586
+ gap: 8,
9587
+ justifyContent: "flex-end",
9588
+ paddingRight: 12,
9589
+ zIndex: 2147483201
9590
+ },
9591
+ children: [
9592
+ /* @__PURE__ */ jsx17(ReviewButton, { label: "Accept", onClick: () => decide("accept"), background: PRIMARY2, color: "#ffffff", children: /* @__PURE__ */ jsx17(Check, { size: 16, strokeWidth: 2.5, "aria-hidden": true }) }),
9593
+ /* @__PURE__ */ jsx17(ReviewButton, { label: "Discard", onClick: () => decide("discard"), background: "#EFF6FF", color: "#0c0a09", children: /* @__PURE__ */ jsx17(X, { size: 16, strokeWidth: 2, "aria-hidden": true }) })
9594
+ ]
9595
+ }
9596
+ )
9597
+ ] })
9347
9598
  ] });
9348
9599
  }
9349
9600
 
@@ -9813,7 +10064,7 @@ function SectionTreeItem({
9813
10064
  /* @__PURE__ */ jsx20(
9814
10065
  "div",
9815
10066
  {
9816
- className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
10067
+ className: "-mr-px h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
9817
10068
  "aria-hidden": true
9818
10069
  }
9819
10070
  ),
@@ -9832,7 +10083,7 @@ function SectionTreeItem({
9832
10083
  className: cn(
9833
10084
  "flex h-9 min-w-0 flex-1 items-center gap-2 rounded-md border border-border bg-background p-3",
9834
10085
  interactive && "cursor-pointer hover:bg-muted/30",
9835
- interactive && selected && "border-primary"
10086
+ interactive && selected && "border-bridge-primary"
9836
10087
  ),
9837
10088
  children: [
9838
10089
  /* @__PURE__ */ jsx20(
@@ -9962,7 +10213,7 @@ function UrlOrPageInput({
9962
10213
  };
9963
10214
  const fieldClassName = cn(
9964
10215
  "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]",
9965
- urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
10216
+ 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"
9966
10217
  );
9967
10218
  return /* @__PURE__ */ jsxs13("div", { className: "flex w-full flex-col gap-2 p-0", children: [
9968
10219
  /* @__PURE__ */ jsx23(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
@@ -11006,6 +11257,15 @@ function getNavbarDesktopContainer() {
11006
11257
  function getNavbarDrawerContainer() {
11007
11258
  return document.querySelector("[data-ohw-nav-drawer]");
11008
11259
  }
11260
+ function isNavDrawerOpen() {
11261
+ return document.querySelector('[data-ohw-nav-drawer][data-ohw-section="navbar"]') !== null;
11262
+ }
11263
+ function isPointOutsideOpenDrawer(clientX, clientY) {
11264
+ const drawer = document.querySelector('[data-ohw-nav-drawer][data-ohw-section="navbar"]');
11265
+ if (!drawer) return false;
11266
+ const r2 = drawer.getBoundingClientRect();
11267
+ return clientX < r2.left || clientX > r2.right || clientY < r2.top || clientY > r2.bottom;
11268
+ }
11009
11269
  function isNavbarListContainerVisible(el) {
11010
11270
  if (el.hasAttribute("hidden")) return false;
11011
11271
  if (el.closest("[hidden]")) return false;
@@ -13869,6 +14129,7 @@ function readLogoSizeState(content, placement) {
13869
14129
  function getLogoElement(el) {
13870
14130
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
13871
14131
  if (marked) return marked;
14132
+ if (el.closest('[data-ohw-editable="icon"]')) return null;
13872
14133
  const root = el.closest("nav, [data-ohw-nav-root], footer");
13873
14134
  if (!root) return null;
13874
14135
  const anchor = el.closest("a");
@@ -14162,7 +14423,7 @@ function DisplaySwitch({
14162
14423
  onClick: () => onChange(!checked),
14163
14424
  className: cn(
14164
14425
  "relative h-5 w-9 shrink-0 rounded-full transition-colors",
14165
- checked ? "bg-primary" : "bg-primary-50",
14426
+ checked ? "bg-bridge-primary" : "bg-primary-50",
14166
14427
  disabled ? "cursor-default opacity-50" : "cursor-pointer"
14167
14428
  ),
14168
14429
  children: /* @__PURE__ */ jsx28(
@@ -14170,7 +14431,7 @@ function DisplaySwitch({
14170
14431
  {
14171
14432
  className: cn(
14172
14433
  "absolute top-0.5 h-4 w-4 rounded-full bg-white transition-all",
14173
- checked ? "left-[1.125rem]" : "left-0.5"
14434
+ checked ? "left-4.5" : "left-0.5"
14174
14435
  )
14175
14436
  }
14176
14437
  )
@@ -15805,7 +16066,7 @@ var badgeVariants = cva(
15805
16066
  {
15806
16067
  variants: {
15807
16068
  variant: {
15808
- default: "border-transparent bg-primary text-primary-foreground",
16069
+ default: "border-transparent bg-bridge-primary text-primary-foreground",
15809
16070
  secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15810
16071
  destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
15811
16072
  outline: "text-foreground"
@@ -15987,21 +16248,10 @@ function parseSchedulingInsertAfter(insertAfter) {
15987
16248
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
15988
16249
  };
15989
16250
  }
15990
- function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
15991
- const parsed = parseSchedulingInsertAfter(insertAfter);
15992
- const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
15993
- const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
15994
- return { effectiveInsertAfter, insertBefore };
15995
- }
15996
- function getSchedulingMountPoint(insertAfter) {
15997
- const { anchor } = parseSchedulingInsertAfter(insertAfter);
15998
- let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
15999
- if (!anchorEl && anchor === "scheduling") {
16000
- const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
16001
- anchorEl = widgets.at(-1) ?? null;
16002
- }
16003
- if (!anchorEl) return null;
16004
- return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
16251
+ function resolveEntryAnchor(entry) {
16252
+ if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
16253
+ const parsed = parseSchedulingInsertAfter(entry.insertAfter);
16254
+ return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
16005
16255
  }
16006
16256
  function schedulingMountDepth(insertAfter) {
16007
16257
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -16018,8 +16268,7 @@ function getPageSchedulingEntries(raw) {
16018
16268
  }
16019
16269
  }
16020
16270
  function isSchedulingWidgetMissing(entry) {
16021
- const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
16022
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
16271
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
16023
16272
  }
16024
16273
  function hasMissingSchedulingWidgets(entries) {
16025
16274
  return entries.some(isSchedulingWidgetMissing);
@@ -16057,17 +16306,18 @@ function initSectionsFromContent(content, removeExisting = false, currentPath =
16057
16306
  } catch {
16058
16307
  }
16059
16308
  }
16060
- function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
16061
- const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
16062
- const sectionId = schedulingSectionId(effectiveInsertAfter);
16309
+ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
16310
+ const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
16311
+ const sectionId = schedulingSectionId(widgetId);
16063
16312
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
16064
- const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
16065
- if (!mountPoint) return false;
16313
+ const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
16314
+ if (!anchorEl) return false;
16315
+ const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
16066
16316
  const container = document.createElement("div");
16067
16317
  container.dataset.ohwSectionContainer = "scheduling";
16068
16318
  container.dataset.ohwInstance = sectionId;
16069
- if (insertBefore) {
16070
- const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
16319
+ if (beforeId) {
16320
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
16071
16321
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
16072
16322
  if (!beforePoint) return false;
16073
16323
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -16078,20 +16328,26 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
16078
16328
  }
16079
16329
  tail.insertAdjacentElement("afterend", container);
16080
16330
  }
16081
- const root = createRoot2(container);
16082
- schedulingRoots.set(container, root);
16083
- flushSync2(() => {
16084
- root.render(
16085
- /* @__PURE__ */ jsx33(
16086
- SchedulingWidget,
16087
- {
16088
- notifyOnConnect,
16089
- initialScheduleId: scheduleId,
16090
- insertAfter: effectiveInsertAfter
16091
- }
16092
- )
16093
- );
16094
- });
16331
+ try {
16332
+ const root = createRoot2(container);
16333
+ schedulingRoots.set(container, root);
16334
+ flushSync2(() => {
16335
+ root.render(
16336
+ /* @__PURE__ */ jsx33(
16337
+ SchedulingWidget,
16338
+ {
16339
+ notifyOnConnect,
16340
+ initialScheduleId: scheduleId,
16341
+ insertAfter: widgetId
16342
+ }
16343
+ )
16344
+ );
16345
+ });
16346
+ } catch (err) {
16347
+ console.error("[ow:scheduling] render threw", err);
16348
+ container.remove();
16349
+ return false;
16350
+ }
16095
16351
  const tracker = getSectionsTracker();
16096
16352
  let sections = [];
16097
16353
  try {
@@ -16099,10 +16355,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
16099
16355
  } catch {
16100
16356
  }
16101
16357
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
16102
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
16358
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
16103
16359
  sections.push({
16104
16360
  type: "scheduling",
16105
- insertAfter: effectiveInsertAfter,
16361
+ insertAfter: widgetId,
16362
+ anchorId,
16363
+ beforeId: beforeId ?? null,
16106
16364
  pagePath: window.location.pathname,
16107
16365
  ...scheduleId ? { scheduleId } : {}
16108
16366
  });
@@ -16116,7 +16374,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
16116
16374
  for (let i = pending.length - 1; i >= 0; i--) {
16117
16375
  const entry = pending[i];
16118
16376
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
16119
- if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId ?? null)) {
16377
+ const { anchorId, beforeId } = resolveEntryAnchor(entry);
16378
+ if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
16120
16379
  pending.splice(i, 1);
16121
16380
  }
16122
16381
  }
@@ -16280,6 +16539,11 @@ function applyLinkByKey(key, val) {
16280
16539
  hrefAnchors.forEach((el) => applyLinkHref(el, val));
16281
16540
  }
16282
16541
  }
16542
+ function isInsideLinkEditor(target) {
16543
+ return Boolean(
16544
+ 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"]')
16545
+ );
16546
+ }
16283
16547
  function isInsideFloatingPanel(target) {
16284
16548
  return Boolean(target.closest("[data-ohw-floating-panel]"));
16285
16549
  }
@@ -16287,11 +16551,6 @@ function isPointOverFloatingPanel(clientX, clientY) {
16287
16551
  const el = document.elementFromPoint(clientX, clientY);
16288
16552
  return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
16289
16553
  }
16290
- function isInsideLinkEditor(target) {
16291
- return Boolean(
16292
- 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"]')
16293
- );
16294
- }
16295
16554
  function getHrefKeyFromElement(el) {
16296
16555
  if (!el) return null;
16297
16556
  const anchor = el.closest("[data-ohw-href-key]");
@@ -16451,6 +16710,7 @@ function isFooterAddItemDisabled(selected) {
16451
16710
  }
16452
16711
  function resolveFooterColumnSelectionTarget(target, clientX, clientY) {
16453
16712
  if (getNavigationItemAnchor(target)) return null;
16713
+ if (target.closest("[data-ohw-editable]")) return null;
16454
16714
  const column = getFooterColumn(target);
16455
16715
  if (!column) return null;
16456
16716
  if (isPointOverNavItem(column, clientX, clientY)) return null;
@@ -16560,7 +16820,7 @@ function getNavigationSelectionParent(el) {
16560
16820
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
16561
16821
  return getFooterLinksContainer();
16562
16822
  }
16563
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
16823
+ 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)) {
16564
16824
  return getNavigationRoot(el);
16565
16825
  }
16566
16826
  return null;
@@ -16592,6 +16852,12 @@ function collectNavigationItemSiblingHintRects(selected) {
16592
16852
  return [];
16593
16853
  }).filter((el) => isNavigationItem2(el) && el !== selected).map((el) => el.getBoundingClientRect());
16594
16854
  }
16855
+ if (selected.closest('footer, [data-ohw-section="footer"]')) {
16856
+ const group = findFooterItemGroup2(selected);
16857
+ if (group) {
16858
+ return Array.from(group.querySelectorAll("[data-ohw-href-key]")).filter((el) => isNavigationItem2(el) && el !== selected).map((el) => el.getBoundingClientRect());
16859
+ }
16860
+ }
16595
16861
  return listNavigationItems().filter((el) => el !== selected && !el.closest("[data-ohw-nav-children]")).map((el) => el.getBoundingClientRect());
16596
16862
  }
16597
16863
  function placeCaretAtPoint(el, x, y) {
@@ -16776,7 +17042,6 @@ var ICONS = {
16776
17042
  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"/>',
16777
17043
  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"/>'
16778
17044
  };
16779
- var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
16780
17045
  var SELECTION_CHROME_GAP2 = 4;
16781
17046
  var TOOLBAR_STROKE_GAP2 = 4;
16782
17047
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -17160,6 +17425,7 @@ function StateToggle({
17160
17425
  );
17161
17426
  }
17162
17427
  var contentCache = /* @__PURE__ */ new Map();
17428
+ var fetchedContentPaths = /* @__PURE__ */ new Set();
17163
17429
  var brandingCache = /* @__PURE__ */ new Map();
17164
17430
  var OHW_LOADER_STYLE = {
17165
17431
  position: "fixed",
@@ -17281,19 +17547,37 @@ function MadeWithOhhWells() {
17281
17547
  }
17282
17548
  );
17283
17549
  }
17284
- var OHW_LOADER_PREHYDRATE_SCRIPT = `(function(){try{var p=location.hostname.split(".");var fromHost=p.length>=3&&p[0]!=="www"?p[0]:"";var fromQuery=new URLSearchParams(location.search).get("subdomain")||"";if(!fromHost&&!fromQuery)return;var e=document.getElementById("ohw-loader");if(e)e.style.display="flex"}catch(e){}})();`;
17550
+ var OHW_LOADER_PREHYDRATE_SCRIPT = `(function(){try{var fromGlobal=typeof window!=="undefined"&&window.__OHW_SUBDOMAIN__?String(window.__OHW_SUBDOMAIN__):"";var p=location.hostname.split(".");var fromHost=location.hostname.endsWith(".ohhwells.site")&&p[0]&&p[0]!=="www"?p[0]:"";var fromQuery=new URLSearchParams(location.search).get("subdomain")||"";var fromCookie=(document.cookie.match(/(?:^|; )ohw-subdomain=([^;]*)/)||[])[1];fromCookie=fromCookie?decodeURIComponent(fromCookie):"";if(!fromGlobal&&!fromHost&&!fromQuery&&!fromCookie)return;var e=document.getElementById("ohw-loader");if(e)e.style.display="flex"}catch(e){}})();`;
17551
+ function subdomainFromCookie() {
17552
+ if (typeof document === "undefined") return "";
17553
+ const match = document.cookie.match(/(?:^|; )ohw-subdomain=([^;]*)/);
17554
+ return match ? decodeURIComponent(match[1]) : "";
17555
+ }
17285
17556
  function resolveSubdomain(subdomainFromQuery) {
17557
+ if (typeof window !== "undefined") {
17558
+ const fromGlobal = window.__OHW_SUBDOMAIN__;
17559
+ if (fromGlobal) return fromGlobal;
17560
+ }
17286
17561
  if (subdomainFromQuery) return subdomainFromQuery;
17287
17562
  if (typeof window !== "undefined") {
17288
- const parts = window.location.hostname.split(".");
17289
- if (parts.length >= 3 && parts[0] !== "www") return parts[0];
17563
+ const host = window.location.hostname.toLowerCase();
17564
+ if (host.endsWith(".ohhwells.site")) {
17565
+ const label = host.slice(0, -".ohhwells.site".length).split(".").pop();
17566
+ if (label && label !== "www") return label;
17567
+ }
17568
+ const fromCookie = subdomainFromCookie();
17569
+ if (fromCookie) return fromCookie;
17570
+ const isPlatformHost = host.endsWith(".ohhwells.site") || host === "localhost" || host.endsWith(".localhost") || host.endsWith(".vercel.app");
17571
+ if (!isPlatformHost) return "";
17290
17572
  }
17291
17573
  const siteUrl = process.env.NEXT_PUBLIC_SITE_URL;
17292
17574
  if (siteUrl) {
17293
17575
  try {
17294
17576
  const host = new URL(siteUrl).hostname;
17295
- const siteParts = host.split(".");
17296
- if (siteParts.length >= 3 && siteParts[0] !== "www") return siteParts[0];
17577
+ if (host.endsWith(".ohhwells.site")) {
17578
+ const label = host.slice(0, -".ohhwells.site".length).split(".").pop();
17579
+ if (label && label !== "www") return label;
17580
+ }
17297
17581
  } catch {
17298
17582
  }
17299
17583
  }
@@ -17331,7 +17615,26 @@ function OhhwellsBridge() {
17331
17615
  };
17332
17616
  }, []);
17333
17617
  const subdomainFromQuery = searchParams.get("subdomain");
17334
- const subdomain = resolveSubdomain(subdomainFromQuery);
17618
+ const [subdomain, setSubdomain] = useState14(() => resolveSubdomain(subdomainFromQuery));
17619
+ useLayoutEffect5(() => {
17620
+ const next = resolveSubdomain(subdomainFromQuery);
17621
+ if (next !== subdomain) setSubdomain(next);
17622
+ if (next) return;
17623
+ const host = window.location.hostname.toLowerCase();
17624
+ if (host.endsWith(".ohhwells.site")) return;
17625
+ const t = window.setTimeout(() => {
17626
+ const retry = resolveSubdomain(subdomainFromQuery);
17627
+ if (retry) setSubdomain(retry);
17628
+ }, 0);
17629
+ return () => window.clearTimeout(t);
17630
+ }, [subdomainFromQuery, subdomain]);
17631
+ useEffect13(() => {
17632
+ if (!subdomainFromQuery || typeof window === "undefined") return;
17633
+ const url = new URL(window.location.href);
17634
+ if (!url.searchParams.has("subdomain")) return;
17635
+ url.searchParams.delete("subdomain");
17636
+ window.history.replaceState(null, "", url.pathname + url.search + url.hash);
17637
+ }, [subdomainFromQuery]);
17335
17638
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
17336
17639
  useSavedLinkNavigation(isEditMode);
17337
17640
  const postToParent2 = useCallback8((data) => {
@@ -17708,13 +18011,6 @@ function OhhwellsBridge() {
17708
18011
  const [isItemDragging, setIsItemDragging] = useState14(false);
17709
18012
  const [isFooterFrameSelection, setIsFooterFrameSelection] = useState14(false);
17710
18013
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
17711
- const [floatingPanel, setFloatingPanel] = useState14(null);
17712
- const floatingPanelOpenRef = useRef10(false);
17713
- floatingPanelOpenRef.current = floatingPanel !== null;
17714
- const [floatingPanelPos, setFloatingPanelPos] = useState14(null);
17715
- const [logoSizeDraft, setLogoSizeDraft] = useState14(null);
17716
- const [editorViewport, setEditorViewport] = useState14("desktop");
17717
- const [parentScrollSnap, setParentScrollSnap] = useState14(null);
17718
18014
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState14(null);
17719
18015
  const [footerHeadingVisible, setFooterHeadingVisible] = useState14(null);
17720
18016
  const footerDragRef = useRef10(null);
@@ -17731,7 +18027,15 @@ function OhhwellsBridge() {
17731
18027
  const aiSectionsRef = useRef10("");
17732
18028
  const brandKitRef = useRef10("");
17733
18029
  const stylesRef = useRef10("");
18030
+ const appliedDrawerRef = useRef10(null);
17734
18031
  const pendingDeleteUndoRef = useRef10(null);
18032
+ const [floatingPanel, setFloatingPanel] = useState14(null);
18033
+ const floatingPanelOpenRef = useRef10(false);
18034
+ const setFloatingPanelRef = useRef10(setFloatingPanel);
18035
+ const [floatingPanelPos, setFloatingPanelPos] = useState14(null);
18036
+ const [logoSizeDraft, setLogoSizeDraft] = useState14(null);
18037
+ const [editorViewport, setEditorViewport] = useState14("desktop");
18038
+ const [parentScrollSnap, setParentScrollSnap] = useState14(null);
17735
18039
  const [sitePages, setSitePages] = useState14([]);
17736
18040
  const [sectionsByPath, setSectionsByPath] = useState14({});
17737
18041
  const sectionsPrefetchGenRef = useRef10(0);
@@ -17740,7 +18044,18 @@ function OhhwellsBridge() {
17740
18044
  const linkPopoverOpenRef = useRef10(false);
17741
18045
  const linkPopoverGraceUntilRef = useRef10(0);
17742
18046
  setLinkPopoverRef.current = setLinkPopover;
18047
+ setFloatingPanelRef.current = setFloatingPanel;
17743
18048
  linkPopoverSessionRef.current = linkPopover;
18049
+ floatingPanelOpenRef.current = Boolean(floatingPanel);
18050
+ useEffect13(() => {
18051
+ const syncViewport = () => {
18052
+ const next = window.innerWidth <= 480 ? "mobile" : "desktop";
18053
+ setEditorViewport((prev) => prev === next ? prev : next);
18054
+ };
18055
+ syncViewport();
18056
+ window.addEventListener("resize", syncViewport);
18057
+ return () => window.removeEventListener("resize", syncViewport);
18058
+ }, []);
17744
18059
  const {
17745
18060
  navDragRef,
17746
18061
  navPointerDragRef,
@@ -19076,6 +19391,7 @@ function OhhwellsBridge() {
19076
19391
  }
19077
19392
  if (typeof content[STYLE_STORE_KEY] === "string") {
19078
19393
  stylesRef.current = content[STYLE_STORE_KEY];
19394
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
19079
19395
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
19080
19396
  }
19081
19397
  applyBrandChrome(content);
@@ -19083,11 +19399,11 @@ function OhhwellsBridge() {
19083
19399
  for (const [key, val] of Object.entries(content)) {
19084
19400
  if (key === "__ohw_sections") continue;
19085
19401
  if (key === AI_SECTIONS_KEY) continue;
19402
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
19403
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
19086
19404
  if (key === BRAND_KIT_KEY) continue;
19087
19405
  if (key === STYLE_STORE_KEY) continue;
19088
19406
  if (BRAND_CHROME_KEYS.has(key)) continue;
19089
- if (key === LOGO_PLACEHOLDER_KEY) continue;
19090
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
19091
19407
  if (applyVideoSettingNode(key, val)) continue;
19092
19408
  if (applyCarouselNode(key, val)) continue;
19093
19409
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -19158,7 +19474,9 @@ function OhhwellsBridge() {
19158
19474
  let cancelled = false;
19159
19475
  setFetchState("loading");
19160
19476
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
19161
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
19477
+ const initialPath = pathname;
19478
+ fetchedContentPaths.add(`${subdomain}::${initialPath}`);
19479
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
19162
19480
  if (cancelled) return;
19163
19481
  const content = data?.content ?? {};
19164
19482
  const branding = Boolean(data?.showBranding);
@@ -19267,7 +19585,9 @@ function OhhwellsBridge() {
19267
19585
  }, [isEditMode]);
19268
19586
  useEffect13(() => {
19269
19587
  if (isEditMode || fetchState !== "done") return;
19588
+ console.log("env", process.env.NEXT_PUBLIC_FLOWOPS_API_URL);
19270
19589
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
19590
+ console.log({ apiUrl, subdomain });
19271
19591
  bindPublishedForms(apiUrl, subdomain, contentCache.get(subdomain) ?? {});
19272
19592
  }, [isEditMode, fetchState, subdomain]);
19273
19593
  useEffect13(() => {
@@ -19292,16 +19612,17 @@ function OhhwellsBridge() {
19292
19612
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
19293
19613
  }
19294
19614
  if (typeof content[STYLE_STORE_KEY] === "string") {
19615
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
19295
19616
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
19296
19617
  }
19297
19618
  for (const [key, val] of Object.entries(content)) {
19298
19619
  if (key === "__ohw_sections") continue;
19299
19620
  if (key === AI_SECTIONS_KEY) continue;
19621
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
19622
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
19300
19623
  if (key === BRAND_KIT_KEY) continue;
19301
19624
  if (key === STYLE_STORE_KEY) continue;
19302
19625
  if (BRAND_CHROME_KEYS.has(key)) continue;
19303
- if (key === LOGO_PLACEHOLDER_KEY) continue;
19304
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
19305
19626
  if (applyVideoSettingNode(key, val)) continue;
19306
19627
  if (applyCarouselNode(key, val)) continue;
19307
19628
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -19352,6 +19673,17 @@ function OhhwellsBridge() {
19352
19673
  debounceTimer = setTimeout(applyFromCache, 150);
19353
19674
  };
19354
19675
  applyFromCache();
19676
+ const pathCacheKey = `${subdomain}::${pathname}`;
19677
+ if (!fetchedContentPaths.has(pathCacheKey)) {
19678
+ fetchedContentPaths.add(pathCacheKey);
19679
+ const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
19680
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
19681
+ if (!data?.content) return;
19682
+ contentCache.set(subdomain, data.content);
19683
+ applyFromCache();
19684
+ }).catch(() => {
19685
+ });
19686
+ }
19355
19687
  observer = new MutationObserver(scheduleApply);
19356
19688
  observer.observe(document.body, { childList: true, subtree: true });
19357
19689
  return () => {
@@ -19396,6 +19728,17 @@ function OhhwellsBridge() {
19396
19728
  if (!isEditMode) return;
19397
19729
  initSectionInstancesFromContent(editContentRef.current, pathname);
19398
19730
  }, [pathname, isEditMode]);
19731
+ useEffect13(() => {
19732
+ if (!isEditMode) return;
19733
+ const observer = new MutationObserver(() => {
19734
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
19735
+ if (!drawer || drawer === appliedDrawerRef.current) return;
19736
+ appliedDrawerRef.current = drawer;
19737
+ applyStylesToDom(parseStyleStore(stylesRef.current));
19738
+ });
19739
+ observer.observe(document.body, { childList: true, subtree: true });
19740
+ return () => observer.disconnect();
19741
+ }, [isEditMode, pathname]);
19399
19742
  useEffect13(() => {
19400
19743
  const contentForNav = () => {
19401
19744
  if (isEditMode) return editContentRef.current;
@@ -19487,26 +19830,11 @@ function OhhwellsBridge() {
19487
19830
  const t2 = setTimeout(measure, 500);
19488
19831
  const ro = new ResizeObserver(schedule);
19489
19832
  ro.observe(document.body);
19490
- let lastWidth = window.innerWidth;
19491
- let resizeTimers = [];
19492
- const clearResizeTimers = () => {
19493
- resizeTimers.forEach(clearTimeout);
19494
- resizeTimers = [];
19495
- };
19496
- const handleResize = () => {
19497
- if (window.innerWidth === lastWidth) return;
19498
- lastWidth = window.innerWidth;
19499
- clearResizeTimers();
19500
- resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
19501
- };
19502
- window.addEventListener("resize", handleResize);
19503
19833
  return () => {
19504
19834
  clearTimeout(t1);
19505
19835
  clearTimeout(t2);
19506
19836
  if (raf != null) cancelAnimationFrame(raf);
19507
19837
  ro.disconnect();
19508
- clearResizeTimers();
19509
- window.removeEventListener("resize", handleResize);
19510
19838
  };
19511
19839
  }, [pathname, isEditMode, postToParent2]);
19512
19840
  useEffect13(() => {
@@ -19801,9 +20129,6 @@ function OhhwellsBridge() {
19801
20129
  if (target.closest("[data-ohw-state-toggle]")) return;
19802
20130
  if (target.closest("[data-ohw-max-badge]")) return;
19803
20131
  if (isInsideLinkEditor(target)) return;
19804
- if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
19805
- clearMediaSelectionRef.current();
19806
- }
19807
20132
  if (isInsideFloatingPanel(target)) return;
19808
20133
  if (target.closest("[data-ohw-form-toolbar]")) return;
19809
20134
  if (target.closest(
@@ -19811,6 +20136,9 @@ function OhhwellsBridge() {
19811
20136
  )) {
19812
20137
  return;
19813
20138
  }
20139
+ if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
20140
+ clearMediaSelectionRef.current();
20141
+ }
19814
20142
  {
19815
20143
  const formEl = getFormElement(target);
19816
20144
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -19974,14 +20302,6 @@ function OhhwellsBridge() {
19974
20302
  }
19975
20303
  const clickedButton = findClosestButtonLike(target);
19976
20304
  const buttonOnMedia = Boolean(clickedButton && isMediaEditable(editable) && editable.contains(clickedButton));
19977
- console.log("[click-debug]", {
19978
- editableType: editable.dataset.ohwEditable,
19979
- editableTag: editable.tagName,
19980
- targetTag: target.tagName,
19981
- clickedButtonTag: clickedButton?.tagName ?? null,
19982
- buttonOnMedia,
19983
- isMediaEditableEditable: isMediaEditable(editable)
19984
- });
19985
20305
  if (isMediaEditable(editable) && !buttonOnMedia) {
19986
20306
  e.preventDefault();
19987
20307
  e.stopPropagation();
@@ -20008,11 +20328,6 @@ function OhhwellsBridge() {
20008
20328
  const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
20009
20329
  const hrefCtx = getHrefKeyFromElement(hrefLookupTarget);
20010
20330
  const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
20011
- console.log("[click-debug 2]", {
20012
- hrefLookupTargetTag: hrefLookupTarget.tagName,
20013
- hrefCtx: hrefCtx ? { key: hrefCtx.key } : null,
20014
- navAnchorTag: navAnchor?.tagName ?? null
20015
- });
20016
20331
  if (navAnchor) {
20017
20332
  e.preventDefault();
20018
20333
  e.stopPropagation();
@@ -20054,7 +20369,7 @@ function OhhwellsBridge() {
20054
20369
  return;
20055
20370
  }
20056
20371
  const footerColClick = target.closest("[data-ohw-footer-col]");
20057
- if (footerColClick) {
20372
+ if (footerColClick && !target.closest("[data-ohw-editable]")) {
20058
20373
  e.preventDefault();
20059
20374
  e.stopPropagation();
20060
20375
  selectFrameRef.current(footerColClick);
@@ -20157,6 +20472,19 @@ function OhhwellsBridge() {
20157
20472
  const handleMouseOver = (e) => {
20158
20473
  if (document.documentElement.hasAttribute("data-ohw-section-picking")) return;
20159
20474
  const target = e.target;
20475
+ if (isNavDrawerOpen() && isPointOutsideOpenDrawer(e.clientX, e.clientY)) {
20476
+ document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
20477
+ hoveredItemElRef.current = null;
20478
+ setHoveredItemRect(null);
20479
+ hoveredNavContainerRef.current = null;
20480
+ setHoveredNavContainerRect(null);
20481
+ formHoverElRef.current = null;
20482
+ setFormHoverRect(null);
20483
+ siblingHintElRef.current = null;
20484
+ setSiblingHintRect(null);
20485
+ setSiblingHintRects([]);
20486
+ return;
20487
+ }
20160
20488
  if (linkPopoverOpenRef.current) {
20161
20489
  hoveredItemElRef.current = null;
20162
20490
  setHoveredItemRect(null);
@@ -20182,6 +20510,9 @@ function OhhwellsBridge() {
20182
20510
  setHoveredItemRect(null);
20183
20511
  hoveredNavContainerRef.current = null;
20184
20512
  setHoveredNavContainerRect(null);
20513
+ siblingHintElRef.current = null;
20514
+ setSiblingHintRect(null);
20515
+ setSiblingHintRects([]);
20185
20516
  return;
20186
20517
  }
20187
20518
  {
@@ -20213,7 +20544,7 @@ function OhhwellsBridge() {
20213
20544
  const navContainer = target.closest("[data-ohw-nav-container]");
20214
20545
  if (!navContainer) {
20215
20546
  const footerLinks = target.closest("[data-ohw-footer-links], .rb-footer-links") ?? null;
20216
- if (footerLinks && selected2 !== footerLinks && !getNavigationItemAnchor(target) && !target.closest("[data-ohw-footer-col], [data-ohw-footer-column]")) {
20547
+ if (footerLinks && selected2 !== footerLinks && !getNavigationItemAnchor(target) && !target.closest("[data-ohw-footer-col], [data-ohw-footer-column]") && !target.closest("[data-ohw-editable]")) {
20217
20548
  hoveredNavContainerRef.current = footerLinks;
20218
20549
  setHoveredNavContainerRect(footerLinks.getBoundingClientRect());
20219
20550
  hoveredItemElRef.current = null;
@@ -20251,7 +20582,7 @@ function OhhwellsBridge() {
20251
20582
  return;
20252
20583
  }
20253
20584
  const footerCol = target.closest("[data-ohw-footer-col]") ?? target.closest("[data-ohw-footer-column]");
20254
- if (footerCol) {
20585
+ if (footerCol && !target.closest("[data-ohw-editable]")) {
20255
20586
  hoveredNavContainerRef.current = null;
20256
20587
  setHoveredNavContainerRect(null);
20257
20588
  if (selectedElRef.current === footerCol) return;
@@ -20302,7 +20633,6 @@ function OhhwellsBridge() {
20302
20633
  hoveredNavContainerRef.current = null;
20303
20634
  setHoveredNavContainerRect(null);
20304
20635
  hoveredItemElRef.current = editable;
20305
- setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
20306
20636
  }
20307
20637
  }
20308
20638
  }
@@ -20599,7 +20929,7 @@ function OhhwellsBridge() {
20599
20929
  }
20600
20930
  };
20601
20931
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
20602
- if (linkPopoverOpenRef.current) {
20932
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
20603
20933
  if (hoveredImageRef.current) {
20604
20934
  hoveredImageRef.current = null;
20605
20935
  hoveredImageHasTextOverlapRef.current = false;
@@ -20972,7 +21302,31 @@ function OhhwellsBridge() {
20972
21302
  };
20973
21303
  const handleMouseMove = (e) => {
20974
21304
  const { clientX, clientY } = e;
20975
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
21305
+ if (pointOwnedByFloatingPanel(clientX, clientY)) {
21306
+ dismissImageHover();
21307
+ clearImageHover();
21308
+ return;
21309
+ }
21310
+ if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) {
21311
+ document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
21312
+ formHoverElRef.current = null;
21313
+ setFormHoverRect(null);
21314
+ hoveredItemElRef.current = null;
21315
+ setHoveredItemRect(null);
21316
+ hoveredNavContainerRef.current = null;
21317
+ setHoveredNavContainerRect(null);
21318
+ dismissImageHover();
21319
+ if (hoveredGapRef.current) {
21320
+ hoveredGapRef.current = null;
21321
+ setSectionGap(null);
21322
+ }
21323
+ if (activeStateElRef.current) {
21324
+ activeStateElRef.current.removeAttribute("data-ohw-state-hovered");
21325
+ activeStateElRef.current = null;
21326
+ setToggleState(null);
21327
+ }
21328
+ return;
21329
+ }
20976
21330
  if (isOverEditorChrome(clientX, clientY)) {
20977
21331
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
20978
21332
  formHoverElRef.current = null;
@@ -20981,7 +21335,12 @@ function OhhwellsBridge() {
20981
21335
  setHoveredItemRect(null);
20982
21336
  hoveredNavContainerRef.current = null;
20983
21337
  setHoveredNavContainerRect(null);
21338
+ siblingHintElRef.current = null;
21339
+ setSiblingHintRect(null);
21340
+ setSiblingHintRects([]);
20984
21341
  dismissImageHover();
21342
+ clearImageHover();
21343
+ setSectionGap(null);
20985
21344
  return;
20986
21345
  }
20987
21346
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -20993,7 +21352,12 @@ function OhhwellsBridge() {
20993
21352
  if (e.data?.type !== "ow:pointer-sync") return;
20994
21353
  const { clientX, clientY } = e.data;
20995
21354
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
20996
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
21355
+ if (pointOwnedByFloatingPanel(clientX, clientY)) {
21356
+ dismissImageHover();
21357
+ clearImageHover();
21358
+ return;
21359
+ }
21360
+ if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) return;
20997
21361
  if (probeSocialsRowAt(clientX, clientY)) return;
20998
21362
  probeSectionGapAt(clientX, clientY);
20999
21363
  probeImageAt(clientX, clientY);
@@ -21332,6 +21696,7 @@ function OhhwellsBridge() {
21332
21696
  }
21333
21697
  if (typeof content[STYLE_STORE_KEY] === "string") {
21334
21698
  stylesRef.current = content[STYLE_STORE_KEY];
21699
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
21335
21700
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
21336
21701
  }
21337
21702
  applyBrandChrome(content);
@@ -21343,11 +21708,11 @@ function OhhwellsBridge() {
21343
21708
  continue;
21344
21709
  }
21345
21710
  if (key === AI_SECTIONS_KEY) continue;
21711
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
21712
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
21346
21713
  if (key === BRAND_KIT_KEY) continue;
21347
21714
  if (key === STYLE_STORE_KEY) continue;
21348
21715
  if (BRAND_CHROME_KEYS.has(key)) continue;
21349
- if (key === LOGO_PLACEHOLDER_KEY) continue;
21350
- if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
21351
21716
  if (applyVideoSettingNode(key, val)) continue;
21352
21717
  if (applyCarouselNode(key, val)) continue;
21353
21718
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -21572,10 +21937,26 @@ function OhhwellsBridge() {
21572
21937
  const handleGetBrand = (e) => {
21573
21938
  if (e.data?.type !== "ow:get-brand") return;
21574
21939
  const template = deriveTemplateBrand();
21575
- const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
21940
+ const fallback = template ?? (() => {
21941
+ const fonts = deriveTemplateFonts();
21942
+ return fonts ? { palette: AI_DEFAULT_BRAND.palette, fonts } : null;
21943
+ })();
21944
+ const value = brandKitRef.current || (fallback ? JSON.stringify(fallback) : "");
21576
21945
  postToParentRef.current({ type: "ow:brand-value", value });
21577
21946
  };
21578
21947
  window.addEventListener("message", handleGetBrand);
21948
+ const handleGetTemplateFonts = (e) => {
21949
+ if (e.data?.type !== "ow:get-template-fonts") return;
21950
+ const fonts = deriveTemplateFonts();
21951
+ postToParentRef.current({ type: "ow:template-fonts-value", value: fonts ? JSON.stringify(fonts) : "" });
21952
+ };
21953
+ window.addEventListener("message", handleGetTemplateFonts);
21954
+ const handleGetTemplateBrand = (e) => {
21955
+ if (e.data?.type !== "ow:get-template-brand") return;
21956
+ const brand = deriveTemplateBrand();
21957
+ postToParentRef.current({ type: "ow:template-brand-value", value: brand ? JSON.stringify(brand) : "" });
21958
+ };
21959
+ window.addEventListener("message", handleGetTemplateBrand);
21579
21960
  const handleMoveSection = (e) => {
21580
21961
  if (e.data?.type !== "ow:move-section") return;
21581
21962
  const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
@@ -21768,6 +22149,12 @@ function OhhwellsBridge() {
21768
22149
  closeLinkPopoverRef.current();
21769
22150
  return;
21770
22151
  }
22152
+ if (floatingPanelOpenRef.current) {
22153
+ setFloatingPanelRef.current(null);
22154
+ deselectRef.current();
22155
+ deactivateRef.current();
22156
+ return;
22157
+ }
21771
22158
  deselectRef.current();
21772
22159
  deactivateRef.current();
21773
22160
  clearMediaSelectionRef.current();
@@ -22056,8 +22443,12 @@ function OhhwellsBridge() {
22056
22443
  if (inserted) {
22057
22444
  const tracker = getSectionsTracker();
22058
22445
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
22059
- const h = document.body.scrollHeight;
22060
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
22446
+ const reportHeight = () => {
22447
+ const h = document.body.scrollHeight;
22448
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
22449
+ };
22450
+ reportHeight();
22451
+ setTimeout(reportHeight, 500);
22061
22452
  }
22062
22453
  };
22063
22454
  const handleSwitchSchedule = (e) => {
@@ -22478,15 +22869,17 @@ function OhhwellsBridge() {
22478
22869
  window.removeEventListener("message", handleAiSetBrand);
22479
22870
  window.removeEventListener("message", handleAiSetStyles);
22480
22871
  window.removeEventListener("message", handleGetBrand);
22872
+ window.removeEventListener("message", handleGetTemplateFonts);
22873
+ window.removeEventListener("message", handleGetTemplateBrand);
22481
22874
  window.removeEventListener("message", handleMoveSection);
22482
22875
  window.removeEventListener("message", handlePanelDragging);
22483
22876
  window.removeEventListener("message", handleDeleteSection);
22484
22877
  window.removeEventListener("message", handleDuplicateSection);
22485
22878
  window.removeEventListener("message", handleDeactivate);
22486
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
22487
22879
  window.removeEventListener("message", handleToastAction);
22488
22880
  window.removeEventListener("message", handleFormCount);
22489
22881
  window.removeEventListener("message", handleUiEscape);
22882
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
22490
22883
  autoSaveTimers.current.forEach(clearTimeout);
22491
22884
  autoSaveTimers.current.clear();
22492
22885
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
@@ -22689,7 +23082,7 @@ function OhhwellsBridge() {
22689
23082
  postToParent2({
22690
23083
  type: "ow:ready",
22691
23084
  version: "1",
22692
- bridgeVersion: "0.1.111",
23085
+ bridgeVersion: "0.1.113",
22693
23086
  path: pathname,
22694
23087
  nodes: collectEditableNodes(editContentRef.current),
22695
23088
  sections
@@ -23305,7 +23698,7 @@ function OhhwellsBridge() {
23305
23698
  "span",
23306
23699
  {
23307
23700
  "data-ohw-form-count": "",
23308
- 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",
23701
+ 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",
23309
23702
  children: formPickCount
23310
23703
  }
23311
23704
  )
@@ -23543,11 +23936,11 @@ function OhhwellsBridge() {
23543
23936
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
23544
23937
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
23545
23938
  children: [
23546
- /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
23939
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-bridge-primary", style: { height: 3 } }),
23547
23940
  /* @__PURE__ */ jsx33(
23548
23941
  Badge,
23549
23942
  {
23550
- 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",
23943
+ 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",
23551
23944
  onClick: () => {
23552
23945
  window.parent.postMessage(
23553
23946
  {
@@ -23561,7 +23954,7 @@ function OhhwellsBridge() {
23561
23954
  children: "Add Section"
23562
23955
  }
23563
23956
  ),
23564
- /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } })
23957
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-bridge-primary", style: { height: 3 } })
23565
23958
  ]
23566
23959
  }
23567
23960
  ),
@@ -23609,6 +24002,59 @@ function OhhwellsBridge() {
23609
24002
  ) : null
23610
24003
  ] });
23611
24004
  }
24005
+
24006
+ // src/ui/EmptySection.tsx
24007
+ import Link3 from "next/link";
24008
+ import { Fragment as Fragment9, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
24009
+ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
24010
+ return /* @__PURE__ */ jsxs21(Fragment9, { children: [
24011
+ /* @__PURE__ */ jsx34(
24012
+ "p",
24013
+ {
24014
+ style: {
24015
+ fontFamily: "var(--brand-font-body)",
24016
+ fontSize: "0.75rem",
24017
+ fontWeight: 500,
24018
+ letterSpacing: "0.15em",
24019
+ textTransform: "uppercase",
24020
+ color: "var(--brand-accent)",
24021
+ marginBottom: "1.5rem"
24022
+ },
24023
+ children: /* @__PURE__ */ jsx34(Link3, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx34("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
24024
+ }
24025
+ ),
24026
+ /* @__PURE__ */ jsx34(
24027
+ "h1",
24028
+ {
24029
+ style: {
24030
+ fontFamily: "var(--brand-font-heading)",
24031
+ fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
24032
+ lineHeight: 1.1,
24033
+ letterSpacing: "-0.025em",
24034
+ color: "var(--brand-text)",
24035
+ marginBottom: "1rem"
24036
+ },
24037
+ ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
24038
+ children: title
24039
+ }
24040
+ ),
24041
+ /* @__PURE__ */ jsx34(
24042
+ "p",
24043
+ {
24044
+ style: {
24045
+ fontFamily: "var(--brand-font-body)",
24046
+ fontSize: "1rem",
24047
+ lineHeight: 1.7,
24048
+ fontWeight: 300,
24049
+ color: "var(--brand-text-muted)",
24050
+ maxWidth: "340px"
24051
+ },
24052
+ ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
24053
+ children: "This page doesn't have any content yet."
24054
+ }
24055
+ )
24056
+ ] });
24057
+ }
23612
24058
  export {
23613
24059
  AI_DEFAULT_BRAND,
23614
24060
  AI_TREE_SCHEMA_VERSIONS,
@@ -23625,6 +24071,7 @@ export {
23625
24071
  DropdownMenuItem,
23626
24072
  DropdownMenuSeparator,
23627
24073
  DropdownMenuTrigger,
24074
+ EmptySection,
23628
24075
  ItemActionToolbar,
23629
24076
  ItemInteractionLayer,
23630
24077
  LinkEditorPanel,