@ohhwells/bridge 0.1.60 → 0.1.61-next.172

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
@@ -96,6 +96,7 @@ function applyTreeToState(state, payload) {
96
96
  const entry = {
97
97
  id: payload.id,
98
98
  label: payload.label ?? "Generated section",
99
+ ...typeof payload.path === "string" && payload.path ? { path: payload.path } : {},
99
100
  afterSection: payload.mode === "insert" && !insertBefore ? payload.targetSectionId ?? null : null,
100
101
  ...insertBefore && payload.targetSectionId ? { beforeSection: payload.targetSectionId } : {},
101
102
  ...payload.mode === "replace" && payload.targetSectionId ? { replaces: payload.targetSectionId } : {},
@@ -118,6 +119,272 @@ function deleteSectionFromState(state, sectionId) {
118
119
  return { ...state, v: 1, sections: state.sections, removed: [...removed, sectionId] };
119
120
  }
120
121
 
122
+ // src/lib/brand-chrome.ts
123
+ var BRAND_NAME_KEY = "__ohw_brand_name";
124
+ var BRAND_TITLE_KEY = "__ohw_site_title";
125
+ var BRAND_FAVICON_LETTER_KEY = "__ohw_favicon_letter";
126
+ var BRAND_CHROME_KEYS = /* @__PURE__ */ new Set([
127
+ BRAND_NAME_KEY,
128
+ BRAND_TITLE_KEY,
129
+ BRAND_FAVICON_LETTER_KEY
130
+ ]);
131
+ function upsertMeta(selector, attr, token, value) {
132
+ let el = document.head.querySelector(selector);
133
+ if (!el) {
134
+ el = document.createElement("meta");
135
+ el.setAttribute(attr, token);
136
+ document.head.appendChild(el);
137
+ }
138
+ if (el.getAttribute("content") !== value) el.setAttribute("content", value);
139
+ }
140
+ function escapeXml(value) {
141
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
142
+ }
143
+ function applyLetterFavicon(letter) {
144
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="12" fill="#111827"/><text x="32" y="46" font-family="system-ui,-apple-system,Segoe UI,sans-serif" font-size="40" font-weight="700" text-anchor="middle" fill="#ffffff">${escapeXml(letter)}</text></svg>`;
145
+ const href = `data:image/svg+xml,${encodeURIComponent(svg)}`;
146
+ let link = document.head.querySelector('link[rel="icon"]');
147
+ if (!link) {
148
+ link = document.createElement("link");
149
+ link.rel = "icon";
150
+ document.head.appendChild(link);
151
+ }
152
+ link.type = "image/svg+xml";
153
+ if (link.href !== href) link.href = href;
154
+ }
155
+ function applyBrandChrome(content) {
156
+ const name = content[BRAND_NAME_KEY];
157
+ if (typeof name === "string" && name.length > 0) {
158
+ document.querySelectorAll("[data-ohw-wordmark]").forEach((el) => {
159
+ if (el.textContent !== name) el.textContent = name;
160
+ if (el.getAttribute("title") !== name) el.setAttribute("title", name);
161
+ });
162
+ }
163
+ const title = content[BRAND_TITLE_KEY];
164
+ if (typeof title === "string" && title.length > 0) {
165
+ if (document.title !== title) document.title = title;
166
+ upsertMeta('meta[property="og:title"]', "property", "og:title", title);
167
+ upsertMeta('meta[name="twitter:title"]', "name", "twitter:title", title);
168
+ }
169
+ const letter = content[BRAND_FAVICON_LETTER_KEY];
170
+ if (typeof letter === "string" && letter.length > 0) {
171
+ applyLetterFavicon(letter);
172
+ }
173
+ }
174
+
175
+ // src/lib/brand-kit.ts
176
+ var BRAND_KIT_KEY = "__ohw_brand";
177
+ var BRAND_VAR_PREFIX = "--ohw-brand-";
178
+ var BRAND_VAR_NAMES = ["primary", "accent", "light", "dark", "surface", "border", "muted"].map(
179
+ (role) => `${BRAND_VAR_PREFIX}${role}`
180
+ );
181
+ var FONT_VARS = { heading: ["--font-heading", "--font-display"], body: ["--font-body"] };
182
+ var BRAND_FONT_LINK_ID = "ohw-brand-fonts";
183
+ function brandColorVars(kit) {
184
+ const { dark, primary, accent, light } = kit.palette;
185
+ const mix = (a, aPct, b) => `color-mix(in srgb, ${a} ${aPct}%, ${b})`;
186
+ return {
187
+ [`${BRAND_VAR_PREFIX}primary`]: primary,
188
+ [`${BRAND_VAR_PREFIX}accent`]: accent,
189
+ [`${BRAND_VAR_PREFIX}light`]: light,
190
+ [`${BRAND_VAR_PREFIX}dark`]: dark,
191
+ [`${BRAND_VAR_PREFIX}surface`]: mix(light, 95, dark),
192
+ [`${BRAND_VAR_PREFIX}border`]: mix(light, 85, dark),
193
+ [`${BRAND_VAR_PREFIX}muted`]: mix(dark, 62, light)
194
+ };
195
+ }
196
+ function parseBrandKit(raw) {
197
+ if (!raw) return null;
198
+ try {
199
+ const parsed = JSON.parse(raw);
200
+ const p = parsed?.palette;
201
+ const f = parsed?.fonts;
202
+ 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") {
203
+ return null;
204
+ }
205
+ return {
206
+ palette: { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light },
207
+ fonts: { heading: f.heading, body: f.body }
208
+ };
209
+ } catch {
210
+ return null;
211
+ }
212
+ }
213
+ function familyOf(stack) {
214
+ const first = stack.split(",")[0]?.trim() ?? "";
215
+ return first.replace(/^['"]|['"]$/g, "");
216
+ }
217
+ function loadBrandFonts(families) {
218
+ const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
219
+ if (unique.length === 0) return;
220
+ const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
221
+ const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
222
+ let link = document.getElementById(BRAND_FONT_LINK_ID);
223
+ if (!link) {
224
+ link = document.createElement("link");
225
+ link.id = BRAND_FONT_LINK_ID;
226
+ link.rel = "stylesheet";
227
+ document.head.appendChild(link);
228
+ }
229
+ if (link.href !== href) link.href = href;
230
+ }
231
+ function applyBrandToDom(kit) {
232
+ const root = document.documentElement;
233
+ if (!kit) {
234
+ for (const name of BRAND_VAR_NAMES) root.style.removeProperty(name);
235
+ for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
236
+ document.getElementById(BRAND_FONT_LINK_ID)?.remove();
237
+ return;
238
+ }
239
+ for (const [name, value] of Object.entries(brandColorVars(kit))) root.style.setProperty(name, value);
240
+ for (const name of FONT_VARS.heading) root.style.setProperty(name, kit.fonts.heading);
241
+ for (const name of FONT_VARS.body) root.style.setProperty(name, kit.fonts.body);
242
+ loadBrandFonts([familyOf(kit.fonts.heading), familyOf(kit.fonts.body)]);
243
+ }
244
+
245
+ // src/lib/section-styles.ts
246
+ var STYLE_STORE_KEY = "__ohw_styles";
247
+ var STYLE_SHEET_ID = "ohw-section-styles";
248
+ function parseStyleStore(raw) {
249
+ if (!raw) return null;
250
+ try {
251
+ const parsed = JSON.parse(raw);
252
+ if (parsed?.v !== 1) return null;
253
+ return {
254
+ v: 1,
255
+ sections: typeof parsed.sections === "object" && parsed.sections ? parsed.sections : {},
256
+ nodes: typeof parsed.nodes === "object" && parsed.nodes ? parsed.nodes : {}
257
+ };
258
+ } catch {
259
+ return null;
260
+ }
261
+ }
262
+ var BG_VALUES = {
263
+ surface: "color-mix(in srgb, var(--ohw-brand-light, var(--color-light, #ECECEB)) 94%, var(--ohw-brand-dark, var(--color-dark, #0C0A09)))",
264
+ accent: "var(--ohw-brand-primary, var(--color-primary, #0078E5))",
265
+ "accent-soft": "color-mix(in srgb, var(--ohw-brand-primary, var(--color-primary, #0078E5)) 12%, var(--ohw-brand-light, var(--color-light, #FFFFFF)))"
266
+ };
267
+ var HEADLINE_SIZES = { md: 24, lg: 32, xl: 40, display: 56 };
268
+ function styleSheetCss() {
269
+ const rules = [];
270
+ for (const [tone, value] of Object.entries(BG_VALUES)) {
271
+ rules.push(`[data-ohw-style-bg="${tone}"] { background: ${value} !important; }`);
272
+ }
273
+ rules.push(
274
+ `[data-ohw-style-bg="accent"] { color: var(--ohw-brand-light, var(--color-light, #FFFFFF)) !important; }`
275
+ );
276
+ rules.push(
277
+ `[data-ohw-style-distribution="space-between"] { display: flex !important; flex-direction: column; justify-content: space-between; }`,
278
+ `[data-ohw-style-distribution="center"] { display: flex !important; flex-direction: column; justify-content: center; }`
279
+ );
280
+ for (const [scale, size] of Object.entries(HEADLINE_SIZES)) {
281
+ rules.push(
282
+ `[data-ohw-style-headline="${scale}"] :is(h1, h2, h3) { font-size: ${size}px !important; line-height: 1.15 !important; }`
283
+ );
284
+ }
285
+ rules.push(
286
+ `[data-ohw-style-aspect="fill-height"] img { height: 100% !important; aspect-ratio: auto !important; object-fit: cover; }`
287
+ );
288
+ for (const aspect of ["1:1", "4:5", "3:4", "3:2", "16:9", "2:1", "3:1"]) {
289
+ rules.push(
290
+ `[data-ohw-style-aspect="${aspect.replace(":", "-")}"] img { aspect-ratio: ${aspect.replace(":", " / ")} !important; height: auto !important; object-fit: cover; }`
291
+ );
292
+ }
293
+ const pad = { tight: 40, balanced: 64, airy: 96 };
294
+ for (const [spacing, px] of Object.entries(pad)) {
295
+ rules.push(
296
+ `[data-ohw-style-spacing="${spacing}"] { padding-top: ${px}px !important; padding-bottom: ${px}px !important; }`
297
+ );
298
+ }
299
+ return rules.join("\n");
300
+ }
301
+ var SECTION_ATTRS = {
302
+ sectionBackground: "data-ohw-style-bg",
303
+ textDistribution: "data-ohw-style-distribution",
304
+ headlineScale: "data-ohw-style-headline",
305
+ imageAspect: "data-ohw-style-aspect",
306
+ spacing: "data-ohw-style-spacing"
307
+ };
308
+ var NODE_WROTE_ATTR = "data-ohw-style-node";
309
+ function ensureStyleSheet() {
310
+ let el = document.getElementById(STYLE_SHEET_ID);
311
+ if (!el) {
312
+ el = document.createElement("style");
313
+ el.id = STYLE_SHEET_ID;
314
+ document.head.appendChild(el);
315
+ }
316
+ const css = styleSheetCss();
317
+ if (el.textContent !== css) el.textContent = css;
318
+ }
319
+ function clearSectionAttrs(root) {
320
+ for (const attr of Object.values(SECTION_ATTRS)) {
321
+ for (const el of Array.from(root.querySelectorAll(`[${attr}]`))) el.removeAttribute(attr);
322
+ }
323
+ for (const el of Array.from(root.querySelectorAll("[data-ohw-style-bgcolor]"))) {
324
+ ;
325
+ el.style.removeProperty("background");
326
+ el.removeAttribute("data-ohw-style-bgcolor");
327
+ }
328
+ }
329
+ function clearNodeProps(root) {
330
+ for (const el of Array.from(root.querySelectorAll(`[${NODE_WROTE_ATTR}]`))) {
331
+ const h = el;
332
+ h.style.removeProperty("color");
333
+ h.style.removeProperty("font-size");
334
+ h.style.removeProperty("background");
335
+ h.removeAttribute(NODE_WROTE_ATTR);
336
+ }
337
+ }
338
+ function buttonSurfaceOf(el) {
339
+ return el.closest("a, button") ?? el;
340
+ }
341
+ function applyStylesToDom(store) {
342
+ ensureStyleSheet();
343
+ clearSectionAttrs(document);
344
+ clearNodeProps(document);
345
+ if (!store) return;
346
+ for (const [sectionId, override] of Object.entries(store.sections)) {
347
+ const sections = document.querySelectorAll(
348
+ `[data-ohw-section="${CSS.escape(sectionId)}"]`
349
+ );
350
+ for (const section of Array.from(sections)) {
351
+ for (const [prop, attr] of Object.entries(SECTION_ATTRS)) {
352
+ const value = override[prop];
353
+ if (value === void 0) continue;
354
+ if (prop === "sectionBackground" && override.sectionBackgroundColor !== void 0) continue;
355
+ section.setAttribute(attr, String(value).replace(":", "-"));
356
+ }
357
+ if (override.sectionBackgroundColor !== void 0) {
358
+ section.style.setProperty("background", override.sectionBackgroundColor, "important");
359
+ section.setAttribute("data-ohw-style-bgcolor", "");
360
+ }
361
+ }
362
+ }
363
+ for (const [key, override] of Object.entries(store.nodes)) {
364
+ const nodes = document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`);
365
+ for (const el of Array.from(nodes)) {
366
+ if (override.color !== void 0) {
367
+ el.style.setProperty("color", override.color, "important");
368
+ el.setAttribute(NODE_WROTE_ATTR, "");
369
+ }
370
+ if (override.fontSize !== void 0) {
371
+ el.style.setProperty("font-size", `${override.fontSize}px`, "important");
372
+ el.setAttribute(NODE_WROTE_ATTR, "");
373
+ }
374
+ if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
375
+ const surface = buttonSurfaceOf(el);
376
+ if (override.buttonBackground !== void 0) {
377
+ surface.style.setProperty("background", override.buttonBackground, "important");
378
+ }
379
+ if (override.buttonText !== void 0) {
380
+ surface.style.setProperty("color", override.buttonText, "important");
381
+ }
382
+ surface.setAttribute(NODE_WROTE_ATTR, "");
383
+ }
384
+ }
385
+ }
386
+ }
387
+
121
388
  // src/ui/ai-tree/aiSectionsManager.tsx
122
389
  import { flushSync } from "react-dom";
123
390
  import { createRoot } from "react-dom/client";
@@ -132,7 +399,8 @@ function lucideByName(name) {
132
399
  }
133
400
  var typeStyle = (spec, font) => ({
134
401
  fontFamily: font,
135
- fontSize: spec.size,
402
+ // Headings shrink with the viewport (reaching full size around ~900px wide); body copy stays put.
403
+ fontSize: spec.size >= 24 ? `clamp(${Math.max(18, Math.round(spec.size * 0.6))}px, ${(spec.size / 9).toFixed(2)}vw, ${spec.size}px)` : spec.size,
136
404
  lineHeight: spec.line,
137
405
  fontWeight: spec.weight
138
406
  });
@@ -159,6 +427,8 @@ var AI_RESPONSIVE_CSS = [
159
427
  " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
160
428
  " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
161
429
  " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
430
+ " [data-ai-responsive] { overflow-x: hidden; }",
431
+ " [data-ai-responsive] img { max-width: 100%; }",
162
432
  "}"
163
433
  ].join("\n");
164
434
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
@@ -1166,6 +1436,20 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1166
1436
  const overlayAlpha = { low: 0.25, medium: 0.45, high: 0.65 }[settings.overlayIntensity ?? "medium"];
1167
1437
  const backgroundUrl = isOverlay && settings.backgroundMedia ? ctx.resolveMedia(settings.backgroundMedia) : null;
1168
1438
  const bgAttrs = ctx.keyFor && isOverlay && settings.backgroundMedia ? { "data-ohw-key": ctx.keyFor("settings.backgroundMedia"), "data-ohw-editable": "bg-image" } : {};
1439
+ const toneBackground = (() => {
1440
+ const { dark, primary, light } = resolvedBrand.palette;
1441
+ switch (settings.sectionBackground) {
1442
+ case "surface":
1443
+ return `color-mix(in srgb, ${light} 94%, ${dark})`;
1444
+ case "accent":
1445
+ return primary;
1446
+ case "accent-soft":
1447
+ return `color-mix(in srgb, ${primary} 12%, ${light})`;
1448
+ default:
1449
+ return void 0;
1450
+ }
1451
+ })();
1452
+ const distributed = !isOverlay && settings.textDistribution;
1169
1453
  return /* @__PURE__ */ jsxs(
1170
1454
  "section",
1171
1455
  {
@@ -1175,10 +1459,11 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1175
1459
  style: {
1176
1460
  position: "relative",
1177
1461
  padding: `${pad}px 0`,
1178
- background: isOverlay && !backgroundUrl ? "#DBEAFE" : void 0,
1462
+ background: toneBackground ?? (isOverlay && !backgroundUrl ? "#DBEAFE" : void 0),
1179
1463
  backgroundImage: backgroundUrl ? `url(${backgroundUrl})` : void 0,
1180
1464
  backgroundSize: "cover",
1181
- backgroundPosition: "center"
1465
+ backgroundPosition: "center",
1466
+ color: settings.sectionBackground === "accent" ? resolvedBrand.palette.light : void 0
1182
1467
  },
1183
1468
  children: [
1184
1469
  /* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
@@ -1202,10 +1487,24 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1202
1487
  display: "grid",
1203
1488
  gridTemplateColumns: "repeat(12, 1fr)",
1204
1489
  gap: AI_TREE_TOKENS.spacing6,
1205
- alignItems: settings.verticalPosition === "top" ? "start" : "center",
1490
+ alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
1206
1491
  marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
1207
1492
  },
1208
- children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ jsx("div", { style: { gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`, minWidth: 0 }, children: renderNode(block, ctx, `r${r2}.b${b}`) }, b))
1493
+ children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ jsx(
1494
+ "div",
1495
+ {
1496
+ "data-ai-cell": "",
1497
+ style: {
1498
+ gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
1499
+ minWidth: 0,
1500
+ // space-between: each column becomes a flex column whose content spreads over
1501
+ // the full row height instead of clumping at the top.
1502
+ ...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
1503
+ },
1504
+ children: renderNode(block, ctx, `r${r2}.b${b}`)
1505
+ },
1506
+ b
1507
+ ))
1209
1508
  },
1210
1509
  r2
1211
1510
  ))
@@ -1221,17 +1520,34 @@ import { jsx as jsx2 } from "react/jsx-runtime";
1221
1520
  var CONTAINER_ATTR = "data-ohw-ai-generated";
1222
1521
  var REPLACED_ATTR = "data-ohw-ai-replaced-by";
1223
1522
  var REMOVED_ATTR = "data-ohw-ai-removed";
1523
+ function readRootVar(name) {
1524
+ if (typeof document === "undefined") return "";
1525
+ return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
1526
+ }
1527
+ function deriveBrandOverride() {
1528
+ const dark = readRootVar("--ohw-brand-dark");
1529
+ const primary = readRootVar("--ohw-brand-primary");
1530
+ const light = readRootVar("--ohw-brand-light");
1531
+ if (!dark || !primary || !light) return null;
1532
+ const accent = readRootVar("--ohw-brand-accent");
1533
+ const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1534
+ const body = readRootVar("--font-body");
1535
+ return {
1536
+ palette: { dark, primary, accent: accent || dark, light },
1537
+ fonts: {
1538
+ heading: heading || AI_DEFAULT_BRAND.fonts.heading,
1539
+ body: body || AI_DEFAULT_BRAND.fonts.body
1540
+ }
1541
+ };
1542
+ }
1224
1543
  function deriveTemplateBrand() {
1225
- if (typeof document === "undefined") return null;
1226
- const cs = getComputedStyle(document.documentElement);
1227
- const read = (name) => cs.getPropertyValue(name).trim();
1228
- const dark = read("--color-dark");
1229
- const primary = read("--color-primary");
1230
- const light = read("--color-light");
1544
+ const dark = readRootVar("--color-dark");
1545
+ const primary = readRootVar("--color-primary");
1546
+ const light = readRootVar("--color-light");
1231
1547
  if (!dark || !primary || !light) return null;
1232
- const accent = read("--color-accent");
1233
- const heading = read("--font-heading") || read("--font-display");
1234
- const body = read("--font-body");
1548
+ const accent = readRootVar("--color-accent");
1549
+ const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1550
+ const body = readRootVar("--font-body");
1235
1551
  return {
1236
1552
  palette: { dark, primary, accent: accent || dark, light },
1237
1553
  fonts: {
@@ -1323,8 +1639,12 @@ function syncReplacedOriginals(state) {
1323
1639
  }
1324
1640
  function applyAiSectionsToDom(state, options) {
1325
1641
  if (typeof document === "undefined") return;
1642
+ const brandOverride = deriveBrandOverride();
1326
1643
  const templateBrand = deriveTemplateBrand();
1327
- const activeIds = new Set(state.sections.map((entry) => entry.id));
1644
+ const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
1645
+ const pagePath = window.location.pathname;
1646
+ const pageSections = state.sections.filter((entry) => !entry.path || entry.path === pagePath);
1647
+ const activeIds = new Set(pageSections.map((entry) => entry.id));
1328
1648
  for (const [id, section] of mounted) {
1329
1649
  if (!activeIds.has(id)) {
1330
1650
  section.root.unmount();
@@ -1332,8 +1652,8 @@ function applyAiSectionsToDom(state, options) {
1332
1652
  mounted.delete(id);
1333
1653
  }
1334
1654
  }
1335
- for (const entry of state.sections) {
1336
- const serialized = JSON.stringify(entry);
1655
+ for (const entry of pageSections) {
1656
+ const serialized = JSON.stringify(entry) + brandKey;
1337
1657
  const existing = mounted.get(entry.id);
1338
1658
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
1339
1659
  continue;
@@ -1347,6 +1667,7 @@ function applyAiSectionsToDom(state, options) {
1347
1667
  mounted.delete(entry.id);
1348
1668
  }
1349
1669
  container.setAttribute("data-ohw-section", entry.id);
1670
+ container.setAttribute("data-ohw-instance", entry.id);
1350
1671
  container.setAttribute("data-ohw-section-label", entry.label);
1351
1672
  placeContainer(container, entry);
1352
1673
  const root = mounted.get(entry.id)?.root ?? createRoot(container);
@@ -1357,7 +1678,7 @@ function applyAiSectionsToDom(state, options) {
1357
1678
  AiTreeRenderer,
1358
1679
  {
1359
1680
  tree: entry.tree,
1360
- brand: entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1681
+ brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1361
1682
  resolveMedia,
1362
1683
  editKeyPrefix: `ai.${entry.id}`
1363
1684
  }
@@ -1974,7 +2295,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
1974
2295
  const autoId = useId();
1975
2296
  const insertAfter = insertAfterProp ?? autoId;
1976
2297
  const [schedule, setSchedule] = useState2(null);
1977
- const [loading, setLoading] = useState2(true);
2298
+ const [loading, setLoading] = useState2(initialScheduleId !== null);
1978
2299
  const [inEditor, setInEditor] = useState2(false);
1979
2300
  const [isHovered, setIsHovered] = useState2(false);
1980
2301
  const [modalState, setModalState] = useState2(null);
@@ -2148,8 +2469,10 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2148
2469
  "*"
2149
2470
  );
2150
2471
  };
2151
- if (!inEditor && !loading && !schedule) return null;
2152
2472
  const sectionId = `scheduling-${insertAfter}`;
2473
+ if (!inEditor && !loading && !schedule) {
2474
+ return /* @__PURE__ */ jsx4("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
2475
+ }
2153
2476
  return /* @__PURE__ */ jsxs3(
2154
2477
  "section",
2155
2478
  {
@@ -6668,8 +6991,12 @@ function parseSectionsFromHtml(html) {
6668
6991
 
6669
6992
  // src/ui/ai-section/AiSectionOverlay.tsx
6670
6993
  import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
6671
- function readRect(sectionId) {
6672
- const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
6994
+ function findSectionElement(instanceId) {
6995
+ const escaped = CSS.escape(instanceId);
6996
+ return document.querySelector(`[data-ohw-instance="${escaped}"]`) ?? document.querySelector(`[data-ohw-section="${escaped}"]:not([data-ohw-instance])`);
6997
+ }
6998
+ function readRect(instanceId) {
6999
+ const el = findSectionElement(instanceId);
6673
7000
  if (!el) return null;
6674
7001
  const r2 = el.getBoundingClientRect();
6675
7002
  if (r2.width <= 0 || r2.height <= 0) return null;
@@ -6692,7 +7019,7 @@ function useLiveSectionRect(sectionId) {
6692
7019
  const opts = { capture: true, passive: true };
6693
7020
  window.addEventListener("scroll", update, opts);
6694
7021
  window.addEventListener("resize", update);
6695
- const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
7022
+ const el = findSectionElement(sectionId);
6696
7023
  const ro = el ? new ResizeObserver(update) : null;
6697
7024
  if (el && ro) ro.observe(el);
6698
7025
  const interval = setInterval(update, 500);
@@ -6705,6 +7032,14 @@ function useLiveSectionRect(sectionId) {
6705
7032
  }, [sectionId]);
6706
7033
  return rect;
6707
7034
  }
7035
+ function computeSectionBoundaryFlags(instanceId) {
7036
+ const topLevel = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
7037
+ (el) => !el.parentElement?.closest("[data-ohw-section]")
7038
+ );
7039
+ const index = topLevel.findIndex((el) => (el.dataset.ohwInstance ?? el.dataset.ohwSection) === instanceId);
7040
+ if (index === -1) return { isFirst: true, isLast: true };
7041
+ return { isFirst: index === 0, isLast: index === topLevel.length - 1 };
7042
+ }
6708
7043
  var PRIMARY2 = "#0885FE";
6709
7044
  function edgeAwareRadius(rect) {
6710
7045
  const container = window.innerWidth <= 480 ? 16 : 24;
@@ -6778,6 +7113,7 @@ function AiSectionOverlay({
6778
7113
  }) {
6779
7114
  const [selectedId, setSelectedId] = useState4(null);
6780
7115
  const [reviewId, setReviewId] = useState4(null);
7116
+ const [reviewButtonsHidden, setReviewButtonsHidden] = useState4(false);
6781
7117
  const reviewIdRef = useRef4(null);
6782
7118
  reviewIdRef.current = reviewId;
6783
7119
  const selectedIdRef = useRef4(null);
@@ -6786,7 +7122,7 @@ function AiSectionOverlay({
6786
7122
  (el) => {
6787
7123
  postToParent2({
6788
7124
  type: "ow:section-selected",
6789
- sectionId: el?.dataset.ohwSection ?? null,
7125
+ sectionId: el ? el.dataset.ohwInstance ?? el.dataset.ohwSection ?? null : null,
6790
7126
  sectionLabel: el ? el.dataset.ohwSectionLabel ?? titleCaseSectionId(el.dataset.ohwSection ?? "") : null
6791
7127
  });
6792
7128
  },
@@ -6795,7 +7131,7 @@ function AiSectionOverlay({
6795
7131
  const selectFromElement = useCallback2(
6796
7132
  (el, options) => {
6797
7133
  const sectionEl = el?.closest("[data-ohw-section]") ?? null;
6798
- const id = sectionEl?.dataset.ohwSection ?? null;
7134
+ const id = sectionEl ? sectionEl.dataset.ohwInstance ?? sectionEl.dataset.ohwSection ?? null : null;
6799
7135
  if (id === selectedIdRef.current) return;
6800
7136
  setSelectedId(id);
6801
7137
  if (options?.report !== false) report(sectionEl);
@@ -6836,9 +7172,10 @@ function AiSectionOverlay({
6836
7172
  }
6837
7173
  const found = readRect(sectionId) != null;
6838
7174
  setReviewId(found ? sectionId : null);
7175
+ setReviewButtonsHidden(e.data.hideButtons === true);
6839
7176
  postToParent2({ type: "ow:ai-review-started", sectionId, found });
6840
7177
  if (found) {
6841
- document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
7178
+ document.querySelector(`[data-ohw-instance="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
6842
7179
  }
6843
7180
  }
6844
7181
  };
@@ -6857,7 +7194,7 @@ function AiSectionOverlay({
6857
7194
  return;
6858
7195
  }
6859
7196
  const sec = t.closest("[data-ohw-section]");
6860
- setHoveredId(sec?.dataset.ohwSection ?? null);
7197
+ setHoveredId(sec ? sec.dataset.ohwInstance ?? sec.dataset.ohwSection ?? null : null);
6861
7198
  };
6862
7199
  const onLeave = () => setHoveredId(null);
6863
7200
  document.addEventListener("mousemove", onMove, { passive: true });
@@ -6889,9 +7226,29 @@ function AiSectionOverlay({
6889
7226
  },
6890
7227
  [postToParent2]
6891
7228
  );
6892
- const selectionRect = useLiveSectionRect(reviewId ? null : selectedId);
7229
+ const activeSelectionId = reviewId ? null : selectedId;
7230
+ const selectionRect = useLiveSectionRect(activeSelectionId);
6893
7231
  const reviewRect = useLiveSectionRect(reviewId);
6894
7232
  const hoverRect = useLiveSectionRect(reviewId || hoveredId === selectedId ? null : hoveredId);
7233
+ useEffect4(() => {
7234
+ if (!activeSelectionId || !selectionRect) {
7235
+ postToParent2({ type: "ow:section-rect", instanceId: null, rect: null });
7236
+ return;
7237
+ }
7238
+ const { isFirst, isLast } = computeSectionBoundaryFlags(activeSelectionId);
7239
+ postToParent2({
7240
+ type: "ow:section-rect",
7241
+ instanceId: activeSelectionId,
7242
+ rect: {
7243
+ top: selectionRect.top + window.scrollY,
7244
+ left: selectionRect.left + window.scrollX,
7245
+ width: selectionRect.width,
7246
+ height: selectionRect.height
7247
+ },
7248
+ isFirst,
7249
+ isLast
7250
+ });
7251
+ }, [activeSelectionId, selectionRect, postToParent2]);
6895
7252
  return /* @__PURE__ */ jsxs8(Fragment5, { children: [
6896
7253
  hoverRect && /* @__PURE__ */ jsx16(
6897
7254
  "div",
@@ -6942,13 +7299,16 @@ function AiSectionOverlay({
6942
7299
  border: `2px solid ${PRIMARY2}`,
6943
7300
  borderRadius: edgeAwareRadius(reviewRect),
6944
7301
  zIndex: 2147483200,
6945
- // The veil itself: swallows clicks so the section stays locked until decided.
7302
+ // The veil itself: swallows clicks so the section stays locked until decided. This
7303
+ // stopPropagation only guards the bubble phase; the bridge's capture-phase click
7304
+ // handlers additionally bail on `[data-ohw-ai-review]`, without which a click on
7305
+ // Accept/Discard resolves to the media beneath and opens the file picker.
6946
7306
  background: "rgba(8, 133, 254, 0.04)",
6947
7307
  pointerEvents: "auto",
6948
7308
  cursor: "default"
6949
7309
  },
6950
7310
  onClick: (e) => e.stopPropagation(),
6951
- children: /* @__PURE__ */ jsxs8(
7311
+ children: !reviewButtonsHidden && /* @__PURE__ */ jsxs8(
6952
7312
  "div",
6953
7313
  {
6954
7314
  style: {
@@ -10848,6 +11208,329 @@ function deleteFooterColumn(column) {
10848
11208
  };
10849
11209
  }
10850
11210
 
11211
+ // src/lib/logo-identity.ts
11212
+ var LOGO_TEXT_KEYS = ["nav-logo-text", "footer-logo-text", "logo-text"];
11213
+ var LOGO_IMAGE_KEYS = ["nav-logo-image", "footer-logo", "footer-logo-image"];
11214
+ var LOGO_HREF_KEYS = ["nav-logo-href", "footer-logo-href", "logo-href"];
11215
+ var LOGO_PLACEHOLDER_KEY = "logo-is-placeholder";
11216
+ var LOGO_ALT_KEY = "logo-alt";
11217
+ var LOGO_IMAGE_URL_KEY = "nav-logo-image";
11218
+ var PLACEHOLDER_BUSINESS_NAME = "Business name";
11219
+ function resolveLogoDisplayText(text) {
11220
+ const trimmed = (text ?? "").trim();
11221
+ return trimmed || PLACEHOLDER_BUSINESS_NAME;
11222
+ }
11223
+ function isFooterLogoRoot(root) {
11224
+ return Boolean(root.closest("footer") || root.closest('[data-ohw-section="footer"]'));
11225
+ }
11226
+ function imageKeyForRoot(root) {
11227
+ return isFooterLogoRoot(root) ? "footer-logo" : "nav-logo-image";
11228
+ }
11229
+ function textKeyForRoot(root) {
11230
+ return isFooterLogoRoot(root) ? "footer-logo-text" : "nav-logo-text";
11231
+ }
11232
+ function ensureLogoHrefKey(root) {
11233
+ if (!(root instanceof HTMLAnchorElement)) return;
11234
+ if (root.hasAttribute("data-ohw-href-key")) return;
11235
+ root.setAttribute("data-ohw-href-key", isFooterLogoRoot(root) ? "footer-logo-href" : "nav-logo-href");
11236
+ }
11237
+ function applyLogoIdentity(text, isPlaceholder) {
11238
+ const display = resolveLogoDisplayText(text);
11239
+ const placeholder = isPlaceholder || !text.trim() || display === PLACEHOLDER_BUSINESS_NAME;
11240
+ for (const key of LOGO_TEXT_KEYS) {
11241
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
11242
+ if (el.textContent !== display) el.textContent = display;
11243
+ });
11244
+ }
11245
+ for (const key of LOGO_IMAGE_KEYS) {
11246
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
11247
+ const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
11248
+ if (img) img.alt = display;
11249
+ });
11250
+ }
11251
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((el) => {
11252
+ if (placeholder) el.setAttribute("data-ohw-placeholder", "");
11253
+ else el.removeAttribute("data-ohw-placeholder");
11254
+ });
11255
+ return display;
11256
+ }
11257
+ function applyLogoImage(url, alt) {
11258
+ const displayAlt = resolveLogoDisplayText(alt);
11259
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11260
+ ensureLogoHrefKey(root);
11261
+ const imageKey = imageKeyForRoot(root);
11262
+ const textKey = textKeyForRoot(root);
11263
+ let img = root.querySelector(`img[data-ohw-key="${imageKey}"]`) ?? (root.querySelector(`[data-ohw-key="${imageKey}"]`) instanceof HTMLImageElement ? root.querySelector(`[data-ohw-key="${imageKey}"]`) : null) ?? root.querySelector("img");
11264
+ let textEl = root.querySelector(`[data-ohw-key="${textKey}"]`) ?? root.querySelector('[data-ohw-key="logo-text"]');
11265
+ if (url) {
11266
+ if (!img) {
11267
+ img = document.createElement("img");
11268
+ img.setAttribute("data-ohw-editable", "image");
11269
+ img.setAttribute("data-ohw-key", imageKey);
11270
+ img.alt = displayAlt;
11271
+ img.style.height = "";
11272
+ img.style.maxHeight = "none";
11273
+ img.style.width = "auto";
11274
+ img.style.display = "block";
11275
+ img.style.objectFit = "contain";
11276
+ root.insertBefore(img, root.firstChild);
11277
+ } else {
11278
+ img.setAttribute("data-ohw-editable", "image");
11279
+ img.setAttribute("data-ohw-key", imageKey);
11280
+ }
11281
+ img.removeAttribute("srcset");
11282
+ img.removeAttribute("sizes");
11283
+ img.src = url;
11284
+ img.alt = displayAlt;
11285
+ img.style.display = "block";
11286
+ if (textEl) textEl.style.display = "none";
11287
+ root.removeAttribute("data-ohw-placeholder");
11288
+ return;
11289
+ }
11290
+ if (img) {
11291
+ img.removeAttribute("src");
11292
+ img.removeAttribute("srcset");
11293
+ img.removeAttribute("sizes");
11294
+ img.alt = displayAlt;
11295
+ img.style.display = "none";
11296
+ }
11297
+ if (!textEl) {
11298
+ textEl = document.createElement("span");
11299
+ textEl.setAttribute("data-ohw-editable", "plain");
11300
+ textEl.setAttribute("data-ohw-key", textKey);
11301
+ root.appendChild(textEl);
11302
+ }
11303
+ textEl.style.display = "";
11304
+ if (textEl.textContent !== displayAlt) textEl.textContent = displayAlt;
11305
+ if (!displayAlt.trim() || displayAlt === PLACEHOLDER_BUSINESS_NAME) {
11306
+ root.setAttribute("data-ohw-placeholder", "");
11307
+ } else {
11308
+ root.removeAttribute("data-ohw-placeholder");
11309
+ }
11310
+ });
11311
+ }
11312
+ function applyLogoHref(href) {
11313
+ const target = href.trim() || "/";
11314
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11315
+ ensureLogoHrefKey(root);
11316
+ if (root instanceof HTMLAnchorElement) {
11317
+ root.setAttribute("href", target);
11318
+ }
11319
+ });
11320
+ for (const key of LOGO_HREF_KEYS) setStoredLinkHref(key, target);
11321
+ }
11322
+ function readLogoIdentityFromDom() {
11323
+ let imageUrl = null;
11324
+ for (const key of LOGO_IMAGE_KEYS) {
11325
+ const el = document.querySelector(`[data-ohw-key="${key}"]`);
11326
+ const img = el instanceof HTMLImageElement ? el : el?.querySelector("img");
11327
+ const attrSrc = img?.getAttribute("src")?.trim() ?? "";
11328
+ if (attrSrc && !attrSrc.startsWith("data:") && img && img.style.display !== "none") {
11329
+ imageUrl = img.currentSrc || img.src;
11330
+ break;
11331
+ }
11332
+ }
11333
+ let text = PLACEHOLDER_BUSINESS_NAME;
11334
+ let isPlaceholder = true;
11335
+ for (const key of LOGO_TEXT_KEYS) {
11336
+ const el = document.querySelector(`[data-ohw-key="${key}"]`);
11337
+ if (el?.textContent?.trim()) {
11338
+ text = el.textContent.trim();
11339
+ const logoRoot2 = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
11340
+ isPlaceholder = logoRoot2?.hasAttribute("data-ohw-placeholder") === true || text === PLACEHOLDER_BUSINESS_NAME;
11341
+ break;
11342
+ }
11343
+ }
11344
+ if (imageUrl) {
11345
+ const logoImg = document.querySelector(
11346
+ '[data-ohw-key="nav-logo-image"], [data-ohw-key="footer-logo"]'
11347
+ );
11348
+ const alt = logoImg?.alt?.trim() || text;
11349
+ isPlaceholder = false;
11350
+ const hrefEl = document.querySelector(
11351
+ 'a[data-ohw-role="logo"], a[data-ohw-logo], [data-ohw-role="logo"]'
11352
+ );
11353
+ const href2 = (hrefEl instanceof HTMLAnchorElement ? hrefEl.getAttribute("href") : null) || hrefEl?.closest("a")?.getAttribute("href") || "/";
11354
+ return { text, isPlaceholder, imageUrl, href: href2, alt };
11355
+ }
11356
+ const logoRoot = document.querySelector('[data-ohw-role="logo"], [data-ohw-logo]');
11357
+ const href = (logoRoot instanceof HTMLAnchorElement ? logoRoot.getAttribute("href") : null) || logoRoot?.closest("a")?.getAttribute("href") || "/";
11358
+ return { text, isPlaceholder, imageUrl: null, href, alt: text };
11359
+ }
11360
+ function applyLogoFromContent(content) {
11361
+ const hasLogoIdentity = LOGO_PLACEHOLDER_KEY in content || LOGO_TEXT_KEYS.some((key) => key in content) || LOGO_IMAGE_KEYS.some((key) => key in content) || LOGO_ALT_KEY in content || LOGO_HREF_KEYS.some((key) => key in content);
11362
+ if (!hasLogoIdentity) return false;
11363
+ const logoText = content[LOGO_TEXT_KEYS[0]] ?? content[LOGO_TEXT_KEYS[1]] ?? readLogoIdentityFromDom().text;
11364
+ const logoAlt = content[LOGO_ALT_KEY] ?? logoText;
11365
+ const rawLogoImage = content[LOGO_IMAGE_URL_KEY] ?? content["footer-logo"] ?? content["footer-logo-image"] ?? null;
11366
+ const logoImageUrl = typeof rawLogoImage === "string" && rawLogoImage.trim() ? rawLogoImage.trim() : null;
11367
+ const imageExplicitlyCleared = LOGO_IMAGE_KEYS.some((key) => key in content) && !logoImageUrl;
11368
+ const logoIsPlaceholder = LOGO_PLACEHOLDER_KEY in content ? content[LOGO_PLACEHOLDER_KEY] !== "false" : !logoImageUrl && (!logoText.trim() || logoText === PLACEHOLDER_BUSINESS_NAME);
11369
+ if (logoImageUrl) {
11370
+ applyLogoImage(logoImageUrl, logoAlt);
11371
+ } else {
11372
+ if (imageExplicitlyCleared) applyLogoImage(null, logoAlt);
11373
+ applyLogoIdentity(logoText, logoIsPlaceholder);
11374
+ }
11375
+ const logoHref = content["nav-logo-href"] ?? content["footer-logo-href"] ?? content["logo-href"];
11376
+ if (typeof logoHref === "string" && logoHref.trim()) {
11377
+ applyLogoHref(logoHref);
11378
+ }
11379
+ return true;
11380
+ }
11381
+
11382
+ // src/lib/logo-size.ts
11383
+ var LOGO_SIZE_DEFAULTS = {
11384
+ navbar: 28,
11385
+ footer: 32
11386
+ };
11387
+ var LOGO_SIZE_MIN = 16;
11388
+ var LOGO_SIZE_MAX = 80;
11389
+ var LOGO_SIZE_DESKTOP_KEYS = {
11390
+ navbar: "nav-logo-size",
11391
+ footer: "footer-logo-size"
11392
+ };
11393
+ var LOGO_SIZE_MOBILE_KEYS = {
11394
+ navbar: "nav-logo-size-mobile",
11395
+ footer: "footer-logo-size-mobile"
11396
+ };
11397
+ var LOGO_SIZE_KEYS = [
11398
+ LOGO_SIZE_DESKTOP_KEYS.navbar,
11399
+ LOGO_SIZE_DESKTOP_KEYS.footer,
11400
+ LOGO_SIZE_MOBILE_KEYS.navbar,
11401
+ LOGO_SIZE_MOBILE_KEYS.footer
11402
+ ];
11403
+ function isFooterLogoRoot2(root) {
11404
+ return Boolean(root.closest("footer") || root.closest('[data-ohw-section="footer"]'));
11405
+ }
11406
+ function getLogoPlacement(root) {
11407
+ return isFooterLogoRoot2(root) ? "footer" : "navbar";
11408
+ }
11409
+ function parseLogoSizePx(raw, fallback) {
11410
+ if (raw == null || raw === "") return fallback;
11411
+ const n = Number.parseFloat(raw);
11412
+ if (!Number.isFinite(n)) return fallback;
11413
+ return Math.min(LOGO_SIZE_MAX, Math.max(LOGO_SIZE_MIN, Math.round(n)));
11414
+ }
11415
+ function isMobileLogoSizeFollowing(content, placement) {
11416
+ const raw = content[LOGO_SIZE_MOBILE_KEYS[placement]];
11417
+ return raw == null || raw.trim() === "";
11418
+ }
11419
+ function resolveDesktopLogoSize(content, placement) {
11420
+ return parseLogoSizePx(content[LOGO_SIZE_DESKTOP_KEYS[placement]], LOGO_SIZE_DEFAULTS[placement]);
11421
+ }
11422
+ function resolveMobileLogoSize(content, placement) {
11423
+ if (isMobileLogoSizeFollowing(content, placement)) {
11424
+ return resolveDesktopLogoSize(content, placement);
11425
+ }
11426
+ return parseLogoSizePx(
11427
+ content[LOGO_SIZE_MOBILE_KEYS[placement]],
11428
+ resolveDesktopLogoSize(content, placement)
11429
+ );
11430
+ }
11431
+ function setRootSizeVars(root, desktopPx, mobilePx, following) {
11432
+ root.style.setProperty("--ohw-logo-size", `${desktopPx}px`);
11433
+ if (following) {
11434
+ root.style.removeProperty("--ohw-logo-size-mobile");
11435
+ } else {
11436
+ root.style.setProperty("--ohw-logo-size-mobile", `${mobilePx}px`);
11437
+ }
11438
+ root.querySelectorAll("img").forEach((img) => {
11439
+ img.style.height = "";
11440
+ img.style.maxHeight = "none";
11441
+ img.style.width = "auto";
11442
+ img.style.objectFit = "contain";
11443
+ });
11444
+ }
11445
+ function applyLogoSizes(content) {
11446
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11447
+ const placement = getLogoPlacement(root);
11448
+ const desktop = resolveDesktopLogoSize(content, placement);
11449
+ const following = isMobileLogoSizeFollowing(content, placement);
11450
+ const mobile = following ? desktop : resolveMobileLogoSize(content, placement);
11451
+ setRootSizeVars(root, desktop, mobile, following);
11452
+ });
11453
+ }
11454
+ function applyLogoSizeToPlacement(placement, desktopPx, mobilePx, following) {
11455
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11456
+ if (getLogoPlacement(root) !== placement) return;
11457
+ setRootSizeVars(root, desktopPx, mobilePx, following);
11458
+ });
11459
+ }
11460
+ function logoHasUploadedImage(logoEl) {
11461
+ if (logoEl.hasAttribute("data-ohw-placeholder")) return false;
11462
+ const img = logoEl.querySelector('img[data-ohw-key="nav-logo-image"], img[data-ohw-key="footer-logo"], img[data-ohw-key="footer-logo-image"]') ?? logoEl.querySelector("img");
11463
+ if (!img) return false;
11464
+ const src = img.getAttribute("src")?.trim() ?? "";
11465
+ if (!src || src.startsWith("data:")) return false;
11466
+ if (img.style.display === "none") return false;
11467
+ return true;
11468
+ }
11469
+ function getLogoInteractionRect(logoEl) {
11470
+ if (logoHasUploadedImage(logoEl)) {
11471
+ const img = logoEl.querySelector('img[data-ohw-key="nav-logo-image"], img[data-ohw-key="footer-logo"], img[data-ohw-key="footer-logo-image"]') ?? logoEl.querySelector("img");
11472
+ if (img) {
11473
+ const r2 = img.getBoundingClientRect();
11474
+ if (r2.width > 0 && r2.height > 0) return r2;
11475
+ }
11476
+ }
11477
+ const text = logoEl.querySelector(
11478
+ '[data-ohw-key="nav-logo-text"], [data-ohw-key="footer-logo-text"]'
11479
+ );
11480
+ if (text) {
11481
+ const style = window.getComputedStyle(text);
11482
+ if (style.display !== "none" && style.visibility !== "hidden") {
11483
+ const r2 = text.getBoundingClientRect();
11484
+ if (r2.width > 0 && r2.height > 0) return r2;
11485
+ }
11486
+ }
11487
+ return logoEl.getBoundingClientRect();
11488
+ }
11489
+ function readLogoSizeState(content, placement) {
11490
+ const desktopPx = resolveDesktopLogoSize(content, placement);
11491
+ const mobileFollowing = isMobileLogoSizeFollowing(content, placement);
11492
+ const mobilePx = mobileFollowing ? desktopPx : resolveMobileLogoSize(content, placement);
11493
+ return { desktopPx, mobilePx, mobileFollowing };
11494
+ }
11495
+
11496
+ // src/lib/site-wide-scope.ts
11497
+ function getLogoElement(el) {
11498
+ const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
11499
+ if (marked) return marked;
11500
+ if (el.closest('[data-ohw-editable="icon"]')) return null;
11501
+ const root = el.closest("nav, [data-ohw-nav-root], footer");
11502
+ if (!root) return null;
11503
+ const anchor = el.closest("a");
11504
+ if (anchor && root.contains(anchor) && !anchor.hasAttribute("data-ohw-href-key") && !anchor.closest("[data-ohw-nav-container]") && Boolean(anchor.querySelector("img") || anchor.matches("img"))) {
11505
+ return anchor;
11506
+ }
11507
+ const img = el.matches("img") ? el : null;
11508
+ if (img && !img.closest("[data-ohw-href-key]") && !img.closest("[data-ohw-nav-container]") && (img.closest("footer") || img.closest("nav, [data-ohw-nav-root]"))) {
11509
+ return img;
11510
+ }
11511
+ return null;
11512
+ }
11513
+ function isInFooter(el) {
11514
+ if (!el) return false;
11515
+ return Boolean(el.closest("footer") || el.closest('[data-ohw-section="footer"]'));
11516
+ }
11517
+ function isSiteWideElement(el) {
11518
+ if (!el) return false;
11519
+ if (getLogoElement(el)) return true;
11520
+ if (isInFooter(el)) return true;
11521
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-root")) {
11522
+ return true;
11523
+ }
11524
+ if (el.hasAttribute("data-ohw-href-key") && el.closest("nav, [data-ohw-nav-root], [data-ohw-nav-drawer], aside")) {
11525
+ return true;
11526
+ }
11527
+ if (el.closest('[data-ohw-role="navbar-button"]')) return true;
11528
+ return false;
11529
+ }
11530
+ function isSiteWideScopeActive(args) {
11531
+ return isSiteWideElement(args.selected) || isSiteWideElement(args.hoveredItem) || isSiteWideElement(args.hoveredNavContainer) || isSiteWideElement(args.active);
11532
+ }
11533
+
10851
11534
  // src/lib/add-footer-column.ts
10852
11535
  function buildFooterColumnEditContentPatch(result) {
10853
11536
  return {
@@ -11069,16 +11752,127 @@ function FloatingPanel({
11069
11752
  );
11070
11753
  }
11071
11754
 
11072
- // src/ui/socials-display-panel.tsx
11755
+ // src/ui/logo-size-panel.tsx
11756
+ import { ArrowUpRight, Link as Link2 } from "lucide-react";
11073
11757
  import { jsx as jsx27, jsxs as jsxs17 } from "react/jsx-runtime";
11758
+ function SizeSlider({
11759
+ value,
11760
+ onChange
11761
+ }) {
11762
+ const pct = (value - LOGO_SIZE_MIN) / (LOGO_SIZE_MAX - LOGO_SIZE_MIN) * 100;
11763
+ return /* @__PURE__ */ jsxs17("div", { className: "flex w-full flex-col gap-3", children: [
11764
+ /* @__PURE__ */ jsxs17("div", { className: "flex w-full items-center gap-2 text-sm font-medium leading-5", children: [
11765
+ /* @__PURE__ */ jsx27("span", { className: "min-w-0 flex-1 text-foreground", children: "Size" }),
11766
+ /* @__PURE__ */ jsxs17("span", { className: "shrink-0 whitespace-nowrap text-muted-foreground", children: [
11767
+ value,
11768
+ " px"
11769
+ ] })
11770
+ ] }),
11771
+ /* @__PURE__ */ jsxs17("div", { className: "relative h-2 w-full rounded-full bg-primary-50", children: [
11772
+ /* @__PURE__ */ jsx27(
11773
+ "div",
11774
+ {
11775
+ className: "absolute inset-y-0 left-0 rounded-full bg-primary",
11776
+ style: { width: `${pct}%` }
11777
+ }
11778
+ ),
11779
+ /* @__PURE__ */ jsx27(
11780
+ "input",
11781
+ {
11782
+ type: "range",
11783
+ min: LOGO_SIZE_MIN,
11784
+ max: LOGO_SIZE_MAX,
11785
+ step: 1,
11786
+ value,
11787
+ "aria-label": "Logo size",
11788
+ className: cn(
11789
+ "absolute inset-0 h-full w-full cursor-pointer appearance-none bg-transparent",
11790
+ "[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:size-5",
11791
+ "[&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2",
11792
+ "[&::-webkit-slider-thumb]:border-primary [&::-webkit-slider-thumb]:bg-background",
11793
+ "[&::-moz-range-thumb]:size-5 [&::-moz-range-thumb]:rounded-full",
11794
+ "[&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-primary",
11795
+ "[&::-moz-range-thumb]:bg-background"
11796
+ ),
11797
+ onChange: (e) => onChange(Number(e.target.value))
11798
+ }
11799
+ )
11800
+ ] })
11801
+ ] });
11802
+ }
11803
+ function LogoSizePanel({
11804
+ viewport,
11805
+ sizePx,
11806
+ mobileFollowing = true,
11807
+ onSizeChange,
11808
+ onCustomizeMobile,
11809
+ onResetMobile,
11810
+ onUpdateEverywhere,
11811
+ className
11812
+ }) {
11813
+ const showFollowing = viewport === "mobile" && mobileFollowing;
11814
+ const showMobileSlider = viewport === "mobile" && !mobileFollowing;
11815
+ const showDesktopSlider = viewport === "desktop";
11816
+ return /* @__PURE__ */ jsxs17("div", { className: cn("flex w-full flex-col gap-4", className), children: [
11817
+ showFollowing ? /* @__PURE__ */ jsxs17("div", { className: "flex w-full flex-col gap-2", children: [
11818
+ /* @__PURE__ */ jsxs17("div", { className: "flex items-start gap-1", children: [
11819
+ /* @__PURE__ */ jsx27(Link2, { size: 16, className: "mt-0.5 shrink-0 text-foreground", "aria-hidden": true }),
11820
+ /* @__PURE__ */ jsx27("p", { className: "text-sm font-semibold leading-5 text-foreground", children: "Following desktop size" })
11821
+ ] }),
11822
+ /* @__PURE__ */ jsx27("p", { className: "text-sm leading-5 text-muted-foreground", children: "Mobile uses the desktop size until you customize it. Change the desktop size and it follows automatically." }),
11823
+ /* @__PURE__ */ jsx27(
11824
+ Button,
11825
+ {
11826
+ type: "button",
11827
+ variant: "outline",
11828
+ size: "sm",
11829
+ className: "h-9 w-full min-w-0 cursor-pointer",
11830
+ onClick: onCustomizeMobile,
11831
+ children: "Customize for mobile"
11832
+ }
11833
+ )
11834
+ ] }) : null,
11835
+ showDesktopSlider || showMobileSlider ? /* @__PURE__ */ jsx27(SizeSlider, { value: sizePx, onChange: onSizeChange }) : null,
11836
+ showMobileSlider ? /* @__PURE__ */ jsx27(
11837
+ Button,
11838
+ {
11839
+ type: "button",
11840
+ variant: "outline",
11841
+ size: "sm",
11842
+ className: "h-9 w-full min-w-0 cursor-pointer",
11843
+ onClick: onResetMobile,
11844
+ children: "Reset to desktop size"
11845
+ }
11846
+ ) : null,
11847
+ /* @__PURE__ */ jsx27("div", { className: "h-px w-full bg-border", role: "separator" }),
11848
+ /* @__PURE__ */ jsxs17(
11849
+ Button,
11850
+ {
11851
+ type: "button",
11852
+ variant: "outline",
11853
+ size: "sm",
11854
+ className: "h-9 w-full min-w-0 cursor-pointer gap-1",
11855
+ onClick: onUpdateEverywhere,
11856
+ children: [
11857
+ "Update logo everywhere",
11858
+ /* @__PURE__ */ jsx27(ArrowUpRight, { size: 16, "aria-hidden": true })
11859
+ ]
11860
+ }
11861
+ ),
11862
+ /* @__PURE__ */ jsx27("p", { className: "text-sm leading-5 text-muted-foreground", children: "Matches the right version to your background." })
11863
+ ] });
11864
+ }
11865
+
11866
+ // src/ui/socials-display-panel.tsx
11867
+ import { jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
11074
11868
  function DisplaySwitch({
11075
11869
  label,
11076
11870
  checked,
11077
11871
  disabled,
11078
11872
  onChange
11079
11873
  }) {
11080
- return /* @__PURE__ */ jsxs17("div", { className: "flex w-full items-center gap-2", children: [
11081
- /* @__PURE__ */ jsx27(
11874
+ return /* @__PURE__ */ jsxs18("div", { className: "flex w-full items-center gap-2", children: [
11875
+ /* @__PURE__ */ jsx28(
11082
11876
  "span",
11083
11877
  {
11084
11878
  className: cn(
@@ -11088,7 +11882,7 @@ function DisplaySwitch({
11088
11882
  children: label
11089
11883
  }
11090
11884
  ),
11091
- /* @__PURE__ */ jsx27(
11885
+ /* @__PURE__ */ jsx28(
11092
11886
  "button",
11093
11887
  {
11094
11888
  type: "button",
@@ -11102,7 +11896,7 @@ function DisplaySwitch({
11102
11896
  checked ? "bg-primary" : "bg-primary-50",
11103
11897
  disabled ? "cursor-default opacity-50" : "cursor-pointer"
11104
11898
  ),
11105
- children: /* @__PURE__ */ jsx27(
11899
+ children: /* @__PURE__ */ jsx28(
11106
11900
  "span",
11107
11901
  {
11108
11902
  className: cn(
@@ -11116,8 +11910,8 @@ function DisplaySwitch({
11116
11910
  ] });
11117
11911
  }
11118
11912
  function SocialsDisplayPanel({ display, onChange, className }) {
11119
- return /* @__PURE__ */ jsxs17("div", { className: cn("flex w-full flex-col gap-3", className), children: [
11120
- /* @__PURE__ */ jsx27(
11913
+ return /* @__PURE__ */ jsxs18("div", { className: cn("flex w-full flex-col gap-3", className), children: [
11914
+ /* @__PURE__ */ jsx28(
11121
11915
  DisplaySwitch,
11122
11916
  {
11123
11917
  label: "Text",
@@ -11126,7 +11920,7 @@ function SocialsDisplayPanel({ display, onChange, className }) {
11126
11920
  onChange: (text) => onChange({ ...display, text })
11127
11921
  }
11128
11922
  ),
11129
- /* @__PURE__ */ jsx27(
11923
+ /* @__PURE__ */ jsx28(
11130
11924
  DisplaySwitch,
11131
11925
  {
11132
11926
  label: "Icon",
@@ -11687,7 +12481,7 @@ function useNavItemDrag({
11687
12481
 
11688
12482
  // src/ui/footer-container-chrome.tsx
11689
12483
  import { Plus as Plus2 } from "lucide-react";
11690
- import { jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
12484
+ import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
11691
12485
  function FooterContainerChrome({
11692
12486
  rect,
11693
12487
  onAdd,
@@ -11695,7 +12489,7 @@ function FooterContainerChrome({
11695
12489
  }) {
11696
12490
  const chromeGap = 6;
11697
12491
  const buttonMargin = 7;
11698
- return /* @__PURE__ */ jsx28(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx28(
12492
+ return /* @__PURE__ */ jsx29(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx29(
11699
12493
  "div",
11700
12494
  {
11701
12495
  "data-ohw-footer-container-chrome": "",
@@ -11707,8 +12501,8 @@ function FooterContainerChrome({
11707
12501
  width: rect.width + chromeGap * 2,
11708
12502
  height: rect.height + chromeGap * 2
11709
12503
  },
11710
- children: /* @__PURE__ */ jsxs18(Tooltip, { children: [
11711
- /* @__PURE__ */ jsx28(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx28(
12504
+ children: /* @__PURE__ */ jsxs19(Tooltip, { children: [
12505
+ /* @__PURE__ */ jsx29(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx29(
11712
12506
  "button",
11713
12507
  {
11714
12508
  type: "button",
@@ -11727,10 +12521,10 @@ function FooterContainerChrome({
11727
12521
  if (addDisabled) return;
11728
12522
  onAdd();
11729
12523
  },
11730
- children: /* @__PURE__ */ jsx28(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
12524
+ children: /* @__PURE__ */ jsx29(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
11731
12525
  }
11732
12526
  ) }),
11733
- /* @__PURE__ */ jsx28(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
12527
+ /* @__PURE__ */ jsx29(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
11734
12528
  ] })
11735
12529
  }
11736
12530
  ) });
@@ -11913,6 +12707,18 @@ function collectEditableNodes(extraContent, root = document) {
11913
12707
  }
11914
12708
  if (extraContent && !isScoped) {
11915
12709
  applyNavFooterDeleteOverrides(byKey, extraContent);
12710
+ for (const key of LOGO_IMAGE_KEYS) {
12711
+ if (!(key in extraContent)) continue;
12712
+ byKey.set(key, { key, type: "image", text: extraContent[key] ?? "" });
12713
+ }
12714
+ for (const key of [LOGO_PLACEHOLDER_KEY, LOGO_ALT_KEY]) {
12715
+ if (!(key in extraContent)) continue;
12716
+ byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
12717
+ }
12718
+ for (const key of LOGO_SIZE_KEYS) {
12719
+ if (!(key in extraContent)) continue;
12720
+ byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
12721
+ }
11916
12722
  }
11917
12723
  return Array.from(byKey.values());
11918
12724
  }
@@ -12179,13 +12985,13 @@ function deleteSelectedNavFooterItem(deps) {
12179
12985
 
12180
12986
  // src/ui/navbar-container-chrome.tsx
12181
12987
  import { Plus as Plus3 } from "lucide-react";
12182
- import { jsx as jsx29 } from "react/jsx-runtime";
12988
+ import { jsx as jsx30 } from "react/jsx-runtime";
12183
12989
  function NavbarContainerChrome({
12184
12990
  rect,
12185
12991
  onAdd
12186
12992
  }) {
12187
12993
  const chromeGap = 6;
12188
- return /* @__PURE__ */ jsx29(
12994
+ return /* @__PURE__ */ jsx30(
12189
12995
  "div",
12190
12996
  {
12191
12997
  "data-ohw-navbar-container-chrome": "",
@@ -12197,7 +13003,7 @@ function NavbarContainerChrome({
12197
13003
  width: rect.width + chromeGap * 2,
12198
13004
  height: rect.height + chromeGap * 2
12199
13005
  },
12200
- children: /* @__PURE__ */ jsx29(
13006
+ children: /* @__PURE__ */ jsx30(
12201
13007
  "button",
12202
13008
  {
12203
13009
  type: "button",
@@ -12214,7 +13020,7 @@ function NavbarContainerChrome({
12214
13020
  e.stopPropagation();
12215
13021
  onAdd();
12216
13022
  },
12217
- children: /* @__PURE__ */ jsx29(Plus3, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
13023
+ children: /* @__PURE__ */ jsx30(Plus3, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
12218
13024
  }
12219
13025
  )
12220
13026
  }
@@ -12223,7 +13029,7 @@ function NavbarContainerChrome({
12223
13029
 
12224
13030
  // src/ui/drop-indicator.tsx
12225
13031
  import * as React10 from "react";
12226
- import { jsx as jsx30 } from "react/jsx-runtime";
13032
+ import { jsx as jsx31 } from "react/jsx-runtime";
12227
13033
  var dropIndicatorVariants = cva(
12228
13034
  "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
12229
13035
  {
@@ -12247,7 +13053,7 @@ var dropIndicatorVariants = cva(
12247
13053
  );
12248
13054
  var DropIndicator = React10.forwardRef(
12249
13055
  ({ className, direction, state, ...props }, ref) => {
12250
- return /* @__PURE__ */ jsx30(
13056
+ return /* @__PURE__ */ jsx31(
12251
13057
  "div",
12252
13058
  {
12253
13059
  ref,
@@ -12264,7 +13070,7 @@ var DropIndicator = React10.forwardRef(
12264
13070
  DropIndicator.displayName = "DropIndicator";
12265
13071
 
12266
13072
  // src/ui/badge.tsx
12267
- import { jsx as jsx31 } from "react/jsx-runtime";
13073
+ import { jsx as jsx32 } from "react/jsx-runtime";
12268
13074
  var badgeVariants = cva(
12269
13075
  "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
12270
13076
  {
@@ -12282,12 +13088,12 @@ var badgeVariants = cva(
12282
13088
  }
12283
13089
  );
12284
13090
  function Badge({ className, variant, ...props }) {
12285
- return /* @__PURE__ */ jsx31("div", { className: cn(badgeVariants({ variant }), className), ...props });
13091
+ return /* @__PURE__ */ jsx32("div", { className: cn(badgeVariants({ variant }), className), ...props });
12286
13092
  }
12287
13093
 
12288
13094
  // src/OhhwellsBridge.tsx
12289
- import { Link as Link2 } from "lucide-react";
12290
- import { Fragment as Fragment8, jsx as jsx32, jsxs as jsxs19 } from "react/jsx-runtime";
13095
+ import { Link as Link3 } from "lucide-react";
13096
+ import { Fragment as Fragment8, jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
12291
13097
  var PRIMARY3 = "#0885FE";
12292
13098
  var IMAGE_FADE_MS = 300;
12293
13099
  function runOpacityFade(el, onDone) {
@@ -12381,21 +13187,10 @@ function parseSchedulingInsertAfter(insertAfter) {
12381
13187
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
12382
13188
  };
12383
13189
  }
12384
- function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
12385
- const parsed = parseSchedulingInsertAfter(insertAfter);
12386
- const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
12387
- const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
12388
- return { effectiveInsertAfter, insertBefore };
12389
- }
12390
- function getSchedulingMountPoint(insertAfter) {
12391
- const { anchor } = parseSchedulingInsertAfter(insertAfter);
12392
- let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
12393
- if (!anchorEl && anchor === "scheduling") {
12394
- const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
12395
- anchorEl = widgets.at(-1) ?? null;
12396
- }
12397
- if (!anchorEl) return null;
12398
- return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
13190
+ function resolveEntryAnchor(entry) {
13191
+ if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
13192
+ const parsed = parseSchedulingInsertAfter(entry.insertAfter);
13193
+ return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
12399
13194
  }
12400
13195
  function schedulingMountDepth(insertAfter) {
12401
13196
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -12412,8 +13207,7 @@ function getPageSchedulingEntries(raw) {
12412
13207
  }
12413
13208
  }
12414
13209
  function isSchedulingWidgetMissing(entry) {
12415
- const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
12416
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
13210
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
12417
13211
  }
12418
13212
  function hasMissingSchedulingWidgets(entries) {
12419
13213
  return entries.some(isSchedulingWidgetMissing);
@@ -12443,16 +13237,17 @@ function initSectionsFromContent(content, removeExisting = false) {
12443
13237
  } catch {
12444
13238
  }
12445
13239
  }
12446
- function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
12447
- const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
12448
- const sectionId = schedulingSectionId(effectiveInsertAfter);
13240
+ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
13241
+ const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
13242
+ const sectionId = schedulingSectionId(widgetId);
12449
13243
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
12450
- const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
12451
- if (!mountPoint) return false;
13244
+ const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
13245
+ if (!anchorEl) return false;
13246
+ const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
12452
13247
  const container = document.createElement("div");
12453
13248
  container.dataset.ohwSectionContainer = "scheduling";
12454
- if (insertBefore) {
12455
- const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
13249
+ if (beforeId) {
13250
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
12456
13251
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
12457
13252
  if (!beforePoint) return false;
12458
13253
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -12463,19 +13258,25 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
12463
13258
  }
12464
13259
  tail.insertAdjacentElement("afterend", container);
12465
13260
  }
12466
- const root = createRoot2(container);
12467
- flushSync2(() => {
12468
- root.render(
12469
- /* @__PURE__ */ jsx32(
12470
- SchedulingWidget,
12471
- {
12472
- notifyOnConnect,
12473
- initialScheduleId: scheduleId,
12474
- insertAfter: effectiveInsertAfter
12475
- }
12476
- )
12477
- );
12478
- });
13261
+ try {
13262
+ const root = createRoot2(container);
13263
+ flushSync2(() => {
13264
+ root.render(
13265
+ /* @__PURE__ */ jsx33(
13266
+ SchedulingWidget,
13267
+ {
13268
+ notifyOnConnect,
13269
+ initialScheduleId: scheduleId,
13270
+ insertAfter: widgetId
13271
+ }
13272
+ )
13273
+ );
13274
+ });
13275
+ } catch (err) {
13276
+ console.error("[ow:scheduling] render threw", err);
13277
+ container.remove();
13278
+ return false;
13279
+ }
12479
13280
  const tracker = getSectionsTracker();
12480
13281
  let sections = [];
12481
13282
  try {
@@ -12483,10 +13284,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
12483
13284
  } catch {
12484
13285
  }
12485
13286
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
12486
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
13287
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
12487
13288
  sections.push({
12488
13289
  type: "scheduling",
12489
- insertAfter: effectiveInsertAfter,
13290
+ insertAfter: widgetId,
13291
+ anchorId,
13292
+ beforeId: beforeId ?? null,
12490
13293
  pagePath: window.location.pathname,
12491
13294
  ...scheduleId ? { scheduleId } : {}
12492
13295
  });
@@ -12500,7 +13303,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
12500
13303
  for (let i = pending.length - 1; i >= 0; i--) {
12501
13304
  const entry = pending[i];
12502
13305
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
12503
- if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
13306
+ const { anchorId, beforeId } = resolveEntryAnchor(entry);
13307
+ if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
12504
13308
  pending.splice(i, 1);
12505
13309
  }
12506
13310
  }
@@ -12644,6 +13448,13 @@ function isInsideLinkEditor(target) {
12644
13448
  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"]')
12645
13449
  );
12646
13450
  }
13451
+ function isInsideFloatingPanel(target) {
13452
+ return Boolean(target.closest("[data-ohw-floating-panel]"));
13453
+ }
13454
+ function isPointOverFloatingPanel(clientX, clientY) {
13455
+ const el = document.elementFromPoint(clientX, clientY);
13456
+ return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
13457
+ }
12647
13458
  function getHrefKeyFromElement(el) {
12648
13459
  if (!el) return null;
12649
13460
  const anchor = el.closest("[data-ohw-href-key]");
@@ -12881,7 +13692,7 @@ function getNavigationSelectionParent(el) {
12881
13692
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
12882
13693
  return getFooterLinksContainer();
12883
13694
  }
12884
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
13695
+ 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)) {
12885
13696
  return getNavigationRoot(el);
12886
13697
  }
12887
13698
  return null;
@@ -13127,7 +13938,7 @@ function EditGlowChrome({
13127
13938
  hideHandle = false
13128
13939
  }) {
13129
13940
  const GAP = SELECTION_CHROME_GAP2;
13130
- return /* @__PURE__ */ jsxs19(
13941
+ return /* @__PURE__ */ jsxs20(
13131
13942
  "div",
13132
13943
  {
13133
13944
  ref: elRef,
@@ -13142,7 +13953,7 @@ function EditGlowChrome({
13142
13953
  zIndex: 2147483646
13143
13954
  },
13144
13955
  children: [
13145
- /* @__PURE__ */ jsx32(
13956
+ /* @__PURE__ */ jsx33(
13146
13957
  "div",
13147
13958
  {
13148
13959
  style: {
@@ -13155,7 +13966,7 @@ function EditGlowChrome({
13155
13966
  }
13156
13967
  }
13157
13968
  ),
13158
- reorderHrefKey && !hideHandle && /* @__PURE__ */ jsx32(
13969
+ reorderHrefKey && !hideHandle && /* @__PURE__ */ jsx33(
13159
13970
  "div",
13160
13971
  {
13161
13972
  "data-ohw-drag-handle-container": "",
@@ -13167,7 +13978,7 @@ function EditGlowChrome({
13167
13978
  transform: "translate(calc(-100% - 7px), -50%)",
13168
13979
  pointerEvents: dragDisabled ? "none" : "auto"
13169
13980
  },
13170
- children: /* @__PURE__ */ jsx32(
13981
+ children: /* @__PURE__ */ jsx33(
13171
13982
  DragHandle,
13172
13983
  {
13173
13984
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -13377,7 +14188,7 @@ function FloatingToolbar({
13377
14188
  return () => ro.disconnect();
13378
14189
  }, [showEditLink, activeCommands]);
13379
14190
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, measuredW);
13380
- return /* @__PURE__ */ jsx32(
14191
+ return /* @__PURE__ */ jsx33(
13381
14192
  "div",
13382
14193
  {
13383
14194
  ref: setRefs,
@@ -13389,12 +14200,12 @@ function FloatingToolbar({
13389
14200
  zIndex: 2147483647,
13390
14201
  pointerEvents: "auto"
13391
14202
  },
13392
- children: /* @__PURE__ */ jsxs19(CustomToolbar, { children: [
13393
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs19(React11.Fragment, { children: [
13394
- gi > 0 && /* @__PURE__ */ jsx32(CustomToolbarDivider, {}),
14203
+ children: /* @__PURE__ */ jsxs20(CustomToolbar, { children: [
14204
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs20(React11.Fragment, { children: [
14205
+ gi > 0 && /* @__PURE__ */ jsx33(CustomToolbarDivider, {}),
13395
14206
  btns.map((btn) => {
13396
14207
  const isActive = activeCommands.has(btn.cmd);
13397
- return /* @__PURE__ */ jsx32(
14208
+ return /* @__PURE__ */ jsx33(
13398
14209
  CustomToolbarButton,
13399
14210
  {
13400
14211
  title: btn.title,
@@ -13403,7 +14214,7 @@ function FloatingToolbar({
13403
14214
  e.preventDefault();
13404
14215
  onCommand(btn.cmd);
13405
14216
  },
13406
- children: /* @__PURE__ */ jsx32(
14217
+ children: /* @__PURE__ */ jsx33(
13407
14218
  "svg",
13408
14219
  {
13409
14220
  width: "16",
@@ -13424,7 +14235,7 @@ function FloatingToolbar({
13424
14235
  );
13425
14236
  })
13426
14237
  ] }, gi)),
13427
- showEditLink ? /* @__PURE__ */ jsx32(
14238
+ showEditLink ? /* @__PURE__ */ jsx33(
13428
14239
  CustomToolbarButton,
13429
14240
  {
13430
14241
  type: "button",
@@ -13438,7 +14249,7 @@ function FloatingToolbar({
13438
14249
  e.preventDefault();
13439
14250
  e.stopPropagation();
13440
14251
  },
13441
- children: /* @__PURE__ */ jsx32(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
14252
+ children: /* @__PURE__ */ jsx33(Link3, { className: "size-4 shrink-0", "aria-hidden": true })
13442
14253
  }
13443
14254
  ) : null
13444
14255
  ] })
@@ -13455,7 +14266,7 @@ function StateToggle({
13455
14266
  states,
13456
14267
  onStateChange
13457
14268
  }) {
13458
- return /* @__PURE__ */ jsx32(
14269
+ return /* @__PURE__ */ jsx33(
13459
14270
  ToggleGroup,
13460
14271
  {
13461
14272
  "data-ohw-state-toggle": "",
@@ -13469,11 +14280,12 @@ function StateToggle({
13469
14280
  left: rect.right - 8,
13470
14281
  transform: "translateX(-100%)"
13471
14282
  },
13472
- children: states.map((state) => /* @__PURE__ */ jsx32(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
14283
+ children: states.map((state) => /* @__PURE__ */ jsx33(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
13473
14284
  }
13474
14285
  );
13475
14286
  }
13476
14287
  var contentCache = /* @__PURE__ */ new Map();
14288
+ var fetchedContentPaths = /* @__PURE__ */ new Set();
13477
14289
  function resolveSubdomain(subdomainFromQuery) {
13478
14290
  if (subdomainFromQuery) return subdomainFromQuery;
13479
14291
  if (typeof window !== "undefined") {
@@ -13568,8 +14380,14 @@ function OhhwellsBridge() {
13568
14380
  });
13569
14381
  const selectFrameRef = useRef9(() => {
13570
14382
  });
14383
+ const selectLogoRef = useRef9(() => {
14384
+ });
14385
+ const openLogoSizePanelRef = useRef9(() => {
14386
+ });
13571
14387
  const deselectRef = useRef9(() => {
13572
14388
  });
14389
+ const closeFloatingPanelOnlyRef = useRef9(() => {
14390
+ });
13573
14391
  const reselectNavigationItemRef = useRef9(() => {
13574
14392
  });
13575
14393
  const commitNavigationTextEditRef = useRef9(() => {
@@ -13630,11 +14448,6 @@ function OhhwellsBridge() {
13630
14448
  const [isItemDragging, setIsItemDragging] = useState11(false);
13631
14449
  const [isFooterFrameSelection, setIsFooterFrameSelection] = useState11(false);
13632
14450
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
13633
- const [floatingPanel, setFloatingPanel] = useState11(null);
13634
- const floatingPanelOpenRef = useRef9(false);
13635
- floatingPanelOpenRef.current = floatingPanel !== null;
13636
- const [floatingPanelPos, setFloatingPanelPos] = useState11(null);
13637
- const [parentScrollSnap, setParentScrollSnap] = useState11(null);
13638
14451
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState11(null);
13639
14452
  const [footerHeadingVisible, setFooterHeadingVisible] = useState11(null);
13640
14453
  const footerDragRef = useRef9(null);
@@ -13649,7 +14462,16 @@ function OhhwellsBridge() {
13649
14462
  const addNavAfterAnchorRef = useRef9(null);
13650
14463
  const editContentRef = useRef9({});
13651
14464
  const aiSectionsRef = useRef9("");
14465
+ const brandKitRef = useRef9("");
14466
+ const stylesRef = useRef9("");
13652
14467
  const pendingDeleteUndoRef = useRef9(null);
14468
+ const [floatingPanel, setFloatingPanel] = useState11(null);
14469
+ const floatingPanelOpenRef = useRef9(false);
14470
+ const setFloatingPanelRef = useRef9(setFloatingPanel);
14471
+ const [floatingPanelPos, setFloatingPanelPos] = useState11(null);
14472
+ const [logoSizeDraft, setLogoSizeDraft] = useState11(null);
14473
+ const [editorViewport, setEditorViewport] = useState11("desktop");
14474
+ const [parentScrollSnap, setParentScrollSnap] = useState11(null);
13653
14475
  const [sitePages, setSitePages] = useState11([]);
13654
14476
  const [sectionsByPath, setSectionsByPath] = useState11({});
13655
14477
  const sectionsPrefetchGenRef = useRef9(0);
@@ -13658,7 +14480,18 @@ function OhhwellsBridge() {
13658
14480
  const linkPopoverOpenRef = useRef9(false);
13659
14481
  const linkPopoverGraceUntilRef = useRef9(0);
13660
14482
  setLinkPopoverRef.current = setLinkPopover;
14483
+ setFloatingPanelRef.current = setFloatingPanel;
13661
14484
  linkPopoverSessionRef.current = linkPopover;
14485
+ floatingPanelOpenRef.current = Boolean(floatingPanel);
14486
+ useEffect12(() => {
14487
+ const syncViewport = () => {
14488
+ const next = window.innerWidth <= 480 ? "mobile" : "desktop";
14489
+ setEditorViewport((prev) => prev === next ? prev : next);
14490
+ };
14491
+ syncViewport();
14492
+ window.addEventListener("resize", syncViewport);
14493
+ return () => window.removeEventListener("resize", syncViewport);
14494
+ }, []);
13662
14495
  const {
13663
14496
  navDragRef,
13664
14497
  navDropSlots,
@@ -13881,6 +14714,10 @@ function OhhwellsBridge() {
13881
14714
  setIsItemDragging(false);
13882
14715
  hoveredNavContainerRef.current = null;
13883
14716
  setHoveredNavContainerRect(null);
14717
+ hoveredItemElRef.current = null;
14718
+ setHoveredItemRect(null);
14719
+ setFloatingPanel(null);
14720
+ setLogoSizeDraft(null);
13884
14721
  if (!activeElRef.current) {
13885
14722
  setNavGroupForceOpen(null, false);
13886
14723
  setToolbarRect(null);
@@ -14586,6 +15423,8 @@ function OhhwellsBridge() {
14586
15423
  setToolbarRect(anchor.getBoundingClientRect());
14587
15424
  setToolbarShowEditLink(false);
14588
15425
  setActiveCommands(/* @__PURE__ */ new Set());
15426
+ setFloatingPanel(null);
15427
+ setLogoSizeDraft(null);
14589
15428
  }, [deactivate, markSelected]);
14590
15429
  const selectFrame = useCallback7((el) => {
14591
15430
  if (!isNavigationContainer(el)) return;
@@ -14635,7 +15474,51 @@ function OhhwellsBridge() {
14635
15474
  setToolbarRect(el.getBoundingClientRect());
14636
15475
  setToolbarShowEditLink(false);
14637
15476
  setActiveCommands(/* @__PURE__ */ new Set());
15477
+ setFloatingPanel(null);
15478
+ setLogoSizeDraft(null);
14638
15479
  }, [deactivate, markSelected, postToParent2]);
15480
+ const selectLogo = useCallback7(
15481
+ (logoEl) => {
15482
+ if (activeElRef.current) deactivate();
15483
+ selectedElRef.current = logoEl;
15484
+ selectedHrefKeyRef.current = null;
15485
+ selectedFooterColAttrRef.current = null;
15486
+ markSelected(logoEl);
15487
+ setSelectedIsCta(false);
15488
+ setSelectedIsSocial(false);
15489
+ setSelectedIsSocialsRow(false);
15490
+ clearHrefKeyHover(logoEl);
15491
+ hoveredNavContainerRef.current = null;
15492
+ setHoveredNavContainerRect(null);
15493
+ setHoveredItemRect(null);
15494
+ hoveredItemElRef.current = null;
15495
+ siblingHintElRef.current = null;
15496
+ setSiblingHintRect(null);
15497
+ setSiblingHintRects([]);
15498
+ setIsItemDragging(false);
15499
+ setReorderHrefKey(null);
15500
+ setReorderDragDisabled(false);
15501
+ setIsFooterFrameSelection(false);
15502
+ setToolbarVariant("logo");
15503
+ setToolbarRect(getLogoInteractionRect(logoEl));
15504
+ setToolbarShowEditLink(false);
15505
+ setActiveCommands(/* @__PURE__ */ new Set());
15506
+ },
15507
+ [deactivate, markSelected]
15508
+ );
15509
+ const openLogoSizePanel = useCallback7((logoEl) => {
15510
+ const placement = getLogoPlacement(logoEl);
15511
+ const draft = readLogoSizeState(editContentRef.current, placement);
15512
+ setLogoSizeDraft(draft);
15513
+ setParentScrollSnap(parentScrollRef.current);
15514
+ setFloatingPanel({
15515
+ key: `logo-size:${placement}`,
15516
+ title: "Logo",
15517
+ context: placement === "navbar" ? "Navbar" : "Footer",
15518
+ kind: "logo-size",
15519
+ placement
15520
+ });
15521
+ }, []);
14639
15522
  const openSocialsDisplayPanel = useCallback7((row) => {
14640
15523
  setParentScrollSnap(parentScrollRef.current);
14641
15524
  setFloatingPanel({
@@ -14671,13 +15554,53 @@ function OhhwellsBridge() {
14671
15554
  );
14672
15555
  const closeFloatingPanelOnly = useCallback7(() => {
14673
15556
  setFloatingPanel(null);
15557
+ setLogoSizeDraft(null);
14674
15558
  }, []);
14675
- const closeFloatingPanelOnlyRef = useRef9(closeFloatingPanelOnly);
14676
- closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
14677
15559
  const closeFloatingPanelAndDeselect = useCallback7(() => {
14678
15560
  setFloatingPanel(null);
15561
+ setLogoSizeDraft(null);
14679
15562
  deselectRef.current();
14680
15563
  }, []);
15564
+ const persistLogoSizeDraft = useCallback7(
15565
+ (placement, draft) => {
15566
+ const desktopKey = LOGO_SIZE_DESKTOP_KEYS[placement];
15567
+ const mobileKey = LOGO_SIZE_MOBILE_KEYS[placement];
15568
+ const nodes = [
15569
+ { key: desktopKey, text: String(draft.desktopPx) }
15570
+ ];
15571
+ if (draft.mobileFollowing) {
15572
+ nodes.push({ key: mobileKey, text: "" });
15573
+ } else {
15574
+ nodes.push({ key: mobileKey, text: String(draft.mobilePx) });
15575
+ }
15576
+ editContentRef.current = {
15577
+ ...editContentRef.current,
15578
+ [desktopKey]: String(draft.desktopPx),
15579
+ [mobileKey]: draft.mobileFollowing ? "" : String(draft.mobilePx)
15580
+ };
15581
+ applyLogoSizeToPlacement(
15582
+ placement,
15583
+ draft.desktopPx,
15584
+ draft.mobileFollowing ? draft.desktopPx : draft.mobilePx,
15585
+ draft.mobileFollowing
15586
+ );
15587
+ postToParent2({ type: "ow:change", nodes });
15588
+ requestAnimationFrame(() => {
15589
+ const selected = selectedElRef.current;
15590
+ if (!selected || toolbarVariantRef.current !== "logo") return;
15591
+ const rect = getLogoInteractionRect(selected);
15592
+ setToolbarRect(rect);
15593
+ if (glowElRef.current) {
15594
+ const GAP = SELECTION_CHROME_GAP2;
15595
+ glowElRef.current.style.top = `${rect.top - GAP}px`;
15596
+ glowElRef.current.style.left = `${rect.left - GAP}px`;
15597
+ glowElRef.current.style.width = `${rect.width + GAP * 2}px`;
15598
+ glowElRef.current.style.height = `${rect.height + GAP * 2}px`;
15599
+ }
15600
+ });
15601
+ },
15602
+ [postToParent2]
15603
+ );
14681
15604
  const activate = useCallback7((el, options) => {
14682
15605
  if (activeElRef.current === el) return;
14683
15606
  if (isIconEditable(el)) return;
@@ -14758,7 +15681,37 @@ function OhhwellsBridge() {
14758
15681
  deactivateRef.current = deactivate;
14759
15682
  selectRef.current = select;
14760
15683
  selectFrameRef.current = selectFrame;
15684
+ selectLogoRef.current = selectLogo;
15685
+ openLogoSizePanelRef.current = openLogoSizePanel;
14761
15686
  deselectRef.current = deselect;
15687
+ closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
15688
+ const lastSiteWideScopeRef = useRef9(null);
15689
+ useEffect12(() => {
15690
+ if (!isEditMode) {
15691
+ if (lastSiteWideScopeRef.current !== false) {
15692
+ lastSiteWideScopeRef.current = false;
15693
+ postToParent2({ type: "ow:site-wide-scope", active: false });
15694
+ }
15695
+ return;
15696
+ }
15697
+ const active = isSiteWideScopeActive({
15698
+ selected: selectedElRef.current,
15699
+ hoveredItem: hoveredItemElRef.current,
15700
+ hoveredNavContainer: hoveredNavContainerRef.current,
15701
+ active: activeElRef.current
15702
+ });
15703
+ if (lastSiteWideScopeRef.current === active) return;
15704
+ lastSiteWideScopeRef.current = active;
15705
+ postToParent2({ type: "ow:site-wide-scope", active });
15706
+ }, [
15707
+ isEditMode,
15708
+ hoveredItemRect,
15709
+ hoveredNavContainerRect,
15710
+ toolbarVariant,
15711
+ toolbarRect,
15712
+ isFooterFrameSelection,
15713
+ postToParent2
15714
+ ]);
14762
15715
  useLayoutEffect4(() => {
14763
15716
  if (!subdomain || isEditMode) {
14764
15717
  setFetchState("done");
@@ -14770,9 +15723,23 @@ function OhhwellsBridge() {
14770
15723
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
14771
15724
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
14772
15725
  }
15726
+ if (typeof content[BRAND_KIT_KEY] === "string") {
15727
+ brandKitRef.current = content[BRAND_KIT_KEY];
15728
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
15729
+ }
15730
+ if (typeof content[STYLE_STORE_KEY] === "string") {
15731
+ stylesRef.current = content[STYLE_STORE_KEY];
15732
+ applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
15733
+ }
15734
+ applyBrandChrome(content);
14773
15735
  for (const [key, val] of Object.entries(content)) {
14774
15736
  if (key === "__ohw_sections") continue;
14775
15737
  if (key === AI_SECTIONS_KEY) continue;
15738
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
15739
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
15740
+ if (key === BRAND_KIT_KEY) continue;
15741
+ if (key === STYLE_STORE_KEY) continue;
15742
+ if (BRAND_CHROME_KEYS.has(key)) continue;
14776
15743
  if (applyVideoSettingNode(key, val)) continue;
14777
15744
  if (applyCarouselNode(key, val)) continue;
14778
15745
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -14807,6 +15774,8 @@ function OhhwellsBridge() {
14807
15774
  });
14808
15775
  applyLinkByKey(key, val);
14809
15776
  }
15777
+ applyLogoFromContent(content);
15778
+ applyLogoSizes(content);
14810
15779
  reconcileNavbarItemsFromContent(content);
14811
15780
  reconcileFooterOrderFromContent(content);
14812
15781
  reconcileSocialsFromContent(content);
@@ -14827,7 +15796,9 @@ function OhhwellsBridge() {
14827
15796
  let cancelled = false;
14828
15797
  setFetchState("loading");
14829
15798
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
14830
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
15799
+ const initialPath = pathname;
15800
+ fetchedContentPaths.add(`${subdomain}::${initialPath}`);
15801
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
14831
15802
  if (cancelled) return;
14832
15803
  const content = data?.content ?? {};
14833
15804
  contentCache.set(subdomain, content);
@@ -14851,8 +15822,21 @@ function OhhwellsBridge() {
14851
15822
  initSectionInstancesFromContent(content, window.location.pathname);
14852
15823
  observer?.disconnect();
14853
15824
  try {
15825
+ applyBrandChrome(content);
15826
+ if (typeof content[BRAND_KIT_KEY] === "string") {
15827
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
15828
+ }
15829
+ if (typeof content[STYLE_STORE_KEY] === "string") {
15830
+ applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
15831
+ }
14854
15832
  for (const [key, val] of Object.entries(content)) {
14855
15833
  if (key === "__ohw_sections") continue;
15834
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
15835
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
15836
+ if (key === BRAND_KIT_KEY) continue;
15837
+ if (key === STYLE_STORE_KEY) continue;
15838
+ if (key === STYLE_STORE_KEY) continue;
15839
+ if (BRAND_CHROME_KEYS.has(key)) continue;
14856
15840
  if (applyVideoSettingNode(key, val)) continue;
14857
15841
  if (applyCarouselNode(key, val)) continue;
14858
15842
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -14873,6 +15857,7 @@ function OhhwellsBridge() {
14873
15857
  });
14874
15858
  applyLinkByKey(key, val);
14875
15859
  }
15860
+ applyLogoFromContent(content);
14876
15861
  reconcileNavbarItemsFromContent(content);
14877
15862
  reconcileFooterOrderFromContent(content);
14878
15863
  reconcileSocialsFromContent(content);
@@ -14887,6 +15872,17 @@ function OhhwellsBridge() {
14887
15872
  debounceTimer = setTimeout(applyFromCache, 150);
14888
15873
  };
14889
15874
  applyFromCache();
15875
+ const pathCacheKey = `${subdomain}::${pathname}`;
15876
+ if (!fetchedContentPaths.has(pathCacheKey)) {
15877
+ fetchedContentPaths.add(pathCacheKey);
15878
+ const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
15879
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
15880
+ if (!data?.content) return;
15881
+ contentCache.set(subdomain, data.content);
15882
+ applyFromCache();
15883
+ }).catch(() => {
15884
+ });
15885
+ }
14890
15886
  observer = new MutationObserver(scheduleApply);
14891
15887
  observer.observe(document.body, { childList: true, subtree: true });
14892
15888
  return () => {
@@ -14980,26 +15976,31 @@ function OhhwellsBridge() {
14980
15976
  }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
14981
15977
  useEffect12(() => {
14982
15978
  if (!isEditMode) return;
15979
+ let lastPosted = 0;
14983
15980
  const measure = () => {
14984
15981
  const h = document.body.scrollHeight;
14985
- if (h > 50) postToParent2({ type: "ow:height", height: h });
15982
+ if (h > 50 && Math.abs(h - lastPosted) > 1) {
15983
+ lastPosted = h;
15984
+ postToParent2({ type: "ow:height", height: h });
15985
+ }
15986
+ };
15987
+ let raf = null;
15988
+ const schedule = () => {
15989
+ if (raf != null) return;
15990
+ raf = requestAnimationFrame(() => {
15991
+ raf = null;
15992
+ measure();
15993
+ });
14986
15994
  };
14987
15995
  const t1 = setTimeout(measure, 50);
14988
15996
  const t2 = setTimeout(measure, 500);
14989
- let lastWidth = window.innerWidth;
14990
- let resizeTimer = null;
14991
- const handleResize = () => {
14992
- if (window.innerWidth === lastWidth) return;
14993
- lastWidth = window.innerWidth;
14994
- if (resizeTimer) clearTimeout(resizeTimer);
14995
- resizeTimer = setTimeout(measure, 150);
14996
- };
14997
- window.addEventListener("resize", handleResize);
15997
+ const ro = new ResizeObserver(schedule);
15998
+ ro.observe(document.body);
14998
15999
  return () => {
14999
16000
  clearTimeout(t1);
15000
16001
  clearTimeout(t2);
15001
- if (resizeTimer) clearTimeout(resizeTimer);
15002
- window.removeEventListener("resize", handleResize);
16002
+ if (raf != null) cancelAnimationFrame(raf);
16003
+ ro.disconnect();
15003
16004
  };
15004
16005
  }, [pathname, isEditMode, postToParent2]);
15005
16006
  useEffect12(() => {
@@ -15149,10 +16150,12 @@ function OhhwellsBridge() {
15149
16150
  return;
15150
16151
  }
15151
16152
  const target = e.target;
16153
+ if (target.closest("[data-ohw-ai-review]")) return;
15152
16154
  if (target.closest("[data-ohw-toolbar]")) return;
15153
16155
  if (target.closest("[data-ohw-state-toggle]")) return;
15154
16156
  if (target.closest("[data-ohw-max-badge]")) return;
15155
16157
  if (isInsideLinkEditor(target)) return;
16158
+ if (isInsideFloatingPanel(target)) return;
15156
16159
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
15157
16160
  const beneath = document.elementsFromPoint(e.clientX, e.clientY).find(
15158
16161
  (el) => el instanceof HTMLElement && !el.closest("[data-ohw-bridge-root]") && el.closest("[data-ohw-section]") != null
@@ -15216,6 +16219,21 @@ function OhhwellsBridge() {
15216
16219
  return;
15217
16220
  }
15218
16221
  }
16222
+ const logoEl = getLogoElement(target);
16223
+ if (logoEl) {
16224
+ e.preventDefault();
16225
+ e.stopPropagation();
16226
+ if (!logoHasUploadedImage(logoEl)) {
16227
+ deselectRef.current();
16228
+ deactivateRef.current();
16229
+ const identity = readLogoIdentityFromDom();
16230
+ postToParentRef.current({ type: "ow:open-logo-settings", ...identity });
16231
+ return;
16232
+ }
16233
+ selectLogoRef.current(logoEl);
16234
+ openLogoSizePanelRef.current(logoEl);
16235
+ return;
16236
+ }
15219
16237
  const editable = target.closest("[data-ohw-editable]");
15220
16238
  if (editable) {
15221
16239
  if (editable.dataset.ohwEditable === "link") {
@@ -15368,10 +16386,12 @@ function OhhwellsBridge() {
15368
16386
  };
15369
16387
  const handleDblClick = (e) => {
15370
16388
  const target = e.target;
16389
+ if (target.closest("[data-ohw-ai-review]")) return;
15371
16390
  if (target.closest("[data-ohw-toolbar]")) return;
15372
16391
  if (target.closest("[data-ohw-state-toggle]")) return;
15373
16392
  if (target.closest("[data-ohw-max-badge]")) return;
15374
16393
  if (isInsideLinkEditor(target)) return;
16394
+ if (isInsideFloatingPanel(target)) return;
15375
16395
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
15376
16396
  return;
15377
16397
  }
@@ -15399,11 +16419,14 @@ function OhhwellsBridge() {
15399
16419
  setHoveredNavContainerRect(null);
15400
16420
  return;
15401
16421
  }
15402
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || target.closest("[data-ohw-floating-panel]")) {
16422
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || isInsideFloatingPanel(target) || isPointOverFloatingPanel(e.clientX, e.clientY)) {
15403
16423
  hoveredItemElRef.current = null;
15404
16424
  setHoveredItemRect(null);
15405
16425
  hoveredNavContainerRef.current = null;
15406
16426
  setHoveredNavContainerRect(null);
16427
+ siblingHintElRef.current = null;
16428
+ setSiblingHintRect(null);
16429
+ setSiblingHintRects([]);
15407
16430
  return;
15408
16431
  }
15409
16432
  {
@@ -15413,7 +16436,7 @@ function OhhwellsBridge() {
15413
16436
  const allowFooterLinksHover = toolbarVariantRef.current !== "select-frame" || selectedIsFooterColumn;
15414
16437
  if (allowNavContainerHover) {
15415
16438
  const navContainer = target.closest("[data-ohw-nav-container]");
15416
- if (navContainer && !getNavigationItemAnchor(target)) {
16439
+ if (navContainer && !getNavigationItemAnchor(target) && !getLogoElement(target)) {
15417
16440
  hoveredNavContainerRef.current = navContainer;
15418
16441
  setHoveredNavContainerRect(navContainer.getBoundingClientRect());
15419
16442
  hoveredItemElRef.current = null;
@@ -15442,6 +16465,15 @@ function OhhwellsBridge() {
15442
16465
  setHoveredNavContainerRect(null);
15443
16466
  }
15444
16467
  }
16468
+ const logoEl = getLogoElement(target);
16469
+ if (logoEl) {
16470
+ hoveredNavContainerRef.current = null;
16471
+ setHoveredNavContainerRect(null);
16472
+ if (selectedElRef.current === logoEl) return;
16473
+ hoveredItemElRef.current = logoEl;
16474
+ setHoveredItemRect(getLogoInteractionRect(logoEl));
16475
+ return;
16476
+ }
15445
16477
  const navAnchor = getNavigationItemAnchor(target);
15446
16478
  if (navAnchor) {
15447
16479
  hoveredNavContainerRef.current = null;
@@ -15479,6 +16511,11 @@ function OhhwellsBridge() {
15479
16511
  setHoveredItemRect(hoverTarget.getBoundingClientRect());
15480
16512
  } else if (!isInsideNavigationItem(editable)) {
15481
16513
  hoverTarget.setAttribute("data-ohw-hovered", "");
16514
+ if (editable.closest("footer") || editable.closest('[data-ohw-section="footer"]')) {
16515
+ hoveredNavContainerRef.current = null;
16516
+ setHoveredNavContainerRect(null);
16517
+ hoveredItemElRef.current = editable;
16518
+ }
15482
16519
  }
15483
16520
  }
15484
16521
  };
@@ -15514,6 +16551,18 @@ function OhhwellsBridge() {
15514
16551
  }
15515
16552
  return;
15516
16553
  }
16554
+ const logoEl = getLogoElement(target);
16555
+ if (logoEl) {
16556
+ const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
16557
+ if (related2 && (logoEl === related2 || logoEl.contains(related2) || related2.closest?.('[data-ohw-role="logo"], [data-ohw-logo]'))) {
16558
+ return;
16559
+ }
16560
+ if (hoveredItemElRef.current === logoEl) {
16561
+ hoveredItemElRef.current = null;
16562
+ setHoveredItemRect(null);
16563
+ }
16564
+ return;
16565
+ }
15517
16566
  const editable = target.closest("[data-ohw-editable]");
15518
16567
  if (!editable) return;
15519
16568
  const related = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -15534,6 +16583,13 @@ function OhhwellsBridge() {
15534
16583
  }
15535
16584
  } else {
15536
16585
  hoverTarget.removeAttribute("data-ohw-hovered");
16586
+ if (hoveredItemElRef.current === editable) {
16587
+ const stillOnEditable = related instanceof Element && related.closest("[data-ohw-editable]") === editable;
16588
+ if (!stillOnEditable) {
16589
+ hoveredItemElRef.current = null;
16590
+ setHoveredItemRect(null);
16591
+ }
16592
+ }
15537
16593
  }
15538
16594
  }
15539
16595
  };
@@ -15650,6 +16706,26 @@ function OhhwellsBridge() {
15650
16706
  hoveredNavContainerRef.current = null;
15651
16707
  setHoveredNavContainerRect(null);
15652
16708
  }
16709
+ const logoCandidates = [
16710
+ ...document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]'),
16711
+ ...document.querySelectorAll("nav a:not([data-ohw-href-key]), [data-ohw-nav-root] a:not([data-ohw-href-key])"),
16712
+ ...document.querySelectorAll("footer img")
16713
+ ];
16714
+ const seenLogos = /* @__PURE__ */ new Set();
16715
+ for (const candidate of logoCandidates) {
16716
+ const logo = getLogoElement(candidate);
16717
+ if (!logo || seenLogos.has(logo)) continue;
16718
+ seenLogos.add(logo);
16719
+ const r2 = logo.getBoundingClientRect();
16720
+ if (x < r2.left || x > r2.right || y < r2.top || y > r2.bottom) continue;
16721
+ hoveredNavContainerRef.current = null;
16722
+ setHoveredNavContainerRect(null);
16723
+ if (selectedElRef.current !== logo) {
16724
+ hoveredItemElRef.current = logo;
16725
+ setHoveredItemRect(getLogoInteractionRect(logo));
16726
+ }
16727
+ return;
16728
+ }
15653
16729
  const navContainers = Array.from(
15654
16730
  document.querySelectorAll("[data-ohw-nav-container]")
15655
16731
  );
@@ -15735,7 +16811,7 @@ function OhhwellsBridge() {
15735
16811
  }
15736
16812
  };
15737
16813
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
15738
- if (linkPopoverOpenRef.current) {
16814
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
15739
16815
  if (hoveredImageRef.current) {
15740
16816
  hoveredImageRef.current = null;
15741
16817
  hoveredImageHasTextOverlapRef.current = false;
@@ -15989,7 +17065,7 @@ function OhhwellsBridge() {
15989
17065
  }
15990
17066
  };
15991
17067
  const probeHoverCardsAt = (clientX, clientY, fromParentViewport = false) => {
15992
- if (linkPopoverOpenRef.current || document.documentElement.hasAttribute("data-ohw-section-picking")) {
17068
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || document.documentElement.hasAttribute("data-ohw-section-picking")) {
15993
17069
  if (activeStateElRef.current) {
15994
17070
  activeStateElRef.current.removeAttribute("data-ohw-state-hovered");
15995
17071
  activeStateElRef.current = null;
@@ -16055,16 +17131,21 @@ function OhhwellsBridge() {
16055
17131
  setSectionGap(null);
16056
17132
  }
16057
17133
  };
16058
- const pointOwnedByFloatingPanel = (clientX, clientY) => {
16059
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging")) return true;
16060
- const panel = document.querySelector("[data-ohw-floating-panel]");
16061
- if (!panel) return false;
16062
- const rect = panel.getBoundingClientRect();
16063
- return clientX >= rect.left && clientX <= rect.right && clientY >= rect.top && clientY <= rect.bottom;
16064
- };
16065
17134
  const handleMouseMove = (e) => {
16066
17135
  const { clientX, clientY } = e;
16067
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
17136
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
17137
+ hoveredItemElRef.current = null;
17138
+ setHoveredItemRect(null);
17139
+ hoveredNavContainerRef.current = null;
17140
+ setHoveredNavContainerRect(null);
17141
+ siblingHintElRef.current = null;
17142
+ setSiblingHintRect(null);
17143
+ setSiblingHintRects([]);
17144
+ dismissImageHover();
17145
+ clearImageHover();
17146
+ setSectionGap(null);
17147
+ return;
17148
+ }
16068
17149
  probeSectionGapAt(clientX, clientY);
16069
17150
  probeImageAt(clientX, clientY);
16070
17151
  probeHoverCardsAt(clientX, clientY);
@@ -16073,7 +17154,11 @@ function OhhwellsBridge() {
16073
17154
  if (e.data?.type !== "ow:pointer-sync") return;
16074
17155
  const { clientX, clientY } = e.data;
16075
17156
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
16076
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
17157
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
17158
+ dismissImageHover();
17159
+ clearImageHover();
17160
+ return;
17161
+ }
16077
17162
  probeSectionGapAt(clientX, clientY);
16078
17163
  probeImageAt(clientX, clientY);
16079
17164
  probeHoverCardsAt(clientX, clientY);
@@ -16323,6 +17408,15 @@ function OhhwellsBridge() {
16323
17408
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
16324
17409
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
16325
17410
  }
17411
+ if (typeof content[BRAND_KIT_KEY] === "string") {
17412
+ brandKitRef.current = content[BRAND_KIT_KEY];
17413
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17414
+ }
17415
+ if (typeof content[STYLE_STORE_KEY] === "string") {
17416
+ stylesRef.current = content[STYLE_STORE_KEY];
17417
+ applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
17418
+ }
17419
+ applyBrandChrome(content);
16326
17420
  let sectionsJson = null;
16327
17421
  for (const [key, val] of Object.entries(content)) {
16328
17422
  if (key === "__ohw_sections") {
@@ -16330,6 +17424,11 @@ function OhhwellsBridge() {
16330
17424
  continue;
16331
17425
  }
16332
17426
  if (key === AI_SECTIONS_KEY) continue;
17427
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
17428
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
17429
+ if (key === BRAND_KIT_KEY) continue;
17430
+ if (key === STYLE_STORE_KEY) continue;
17431
+ if (BRAND_CHROME_KEYS.has(key)) continue;
16333
17432
  if (applyVideoSettingNode(key, val)) continue;
16334
17433
  if (applyCarouselNode(key, val)) continue;
16335
17434
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -16349,6 +17448,8 @@ function OhhwellsBridge() {
16349
17448
  });
16350
17449
  applyLinkByKey(key, val);
16351
17450
  }
17451
+ applyLogoFromContent(content);
17452
+ applyLogoSizes(content);
16352
17453
  if (sectionsJson) {
16353
17454
  initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
16354
17455
  sectionsLoadedRef.current = true;
@@ -16364,6 +17465,58 @@ function OhhwellsBridge() {
16364
17465
  if (hydratedHeight > 50) postToParentRef.current({ type: "ow:height", height: hydratedHeight });
16365
17466
  postToParentRef.current({ type: "ow:hydrate-done" });
16366
17467
  };
17468
+ const handleUpdateLogoIdentity = (e) => {
17469
+ if (e.data?.type !== "ow:update-logo-identity") return;
17470
+ const rawText = typeof e.data.text === "string" ? e.data.text : "";
17471
+ const alt = typeof e.data.alt === "string" ? e.data.alt : rawText;
17472
+ const href = typeof e.data.href === "string" ? e.data.href : void 0;
17473
+ const imageProvided = "image" in e.data;
17474
+ const imageUrl = imageProvided && typeof e.data.image === "string" && e.data.image.trim() ? e.data.image.trim() : imageProvided ? null : void 0;
17475
+ let isPlaceholder = e.data.isPlaceholder !== false;
17476
+ if (imageUrl) isPlaceholder = false;
17477
+ else if (imageProvided && imageUrl === null) {
17478
+ isPlaceholder = e.data.isPlaceholder === true || !rawText.trim() || resolveLogoDisplayText(rawText) === PLACEHOLDER_BUSINESS_NAME;
17479
+ }
17480
+ const display = applyLogoIdentity(rawText, isPlaceholder);
17481
+ const displayAlt = resolveLogoDisplayText(alt || display);
17482
+ if (imageUrl !== void 0) {
17483
+ applyLogoImage(imageUrl, displayAlt);
17484
+ } else {
17485
+ for (const key of LOGO_IMAGE_KEYS) {
17486
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
17487
+ const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
17488
+ if (img) img.alt = displayAlt;
17489
+ });
17490
+ }
17491
+ }
17492
+ if (href !== void 0) {
17493
+ applyLogoHref(href);
17494
+ applyLinkByKey("nav-logo-href", href);
17495
+ applyLinkByKey("footer-logo-href", href);
17496
+ applyLinkByKey("logo-href", href);
17497
+ }
17498
+ const nodes = [
17499
+ ...LOGO_TEXT_KEYS.map((key) => ({ key, text: display })),
17500
+ { key: LOGO_PLACEHOLDER_KEY, text: isPlaceholder ? "true" : "false" },
17501
+ { key: LOGO_ALT_KEY, text: displayAlt }
17502
+ ];
17503
+ if (imageUrl !== void 0) {
17504
+ if (imageUrl) {
17505
+ for (const key of LOGO_IMAGE_KEYS) nodes.push({ key, text: imageUrl });
17506
+ } else {
17507
+ for (const key of LOGO_IMAGE_KEYS) nodes.push({ key, text: "" });
17508
+ }
17509
+ }
17510
+ if (href !== void 0) {
17511
+ for (const key of LOGO_HREF_KEYS) nodes.push({ key, text: href.trim() || "/" });
17512
+ }
17513
+ editContentRef.current = {
17514
+ ...editContentRef.current,
17515
+ ...Object.fromEntries(nodes.map(({ key, text }) => [key, text]))
17516
+ };
17517
+ applyLogoSizes(editContentRef.current);
17518
+ postToParentRef.current({ type: "ow:change", nodes });
17519
+ };
16367
17520
  window.addEventListener("message", handleHydrate);
16368
17521
  const postAiSectionsChanged = () => {
16369
17522
  postToParentRef.current({
@@ -16377,7 +17530,10 @@ function OhhwellsBridge() {
16377
17530
  const payload = e.data.payload;
16378
17531
  if (!payload || typeof payload.id !== "string" || !isRenderableTree(payload.tree)) return;
16379
17532
  const previous = aiSectionsRef.current;
16380
- const nextState = applyTreeToState(parseAiSectionsState(previous), payload);
17533
+ const nextState = applyTreeToState(parseAiSectionsState(previous), {
17534
+ ...payload,
17535
+ path: payload.path ?? window.location.pathname
17536
+ });
16381
17537
  const nextValue = serializeAiSectionsState(nextState);
16382
17538
  aiSectionsRef.current = nextValue;
16383
17539
  applyAiSectionsToDom(nextState);
@@ -16414,12 +17570,42 @@ function OhhwellsBridge() {
16414
17570
  const value = typeof e.data.value === "string" ? e.data.value : "";
16415
17571
  aiSectionsRef.current = value;
16416
17572
  applyAiSectionsToDom(parseAiSectionsState(value));
17573
+ applyStylesToDom(parseStyleStore(stylesRef.current));
16417
17574
  const restoredHeight = document.documentElement.scrollHeight;
16418
17575
  if (restoredHeight > 50) postToParentRef.current({ type: "ow:height", height: restoredHeight });
16419
17576
  postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: value }] });
16420
17577
  postAiSectionsChanged();
16421
17578
  };
16422
17579
  window.addEventListener("message", handleAiSetSections);
17580
+ const handleAiSetBrand = (e) => {
17581
+ if (e.data?.type !== "ow:ai-set-brand") return;
17582
+ const value = typeof e.data.value === "string" ? e.data.value : "";
17583
+ const previous = brandKitRef.current;
17584
+ brandKitRef.current = value;
17585
+ applyBrandToDom(parseBrandKit(value));
17586
+ if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
17587
+ applyStylesToDom(parseStyleStore(stylesRef.current));
17588
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
17589
+ postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
17590
+ };
17591
+ window.addEventListener("message", handleAiSetBrand);
17592
+ const handleAiSetStyles = (e) => {
17593
+ if (e.data?.type !== "ow:ai-set-styles") return;
17594
+ const value = typeof e.data.value === "string" ? e.data.value : "";
17595
+ const previous = stylesRef.current;
17596
+ stylesRef.current = value;
17597
+ applyStylesToDom(parseStyleStore(value));
17598
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
17599
+ postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
17600
+ };
17601
+ window.addEventListener("message", handleAiSetStyles);
17602
+ const handleGetBrand = (e) => {
17603
+ if (e.data?.type !== "ow:get-brand") return;
17604
+ const template = deriveTemplateBrand();
17605
+ const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
17606
+ postToParentRef.current({ type: "ow:brand-value", value });
17607
+ };
17608
+ window.addEventListener("message", handleGetBrand);
16423
17609
  const handleDeactivate = (e) => {
16424
17610
  if (e.data?.type !== "ow:deactivate") return;
16425
17611
  if (Date.now() < linkPopoverGraceUntilRef.current) return;
@@ -16428,6 +17614,12 @@ function OhhwellsBridge() {
16428
17614
  closeLinkPopoverRef.current();
16429
17615
  return;
16430
17616
  }
17617
+ if (floatingPanelOpenRef.current) {
17618
+ setFloatingPanelRef.current(null);
17619
+ deselectRef.current();
17620
+ deactivateRef.current();
17621
+ return;
17622
+ }
16431
17623
  deselectRef.current();
16432
17624
  deactivateRef.current();
16433
17625
  };
@@ -16481,6 +17673,10 @@ function OhhwellsBridge() {
16481
17673
  return;
16482
17674
  }
16483
17675
  if (selectedElRef.current) {
17676
+ if (toolbarVariantRef.current === "logo") {
17677
+ deselectRef.current();
17678
+ return;
17679
+ }
16484
17680
  if (toolbarVariantRef.current === "select-frame") {
16485
17681
  const parent2 = getNavigationSelectionParent(selectedElRef.current);
16486
17682
  if (parent2 && isNavigationContainer(parent2) && isFooterLinksContainer(parent2) && !isFooterLinksContainer(selectedElRef.current)) {
@@ -16520,6 +17716,10 @@ function OhhwellsBridge() {
16520
17716
  return;
16521
17717
  }
16522
17718
  if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
17719
+ if (toolbarVariantRef.current === "logo") {
17720
+ deselectRef.current();
17721
+ return;
17722
+ }
16523
17723
  if (toolbarVariantRef.current === "select-frame") {
16524
17724
  const parent2 = getNavigationSelectionParent(selectedElRef.current);
16525
17725
  if (parent2 && isNavigationContainer(parent2) && isFooterLinksContainer(parent2) && !isFooterLinksContainer(selectedElRef.current)) {
@@ -16597,7 +17797,8 @@ function OhhwellsBridge() {
16597
17797
  const handleScroll = () => {
16598
17798
  const focusEl = activeElRef.current ?? selectedElRef.current;
16599
17799
  if (focusEl) {
16600
- const r2 = activeElRef.current ? getEditMeasureEl(activeElRef.current).getBoundingClientRect() : focusEl.getBoundingClientRect();
17800
+ const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : toolbarVariantRef.current === "logo" ? null : focusEl;
17801
+ const r2 = measureEl ? measureEl.getBoundingClientRect() : toolbarVariantRef.current === "logo" ? getLogoInteractionRect(focusEl) : focusEl.getBoundingClientRect();
16601
17802
  applyToolbarPos(r2);
16602
17803
  setToolbarRect(r2);
16603
17804
  setMaxBadge((prev) => prev && activeElRef.current ? { ...prev, rect: r2 } : prev);
@@ -16607,7 +17808,9 @@ function OhhwellsBridge() {
16607
17808
  setToggleState((prev) => prev ? { ...prev, rect } : null);
16608
17809
  }
16609
17810
  if (hoveredItemElRef.current) {
16610
- setHoveredItemRect(hoveredItemElRef.current.getBoundingClientRect());
17811
+ const hoverEl = hoveredItemElRef.current;
17812
+ const logo = getLogoElement(hoverEl);
17813
+ setHoveredItemRect(logo ? getLogoInteractionRect(logo) : hoverEl.getBoundingClientRect());
16611
17814
  }
16612
17815
  if (hoveredNavContainerRef.current) {
16613
17816
  setHoveredNavContainerRect(hoveredNavContainerRef.current.getBoundingClientRect());
@@ -16651,6 +17854,12 @@ function OhhwellsBridge() {
16651
17854
  if (aiSectionsRef.current) {
16652
17855
  nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
16653
17856
  }
17857
+ if (stylesRef.current) {
17858
+ nodes.push({ key: STYLE_STORE_KEY, type: "styles", text: stylesRef.current });
17859
+ }
17860
+ if (brandKitRef.current) {
17861
+ nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
17862
+ }
16654
17863
  postToParentRef.current({ type: "ow:save-result", nodes });
16655
17864
  };
16656
17865
  const handleInsertSection = (e) => {
@@ -16661,8 +17870,12 @@ function OhhwellsBridge() {
16661
17870
  if (inserted) {
16662
17871
  const tracker = getSectionsTracker();
16663
17872
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
16664
- const h = document.documentElement.scrollHeight;
16665
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
17873
+ const reportHeight = () => {
17874
+ const h = document.body.scrollHeight;
17875
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
17876
+ };
17877
+ reportHeight();
17878
+ setTimeout(reportHeight, 500);
16666
17879
  }
16667
17880
  };
16668
17881
  const handleSwitchSchedule = (e) => {
@@ -16855,13 +18068,17 @@ function OhhwellsBridge() {
16855
18068
  if (e.data?.type !== "ow:parent-scroll") return;
16856
18069
  const { iframeOffsetTop, headerH, canvasH } = e.data;
16857
18070
  parentScrollRef.current = { iframeOffsetTop, headerH, canvasH };
18071
+ if (floatingPanelOpenRef.current) {
18072
+ setParentScrollSnap({ iframeOffsetTop, headerH, canvasH });
18073
+ }
16858
18074
  if (visibleViewportRef.current) {
16859
18075
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
16860
18076
  }
16861
18077
  const focusEl = activeElRef.current ?? selectedElRef.current;
16862
18078
  if (focusEl) {
16863
- const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : focusEl;
16864
- applyToolbarPos(measureEl.getBoundingClientRect());
18079
+ const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : toolbarVariantRef.current === "logo" ? null : focusEl;
18080
+ const r2 = measureEl ? measureEl.getBoundingClientRect() : toolbarVariantRef.current === "logo" ? getLogoInteractionRect(focusEl) : focusEl.getBoundingClientRect();
18081
+ applyToolbarPos(r2);
16865
18082
  }
16866
18083
  };
16867
18084
  const handleClickAt = (e) => {
@@ -16886,6 +18103,25 @@ function OhhwellsBridge() {
16886
18103
  postToParentRef.current({ type: "ow:image-pick", key: stateCardImage.dataset.ohwKey ?? "", elementType: stateCardImage.dataset.ohwEditable ?? "image" });
16887
18104
  return;
16888
18105
  }
18106
+ const logoAtPoint = Array.from(
18107
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]')
18108
+ ).map((el) => getLogoElement(el)).find((logo) => {
18109
+ if (!logo) return false;
18110
+ const r2 = logo.getBoundingClientRect();
18111
+ return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
18112
+ });
18113
+ if (logoAtPoint) {
18114
+ if (!logoHasUploadedImage(logoAtPoint)) {
18115
+ deselectRef.current();
18116
+ deactivateRef.current();
18117
+ const identity = readLogoIdentityFromDom();
18118
+ postToParentRef.current({ type: "ow:open-logo-settings", ...identity });
18119
+ return;
18120
+ }
18121
+ selectLogoRef.current(logoAtPoint);
18122
+ openLogoSizePanelRef.current(logoAtPoint);
18123
+ return;
18124
+ }
16889
18125
  const textEditable = Array.from(
16890
18126
  document.querySelectorAll(NON_MEDIA_SELECTOR)
16891
18127
  ).find((el) => {
@@ -16957,6 +18193,14 @@ function OhhwellsBridge() {
16957
18193
  window.addEventListener("message", handleParentScroll);
16958
18194
  window.addEventListener("message", handlePointerSync);
16959
18195
  window.addEventListener("message", handleClickAt);
18196
+ window.addEventListener("message", handleUpdateLogoIdentity);
18197
+ const handleViewMode = (e) => {
18198
+ if (e.data?.type !== "ow:view-mode") return;
18199
+ const mode = e.data.mode === "Mobile" || e.data.mode === "mobile" ? "mobile" : "desktop";
18200
+ setEditorViewport(mode);
18201
+ applyLogoSizes(editContentRef.current);
18202
+ };
18203
+ window.addEventListener("message", handleViewMode);
16960
18204
  const handleViewportResize = () => {
16961
18205
  if (visibleViewportRef.current) {
16962
18206
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
@@ -17012,10 +18256,15 @@ function OhhwellsBridge() {
17012
18256
  window.removeEventListener("resize", handleViewportResize);
17013
18257
  window.removeEventListener("message", handlePointerSync);
17014
18258
  window.removeEventListener("message", handleClickAt);
18259
+ window.removeEventListener("message", handleUpdateLogoIdentity);
18260
+ window.removeEventListener("message", handleViewMode);
17015
18261
  window.removeEventListener("message", handleHydrate);
17016
18262
  window.removeEventListener("message", handleAiApplyTree);
17017
18263
  window.removeEventListener("message", handleAiDeleteSection);
17018
18264
  window.removeEventListener("message", handleAiSetSections);
18265
+ window.removeEventListener("message", handleAiSetBrand);
18266
+ window.removeEventListener("message", handleAiSetStyles);
18267
+ window.removeEventListener("message", handleGetBrand);
17019
18268
  window.removeEventListener("message", handleDeactivate);
17020
18269
  window.removeEventListener("message", handleToastAction);
17021
18270
  window.removeEventListener("message", handleUiEscape);
@@ -17219,7 +18468,7 @@ function OhhwellsBridge() {
17219
18468
  postToParent2({
17220
18469
  type: "ow:ready",
17221
18470
  version: "1",
17222
- bridgeVersion: "0.1.59",
18471
+ bridgeVersion: "0.1.61",
17223
18472
  path: pathname,
17224
18473
  nodes: collectEditableNodes(editContentRef.current),
17225
18474
  sections
@@ -17614,10 +18863,10 @@ function OhhwellsBridge() {
17614
18863
  [postToParent2]
17615
18864
  );
17616
18865
  return bridgeRoot ? createPortal2(
17617
- /* @__PURE__ */ jsxs19(Fragment8, { children: [
17618
- /* @__PURE__ */ jsx32("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
17619
- isEditMode && /* @__PURE__ */ jsx32(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
17620
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx32(
18866
+ /* @__PURE__ */ jsxs20(Fragment8, { children: [
18867
+ /* @__PURE__ */ jsx33("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
18868
+ isEditMode && /* @__PURE__ */ jsx33(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
18869
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx33(
17621
18870
  MediaOverlay,
17622
18871
  {
17623
18872
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -17628,7 +18877,7 @@ function OhhwellsBridge() {
17628
18877
  },
17629
18878
  `uploading-${key}`
17630
18879
  )),
17631
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx32(
18880
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx33(
17632
18881
  MediaOverlay,
17633
18882
  {
17634
18883
  hover: mediaHover,
@@ -17637,11 +18886,11 @@ function OhhwellsBridge() {
17637
18886
  onVideoSettingsChange: handleVideoSettingsChange
17638
18887
  }
17639
18888
  ),
17640
- carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx32(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
17641
- siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx32(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
17642
- siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx32(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
17643
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx32(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
17644
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx32(
18889
+ carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
18890
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
18891
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
18892
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
18893
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
17645
18894
  "div",
17646
18895
  {
17647
18896
  className: "pointer-events-none fixed z-2147483646",
@@ -17651,7 +18900,7 @@ function OhhwellsBridge() {
17651
18900
  width: slot.width,
17652
18901
  height: slot.height
17653
18902
  },
17654
- children: /* @__PURE__ */ jsx32(
18903
+ children: /* @__PURE__ */ jsx33(
17655
18904
  DropIndicator,
17656
18905
  {
17657
18906
  direction: slot.direction,
@@ -17662,7 +18911,7 @@ function OhhwellsBridge() {
17662
18911
  },
17663
18912
  `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
17664
18913
  )),
17665
- isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx32(
18914
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
17666
18915
  "div",
17667
18916
  {
17668
18917
  className: "pointer-events-none fixed z-2147483646",
@@ -17672,7 +18921,7 @@ function OhhwellsBridge() {
17672
18921
  width: slot.width,
17673
18922
  height: slot.height
17674
18923
  },
17675
- children: /* @__PURE__ */ jsx32(
18924
+ children: /* @__PURE__ */ jsx33(
17676
18925
  DropIndicator,
17677
18926
  {
17678
18927
  direction: slot.direction,
@@ -17683,11 +18932,11 @@ function OhhwellsBridge() {
17683
18932
  },
17684
18933
  `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
17685
18934
  )),
17686
- hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx32(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
17687
- hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx32(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
17688
- hoveredTextRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx32(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
17689
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx32(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
17690
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx32(
18935
+ hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
18936
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
18937
+ hoveredTextRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
18938
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx33(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
18939
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx33(
17691
18940
  FooterContainerChrome,
17692
18941
  {
17693
18942
  rect: toolbarRect,
@@ -17695,7 +18944,7 @@ function OhhwellsBridge() {
17695
18944
  addDisabled: !canAddFooterColumn()
17696
18945
  }
17697
18946
  ),
17698
- toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ jsx32(
18947
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ jsx33(
17699
18948
  ItemInteractionLayer,
17700
18949
  {
17701
18950
  rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
@@ -17707,10 +18956,10 @@ function OhhwellsBridge() {
17707
18956
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
17708
18957
  onDragHandleDragStart: handleItemDragStart,
17709
18958
  onDragHandleDragEnd: handleItemDragEnd,
17710
- onItemPointerDown: handleItemChromePointerDown,
17711
- onItemClick: handleItemChromeClick,
17712
- itemDragSurface: !isFooterFrameSelection,
17713
- toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ jsx32(
18959
+ onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
18960
+ onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
18961
+ itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection,
18962
+ toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ jsx33(
17714
18963
  ItemActionToolbar,
17715
18964
  {
17716
18965
  onEditLink: openLinkPopoverForSelected,
@@ -17746,8 +18995,8 @@ function OhhwellsBridge() {
17746
18995
  ) : void 0
17747
18996
  }
17748
18997
  ),
17749
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs19(Fragment8, { children: [
17750
- /* @__PURE__ */ jsx32(
18998
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs20(Fragment8, { children: [
18999
+ /* @__PURE__ */ jsx33(
17751
19000
  EditGlowChrome,
17752
19001
  {
17753
19002
  rect: toolbarRect,
@@ -17757,7 +19006,7 @@ function OhhwellsBridge() {
17757
19006
  hideHandle: isItemDragging
17758
19007
  }
17759
19008
  ),
17760
- /* @__PURE__ */ jsx32(
19009
+ /* @__PURE__ */ jsx33(
17761
19010
  FloatingToolbar,
17762
19011
  {
17763
19012
  rect: toolbarRect,
@@ -17770,7 +19019,7 @@ function OhhwellsBridge() {
17770
19019
  }
17771
19020
  )
17772
19021
  ] }),
17773
- maxBadge && /* @__PURE__ */ jsxs19(
19022
+ maxBadge && /* @__PURE__ */ jsxs20(
17774
19023
  "div",
17775
19024
  {
17776
19025
  "data-ohw-max-badge": "",
@@ -17796,7 +19045,7 @@ function OhhwellsBridge() {
17796
19045
  ]
17797
19046
  }
17798
19047
  ),
17799
- toggleState && !linkPopover && /* @__PURE__ */ jsx32(
19048
+ toggleState && !linkPopover && /* @__PURE__ */ jsx33(
17800
19049
  StateToggle,
17801
19050
  {
17802
19051
  rect: toggleState.rect,
@@ -17805,15 +19054,15 @@ function OhhwellsBridge() {
17805
19054
  onStateChange: handleStateChange
17806
19055
  }
17807
19056
  ),
17808
- sectionGap && !linkPopover && /* @__PURE__ */ jsxs19(
19057
+ sectionGap && !linkPopover && /* @__PURE__ */ jsxs20(
17809
19058
  "div",
17810
19059
  {
17811
19060
  "data-ohw-section-insert-line": "",
17812
19061
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
17813
19062
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
17814
19063
  children: [
17815
- /* @__PURE__ */ jsx32("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
17816
- /* @__PURE__ */ jsx32(
19064
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
19065
+ /* @__PURE__ */ jsx33(
17817
19066
  Badge,
17818
19067
  {
17819
19068
  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",
@@ -17830,11 +19079,11 @@ function OhhwellsBridge() {
17830
19079
  children: "Add Section"
17831
19080
  }
17832
19081
  ),
17833
- /* @__PURE__ */ jsx32("div", { className: "flex-1 bg-primary", style: { height: 3 } })
19082
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } })
17834
19083
  ]
17835
19084
  }
17836
19085
  ),
17837
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx32(
19086
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx33(
17838
19087
  LinkPopover,
17839
19088
  {
17840
19089
  panelRef: linkPopoverPanelRef,
@@ -17851,7 +19100,7 @@ function OhhwellsBridge() {
17851
19100
  },
17852
19101
  linkPopover.key
17853
19102
  ) : null,
17854
- floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx32(
19103
+ floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx33(
17855
19104
  FloatingPanel,
17856
19105
  {
17857
19106
  open: true,
@@ -17861,7 +19110,7 @@ function OhhwellsBridge() {
17861
19110
  onPositionChange: setFloatingPanelPos,
17862
19111
  parentScroll: parentScrollSnap ?? parentScrollRef.current,
17863
19112
  onClose: closeFloatingPanelOnly,
17864
- children: /* @__PURE__ */ jsx32(
19113
+ children: /* @__PURE__ */ jsx33(
17865
19114
  SocialsDisplayPanel,
17866
19115
  {
17867
19116
  display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
@@ -17872,11 +19121,115 @@ function OhhwellsBridge() {
17872
19121
  }
17873
19122
  )
17874
19123
  }
19124
+ ) : null,
19125
+ floatingPanel && floatingPanel.kind === "logo-size" && logoSizeDraft ? /* @__PURE__ */ jsx33(
19126
+ FloatingPanel,
19127
+ {
19128
+ open: true,
19129
+ title: floatingPanel.title,
19130
+ context: floatingPanel.context,
19131
+ position: floatingPanelPos,
19132
+ onPositionChange: setFloatingPanelPos,
19133
+ parentScroll: parentScrollSnap ?? parentScrollRef.current,
19134
+ onClose: closeFloatingPanelAndDeselect,
19135
+ children: /* @__PURE__ */ jsx33(
19136
+ LogoSizePanel,
19137
+ {
19138
+ viewport: editorViewport,
19139
+ sizePx: editorViewport === "mobile" && !logoSizeDraft.mobileFollowing ? logoSizeDraft.mobilePx : logoSizeDraft.desktopPx,
19140
+ mobileFollowing: logoSizeDraft.mobileFollowing,
19141
+ onSizeChange: (px) => {
19142
+ const next = editorViewport === "mobile" ? { ...logoSizeDraft, mobilePx: px, mobileFollowing: false } : {
19143
+ ...logoSizeDraft,
19144
+ desktopPx: px,
19145
+ mobilePx: logoSizeDraft.mobileFollowing ? px : logoSizeDraft.mobilePx
19146
+ };
19147
+ setLogoSizeDraft(next);
19148
+ persistLogoSizeDraft(floatingPanel.placement, next);
19149
+ },
19150
+ onCustomizeMobile: () => {
19151
+ const next = {
19152
+ ...logoSizeDraft,
19153
+ mobileFollowing: false,
19154
+ mobilePx: logoSizeDraft.desktopPx
19155
+ };
19156
+ setLogoSizeDraft(next);
19157
+ persistLogoSizeDraft(floatingPanel.placement, next);
19158
+ },
19159
+ onResetMobile: () => {
19160
+ const next = {
19161
+ ...logoSizeDraft,
19162
+ mobileFollowing: true,
19163
+ mobilePx: logoSizeDraft.desktopPx
19164
+ };
19165
+ setLogoSizeDraft(next);
19166
+ persistLogoSizeDraft(floatingPanel.placement, next);
19167
+ },
19168
+ onUpdateEverywhere: () => {
19169
+ const identity = readLogoIdentityFromDom();
19170
+ postToParent2({ type: "ow:open-logo-settings", ...identity });
19171
+ }
19172
+ }
19173
+ )
19174
+ }
17875
19175
  ) : null
17876
19176
  ] }),
17877
19177
  bridgeRoot
17878
19178
  ) : null;
17879
19179
  }
19180
+
19181
+ // src/ui/EmptySection.tsx
19182
+ import Link4 from "next/link";
19183
+ import { Fragment as Fragment9, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
19184
+ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
19185
+ return /* @__PURE__ */ jsxs21(Fragment9, { children: [
19186
+ /* @__PURE__ */ jsx34(
19187
+ "p",
19188
+ {
19189
+ style: {
19190
+ fontFamily: "var(--brand-font-body)",
19191
+ fontSize: "0.75rem",
19192
+ fontWeight: 500,
19193
+ letterSpacing: "0.15em",
19194
+ textTransform: "uppercase",
19195
+ color: "var(--brand-accent)",
19196
+ marginBottom: "1.5rem"
19197
+ },
19198
+ children: /* @__PURE__ */ jsx34(Link4, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx34("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
19199
+ }
19200
+ ),
19201
+ /* @__PURE__ */ jsx34(
19202
+ "h1",
19203
+ {
19204
+ style: {
19205
+ fontFamily: "var(--brand-font-heading)",
19206
+ fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
19207
+ lineHeight: 1.1,
19208
+ letterSpacing: "-0.025em",
19209
+ color: "var(--brand-text)",
19210
+ marginBottom: "1rem"
19211
+ },
19212
+ ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
19213
+ children: title
19214
+ }
19215
+ ),
19216
+ /* @__PURE__ */ jsx34(
19217
+ "p",
19218
+ {
19219
+ style: {
19220
+ fontFamily: "var(--brand-font-body)",
19221
+ fontSize: "1rem",
19222
+ lineHeight: 1.7,
19223
+ fontWeight: 300,
19224
+ color: "var(--brand-text-muted)",
19225
+ maxWidth: "340px"
19226
+ },
19227
+ ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
19228
+ children: "This page doesn't have any content yet."
19229
+ }
19230
+ )
19231
+ ] });
19232
+ }
17880
19233
  export {
17881
19234
  AI_DEFAULT_BRAND,
17882
19235
  AI_TREE_SCHEMA_VERSIONS,
@@ -17893,6 +19246,7 @@ export {
17893
19246
  DropdownMenuItem,
17894
19247
  DropdownMenuSeparator,
17895
19248
  DropdownMenuTrigger,
19249
+ EmptySection,
17896
19250
  ItemActionToolbar,
17897
19251
  ItemInteractionLayer,
17898
19252
  LinkEditorPanel,