@ohhwells/bridge 0.1.112 → 0.1.113-next.292

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 : override.sectionBackgroundColor;
652
+ const textOverride = isDrawerMarker ? override.navbarMobileTextColor ?? override.textColor : 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(
@@ -9813,7 +9995,7 @@ function SectionTreeItem({
9813
9995
  /* @__PURE__ */ jsx20(
9814
9996
  "div",
9815
9997
  {
9816
- className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
9998
+ className: "-mr-px h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
9817
9999
  "aria-hidden": true
9818
10000
  }
9819
10001
  ),
@@ -9832,7 +10014,7 @@ function SectionTreeItem({
9832
10014
  className: cn(
9833
10015
  "flex h-9 min-w-0 flex-1 items-center gap-2 rounded-md border border-border bg-background p-3",
9834
10016
  interactive && "cursor-pointer hover:bg-muted/30",
9835
- interactive && selected && "border-primary"
10017
+ interactive && selected && "border-bridge-primary"
9836
10018
  ),
9837
10019
  children: [
9838
10020
  /* @__PURE__ */ jsx20(
@@ -9962,7 +10144,7 @@ function UrlOrPageInput({
9962
10144
  };
9963
10145
  const fieldClassName = cn(
9964
10146
  "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"
10147
+ 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
10148
  );
9967
10149
  return /* @__PURE__ */ jsxs13("div", { className: "flex w-full flex-col gap-2 p-0", children: [
9968
10150
  /* @__PURE__ */ jsx23(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
@@ -13869,6 +14051,7 @@ function readLogoSizeState(content, placement) {
13869
14051
  function getLogoElement(el) {
13870
14052
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
13871
14053
  if (marked) return marked;
14054
+ if (el.closest('[data-ohw-editable="icon"]')) return null;
13872
14055
  const root = el.closest("nav, [data-ohw-nav-root], footer");
13873
14056
  if (!root) return null;
13874
14057
  const anchor = el.closest("a");
@@ -14162,7 +14345,7 @@ function DisplaySwitch({
14162
14345
  onClick: () => onChange(!checked),
14163
14346
  className: cn(
14164
14347
  "relative h-5 w-9 shrink-0 rounded-full transition-colors",
14165
- checked ? "bg-primary" : "bg-primary-50",
14348
+ checked ? "bg-bridge-primary" : "bg-primary-50",
14166
14349
  disabled ? "cursor-default opacity-50" : "cursor-pointer"
14167
14350
  ),
14168
14351
  children: /* @__PURE__ */ jsx28(
@@ -14170,7 +14353,7 @@ function DisplaySwitch({
14170
14353
  {
14171
14354
  className: cn(
14172
14355
  "absolute top-0.5 h-4 w-4 rounded-full bg-white transition-all",
14173
- checked ? "left-[1.125rem]" : "left-0.5"
14356
+ checked ? "left-4.5" : "left-0.5"
14174
14357
  )
14175
14358
  }
14176
14359
  )
@@ -15805,7 +15988,7 @@ var badgeVariants = cva(
15805
15988
  {
15806
15989
  variants: {
15807
15990
  variant: {
15808
- default: "border-transparent bg-primary text-primary-foreground",
15991
+ default: "border-transparent bg-bridge-primary text-primary-foreground",
15809
15992
  secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15810
15993
  destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
15811
15994
  outline: "text-foreground"
@@ -15987,21 +16170,10 @@ function parseSchedulingInsertAfter(insertAfter) {
15987
16170
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
15988
16171
  };
15989
16172
  }
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;
16173
+ function resolveEntryAnchor(entry) {
16174
+ if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
16175
+ const parsed = parseSchedulingInsertAfter(entry.insertAfter);
16176
+ return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
16005
16177
  }
16006
16178
  function schedulingMountDepth(insertAfter) {
16007
16179
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -16018,8 +16190,7 @@ function getPageSchedulingEntries(raw) {
16018
16190
  }
16019
16191
  }
16020
16192
  function isSchedulingWidgetMissing(entry) {
16021
- const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
16022
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
16193
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
16023
16194
  }
16024
16195
  function hasMissingSchedulingWidgets(entries) {
16025
16196
  return entries.some(isSchedulingWidgetMissing);
@@ -16057,17 +16228,18 @@ function initSectionsFromContent(content, removeExisting = false, currentPath =
16057
16228
  } catch {
16058
16229
  }
16059
16230
  }
16060
- function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
16061
- const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
16062
- const sectionId = schedulingSectionId(effectiveInsertAfter);
16231
+ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
16232
+ const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
16233
+ const sectionId = schedulingSectionId(widgetId);
16063
16234
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
16064
- const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
16065
- if (!mountPoint) return false;
16235
+ const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
16236
+ if (!anchorEl) return false;
16237
+ const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
16066
16238
  const container = document.createElement("div");
16067
16239
  container.dataset.ohwSectionContainer = "scheduling";
16068
16240
  container.dataset.ohwInstance = sectionId;
16069
- if (insertBefore) {
16070
- const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
16241
+ if (beforeId) {
16242
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
16071
16243
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
16072
16244
  if (!beforePoint) return false;
16073
16245
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -16078,20 +16250,26 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
16078
16250
  }
16079
16251
  tail.insertAdjacentElement("afterend", container);
16080
16252
  }
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
- });
16253
+ try {
16254
+ const root = createRoot2(container);
16255
+ schedulingRoots.set(container, root);
16256
+ flushSync2(() => {
16257
+ root.render(
16258
+ /* @__PURE__ */ jsx33(
16259
+ SchedulingWidget,
16260
+ {
16261
+ notifyOnConnect,
16262
+ initialScheduleId: scheduleId,
16263
+ insertAfter: widgetId
16264
+ }
16265
+ )
16266
+ );
16267
+ });
16268
+ } catch (err) {
16269
+ console.error("[ow:scheduling] render threw", err);
16270
+ container.remove();
16271
+ return false;
16272
+ }
16095
16273
  const tracker = getSectionsTracker();
16096
16274
  let sections = [];
16097
16275
  try {
@@ -16099,10 +16277,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
16099
16277
  } catch {
16100
16278
  }
16101
16279
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
16102
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
16280
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
16103
16281
  sections.push({
16104
16282
  type: "scheduling",
16105
- insertAfter: effectiveInsertAfter,
16283
+ insertAfter: widgetId,
16284
+ anchorId,
16285
+ beforeId: beforeId ?? null,
16106
16286
  pagePath: window.location.pathname,
16107
16287
  ...scheduleId ? { scheduleId } : {}
16108
16288
  });
@@ -16116,7 +16296,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
16116
16296
  for (let i = pending.length - 1; i >= 0; i--) {
16117
16297
  const entry = pending[i];
16118
16298
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
16119
- if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId ?? null)) {
16299
+ const { anchorId, beforeId } = resolveEntryAnchor(entry);
16300
+ if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
16120
16301
  pending.splice(i, 1);
16121
16302
  }
16122
16303
  }
@@ -16280,6 +16461,11 @@ function applyLinkByKey(key, val) {
16280
16461
  hrefAnchors.forEach((el) => applyLinkHref(el, val));
16281
16462
  }
16282
16463
  }
16464
+ function isInsideLinkEditor(target) {
16465
+ return Boolean(
16466
+ 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"]')
16467
+ );
16468
+ }
16283
16469
  function isInsideFloatingPanel(target) {
16284
16470
  return Boolean(target.closest("[data-ohw-floating-panel]"));
16285
16471
  }
@@ -16287,11 +16473,6 @@ function isPointOverFloatingPanel(clientX, clientY) {
16287
16473
  const el = document.elementFromPoint(clientX, clientY);
16288
16474
  return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
16289
16475
  }
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
16476
  function getHrefKeyFromElement(el) {
16296
16477
  if (!el) return null;
16297
16478
  const anchor = el.closest("[data-ohw-href-key]");
@@ -16451,6 +16632,7 @@ function isFooterAddItemDisabled(selected) {
16451
16632
  }
16452
16633
  function resolveFooterColumnSelectionTarget(target, clientX, clientY) {
16453
16634
  if (getNavigationItemAnchor(target)) return null;
16635
+ if (target.closest("[data-ohw-editable]")) return null;
16454
16636
  const column = getFooterColumn(target);
16455
16637
  if (!column) return null;
16456
16638
  if (isPointOverNavItem(column, clientX, clientY)) return null;
@@ -16560,7 +16742,7 @@ function getNavigationSelectionParent(el) {
16560
16742
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
16561
16743
  return getFooterLinksContainer();
16562
16744
  }
16563
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
16745
+ 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
16746
  return getNavigationRoot(el);
16565
16747
  }
16566
16748
  return null;
@@ -16592,6 +16774,12 @@ function collectNavigationItemSiblingHintRects(selected) {
16592
16774
  return [];
16593
16775
  }).filter((el) => isNavigationItem2(el) && el !== selected).map((el) => el.getBoundingClientRect());
16594
16776
  }
16777
+ if (selected.closest('footer, [data-ohw-section="footer"]')) {
16778
+ const group = findFooterItemGroup2(selected);
16779
+ if (group) {
16780
+ return Array.from(group.querySelectorAll("[data-ohw-href-key]")).filter((el) => isNavigationItem2(el) && el !== selected).map((el) => el.getBoundingClientRect());
16781
+ }
16782
+ }
16595
16783
  return listNavigationItems().filter((el) => el !== selected && !el.closest("[data-ohw-nav-children]")).map((el) => el.getBoundingClientRect());
16596
16784
  }
16597
16785
  function placeCaretAtPoint(el, x, y) {
@@ -16776,7 +16964,6 @@ var ICONS = {
16776
16964
  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
16965
  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
16966
  };
16779
- var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
16780
16967
  var SELECTION_CHROME_GAP2 = 4;
16781
16968
  var TOOLBAR_STROKE_GAP2 = 4;
16782
16969
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -17160,6 +17347,7 @@ function StateToggle({
17160
17347
  );
17161
17348
  }
17162
17349
  var contentCache = /* @__PURE__ */ new Map();
17350
+ var fetchedContentPaths = /* @__PURE__ */ new Set();
17163
17351
  var brandingCache = /* @__PURE__ */ new Map();
17164
17352
  var OHW_LOADER_STYLE = {
17165
17353
  position: "fixed",
@@ -17281,19 +17469,37 @@ function MadeWithOhhWells() {
17281
17469
  }
17282
17470
  );
17283
17471
  }
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){}})();`;
17472
+ 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){}})();`;
17473
+ function subdomainFromCookie() {
17474
+ if (typeof document === "undefined") return "";
17475
+ const match = document.cookie.match(/(?:^|; )ohw-subdomain=([^;]*)/);
17476
+ return match ? decodeURIComponent(match[1]) : "";
17477
+ }
17285
17478
  function resolveSubdomain(subdomainFromQuery) {
17479
+ if (typeof window !== "undefined") {
17480
+ const fromGlobal = window.__OHW_SUBDOMAIN__;
17481
+ if (fromGlobal) return fromGlobal;
17482
+ }
17286
17483
  if (subdomainFromQuery) return subdomainFromQuery;
17287
17484
  if (typeof window !== "undefined") {
17288
- const parts = window.location.hostname.split(".");
17289
- if (parts.length >= 3 && parts[0] !== "www") return parts[0];
17485
+ const host = window.location.hostname.toLowerCase();
17486
+ if (host.endsWith(".ohhwells.site")) {
17487
+ const label = host.slice(0, -".ohhwells.site".length).split(".").pop();
17488
+ if (label && label !== "www") return label;
17489
+ }
17490
+ const fromCookie = subdomainFromCookie();
17491
+ if (fromCookie) return fromCookie;
17492
+ const isPlatformHost = host.endsWith(".ohhwells.site") || host === "localhost" || host.endsWith(".localhost") || host.endsWith(".vercel.app");
17493
+ if (!isPlatformHost) return "";
17290
17494
  }
17291
17495
  const siteUrl = process.env.NEXT_PUBLIC_SITE_URL;
17292
17496
  if (siteUrl) {
17293
17497
  try {
17294
17498
  const host = new URL(siteUrl).hostname;
17295
- const siteParts = host.split(".");
17296
- if (siteParts.length >= 3 && siteParts[0] !== "www") return siteParts[0];
17499
+ if (host.endsWith(".ohhwells.site")) {
17500
+ const label = host.slice(0, -".ohhwells.site".length).split(".").pop();
17501
+ if (label && label !== "www") return label;
17502
+ }
17297
17503
  } catch {
17298
17504
  }
17299
17505
  }
@@ -17331,7 +17537,26 @@ function OhhwellsBridge() {
17331
17537
  };
17332
17538
  }, []);
17333
17539
  const subdomainFromQuery = searchParams.get("subdomain");
17334
- const subdomain = resolveSubdomain(subdomainFromQuery);
17540
+ const [subdomain, setSubdomain] = useState14(() => resolveSubdomain(subdomainFromQuery));
17541
+ useLayoutEffect5(() => {
17542
+ const next = resolveSubdomain(subdomainFromQuery);
17543
+ if (next !== subdomain) setSubdomain(next);
17544
+ if (next) return;
17545
+ const host = window.location.hostname.toLowerCase();
17546
+ if (host.endsWith(".ohhwells.site")) return;
17547
+ const t = window.setTimeout(() => {
17548
+ const retry = resolveSubdomain(subdomainFromQuery);
17549
+ if (retry) setSubdomain(retry);
17550
+ }, 0);
17551
+ return () => window.clearTimeout(t);
17552
+ }, [subdomainFromQuery, subdomain]);
17553
+ useEffect13(() => {
17554
+ if (!subdomainFromQuery || typeof window === "undefined") return;
17555
+ const url = new URL(window.location.href);
17556
+ if (!url.searchParams.has("subdomain")) return;
17557
+ url.searchParams.delete("subdomain");
17558
+ window.history.replaceState(null, "", url.pathname + url.search + url.hash);
17559
+ }, [subdomainFromQuery]);
17335
17560
  useLinkHrefGuardian(pathname, subdomain, isEditMode);
17336
17561
  useSavedLinkNavigation(isEditMode);
17337
17562
  const postToParent2 = useCallback8((data) => {
@@ -17708,13 +17933,6 @@ function OhhwellsBridge() {
17708
17933
  const [isItemDragging, setIsItemDragging] = useState14(false);
17709
17934
  const [isFooterFrameSelection, setIsFooterFrameSelection] = useState14(false);
17710
17935
  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
17936
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState14(null);
17719
17937
  const [footerHeadingVisible, setFooterHeadingVisible] = useState14(null);
17720
17938
  const footerDragRef = useRef10(null);
@@ -17731,7 +17949,15 @@ function OhhwellsBridge() {
17731
17949
  const aiSectionsRef = useRef10("");
17732
17950
  const brandKitRef = useRef10("");
17733
17951
  const stylesRef = useRef10("");
17952
+ const appliedDrawerRef = useRef10(null);
17734
17953
  const pendingDeleteUndoRef = useRef10(null);
17954
+ const [floatingPanel, setFloatingPanel] = useState14(null);
17955
+ const floatingPanelOpenRef = useRef10(false);
17956
+ const setFloatingPanelRef = useRef10(setFloatingPanel);
17957
+ const [floatingPanelPos, setFloatingPanelPos] = useState14(null);
17958
+ const [logoSizeDraft, setLogoSizeDraft] = useState14(null);
17959
+ const [editorViewport, setEditorViewport] = useState14("desktop");
17960
+ const [parentScrollSnap, setParentScrollSnap] = useState14(null);
17735
17961
  const [sitePages, setSitePages] = useState14([]);
17736
17962
  const [sectionsByPath, setSectionsByPath] = useState14({});
17737
17963
  const sectionsPrefetchGenRef = useRef10(0);
@@ -17740,7 +17966,18 @@ function OhhwellsBridge() {
17740
17966
  const linkPopoverOpenRef = useRef10(false);
17741
17967
  const linkPopoverGraceUntilRef = useRef10(0);
17742
17968
  setLinkPopoverRef.current = setLinkPopover;
17969
+ setFloatingPanelRef.current = setFloatingPanel;
17743
17970
  linkPopoverSessionRef.current = linkPopover;
17971
+ floatingPanelOpenRef.current = Boolean(floatingPanel);
17972
+ useEffect13(() => {
17973
+ const syncViewport = () => {
17974
+ const next = window.innerWidth <= 480 ? "mobile" : "desktop";
17975
+ setEditorViewport((prev) => prev === next ? prev : next);
17976
+ };
17977
+ syncViewport();
17978
+ window.addEventListener("resize", syncViewport);
17979
+ return () => window.removeEventListener("resize", syncViewport);
17980
+ }, []);
17744
17981
  const {
17745
17982
  navDragRef,
17746
17983
  navPointerDragRef,
@@ -19076,6 +19313,7 @@ function OhhwellsBridge() {
19076
19313
  }
19077
19314
  if (typeof content[STYLE_STORE_KEY] === "string") {
19078
19315
  stylesRef.current = content[STYLE_STORE_KEY];
19316
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
19079
19317
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
19080
19318
  }
19081
19319
  applyBrandChrome(content);
@@ -19083,11 +19321,11 @@ function OhhwellsBridge() {
19083
19321
  for (const [key, val] of Object.entries(content)) {
19084
19322
  if (key === "__ohw_sections") continue;
19085
19323
  if (key === AI_SECTIONS_KEY) continue;
19324
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
19325
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
19086
19326
  if (key === BRAND_KIT_KEY) continue;
19087
19327
  if (key === STYLE_STORE_KEY) continue;
19088
19328
  if (BRAND_CHROME_KEYS.has(key)) continue;
19089
- if (key === LOGO_PLACEHOLDER_KEY) continue;
19090
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
19091
19329
  if (applyVideoSettingNode(key, val)) continue;
19092
19330
  if (applyCarouselNode(key, val)) continue;
19093
19331
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -19158,7 +19396,9 @@ function OhhwellsBridge() {
19158
19396
  let cancelled = false;
19159
19397
  setFetchState("loading");
19160
19398
  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) => {
19399
+ const initialPath = pathname;
19400
+ fetchedContentPaths.add(`${subdomain}::${initialPath}`);
19401
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
19162
19402
  if (cancelled) return;
19163
19403
  const content = data?.content ?? {};
19164
19404
  const branding = Boolean(data?.showBranding);
@@ -19267,7 +19507,9 @@ function OhhwellsBridge() {
19267
19507
  }, [isEditMode]);
19268
19508
  useEffect13(() => {
19269
19509
  if (isEditMode || fetchState !== "done") return;
19510
+ console.log("env", process.env.NEXT_PUBLIC_FLOWOPS_API_URL);
19270
19511
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
19512
+ console.log({ apiUrl, subdomain });
19271
19513
  bindPublishedForms(apiUrl, subdomain, contentCache.get(subdomain) ?? {});
19272
19514
  }, [isEditMode, fetchState, subdomain]);
19273
19515
  useEffect13(() => {
@@ -19292,16 +19534,17 @@ function OhhwellsBridge() {
19292
19534
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
19293
19535
  }
19294
19536
  if (typeof content[STYLE_STORE_KEY] === "string") {
19537
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
19295
19538
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
19296
19539
  }
19297
19540
  for (const [key, val] of Object.entries(content)) {
19298
19541
  if (key === "__ohw_sections") continue;
19299
19542
  if (key === AI_SECTIONS_KEY) continue;
19543
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
19544
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
19300
19545
  if (key === BRAND_KIT_KEY) continue;
19301
19546
  if (key === STYLE_STORE_KEY) continue;
19302
19547
  if (BRAND_CHROME_KEYS.has(key)) continue;
19303
- if (key === LOGO_PLACEHOLDER_KEY) continue;
19304
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
19305
19548
  if (applyVideoSettingNode(key, val)) continue;
19306
19549
  if (applyCarouselNode(key, val)) continue;
19307
19550
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -19352,6 +19595,17 @@ function OhhwellsBridge() {
19352
19595
  debounceTimer = setTimeout(applyFromCache, 150);
19353
19596
  };
19354
19597
  applyFromCache();
19598
+ const pathCacheKey = `${subdomain}::${pathname}`;
19599
+ if (!fetchedContentPaths.has(pathCacheKey)) {
19600
+ fetchedContentPaths.add(pathCacheKey);
19601
+ const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
19602
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
19603
+ if (!data?.content) return;
19604
+ contentCache.set(subdomain, data.content);
19605
+ applyFromCache();
19606
+ }).catch(() => {
19607
+ });
19608
+ }
19355
19609
  observer = new MutationObserver(scheduleApply);
19356
19610
  observer.observe(document.body, { childList: true, subtree: true });
19357
19611
  return () => {
@@ -19396,6 +19650,17 @@ function OhhwellsBridge() {
19396
19650
  if (!isEditMode) return;
19397
19651
  initSectionInstancesFromContent(editContentRef.current, pathname);
19398
19652
  }, [pathname, isEditMode]);
19653
+ useEffect13(() => {
19654
+ if (!isEditMode) return;
19655
+ const observer = new MutationObserver(() => {
19656
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
19657
+ if (!drawer || drawer === appliedDrawerRef.current) return;
19658
+ appliedDrawerRef.current = drawer;
19659
+ applyStylesToDom(parseStyleStore(stylesRef.current));
19660
+ });
19661
+ observer.observe(document.body, { childList: true, subtree: true });
19662
+ return () => observer.disconnect();
19663
+ }, [isEditMode, pathname]);
19399
19664
  useEffect13(() => {
19400
19665
  const contentForNav = () => {
19401
19666
  if (isEditMode) return editContentRef.current;
@@ -19487,26 +19752,11 @@ function OhhwellsBridge() {
19487
19752
  const t2 = setTimeout(measure, 500);
19488
19753
  const ro = new ResizeObserver(schedule);
19489
19754
  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
19755
  return () => {
19504
19756
  clearTimeout(t1);
19505
19757
  clearTimeout(t2);
19506
19758
  if (raf != null) cancelAnimationFrame(raf);
19507
19759
  ro.disconnect();
19508
- clearResizeTimers();
19509
- window.removeEventListener("resize", handleResize);
19510
19760
  };
19511
19761
  }, [pathname, isEditMode, postToParent2]);
19512
19762
  useEffect13(() => {
@@ -19801,9 +20051,6 @@ function OhhwellsBridge() {
19801
20051
  if (target.closest("[data-ohw-state-toggle]")) return;
19802
20052
  if (target.closest("[data-ohw-max-badge]")) return;
19803
20053
  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
20054
  if (isInsideFloatingPanel(target)) return;
19808
20055
  if (target.closest("[data-ohw-form-toolbar]")) return;
19809
20056
  if (target.closest(
@@ -19811,6 +20058,9 @@ function OhhwellsBridge() {
19811
20058
  )) {
19812
20059
  return;
19813
20060
  }
20061
+ if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
20062
+ clearMediaSelectionRef.current();
20063
+ }
19814
20064
  {
19815
20065
  const formEl = getFormElement(target);
19816
20066
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -19974,14 +20224,6 @@ function OhhwellsBridge() {
19974
20224
  }
19975
20225
  const clickedButton = findClosestButtonLike(target);
19976
20226
  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
20227
  if (isMediaEditable(editable) && !buttonOnMedia) {
19986
20228
  e.preventDefault();
19987
20229
  e.stopPropagation();
@@ -20008,11 +20250,6 @@ function OhhwellsBridge() {
20008
20250
  const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
20009
20251
  const hrefCtx = getHrefKeyFromElement(hrefLookupTarget);
20010
20252
  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
20253
  if (navAnchor) {
20017
20254
  e.preventDefault();
20018
20255
  e.stopPropagation();
@@ -20054,7 +20291,7 @@ function OhhwellsBridge() {
20054
20291
  return;
20055
20292
  }
20056
20293
  const footerColClick = target.closest("[data-ohw-footer-col]");
20057
- if (footerColClick) {
20294
+ if (footerColClick && !target.closest("[data-ohw-editable]")) {
20058
20295
  e.preventDefault();
20059
20296
  e.stopPropagation();
20060
20297
  selectFrameRef.current(footerColClick);
@@ -20182,6 +20419,9 @@ function OhhwellsBridge() {
20182
20419
  setHoveredItemRect(null);
20183
20420
  hoveredNavContainerRef.current = null;
20184
20421
  setHoveredNavContainerRect(null);
20422
+ siblingHintElRef.current = null;
20423
+ setSiblingHintRect(null);
20424
+ setSiblingHintRects([]);
20185
20425
  return;
20186
20426
  }
20187
20427
  {
@@ -20213,7 +20453,7 @@ function OhhwellsBridge() {
20213
20453
  const navContainer = target.closest("[data-ohw-nav-container]");
20214
20454
  if (!navContainer) {
20215
20455
  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]")) {
20456
+ if (footerLinks && selected2 !== footerLinks && !getNavigationItemAnchor(target) && !target.closest("[data-ohw-footer-col], [data-ohw-footer-column]") && !target.closest("[data-ohw-editable]")) {
20217
20457
  hoveredNavContainerRef.current = footerLinks;
20218
20458
  setHoveredNavContainerRect(footerLinks.getBoundingClientRect());
20219
20459
  hoveredItemElRef.current = null;
@@ -20251,7 +20491,7 @@ function OhhwellsBridge() {
20251
20491
  return;
20252
20492
  }
20253
20493
  const footerCol = target.closest("[data-ohw-footer-col]") ?? target.closest("[data-ohw-footer-column]");
20254
- if (footerCol) {
20494
+ if (footerCol && !target.closest("[data-ohw-editable]")) {
20255
20495
  hoveredNavContainerRef.current = null;
20256
20496
  setHoveredNavContainerRect(null);
20257
20497
  if (selectedElRef.current === footerCol) return;
@@ -20302,7 +20542,6 @@ function OhhwellsBridge() {
20302
20542
  hoveredNavContainerRef.current = null;
20303
20543
  setHoveredNavContainerRect(null);
20304
20544
  hoveredItemElRef.current = editable;
20305
- setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
20306
20545
  }
20307
20546
  }
20308
20547
  }
@@ -20599,7 +20838,7 @@ function OhhwellsBridge() {
20599
20838
  }
20600
20839
  };
20601
20840
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
20602
- if (linkPopoverOpenRef.current) {
20841
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
20603
20842
  if (hoveredImageRef.current) {
20604
20843
  hoveredImageRef.current = null;
20605
20844
  hoveredImageHasTextOverlapRef.current = false;
@@ -20972,8 +21211,7 @@ function OhhwellsBridge() {
20972
21211
  };
20973
21212
  const handleMouseMove = (e) => {
20974
21213
  const { clientX, clientY } = e;
20975
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
20976
- if (isOverEditorChrome(clientX, clientY)) {
21214
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
20977
21215
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
20978
21216
  formHoverElRef.current = null;
20979
21217
  setFormHoverRect(null);
@@ -20981,7 +21219,12 @@ function OhhwellsBridge() {
20981
21219
  setHoveredItemRect(null);
20982
21220
  hoveredNavContainerRef.current = null;
20983
21221
  setHoveredNavContainerRect(null);
21222
+ siblingHintElRef.current = null;
21223
+ setSiblingHintRect(null);
21224
+ setSiblingHintRects([]);
20984
21225
  dismissImageHover();
21226
+ clearImageHover();
21227
+ setSectionGap(null);
20985
21228
  return;
20986
21229
  }
20987
21230
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -20993,7 +21236,11 @@ function OhhwellsBridge() {
20993
21236
  if (e.data?.type !== "ow:pointer-sync") return;
20994
21237
  const { clientX, clientY } = e.data;
20995
21238
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
20996
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
21239
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
21240
+ dismissImageHover();
21241
+ clearImageHover();
21242
+ return;
21243
+ }
20997
21244
  if (probeSocialsRowAt(clientX, clientY)) return;
20998
21245
  probeSectionGapAt(clientX, clientY);
20999
21246
  probeImageAt(clientX, clientY);
@@ -21332,6 +21579,7 @@ function OhhwellsBridge() {
21332
21579
  }
21333
21580
  if (typeof content[STYLE_STORE_KEY] === "string") {
21334
21581
  stylesRef.current = content[STYLE_STORE_KEY];
21582
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
21335
21583
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
21336
21584
  }
21337
21585
  applyBrandChrome(content);
@@ -21343,11 +21591,11 @@ function OhhwellsBridge() {
21343
21591
  continue;
21344
21592
  }
21345
21593
  if (key === AI_SECTIONS_KEY) continue;
21594
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
21595
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
21346
21596
  if (key === BRAND_KIT_KEY) continue;
21347
21597
  if (key === STYLE_STORE_KEY) continue;
21348
21598
  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
21599
  if (applyVideoSettingNode(key, val)) continue;
21352
21600
  if (applyCarouselNode(key, val)) continue;
21353
21601
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -21572,10 +21820,26 @@ function OhhwellsBridge() {
21572
21820
  const handleGetBrand = (e) => {
21573
21821
  if (e.data?.type !== "ow:get-brand") return;
21574
21822
  const template = deriveTemplateBrand();
21575
- const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
21823
+ const fallback = template ?? (() => {
21824
+ const fonts = deriveTemplateFonts();
21825
+ return fonts ? { palette: AI_DEFAULT_BRAND.palette, fonts } : null;
21826
+ })();
21827
+ const value = brandKitRef.current || (fallback ? JSON.stringify(fallback) : "");
21576
21828
  postToParentRef.current({ type: "ow:brand-value", value });
21577
21829
  };
21578
21830
  window.addEventListener("message", handleGetBrand);
21831
+ const handleGetTemplateFonts = (e) => {
21832
+ if (e.data?.type !== "ow:get-template-fonts") return;
21833
+ const fonts = deriveTemplateFonts();
21834
+ postToParentRef.current({ type: "ow:template-fonts-value", value: fonts ? JSON.stringify(fonts) : "" });
21835
+ };
21836
+ window.addEventListener("message", handleGetTemplateFonts);
21837
+ const handleGetTemplateBrand = (e) => {
21838
+ if (e.data?.type !== "ow:get-template-brand") return;
21839
+ const brand = deriveTemplateBrand();
21840
+ postToParentRef.current({ type: "ow:template-brand-value", value: brand ? JSON.stringify(brand) : "" });
21841
+ };
21842
+ window.addEventListener("message", handleGetTemplateBrand);
21579
21843
  const handleMoveSection = (e) => {
21580
21844
  if (e.data?.type !== "ow:move-section") return;
21581
21845
  const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
@@ -21768,6 +22032,12 @@ function OhhwellsBridge() {
21768
22032
  closeLinkPopoverRef.current();
21769
22033
  return;
21770
22034
  }
22035
+ if (floatingPanelOpenRef.current) {
22036
+ setFloatingPanelRef.current(null);
22037
+ deselectRef.current();
22038
+ deactivateRef.current();
22039
+ return;
22040
+ }
21771
22041
  deselectRef.current();
21772
22042
  deactivateRef.current();
21773
22043
  clearMediaSelectionRef.current();
@@ -22056,8 +22326,12 @@ function OhhwellsBridge() {
22056
22326
  if (inserted) {
22057
22327
  const tracker = getSectionsTracker();
22058
22328
  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 });
22329
+ const reportHeight = () => {
22330
+ const h = document.body.scrollHeight;
22331
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
22332
+ };
22333
+ reportHeight();
22334
+ setTimeout(reportHeight, 500);
22061
22335
  }
22062
22336
  };
22063
22337
  const handleSwitchSchedule = (e) => {
@@ -22478,15 +22752,17 @@ function OhhwellsBridge() {
22478
22752
  window.removeEventListener("message", handleAiSetBrand);
22479
22753
  window.removeEventListener("message", handleAiSetStyles);
22480
22754
  window.removeEventListener("message", handleGetBrand);
22755
+ window.removeEventListener("message", handleGetTemplateFonts);
22756
+ window.removeEventListener("message", handleGetTemplateBrand);
22481
22757
  window.removeEventListener("message", handleMoveSection);
22482
22758
  window.removeEventListener("message", handlePanelDragging);
22483
22759
  window.removeEventListener("message", handleDeleteSection);
22484
22760
  window.removeEventListener("message", handleDuplicateSection);
22485
22761
  window.removeEventListener("message", handleDeactivate);
22486
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
22487
22762
  window.removeEventListener("message", handleToastAction);
22488
22763
  window.removeEventListener("message", handleFormCount);
22489
22764
  window.removeEventListener("message", handleUiEscape);
22765
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
22490
22766
  autoSaveTimers.current.forEach(clearTimeout);
22491
22767
  autoSaveTimers.current.clear();
22492
22768
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
@@ -22689,7 +22965,7 @@ function OhhwellsBridge() {
22689
22965
  postToParent2({
22690
22966
  type: "ow:ready",
22691
22967
  version: "1",
22692
- bridgeVersion: "0.1.111",
22968
+ bridgeVersion: "0.1.113",
22693
22969
  path: pathname,
22694
22970
  nodes: collectEditableNodes(editContentRef.current),
22695
22971
  sections
@@ -23305,7 +23581,7 @@ function OhhwellsBridge() {
23305
23581
  "span",
23306
23582
  {
23307
23583
  "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",
23584
+ 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
23585
  children: formPickCount
23310
23586
  }
23311
23587
  )
@@ -23543,11 +23819,11 @@ function OhhwellsBridge() {
23543
23819
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
23544
23820
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
23545
23821
  children: [
23546
- /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
23822
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-bridge-primary", style: { height: 3 } }),
23547
23823
  /* @__PURE__ */ jsx33(
23548
23824
  Badge,
23549
23825
  {
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",
23826
+ 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
23827
  onClick: () => {
23552
23828
  window.parent.postMessage(
23553
23829
  {
@@ -23561,7 +23837,7 @@ function OhhwellsBridge() {
23561
23837
  children: "Add Section"
23562
23838
  }
23563
23839
  ),
23564
- /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } })
23840
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-bridge-primary", style: { height: 3 } })
23565
23841
  ]
23566
23842
  }
23567
23843
  ),
@@ -23609,6 +23885,59 @@ function OhhwellsBridge() {
23609
23885
  ) : null
23610
23886
  ] });
23611
23887
  }
23888
+
23889
+ // src/ui/EmptySection.tsx
23890
+ import Link3 from "next/link";
23891
+ import { Fragment as Fragment9, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
23892
+ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
23893
+ return /* @__PURE__ */ jsxs21(Fragment9, { children: [
23894
+ /* @__PURE__ */ jsx34(
23895
+ "p",
23896
+ {
23897
+ style: {
23898
+ fontFamily: "var(--brand-font-body)",
23899
+ fontSize: "0.75rem",
23900
+ fontWeight: 500,
23901
+ letterSpacing: "0.15em",
23902
+ textTransform: "uppercase",
23903
+ color: "var(--brand-accent)",
23904
+ marginBottom: "1.5rem"
23905
+ },
23906
+ children: /* @__PURE__ */ jsx34(Link3, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx34("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
23907
+ }
23908
+ ),
23909
+ /* @__PURE__ */ jsx34(
23910
+ "h1",
23911
+ {
23912
+ style: {
23913
+ fontFamily: "var(--brand-font-heading)",
23914
+ fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
23915
+ lineHeight: 1.1,
23916
+ letterSpacing: "-0.025em",
23917
+ color: "var(--brand-text)",
23918
+ marginBottom: "1rem"
23919
+ },
23920
+ ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
23921
+ children: title
23922
+ }
23923
+ ),
23924
+ /* @__PURE__ */ jsx34(
23925
+ "p",
23926
+ {
23927
+ style: {
23928
+ fontFamily: "var(--brand-font-body)",
23929
+ fontSize: "1rem",
23930
+ lineHeight: 1.7,
23931
+ fontWeight: 300,
23932
+ color: "var(--brand-text-muted)",
23933
+ maxWidth: "340px"
23934
+ },
23935
+ ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
23936
+ children: "This page doesn't have any content yet."
23937
+ }
23938
+ )
23939
+ ] });
23940
+ }
23612
23941
  export {
23613
23942
  AI_DEFAULT_BRAND,
23614
23943
  AI_TREE_SCHEMA_VERSIONS,
@@ -23625,6 +23954,7 @@ export {
23625
23954
  DropdownMenuItem,
23626
23955
  DropdownMenuSeparator,
23627
23956
  DropdownMenuTrigger,
23957
+ EmptySection,
23628
23958
  ItemActionToolbar,
23629
23959
  ItemInteractionLayer,
23630
23960
  LinkEditorPanel,