@ohhwells/bridge 0.1.60 → 0.1.61-next.173

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,287 @@ 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
+ var NODE_PROPS = ["color", "font-size", "background"];
310
+ function saveInline(el, prop) {
311
+ const attr = `data-ohw-style-prev-${prop}`;
312
+ if (!el.hasAttribute(attr)) el.setAttribute(attr, el.style.getPropertyValue(prop));
313
+ }
314
+ function restoreInline(el, prop) {
315
+ const attr = `data-ohw-style-prev-${prop}`;
316
+ if (!el.hasAttribute(attr)) return;
317
+ const prev = el.getAttribute(attr) ?? "";
318
+ if (prev) el.style.setProperty(prop, prev);
319
+ else el.style.removeProperty(prop);
320
+ el.removeAttribute(attr);
321
+ }
322
+ function ensureStyleSheet() {
323
+ let el = document.getElementById(STYLE_SHEET_ID);
324
+ if (!el) {
325
+ el = document.createElement("style");
326
+ el.id = STYLE_SHEET_ID;
327
+ document.head.appendChild(el);
328
+ }
329
+ const css = styleSheetCss();
330
+ if (el.textContent !== css) el.textContent = css;
331
+ }
332
+ function clearSectionAttrs(root) {
333
+ for (const attr of Object.values(SECTION_ATTRS)) {
334
+ for (const el of Array.from(root.querySelectorAll(`[${attr}]`))) el.removeAttribute(attr);
335
+ }
336
+ for (const el of Array.from(root.querySelectorAll("[data-ohw-style-bgcolor]"))) {
337
+ restoreInline(el, "background");
338
+ el.removeAttribute("data-ohw-style-bgcolor");
339
+ }
340
+ }
341
+ function clearNodeProps(root) {
342
+ for (const el of Array.from(root.querySelectorAll(`[${NODE_WROTE_ATTR}]`))) {
343
+ const h = el;
344
+ for (const prop of NODE_PROPS) restoreInline(h, prop);
345
+ h.removeAttribute(NODE_WROTE_ATTR);
346
+ }
347
+ }
348
+ function buttonSurfaceOf(el) {
349
+ return el.closest("a, button") ?? el;
350
+ }
351
+ function applyStylesToDom(store) {
352
+ ensureStyleSheet();
353
+ clearSectionAttrs(document);
354
+ clearNodeProps(document);
355
+ if (!store) return;
356
+ for (const [sectionId, override] of Object.entries(store.sections)) {
357
+ const sections = document.querySelectorAll(
358
+ `[data-ohw-section="${CSS.escape(sectionId)}"]`
359
+ );
360
+ for (const section of Array.from(sections)) {
361
+ for (const [prop, attr] of Object.entries(SECTION_ATTRS)) {
362
+ const value = override[prop];
363
+ if (value === void 0) continue;
364
+ if (prop === "sectionBackground" && override.sectionBackgroundColor !== void 0) continue;
365
+ section.setAttribute(attr, String(value).replace(":", "-"));
366
+ }
367
+ if (override.sectionBackgroundColor !== void 0) {
368
+ saveInline(section, "background");
369
+ section.style.setProperty("background", override.sectionBackgroundColor, "important");
370
+ section.setAttribute("data-ohw-style-bgcolor", "");
371
+ }
372
+ }
373
+ }
374
+ for (const [key, override] of Object.entries(store.nodes)) {
375
+ const nodes = document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`);
376
+ for (const el of Array.from(nodes)) {
377
+ if (override.color !== void 0) {
378
+ saveInline(el, "color");
379
+ el.style.setProperty("color", override.color, "important");
380
+ el.setAttribute(NODE_WROTE_ATTR, "");
381
+ }
382
+ if (override.fontSize !== void 0) {
383
+ saveInline(el, "font-size");
384
+ el.style.setProperty("font-size", `${override.fontSize}px`, "important");
385
+ el.setAttribute(NODE_WROTE_ATTR, "");
386
+ }
387
+ if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
388
+ const surface = buttonSurfaceOf(el);
389
+ if (override.buttonBackground !== void 0) {
390
+ saveInline(surface, "background");
391
+ surface.style.setProperty("background", override.buttonBackground, "important");
392
+ }
393
+ if (override.buttonText !== void 0) {
394
+ saveInline(surface, "color");
395
+ surface.style.setProperty("color", override.buttonText, "important");
396
+ }
397
+ surface.setAttribute(NODE_WROTE_ATTR, "");
398
+ }
399
+ }
400
+ }
401
+ }
402
+
121
403
  // src/ui/ai-tree/aiSectionsManager.tsx
122
404
  import { flushSync } from "react-dom";
123
405
  import { createRoot } from "react-dom/client";
@@ -132,7 +414,8 @@ function lucideByName(name) {
132
414
  }
133
415
  var typeStyle = (spec, font) => ({
134
416
  fontFamily: font,
135
- fontSize: spec.size,
417
+ // Headings shrink with the viewport (reaching full size around ~900px wide); body copy stays put.
418
+ 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
419
  lineHeight: spec.line,
137
420
  fontWeight: spec.weight
138
421
  });
@@ -159,6 +442,8 @@ var AI_RESPONSIVE_CSS = [
159
442
  " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
160
443
  " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
161
444
  " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
445
+ " [data-ai-responsive] { overflow-x: hidden; }",
446
+ " [data-ai-responsive] img { max-width: 100%; }",
162
447
  "}"
163
448
  ].join("\n");
164
449
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
@@ -1166,6 +1451,20 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1166
1451
  const overlayAlpha = { low: 0.25, medium: 0.45, high: 0.65 }[settings.overlayIntensity ?? "medium"];
1167
1452
  const backgroundUrl = isOverlay && settings.backgroundMedia ? ctx.resolveMedia(settings.backgroundMedia) : null;
1168
1453
  const bgAttrs = ctx.keyFor && isOverlay && settings.backgroundMedia ? { "data-ohw-key": ctx.keyFor("settings.backgroundMedia"), "data-ohw-editable": "bg-image" } : {};
1454
+ const toneBackground = (() => {
1455
+ const { dark, primary, light } = resolvedBrand.palette;
1456
+ switch (settings.sectionBackground) {
1457
+ case "surface":
1458
+ return `color-mix(in srgb, ${light} 94%, ${dark})`;
1459
+ case "accent":
1460
+ return primary;
1461
+ case "accent-soft":
1462
+ return `color-mix(in srgb, ${primary} 12%, ${light})`;
1463
+ default:
1464
+ return void 0;
1465
+ }
1466
+ })();
1467
+ const distributed = !isOverlay && settings.textDistribution;
1169
1468
  return /* @__PURE__ */ jsxs(
1170
1469
  "section",
1171
1470
  {
@@ -1175,10 +1474,11 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1175
1474
  style: {
1176
1475
  position: "relative",
1177
1476
  padding: `${pad}px 0`,
1178
- background: isOverlay && !backgroundUrl ? "#DBEAFE" : void 0,
1477
+ background: toneBackground ?? (isOverlay && !backgroundUrl ? "#DBEAFE" : void 0),
1179
1478
  backgroundImage: backgroundUrl ? `url(${backgroundUrl})` : void 0,
1180
1479
  backgroundSize: "cover",
1181
- backgroundPosition: "center"
1480
+ backgroundPosition: "center",
1481
+ color: settings.sectionBackground === "accent" ? resolvedBrand.palette.light : void 0
1182
1482
  },
1183
1483
  children: [
1184
1484
  /* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
@@ -1202,10 +1502,24 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1202
1502
  display: "grid",
1203
1503
  gridTemplateColumns: "repeat(12, 1fr)",
1204
1504
  gap: AI_TREE_TOKENS.spacing6,
1205
- alignItems: settings.verticalPosition === "top" ? "start" : "center",
1505
+ alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
1206
1506
  marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
1207
1507
  },
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))
1508
+ children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ jsx(
1509
+ "div",
1510
+ {
1511
+ "data-ai-cell": "",
1512
+ style: {
1513
+ gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
1514
+ minWidth: 0,
1515
+ // space-between: each column becomes a flex column whose content spreads over
1516
+ // the full row height instead of clumping at the top.
1517
+ ...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
1518
+ },
1519
+ children: renderNode(block, ctx, `r${r2}.b${b}`)
1520
+ },
1521
+ b
1522
+ ))
1209
1523
  },
1210
1524
  r2
1211
1525
  ))
@@ -1221,17 +1535,34 @@ import { jsx as jsx2 } from "react/jsx-runtime";
1221
1535
  var CONTAINER_ATTR = "data-ohw-ai-generated";
1222
1536
  var REPLACED_ATTR = "data-ohw-ai-replaced-by";
1223
1537
  var REMOVED_ATTR = "data-ohw-ai-removed";
1538
+ function readRootVar(name) {
1539
+ if (typeof document === "undefined") return "";
1540
+ return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
1541
+ }
1542
+ function deriveBrandOverride() {
1543
+ const dark = readRootVar("--ohw-brand-dark");
1544
+ const primary = readRootVar("--ohw-brand-primary");
1545
+ const light = readRootVar("--ohw-brand-light");
1546
+ if (!dark || !primary || !light) return null;
1547
+ const accent = readRootVar("--ohw-brand-accent");
1548
+ const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1549
+ const body = readRootVar("--font-body");
1550
+ return {
1551
+ palette: { dark, primary, accent: accent || dark, light },
1552
+ fonts: {
1553
+ heading: heading || AI_DEFAULT_BRAND.fonts.heading,
1554
+ body: body || AI_DEFAULT_BRAND.fonts.body
1555
+ }
1556
+ };
1557
+ }
1224
1558
  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");
1559
+ const dark = readRootVar("--color-dark");
1560
+ const primary = readRootVar("--color-primary");
1561
+ const light = readRootVar("--color-light");
1231
1562
  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");
1563
+ const accent = readRootVar("--color-accent");
1564
+ const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1565
+ const body = readRootVar("--font-body");
1235
1566
  return {
1236
1567
  palette: { dark, primary, accent: accent || dark, light },
1237
1568
  fonts: {
@@ -1323,8 +1654,12 @@ function syncReplacedOriginals(state) {
1323
1654
  }
1324
1655
  function applyAiSectionsToDom(state, options) {
1325
1656
  if (typeof document === "undefined") return;
1657
+ const brandOverride = deriveBrandOverride();
1326
1658
  const templateBrand = deriveTemplateBrand();
1327
- const activeIds = new Set(state.sections.map((entry) => entry.id));
1659
+ const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
1660
+ const pagePath = window.location.pathname;
1661
+ const pageSections = state.sections.filter((entry) => !entry.path || entry.path === pagePath);
1662
+ const activeIds = new Set(pageSections.map((entry) => entry.id));
1328
1663
  for (const [id, section] of mounted) {
1329
1664
  if (!activeIds.has(id)) {
1330
1665
  section.root.unmount();
@@ -1332,8 +1667,8 @@ function applyAiSectionsToDom(state, options) {
1332
1667
  mounted.delete(id);
1333
1668
  }
1334
1669
  }
1335
- for (const entry of state.sections) {
1336
- const serialized = JSON.stringify(entry);
1670
+ for (const entry of pageSections) {
1671
+ const serialized = JSON.stringify(entry) + brandKey;
1337
1672
  const existing = mounted.get(entry.id);
1338
1673
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
1339
1674
  continue;
@@ -1347,6 +1682,7 @@ function applyAiSectionsToDom(state, options) {
1347
1682
  mounted.delete(entry.id);
1348
1683
  }
1349
1684
  container.setAttribute("data-ohw-section", entry.id);
1685
+ container.setAttribute("data-ohw-instance", entry.id);
1350
1686
  container.setAttribute("data-ohw-section-label", entry.label);
1351
1687
  placeContainer(container, entry);
1352
1688
  const root = mounted.get(entry.id)?.root ?? createRoot(container);
@@ -1357,7 +1693,7 @@ function applyAiSectionsToDom(state, options) {
1357
1693
  AiTreeRenderer,
1358
1694
  {
1359
1695
  tree: entry.tree,
1360
- brand: entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1696
+ brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1361
1697
  resolveMedia,
1362
1698
  editKeyPrefix: `ai.${entry.id}`
1363
1699
  }
@@ -1974,7 +2310,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
1974
2310
  const autoId = useId();
1975
2311
  const insertAfter = insertAfterProp ?? autoId;
1976
2312
  const [schedule, setSchedule] = useState2(null);
1977
- const [loading, setLoading] = useState2(true);
2313
+ const [loading, setLoading] = useState2(initialScheduleId !== null);
1978
2314
  const [inEditor, setInEditor] = useState2(false);
1979
2315
  const [isHovered, setIsHovered] = useState2(false);
1980
2316
  const [modalState, setModalState] = useState2(null);
@@ -2148,8 +2484,10 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2148
2484
  "*"
2149
2485
  );
2150
2486
  };
2151
- if (!inEditor && !loading && !schedule) return null;
2152
2487
  const sectionId = `scheduling-${insertAfter}`;
2488
+ if (!inEditor && !loading && !schedule) {
2489
+ return /* @__PURE__ */ jsx4("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
2490
+ }
2153
2491
  return /* @__PURE__ */ jsxs3(
2154
2492
  "section",
2155
2493
  {
@@ -6668,8 +7006,12 @@ function parseSectionsFromHtml(html) {
6668
7006
 
6669
7007
  // src/ui/ai-section/AiSectionOverlay.tsx
6670
7008
  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)}"]`);
7009
+ function findSectionElement(instanceId) {
7010
+ const escaped = CSS.escape(instanceId);
7011
+ return document.querySelector(`[data-ohw-instance="${escaped}"]`) ?? document.querySelector(`[data-ohw-section="${escaped}"]:not([data-ohw-instance])`);
7012
+ }
7013
+ function readRect(instanceId) {
7014
+ const el = findSectionElement(instanceId);
6673
7015
  if (!el) return null;
6674
7016
  const r2 = el.getBoundingClientRect();
6675
7017
  if (r2.width <= 0 || r2.height <= 0) return null;
@@ -6692,7 +7034,7 @@ function useLiveSectionRect(sectionId) {
6692
7034
  const opts = { capture: true, passive: true };
6693
7035
  window.addEventListener("scroll", update, opts);
6694
7036
  window.addEventListener("resize", update);
6695
- const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
7037
+ const el = findSectionElement(sectionId);
6696
7038
  const ro = el ? new ResizeObserver(update) : null;
6697
7039
  if (el && ro) ro.observe(el);
6698
7040
  const interval = setInterval(update, 500);
@@ -6705,6 +7047,14 @@ function useLiveSectionRect(sectionId) {
6705
7047
  }, [sectionId]);
6706
7048
  return rect;
6707
7049
  }
7050
+ function computeSectionBoundaryFlags(instanceId) {
7051
+ const topLevel = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
7052
+ (el) => !el.parentElement?.closest("[data-ohw-section]")
7053
+ );
7054
+ const index = topLevel.findIndex((el) => (el.dataset.ohwInstance ?? el.dataset.ohwSection) === instanceId);
7055
+ if (index === -1) return { isFirst: true, isLast: true };
7056
+ return { isFirst: index === 0, isLast: index === topLevel.length - 1 };
7057
+ }
6708
7058
  var PRIMARY2 = "#0885FE";
6709
7059
  function edgeAwareRadius(rect) {
6710
7060
  const container = window.innerWidth <= 480 ? 16 : 24;
@@ -6778,6 +7128,7 @@ function AiSectionOverlay({
6778
7128
  }) {
6779
7129
  const [selectedId, setSelectedId] = useState4(null);
6780
7130
  const [reviewId, setReviewId] = useState4(null);
7131
+ const [reviewButtonsHidden, setReviewButtonsHidden] = useState4(false);
6781
7132
  const reviewIdRef = useRef4(null);
6782
7133
  reviewIdRef.current = reviewId;
6783
7134
  const selectedIdRef = useRef4(null);
@@ -6786,7 +7137,7 @@ function AiSectionOverlay({
6786
7137
  (el) => {
6787
7138
  postToParent2({
6788
7139
  type: "ow:section-selected",
6789
- sectionId: el?.dataset.ohwSection ?? null,
7140
+ sectionId: el ? el.dataset.ohwInstance ?? el.dataset.ohwSection ?? null : null,
6790
7141
  sectionLabel: el ? el.dataset.ohwSectionLabel ?? titleCaseSectionId(el.dataset.ohwSection ?? "") : null
6791
7142
  });
6792
7143
  },
@@ -6795,7 +7146,7 @@ function AiSectionOverlay({
6795
7146
  const selectFromElement = useCallback2(
6796
7147
  (el, options) => {
6797
7148
  const sectionEl = el?.closest("[data-ohw-section]") ?? null;
6798
- const id = sectionEl?.dataset.ohwSection ?? null;
7149
+ const id = sectionEl ? sectionEl.dataset.ohwInstance ?? sectionEl.dataset.ohwSection ?? null : null;
6799
7150
  if (id === selectedIdRef.current) return;
6800
7151
  setSelectedId(id);
6801
7152
  if (options?.report !== false) report(sectionEl);
@@ -6836,9 +7187,10 @@ function AiSectionOverlay({
6836
7187
  }
6837
7188
  const found = readRect(sectionId) != null;
6838
7189
  setReviewId(found ? sectionId : null);
7190
+ setReviewButtonsHidden(e.data.hideButtons === true);
6839
7191
  postToParent2({ type: "ow:ai-review-started", sectionId, found });
6840
7192
  if (found) {
6841
- document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
7193
+ document.querySelector(`[data-ohw-instance="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
6842
7194
  }
6843
7195
  }
6844
7196
  };
@@ -6857,7 +7209,7 @@ function AiSectionOverlay({
6857
7209
  return;
6858
7210
  }
6859
7211
  const sec = t.closest("[data-ohw-section]");
6860
- setHoveredId(sec?.dataset.ohwSection ?? null);
7212
+ setHoveredId(sec ? sec.dataset.ohwInstance ?? sec.dataset.ohwSection ?? null : null);
6861
7213
  };
6862
7214
  const onLeave = () => setHoveredId(null);
6863
7215
  document.addEventListener("mousemove", onMove, { passive: true });
@@ -6889,9 +7241,29 @@ function AiSectionOverlay({
6889
7241
  },
6890
7242
  [postToParent2]
6891
7243
  );
6892
- const selectionRect = useLiveSectionRect(reviewId ? null : selectedId);
7244
+ const activeSelectionId = reviewId ? null : selectedId;
7245
+ const selectionRect = useLiveSectionRect(activeSelectionId);
6893
7246
  const reviewRect = useLiveSectionRect(reviewId);
6894
7247
  const hoverRect = useLiveSectionRect(reviewId || hoveredId === selectedId ? null : hoveredId);
7248
+ useEffect4(() => {
7249
+ if (!activeSelectionId || !selectionRect) {
7250
+ postToParent2({ type: "ow:section-rect", instanceId: null, rect: null });
7251
+ return;
7252
+ }
7253
+ const { isFirst, isLast } = computeSectionBoundaryFlags(activeSelectionId);
7254
+ postToParent2({
7255
+ type: "ow:section-rect",
7256
+ instanceId: activeSelectionId,
7257
+ rect: {
7258
+ top: selectionRect.top + window.scrollY,
7259
+ left: selectionRect.left + window.scrollX,
7260
+ width: selectionRect.width,
7261
+ height: selectionRect.height
7262
+ },
7263
+ isFirst,
7264
+ isLast
7265
+ });
7266
+ }, [activeSelectionId, selectionRect, postToParent2]);
6895
7267
  return /* @__PURE__ */ jsxs8(Fragment5, { children: [
6896
7268
  hoverRect && /* @__PURE__ */ jsx16(
6897
7269
  "div",
@@ -6942,13 +7314,16 @@ function AiSectionOverlay({
6942
7314
  border: `2px solid ${PRIMARY2}`,
6943
7315
  borderRadius: edgeAwareRadius(reviewRect),
6944
7316
  zIndex: 2147483200,
6945
- // The veil itself: swallows clicks so the section stays locked until decided.
7317
+ // The veil itself: swallows clicks so the section stays locked until decided. This
7318
+ // stopPropagation only guards the bubble phase; the bridge's capture-phase click
7319
+ // handlers additionally bail on `[data-ohw-ai-review]`, without which a click on
7320
+ // Accept/Discard resolves to the media beneath and opens the file picker.
6946
7321
  background: "rgba(8, 133, 254, 0.04)",
6947
7322
  pointerEvents: "auto",
6948
7323
  cursor: "default"
6949
7324
  },
6950
7325
  onClick: (e) => e.stopPropagation(),
6951
- children: /* @__PURE__ */ jsxs8(
7326
+ children: !reviewButtonsHidden && /* @__PURE__ */ jsxs8(
6952
7327
  "div",
6953
7328
  {
6954
7329
  style: {
@@ -10848,6 +11223,329 @@ function deleteFooterColumn(column) {
10848
11223
  };
10849
11224
  }
10850
11225
 
11226
+ // src/lib/logo-identity.ts
11227
+ var LOGO_TEXT_KEYS = ["nav-logo-text", "footer-logo-text", "logo-text"];
11228
+ var LOGO_IMAGE_KEYS = ["nav-logo-image", "footer-logo", "footer-logo-image"];
11229
+ var LOGO_HREF_KEYS = ["nav-logo-href", "footer-logo-href", "logo-href"];
11230
+ var LOGO_PLACEHOLDER_KEY = "logo-is-placeholder";
11231
+ var LOGO_ALT_KEY = "logo-alt";
11232
+ var LOGO_IMAGE_URL_KEY = "nav-logo-image";
11233
+ var PLACEHOLDER_BUSINESS_NAME = "Business name";
11234
+ function resolveLogoDisplayText(text) {
11235
+ const trimmed = (text ?? "").trim();
11236
+ return trimmed || PLACEHOLDER_BUSINESS_NAME;
11237
+ }
11238
+ function isFooterLogoRoot(root) {
11239
+ return Boolean(root.closest("footer") || root.closest('[data-ohw-section="footer"]'));
11240
+ }
11241
+ function imageKeyForRoot(root) {
11242
+ return isFooterLogoRoot(root) ? "footer-logo" : "nav-logo-image";
11243
+ }
11244
+ function textKeyForRoot(root) {
11245
+ return isFooterLogoRoot(root) ? "footer-logo-text" : "nav-logo-text";
11246
+ }
11247
+ function ensureLogoHrefKey(root) {
11248
+ if (!(root instanceof HTMLAnchorElement)) return;
11249
+ if (root.hasAttribute("data-ohw-href-key")) return;
11250
+ root.setAttribute("data-ohw-href-key", isFooterLogoRoot(root) ? "footer-logo-href" : "nav-logo-href");
11251
+ }
11252
+ function applyLogoIdentity(text, isPlaceholder) {
11253
+ const display = resolveLogoDisplayText(text);
11254
+ const placeholder = isPlaceholder || !text.trim() || display === PLACEHOLDER_BUSINESS_NAME;
11255
+ for (const key of LOGO_TEXT_KEYS) {
11256
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
11257
+ if (el.textContent !== display) el.textContent = display;
11258
+ });
11259
+ }
11260
+ for (const key of LOGO_IMAGE_KEYS) {
11261
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
11262
+ const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
11263
+ if (img) img.alt = display;
11264
+ });
11265
+ }
11266
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((el) => {
11267
+ if (placeholder) el.setAttribute("data-ohw-placeholder", "");
11268
+ else el.removeAttribute("data-ohw-placeholder");
11269
+ });
11270
+ return display;
11271
+ }
11272
+ function applyLogoImage(url, alt) {
11273
+ const displayAlt = resolveLogoDisplayText(alt);
11274
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11275
+ ensureLogoHrefKey(root);
11276
+ const imageKey = imageKeyForRoot(root);
11277
+ const textKey = textKeyForRoot(root);
11278
+ 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");
11279
+ let textEl = root.querySelector(`[data-ohw-key="${textKey}"]`) ?? root.querySelector('[data-ohw-key="logo-text"]');
11280
+ if (url) {
11281
+ if (!img) {
11282
+ img = document.createElement("img");
11283
+ img.setAttribute("data-ohw-editable", "image");
11284
+ img.setAttribute("data-ohw-key", imageKey);
11285
+ img.alt = displayAlt;
11286
+ img.style.height = "";
11287
+ img.style.maxHeight = "none";
11288
+ img.style.width = "auto";
11289
+ img.style.display = "block";
11290
+ img.style.objectFit = "contain";
11291
+ root.insertBefore(img, root.firstChild);
11292
+ } else {
11293
+ img.setAttribute("data-ohw-editable", "image");
11294
+ img.setAttribute("data-ohw-key", imageKey);
11295
+ }
11296
+ img.removeAttribute("srcset");
11297
+ img.removeAttribute("sizes");
11298
+ img.src = url;
11299
+ img.alt = displayAlt;
11300
+ img.style.display = "block";
11301
+ if (textEl) textEl.style.display = "none";
11302
+ root.removeAttribute("data-ohw-placeholder");
11303
+ return;
11304
+ }
11305
+ if (img) {
11306
+ img.removeAttribute("src");
11307
+ img.removeAttribute("srcset");
11308
+ img.removeAttribute("sizes");
11309
+ img.alt = displayAlt;
11310
+ img.style.display = "none";
11311
+ }
11312
+ if (!textEl) {
11313
+ textEl = document.createElement("span");
11314
+ textEl.setAttribute("data-ohw-editable", "plain");
11315
+ textEl.setAttribute("data-ohw-key", textKey);
11316
+ root.appendChild(textEl);
11317
+ }
11318
+ textEl.style.display = "";
11319
+ if (textEl.textContent !== displayAlt) textEl.textContent = displayAlt;
11320
+ if (!displayAlt.trim() || displayAlt === PLACEHOLDER_BUSINESS_NAME) {
11321
+ root.setAttribute("data-ohw-placeholder", "");
11322
+ } else {
11323
+ root.removeAttribute("data-ohw-placeholder");
11324
+ }
11325
+ });
11326
+ }
11327
+ function applyLogoHref(href) {
11328
+ const target = href.trim() || "/";
11329
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11330
+ ensureLogoHrefKey(root);
11331
+ if (root instanceof HTMLAnchorElement) {
11332
+ root.setAttribute("href", target);
11333
+ }
11334
+ });
11335
+ for (const key of LOGO_HREF_KEYS) setStoredLinkHref(key, target);
11336
+ }
11337
+ function readLogoIdentityFromDom() {
11338
+ let imageUrl = null;
11339
+ for (const key of LOGO_IMAGE_KEYS) {
11340
+ const el = document.querySelector(`[data-ohw-key="${key}"]`);
11341
+ const img = el instanceof HTMLImageElement ? el : el?.querySelector("img");
11342
+ const attrSrc = img?.getAttribute("src")?.trim() ?? "";
11343
+ if (attrSrc && !attrSrc.startsWith("data:") && img && img.style.display !== "none") {
11344
+ imageUrl = img.currentSrc || img.src;
11345
+ break;
11346
+ }
11347
+ }
11348
+ let text = PLACEHOLDER_BUSINESS_NAME;
11349
+ let isPlaceholder = true;
11350
+ for (const key of LOGO_TEXT_KEYS) {
11351
+ const el = document.querySelector(`[data-ohw-key="${key}"]`);
11352
+ if (el?.textContent?.trim()) {
11353
+ text = el.textContent.trim();
11354
+ const logoRoot2 = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
11355
+ isPlaceholder = logoRoot2?.hasAttribute("data-ohw-placeholder") === true || text === PLACEHOLDER_BUSINESS_NAME;
11356
+ break;
11357
+ }
11358
+ }
11359
+ if (imageUrl) {
11360
+ const logoImg = document.querySelector(
11361
+ '[data-ohw-key="nav-logo-image"], [data-ohw-key="footer-logo"]'
11362
+ );
11363
+ const alt = logoImg?.alt?.trim() || text;
11364
+ isPlaceholder = false;
11365
+ const hrefEl = document.querySelector(
11366
+ 'a[data-ohw-role="logo"], a[data-ohw-logo], [data-ohw-role="logo"]'
11367
+ );
11368
+ const href2 = (hrefEl instanceof HTMLAnchorElement ? hrefEl.getAttribute("href") : null) || hrefEl?.closest("a")?.getAttribute("href") || "/";
11369
+ return { text, isPlaceholder, imageUrl, href: href2, alt };
11370
+ }
11371
+ const logoRoot = document.querySelector('[data-ohw-role="logo"], [data-ohw-logo]');
11372
+ const href = (logoRoot instanceof HTMLAnchorElement ? logoRoot.getAttribute("href") : null) || logoRoot?.closest("a")?.getAttribute("href") || "/";
11373
+ return { text, isPlaceholder, imageUrl: null, href, alt: text };
11374
+ }
11375
+ function applyLogoFromContent(content) {
11376
+ 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);
11377
+ if (!hasLogoIdentity) return false;
11378
+ const logoText = content[LOGO_TEXT_KEYS[0]] ?? content[LOGO_TEXT_KEYS[1]] ?? readLogoIdentityFromDom().text;
11379
+ const logoAlt = content[LOGO_ALT_KEY] ?? logoText;
11380
+ const rawLogoImage = content[LOGO_IMAGE_URL_KEY] ?? content["footer-logo"] ?? content["footer-logo-image"] ?? null;
11381
+ const logoImageUrl = typeof rawLogoImage === "string" && rawLogoImage.trim() ? rawLogoImage.trim() : null;
11382
+ const imageExplicitlyCleared = LOGO_IMAGE_KEYS.some((key) => key in content) && !logoImageUrl;
11383
+ const logoIsPlaceholder = LOGO_PLACEHOLDER_KEY in content ? content[LOGO_PLACEHOLDER_KEY] !== "false" : !logoImageUrl && (!logoText.trim() || logoText === PLACEHOLDER_BUSINESS_NAME);
11384
+ if (logoImageUrl) {
11385
+ applyLogoImage(logoImageUrl, logoAlt);
11386
+ } else {
11387
+ if (imageExplicitlyCleared) applyLogoImage(null, logoAlt);
11388
+ applyLogoIdentity(logoText, logoIsPlaceholder);
11389
+ }
11390
+ const logoHref = content["nav-logo-href"] ?? content["footer-logo-href"] ?? content["logo-href"];
11391
+ if (typeof logoHref === "string" && logoHref.trim()) {
11392
+ applyLogoHref(logoHref);
11393
+ }
11394
+ return true;
11395
+ }
11396
+
11397
+ // src/lib/logo-size.ts
11398
+ var LOGO_SIZE_DEFAULTS = {
11399
+ navbar: 28,
11400
+ footer: 32
11401
+ };
11402
+ var LOGO_SIZE_MIN = 16;
11403
+ var LOGO_SIZE_MAX = 80;
11404
+ var LOGO_SIZE_DESKTOP_KEYS = {
11405
+ navbar: "nav-logo-size",
11406
+ footer: "footer-logo-size"
11407
+ };
11408
+ var LOGO_SIZE_MOBILE_KEYS = {
11409
+ navbar: "nav-logo-size-mobile",
11410
+ footer: "footer-logo-size-mobile"
11411
+ };
11412
+ var LOGO_SIZE_KEYS = [
11413
+ LOGO_SIZE_DESKTOP_KEYS.navbar,
11414
+ LOGO_SIZE_DESKTOP_KEYS.footer,
11415
+ LOGO_SIZE_MOBILE_KEYS.navbar,
11416
+ LOGO_SIZE_MOBILE_KEYS.footer
11417
+ ];
11418
+ function isFooterLogoRoot2(root) {
11419
+ return Boolean(root.closest("footer") || root.closest('[data-ohw-section="footer"]'));
11420
+ }
11421
+ function getLogoPlacement(root) {
11422
+ return isFooterLogoRoot2(root) ? "footer" : "navbar";
11423
+ }
11424
+ function parseLogoSizePx(raw, fallback) {
11425
+ if (raw == null || raw === "") return fallback;
11426
+ const n = Number.parseFloat(raw);
11427
+ if (!Number.isFinite(n)) return fallback;
11428
+ return Math.min(LOGO_SIZE_MAX, Math.max(LOGO_SIZE_MIN, Math.round(n)));
11429
+ }
11430
+ function isMobileLogoSizeFollowing(content, placement) {
11431
+ const raw = content[LOGO_SIZE_MOBILE_KEYS[placement]];
11432
+ return raw == null || raw.trim() === "";
11433
+ }
11434
+ function resolveDesktopLogoSize(content, placement) {
11435
+ return parseLogoSizePx(content[LOGO_SIZE_DESKTOP_KEYS[placement]], LOGO_SIZE_DEFAULTS[placement]);
11436
+ }
11437
+ function resolveMobileLogoSize(content, placement) {
11438
+ if (isMobileLogoSizeFollowing(content, placement)) {
11439
+ return resolveDesktopLogoSize(content, placement);
11440
+ }
11441
+ return parseLogoSizePx(
11442
+ content[LOGO_SIZE_MOBILE_KEYS[placement]],
11443
+ resolveDesktopLogoSize(content, placement)
11444
+ );
11445
+ }
11446
+ function setRootSizeVars(root, desktopPx, mobilePx, following) {
11447
+ root.style.setProperty("--ohw-logo-size", `${desktopPx}px`);
11448
+ if (following) {
11449
+ root.style.removeProperty("--ohw-logo-size-mobile");
11450
+ } else {
11451
+ root.style.setProperty("--ohw-logo-size-mobile", `${mobilePx}px`);
11452
+ }
11453
+ root.querySelectorAll("img").forEach((img) => {
11454
+ img.style.height = "";
11455
+ img.style.maxHeight = "none";
11456
+ img.style.width = "auto";
11457
+ img.style.objectFit = "contain";
11458
+ });
11459
+ }
11460
+ function applyLogoSizes(content) {
11461
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11462
+ const placement = getLogoPlacement(root);
11463
+ const desktop = resolveDesktopLogoSize(content, placement);
11464
+ const following = isMobileLogoSizeFollowing(content, placement);
11465
+ const mobile = following ? desktop : resolveMobileLogoSize(content, placement);
11466
+ setRootSizeVars(root, desktop, mobile, following);
11467
+ });
11468
+ }
11469
+ function applyLogoSizeToPlacement(placement, desktopPx, mobilePx, following) {
11470
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11471
+ if (getLogoPlacement(root) !== placement) return;
11472
+ setRootSizeVars(root, desktopPx, mobilePx, following);
11473
+ });
11474
+ }
11475
+ function logoHasUploadedImage(logoEl) {
11476
+ if (logoEl.hasAttribute("data-ohw-placeholder")) return false;
11477
+ 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");
11478
+ if (!img) return false;
11479
+ const src = img.getAttribute("src")?.trim() ?? "";
11480
+ if (!src || src.startsWith("data:")) return false;
11481
+ if (img.style.display === "none") return false;
11482
+ return true;
11483
+ }
11484
+ function getLogoInteractionRect(logoEl) {
11485
+ if (logoHasUploadedImage(logoEl)) {
11486
+ 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");
11487
+ if (img) {
11488
+ const r2 = img.getBoundingClientRect();
11489
+ if (r2.width > 0 && r2.height > 0) return r2;
11490
+ }
11491
+ }
11492
+ const text = logoEl.querySelector(
11493
+ '[data-ohw-key="nav-logo-text"], [data-ohw-key="footer-logo-text"]'
11494
+ );
11495
+ if (text) {
11496
+ const style = window.getComputedStyle(text);
11497
+ if (style.display !== "none" && style.visibility !== "hidden") {
11498
+ const r2 = text.getBoundingClientRect();
11499
+ if (r2.width > 0 && r2.height > 0) return r2;
11500
+ }
11501
+ }
11502
+ return logoEl.getBoundingClientRect();
11503
+ }
11504
+ function readLogoSizeState(content, placement) {
11505
+ const desktopPx = resolveDesktopLogoSize(content, placement);
11506
+ const mobileFollowing = isMobileLogoSizeFollowing(content, placement);
11507
+ const mobilePx = mobileFollowing ? desktopPx : resolveMobileLogoSize(content, placement);
11508
+ return { desktopPx, mobilePx, mobileFollowing };
11509
+ }
11510
+
11511
+ // src/lib/site-wide-scope.ts
11512
+ function getLogoElement(el) {
11513
+ const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
11514
+ if (marked) return marked;
11515
+ if (el.closest('[data-ohw-editable="icon"]')) return null;
11516
+ const root = el.closest("nav, [data-ohw-nav-root], footer");
11517
+ if (!root) return null;
11518
+ const anchor = el.closest("a");
11519
+ if (anchor && root.contains(anchor) && !anchor.hasAttribute("data-ohw-href-key") && !anchor.closest("[data-ohw-nav-container]") && Boolean(anchor.querySelector("img") || anchor.matches("img"))) {
11520
+ return anchor;
11521
+ }
11522
+ const img = el.matches("img") ? el : null;
11523
+ if (img && !img.closest("[data-ohw-href-key]") && !img.closest("[data-ohw-nav-container]") && (img.closest("footer") || img.closest("nav, [data-ohw-nav-root]"))) {
11524
+ return img;
11525
+ }
11526
+ return null;
11527
+ }
11528
+ function isInFooter(el) {
11529
+ if (!el) return false;
11530
+ return Boolean(el.closest("footer") || el.closest('[data-ohw-section="footer"]'));
11531
+ }
11532
+ function isSiteWideElement(el) {
11533
+ if (!el) return false;
11534
+ if (getLogoElement(el)) return true;
11535
+ if (isInFooter(el)) return true;
11536
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-root")) {
11537
+ return true;
11538
+ }
11539
+ if (el.hasAttribute("data-ohw-href-key") && el.closest("nav, [data-ohw-nav-root], [data-ohw-nav-drawer], aside")) {
11540
+ return true;
11541
+ }
11542
+ if (el.closest('[data-ohw-role="navbar-button"]')) return true;
11543
+ return false;
11544
+ }
11545
+ function isSiteWideScopeActive(args) {
11546
+ return isSiteWideElement(args.selected) || isSiteWideElement(args.hoveredItem) || isSiteWideElement(args.hoveredNavContainer) || isSiteWideElement(args.active);
11547
+ }
11548
+
10851
11549
  // src/lib/add-footer-column.ts
10852
11550
  function buildFooterColumnEditContentPatch(result) {
10853
11551
  return {
@@ -11069,16 +11767,127 @@ function FloatingPanel({
11069
11767
  );
11070
11768
  }
11071
11769
 
11072
- // src/ui/socials-display-panel.tsx
11770
+ // src/ui/logo-size-panel.tsx
11771
+ import { ArrowUpRight, Link as Link2 } from "lucide-react";
11073
11772
  import { jsx as jsx27, jsxs as jsxs17 } from "react/jsx-runtime";
11773
+ function SizeSlider({
11774
+ value,
11775
+ onChange
11776
+ }) {
11777
+ const pct = (value - LOGO_SIZE_MIN) / (LOGO_SIZE_MAX - LOGO_SIZE_MIN) * 100;
11778
+ return /* @__PURE__ */ jsxs17("div", { className: "flex w-full flex-col gap-3", children: [
11779
+ /* @__PURE__ */ jsxs17("div", { className: "flex w-full items-center gap-2 text-sm font-medium leading-5", children: [
11780
+ /* @__PURE__ */ jsx27("span", { className: "min-w-0 flex-1 text-foreground", children: "Size" }),
11781
+ /* @__PURE__ */ jsxs17("span", { className: "shrink-0 whitespace-nowrap text-muted-foreground", children: [
11782
+ value,
11783
+ " px"
11784
+ ] })
11785
+ ] }),
11786
+ /* @__PURE__ */ jsxs17("div", { className: "relative h-2 w-full rounded-full bg-primary-50", children: [
11787
+ /* @__PURE__ */ jsx27(
11788
+ "div",
11789
+ {
11790
+ className: "absolute inset-y-0 left-0 rounded-full bg-primary",
11791
+ style: { width: `${pct}%` }
11792
+ }
11793
+ ),
11794
+ /* @__PURE__ */ jsx27(
11795
+ "input",
11796
+ {
11797
+ type: "range",
11798
+ min: LOGO_SIZE_MIN,
11799
+ max: LOGO_SIZE_MAX,
11800
+ step: 1,
11801
+ value,
11802
+ "aria-label": "Logo size",
11803
+ className: cn(
11804
+ "absolute inset-0 h-full w-full cursor-pointer appearance-none bg-transparent",
11805
+ "[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:size-5",
11806
+ "[&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2",
11807
+ "[&::-webkit-slider-thumb]:border-primary [&::-webkit-slider-thumb]:bg-background",
11808
+ "[&::-moz-range-thumb]:size-5 [&::-moz-range-thumb]:rounded-full",
11809
+ "[&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-primary",
11810
+ "[&::-moz-range-thumb]:bg-background"
11811
+ ),
11812
+ onChange: (e) => onChange(Number(e.target.value))
11813
+ }
11814
+ )
11815
+ ] })
11816
+ ] });
11817
+ }
11818
+ function LogoSizePanel({
11819
+ viewport,
11820
+ sizePx,
11821
+ mobileFollowing = true,
11822
+ onSizeChange,
11823
+ onCustomizeMobile,
11824
+ onResetMobile,
11825
+ onUpdateEverywhere,
11826
+ className
11827
+ }) {
11828
+ const showFollowing = viewport === "mobile" && mobileFollowing;
11829
+ const showMobileSlider = viewport === "mobile" && !mobileFollowing;
11830
+ const showDesktopSlider = viewport === "desktop";
11831
+ return /* @__PURE__ */ jsxs17("div", { className: cn("flex w-full flex-col gap-4", className), children: [
11832
+ showFollowing ? /* @__PURE__ */ jsxs17("div", { className: "flex w-full flex-col gap-2", children: [
11833
+ /* @__PURE__ */ jsxs17("div", { className: "flex items-start gap-1", children: [
11834
+ /* @__PURE__ */ jsx27(Link2, { size: 16, className: "mt-0.5 shrink-0 text-foreground", "aria-hidden": true }),
11835
+ /* @__PURE__ */ jsx27("p", { className: "text-sm font-semibold leading-5 text-foreground", children: "Following desktop size" })
11836
+ ] }),
11837
+ /* @__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." }),
11838
+ /* @__PURE__ */ jsx27(
11839
+ Button,
11840
+ {
11841
+ type: "button",
11842
+ variant: "outline",
11843
+ size: "sm",
11844
+ className: "h-9 w-full min-w-0 cursor-pointer",
11845
+ onClick: onCustomizeMobile,
11846
+ children: "Customize for mobile"
11847
+ }
11848
+ )
11849
+ ] }) : null,
11850
+ showDesktopSlider || showMobileSlider ? /* @__PURE__ */ jsx27(SizeSlider, { value: sizePx, onChange: onSizeChange }) : null,
11851
+ showMobileSlider ? /* @__PURE__ */ jsx27(
11852
+ Button,
11853
+ {
11854
+ type: "button",
11855
+ variant: "outline",
11856
+ size: "sm",
11857
+ className: "h-9 w-full min-w-0 cursor-pointer",
11858
+ onClick: onResetMobile,
11859
+ children: "Reset to desktop size"
11860
+ }
11861
+ ) : null,
11862
+ /* @__PURE__ */ jsx27("div", { className: "h-px w-full bg-border", role: "separator" }),
11863
+ /* @__PURE__ */ jsxs17(
11864
+ Button,
11865
+ {
11866
+ type: "button",
11867
+ variant: "outline",
11868
+ size: "sm",
11869
+ className: "h-9 w-full min-w-0 cursor-pointer gap-1",
11870
+ onClick: onUpdateEverywhere,
11871
+ children: [
11872
+ "Update logo everywhere",
11873
+ /* @__PURE__ */ jsx27(ArrowUpRight, { size: 16, "aria-hidden": true })
11874
+ ]
11875
+ }
11876
+ ),
11877
+ /* @__PURE__ */ jsx27("p", { className: "text-sm leading-5 text-muted-foreground", children: "Matches the right version to your background." })
11878
+ ] });
11879
+ }
11880
+
11881
+ // src/ui/socials-display-panel.tsx
11882
+ import { jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
11074
11883
  function DisplaySwitch({
11075
11884
  label,
11076
11885
  checked,
11077
11886
  disabled,
11078
11887
  onChange
11079
11888
  }) {
11080
- return /* @__PURE__ */ jsxs17("div", { className: "flex w-full items-center gap-2", children: [
11081
- /* @__PURE__ */ jsx27(
11889
+ return /* @__PURE__ */ jsxs18("div", { className: "flex w-full items-center gap-2", children: [
11890
+ /* @__PURE__ */ jsx28(
11082
11891
  "span",
11083
11892
  {
11084
11893
  className: cn(
@@ -11088,7 +11897,7 @@ function DisplaySwitch({
11088
11897
  children: label
11089
11898
  }
11090
11899
  ),
11091
- /* @__PURE__ */ jsx27(
11900
+ /* @__PURE__ */ jsx28(
11092
11901
  "button",
11093
11902
  {
11094
11903
  type: "button",
@@ -11102,7 +11911,7 @@ function DisplaySwitch({
11102
11911
  checked ? "bg-primary" : "bg-primary-50",
11103
11912
  disabled ? "cursor-default opacity-50" : "cursor-pointer"
11104
11913
  ),
11105
- children: /* @__PURE__ */ jsx27(
11914
+ children: /* @__PURE__ */ jsx28(
11106
11915
  "span",
11107
11916
  {
11108
11917
  className: cn(
@@ -11116,8 +11925,8 @@ function DisplaySwitch({
11116
11925
  ] });
11117
11926
  }
11118
11927
  function SocialsDisplayPanel({ display, onChange, className }) {
11119
- return /* @__PURE__ */ jsxs17("div", { className: cn("flex w-full flex-col gap-3", className), children: [
11120
- /* @__PURE__ */ jsx27(
11928
+ return /* @__PURE__ */ jsxs18("div", { className: cn("flex w-full flex-col gap-3", className), children: [
11929
+ /* @__PURE__ */ jsx28(
11121
11930
  DisplaySwitch,
11122
11931
  {
11123
11932
  label: "Text",
@@ -11126,7 +11935,7 @@ function SocialsDisplayPanel({ display, onChange, className }) {
11126
11935
  onChange: (text) => onChange({ ...display, text })
11127
11936
  }
11128
11937
  ),
11129
- /* @__PURE__ */ jsx27(
11938
+ /* @__PURE__ */ jsx28(
11130
11939
  DisplaySwitch,
11131
11940
  {
11132
11941
  label: "Icon",
@@ -11687,7 +12496,7 @@ function useNavItemDrag({
11687
12496
 
11688
12497
  // src/ui/footer-container-chrome.tsx
11689
12498
  import { Plus as Plus2 } from "lucide-react";
11690
- import { jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
12499
+ import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
11691
12500
  function FooterContainerChrome({
11692
12501
  rect,
11693
12502
  onAdd,
@@ -11695,7 +12504,7 @@ function FooterContainerChrome({
11695
12504
  }) {
11696
12505
  const chromeGap = 6;
11697
12506
  const buttonMargin = 7;
11698
- return /* @__PURE__ */ jsx28(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx28(
12507
+ return /* @__PURE__ */ jsx29(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx29(
11699
12508
  "div",
11700
12509
  {
11701
12510
  "data-ohw-footer-container-chrome": "",
@@ -11707,8 +12516,8 @@ function FooterContainerChrome({
11707
12516
  width: rect.width + chromeGap * 2,
11708
12517
  height: rect.height + chromeGap * 2
11709
12518
  },
11710
- children: /* @__PURE__ */ jsxs18(Tooltip, { children: [
11711
- /* @__PURE__ */ jsx28(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx28(
12519
+ children: /* @__PURE__ */ jsxs19(Tooltip, { children: [
12520
+ /* @__PURE__ */ jsx29(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx29(
11712
12521
  "button",
11713
12522
  {
11714
12523
  type: "button",
@@ -11727,10 +12536,10 @@ function FooterContainerChrome({
11727
12536
  if (addDisabled) return;
11728
12537
  onAdd();
11729
12538
  },
11730
- children: /* @__PURE__ */ jsx28(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
12539
+ children: /* @__PURE__ */ jsx29(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
11731
12540
  }
11732
12541
  ) }),
11733
- /* @__PURE__ */ jsx28(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
12542
+ /* @__PURE__ */ jsx29(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
11734
12543
  ] })
11735
12544
  }
11736
12545
  ) });
@@ -11913,6 +12722,18 @@ function collectEditableNodes(extraContent, root = document) {
11913
12722
  }
11914
12723
  if (extraContent && !isScoped) {
11915
12724
  applyNavFooterDeleteOverrides(byKey, extraContent);
12725
+ for (const key of LOGO_IMAGE_KEYS) {
12726
+ if (!(key in extraContent)) continue;
12727
+ byKey.set(key, { key, type: "image", text: extraContent[key] ?? "" });
12728
+ }
12729
+ for (const key of [LOGO_PLACEHOLDER_KEY, LOGO_ALT_KEY]) {
12730
+ if (!(key in extraContent)) continue;
12731
+ byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
12732
+ }
12733
+ for (const key of LOGO_SIZE_KEYS) {
12734
+ if (!(key in extraContent)) continue;
12735
+ byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
12736
+ }
11916
12737
  }
11917
12738
  return Array.from(byKey.values());
11918
12739
  }
@@ -12179,13 +13000,13 @@ function deleteSelectedNavFooterItem(deps) {
12179
13000
 
12180
13001
  // src/ui/navbar-container-chrome.tsx
12181
13002
  import { Plus as Plus3 } from "lucide-react";
12182
- import { jsx as jsx29 } from "react/jsx-runtime";
13003
+ import { jsx as jsx30 } from "react/jsx-runtime";
12183
13004
  function NavbarContainerChrome({
12184
13005
  rect,
12185
13006
  onAdd
12186
13007
  }) {
12187
13008
  const chromeGap = 6;
12188
- return /* @__PURE__ */ jsx29(
13009
+ return /* @__PURE__ */ jsx30(
12189
13010
  "div",
12190
13011
  {
12191
13012
  "data-ohw-navbar-container-chrome": "",
@@ -12197,7 +13018,7 @@ function NavbarContainerChrome({
12197
13018
  width: rect.width + chromeGap * 2,
12198
13019
  height: rect.height + chromeGap * 2
12199
13020
  },
12200
- children: /* @__PURE__ */ jsx29(
13021
+ children: /* @__PURE__ */ jsx30(
12201
13022
  "button",
12202
13023
  {
12203
13024
  type: "button",
@@ -12214,7 +13035,7 @@ function NavbarContainerChrome({
12214
13035
  e.stopPropagation();
12215
13036
  onAdd();
12216
13037
  },
12217
- children: /* @__PURE__ */ jsx29(Plus3, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
13038
+ children: /* @__PURE__ */ jsx30(Plus3, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
12218
13039
  }
12219
13040
  )
12220
13041
  }
@@ -12223,7 +13044,7 @@ function NavbarContainerChrome({
12223
13044
 
12224
13045
  // src/ui/drop-indicator.tsx
12225
13046
  import * as React10 from "react";
12226
- import { jsx as jsx30 } from "react/jsx-runtime";
13047
+ import { jsx as jsx31 } from "react/jsx-runtime";
12227
13048
  var dropIndicatorVariants = cva(
12228
13049
  "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
12229
13050
  {
@@ -12247,7 +13068,7 @@ var dropIndicatorVariants = cva(
12247
13068
  );
12248
13069
  var DropIndicator = React10.forwardRef(
12249
13070
  ({ className, direction, state, ...props }, ref) => {
12250
- return /* @__PURE__ */ jsx30(
13071
+ return /* @__PURE__ */ jsx31(
12251
13072
  "div",
12252
13073
  {
12253
13074
  ref,
@@ -12264,7 +13085,7 @@ var DropIndicator = React10.forwardRef(
12264
13085
  DropIndicator.displayName = "DropIndicator";
12265
13086
 
12266
13087
  // src/ui/badge.tsx
12267
- import { jsx as jsx31 } from "react/jsx-runtime";
13088
+ import { jsx as jsx32 } from "react/jsx-runtime";
12268
13089
  var badgeVariants = cva(
12269
13090
  "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
13091
  {
@@ -12282,12 +13103,12 @@ var badgeVariants = cva(
12282
13103
  }
12283
13104
  );
12284
13105
  function Badge({ className, variant, ...props }) {
12285
- return /* @__PURE__ */ jsx31("div", { className: cn(badgeVariants({ variant }), className), ...props });
13106
+ return /* @__PURE__ */ jsx32("div", { className: cn(badgeVariants({ variant }), className), ...props });
12286
13107
  }
12287
13108
 
12288
13109
  // 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";
13110
+ import { Link as Link3 } from "lucide-react";
13111
+ import { Fragment as Fragment8, jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
12291
13112
  var PRIMARY3 = "#0885FE";
12292
13113
  var IMAGE_FADE_MS = 300;
12293
13114
  function runOpacityFade(el, onDone) {
@@ -12381,21 +13202,10 @@ function parseSchedulingInsertAfter(insertAfter) {
12381
13202
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
12382
13203
  };
12383
13204
  }
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;
13205
+ function resolveEntryAnchor(entry) {
13206
+ if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
13207
+ const parsed = parseSchedulingInsertAfter(entry.insertAfter);
13208
+ return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
12399
13209
  }
12400
13210
  function schedulingMountDepth(insertAfter) {
12401
13211
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -12412,8 +13222,7 @@ function getPageSchedulingEntries(raw) {
12412
13222
  }
12413
13223
  }
12414
13224
  function isSchedulingWidgetMissing(entry) {
12415
- const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
12416
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
13225
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
12417
13226
  }
12418
13227
  function hasMissingSchedulingWidgets(entries) {
12419
13228
  return entries.some(isSchedulingWidgetMissing);
@@ -12443,16 +13252,17 @@ function initSectionsFromContent(content, removeExisting = false) {
12443
13252
  } catch {
12444
13253
  }
12445
13254
  }
12446
- function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
12447
- const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
12448
- const sectionId = schedulingSectionId(effectiveInsertAfter);
13255
+ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
13256
+ const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
13257
+ const sectionId = schedulingSectionId(widgetId);
12449
13258
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
12450
- const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
12451
- if (!mountPoint) return false;
13259
+ const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
13260
+ if (!anchorEl) return false;
13261
+ const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
12452
13262
  const container = document.createElement("div");
12453
13263
  container.dataset.ohwSectionContainer = "scheduling";
12454
- if (insertBefore) {
12455
- const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
13264
+ if (beforeId) {
13265
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
12456
13266
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
12457
13267
  if (!beforePoint) return false;
12458
13268
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -12463,19 +13273,25 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
12463
13273
  }
12464
13274
  tail.insertAdjacentElement("afterend", container);
12465
13275
  }
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
- });
13276
+ try {
13277
+ const root = createRoot2(container);
13278
+ flushSync2(() => {
13279
+ root.render(
13280
+ /* @__PURE__ */ jsx33(
13281
+ SchedulingWidget,
13282
+ {
13283
+ notifyOnConnect,
13284
+ initialScheduleId: scheduleId,
13285
+ insertAfter: widgetId
13286
+ }
13287
+ )
13288
+ );
13289
+ });
13290
+ } catch (err) {
13291
+ console.error("[ow:scheduling] render threw", err);
13292
+ container.remove();
13293
+ return false;
13294
+ }
12479
13295
  const tracker = getSectionsTracker();
12480
13296
  let sections = [];
12481
13297
  try {
@@ -12483,10 +13299,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
12483
13299
  } catch {
12484
13300
  }
12485
13301
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
12486
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
13302
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
12487
13303
  sections.push({
12488
13304
  type: "scheduling",
12489
- insertAfter: effectiveInsertAfter,
13305
+ insertAfter: widgetId,
13306
+ anchorId,
13307
+ beforeId: beforeId ?? null,
12490
13308
  pagePath: window.location.pathname,
12491
13309
  ...scheduleId ? { scheduleId } : {}
12492
13310
  });
@@ -12500,7 +13318,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
12500
13318
  for (let i = pending.length - 1; i >= 0; i--) {
12501
13319
  const entry = pending[i];
12502
13320
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
12503
- if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
13321
+ const { anchorId, beforeId } = resolveEntryAnchor(entry);
13322
+ if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
12504
13323
  pending.splice(i, 1);
12505
13324
  }
12506
13325
  }
@@ -12644,6 +13463,13 @@ function isInsideLinkEditor(target) {
12644
13463
  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
13464
  );
12646
13465
  }
13466
+ function isInsideFloatingPanel(target) {
13467
+ return Boolean(target.closest("[data-ohw-floating-panel]"));
13468
+ }
13469
+ function isPointOverFloatingPanel(clientX, clientY) {
13470
+ const el = document.elementFromPoint(clientX, clientY);
13471
+ return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
13472
+ }
12647
13473
  function getHrefKeyFromElement(el) {
12648
13474
  if (!el) return null;
12649
13475
  const anchor = el.closest("[data-ohw-href-key]");
@@ -12881,7 +13707,7 @@ function getNavigationSelectionParent(el) {
12881
13707
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
12882
13708
  return getFooterLinksContainer();
12883
13709
  }
12884
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
13710
+ 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
13711
  return getNavigationRoot(el);
12886
13712
  }
12887
13713
  return null;
@@ -13127,7 +13953,7 @@ function EditGlowChrome({
13127
13953
  hideHandle = false
13128
13954
  }) {
13129
13955
  const GAP = SELECTION_CHROME_GAP2;
13130
- return /* @__PURE__ */ jsxs19(
13956
+ return /* @__PURE__ */ jsxs20(
13131
13957
  "div",
13132
13958
  {
13133
13959
  ref: elRef,
@@ -13142,7 +13968,7 @@ function EditGlowChrome({
13142
13968
  zIndex: 2147483646
13143
13969
  },
13144
13970
  children: [
13145
- /* @__PURE__ */ jsx32(
13971
+ /* @__PURE__ */ jsx33(
13146
13972
  "div",
13147
13973
  {
13148
13974
  style: {
@@ -13155,7 +13981,7 @@ function EditGlowChrome({
13155
13981
  }
13156
13982
  }
13157
13983
  ),
13158
- reorderHrefKey && !hideHandle && /* @__PURE__ */ jsx32(
13984
+ reorderHrefKey && !hideHandle && /* @__PURE__ */ jsx33(
13159
13985
  "div",
13160
13986
  {
13161
13987
  "data-ohw-drag-handle-container": "",
@@ -13167,7 +13993,7 @@ function EditGlowChrome({
13167
13993
  transform: "translate(calc(-100% - 7px), -50%)",
13168
13994
  pointerEvents: dragDisabled ? "none" : "auto"
13169
13995
  },
13170
- children: /* @__PURE__ */ jsx32(
13996
+ children: /* @__PURE__ */ jsx33(
13171
13997
  DragHandle,
13172
13998
  {
13173
13999
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -13377,7 +14203,7 @@ function FloatingToolbar({
13377
14203
  return () => ro.disconnect();
13378
14204
  }, [showEditLink, activeCommands]);
13379
14205
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, measuredW);
13380
- return /* @__PURE__ */ jsx32(
14206
+ return /* @__PURE__ */ jsx33(
13381
14207
  "div",
13382
14208
  {
13383
14209
  ref: setRefs,
@@ -13389,12 +14215,12 @@ function FloatingToolbar({
13389
14215
  zIndex: 2147483647,
13390
14216
  pointerEvents: "auto"
13391
14217
  },
13392
- children: /* @__PURE__ */ jsxs19(CustomToolbar, { children: [
13393
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs19(React11.Fragment, { children: [
13394
- gi > 0 && /* @__PURE__ */ jsx32(CustomToolbarDivider, {}),
14218
+ children: /* @__PURE__ */ jsxs20(CustomToolbar, { children: [
14219
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs20(React11.Fragment, { children: [
14220
+ gi > 0 && /* @__PURE__ */ jsx33(CustomToolbarDivider, {}),
13395
14221
  btns.map((btn) => {
13396
14222
  const isActive = activeCommands.has(btn.cmd);
13397
- return /* @__PURE__ */ jsx32(
14223
+ return /* @__PURE__ */ jsx33(
13398
14224
  CustomToolbarButton,
13399
14225
  {
13400
14226
  title: btn.title,
@@ -13403,7 +14229,7 @@ function FloatingToolbar({
13403
14229
  e.preventDefault();
13404
14230
  onCommand(btn.cmd);
13405
14231
  },
13406
- children: /* @__PURE__ */ jsx32(
14232
+ children: /* @__PURE__ */ jsx33(
13407
14233
  "svg",
13408
14234
  {
13409
14235
  width: "16",
@@ -13424,7 +14250,7 @@ function FloatingToolbar({
13424
14250
  );
13425
14251
  })
13426
14252
  ] }, gi)),
13427
- showEditLink ? /* @__PURE__ */ jsx32(
14253
+ showEditLink ? /* @__PURE__ */ jsx33(
13428
14254
  CustomToolbarButton,
13429
14255
  {
13430
14256
  type: "button",
@@ -13438,7 +14264,7 @@ function FloatingToolbar({
13438
14264
  e.preventDefault();
13439
14265
  e.stopPropagation();
13440
14266
  },
13441
- children: /* @__PURE__ */ jsx32(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
14267
+ children: /* @__PURE__ */ jsx33(Link3, { className: "size-4 shrink-0", "aria-hidden": true })
13442
14268
  }
13443
14269
  ) : null
13444
14270
  ] })
@@ -13455,7 +14281,7 @@ function StateToggle({
13455
14281
  states,
13456
14282
  onStateChange
13457
14283
  }) {
13458
- return /* @__PURE__ */ jsx32(
14284
+ return /* @__PURE__ */ jsx33(
13459
14285
  ToggleGroup,
13460
14286
  {
13461
14287
  "data-ohw-state-toggle": "",
@@ -13469,11 +14295,12 @@ function StateToggle({
13469
14295
  left: rect.right - 8,
13470
14296
  transform: "translateX(-100%)"
13471
14297
  },
13472
- children: states.map((state) => /* @__PURE__ */ jsx32(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
14298
+ children: states.map((state) => /* @__PURE__ */ jsx33(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
13473
14299
  }
13474
14300
  );
13475
14301
  }
13476
14302
  var contentCache = /* @__PURE__ */ new Map();
14303
+ var fetchedContentPaths = /* @__PURE__ */ new Set();
13477
14304
  function resolveSubdomain(subdomainFromQuery) {
13478
14305
  if (subdomainFromQuery) return subdomainFromQuery;
13479
14306
  if (typeof window !== "undefined") {
@@ -13568,8 +14395,14 @@ function OhhwellsBridge() {
13568
14395
  });
13569
14396
  const selectFrameRef = useRef9(() => {
13570
14397
  });
14398
+ const selectLogoRef = useRef9(() => {
14399
+ });
14400
+ const openLogoSizePanelRef = useRef9(() => {
14401
+ });
13571
14402
  const deselectRef = useRef9(() => {
13572
14403
  });
14404
+ const closeFloatingPanelOnlyRef = useRef9(() => {
14405
+ });
13573
14406
  const reselectNavigationItemRef = useRef9(() => {
13574
14407
  });
13575
14408
  const commitNavigationTextEditRef = useRef9(() => {
@@ -13630,11 +14463,6 @@ function OhhwellsBridge() {
13630
14463
  const [isItemDragging, setIsItemDragging] = useState11(false);
13631
14464
  const [isFooterFrameSelection, setIsFooterFrameSelection] = useState11(false);
13632
14465
  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
14466
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState11(null);
13639
14467
  const [footerHeadingVisible, setFooterHeadingVisible] = useState11(null);
13640
14468
  const footerDragRef = useRef9(null);
@@ -13649,7 +14477,16 @@ function OhhwellsBridge() {
13649
14477
  const addNavAfterAnchorRef = useRef9(null);
13650
14478
  const editContentRef = useRef9({});
13651
14479
  const aiSectionsRef = useRef9("");
14480
+ const brandKitRef = useRef9("");
14481
+ const stylesRef = useRef9("");
13652
14482
  const pendingDeleteUndoRef = useRef9(null);
14483
+ const [floatingPanel, setFloatingPanel] = useState11(null);
14484
+ const floatingPanelOpenRef = useRef9(false);
14485
+ const setFloatingPanelRef = useRef9(setFloatingPanel);
14486
+ const [floatingPanelPos, setFloatingPanelPos] = useState11(null);
14487
+ const [logoSizeDraft, setLogoSizeDraft] = useState11(null);
14488
+ const [editorViewport, setEditorViewport] = useState11("desktop");
14489
+ const [parentScrollSnap, setParentScrollSnap] = useState11(null);
13653
14490
  const [sitePages, setSitePages] = useState11([]);
13654
14491
  const [sectionsByPath, setSectionsByPath] = useState11({});
13655
14492
  const sectionsPrefetchGenRef = useRef9(0);
@@ -13658,7 +14495,18 @@ function OhhwellsBridge() {
13658
14495
  const linkPopoverOpenRef = useRef9(false);
13659
14496
  const linkPopoverGraceUntilRef = useRef9(0);
13660
14497
  setLinkPopoverRef.current = setLinkPopover;
14498
+ setFloatingPanelRef.current = setFloatingPanel;
13661
14499
  linkPopoverSessionRef.current = linkPopover;
14500
+ floatingPanelOpenRef.current = Boolean(floatingPanel);
14501
+ useEffect12(() => {
14502
+ const syncViewport = () => {
14503
+ const next = window.innerWidth <= 480 ? "mobile" : "desktop";
14504
+ setEditorViewport((prev) => prev === next ? prev : next);
14505
+ };
14506
+ syncViewport();
14507
+ window.addEventListener("resize", syncViewport);
14508
+ return () => window.removeEventListener("resize", syncViewport);
14509
+ }, []);
13662
14510
  const {
13663
14511
  navDragRef,
13664
14512
  navDropSlots,
@@ -13881,6 +14729,10 @@ function OhhwellsBridge() {
13881
14729
  setIsItemDragging(false);
13882
14730
  hoveredNavContainerRef.current = null;
13883
14731
  setHoveredNavContainerRect(null);
14732
+ hoveredItemElRef.current = null;
14733
+ setHoveredItemRect(null);
14734
+ setFloatingPanel(null);
14735
+ setLogoSizeDraft(null);
13884
14736
  if (!activeElRef.current) {
13885
14737
  setNavGroupForceOpen(null, false);
13886
14738
  setToolbarRect(null);
@@ -14586,6 +15438,8 @@ function OhhwellsBridge() {
14586
15438
  setToolbarRect(anchor.getBoundingClientRect());
14587
15439
  setToolbarShowEditLink(false);
14588
15440
  setActiveCommands(/* @__PURE__ */ new Set());
15441
+ setFloatingPanel(null);
15442
+ setLogoSizeDraft(null);
14589
15443
  }, [deactivate, markSelected]);
14590
15444
  const selectFrame = useCallback7((el) => {
14591
15445
  if (!isNavigationContainer(el)) return;
@@ -14635,7 +15489,51 @@ function OhhwellsBridge() {
14635
15489
  setToolbarRect(el.getBoundingClientRect());
14636
15490
  setToolbarShowEditLink(false);
14637
15491
  setActiveCommands(/* @__PURE__ */ new Set());
15492
+ setFloatingPanel(null);
15493
+ setLogoSizeDraft(null);
14638
15494
  }, [deactivate, markSelected, postToParent2]);
15495
+ const selectLogo = useCallback7(
15496
+ (logoEl) => {
15497
+ if (activeElRef.current) deactivate();
15498
+ selectedElRef.current = logoEl;
15499
+ selectedHrefKeyRef.current = null;
15500
+ selectedFooterColAttrRef.current = null;
15501
+ markSelected(logoEl);
15502
+ setSelectedIsCta(false);
15503
+ setSelectedIsSocial(false);
15504
+ setSelectedIsSocialsRow(false);
15505
+ clearHrefKeyHover(logoEl);
15506
+ hoveredNavContainerRef.current = null;
15507
+ setHoveredNavContainerRect(null);
15508
+ setHoveredItemRect(null);
15509
+ hoveredItemElRef.current = null;
15510
+ siblingHintElRef.current = null;
15511
+ setSiblingHintRect(null);
15512
+ setSiblingHintRects([]);
15513
+ setIsItemDragging(false);
15514
+ setReorderHrefKey(null);
15515
+ setReorderDragDisabled(false);
15516
+ setIsFooterFrameSelection(false);
15517
+ setToolbarVariant("logo");
15518
+ setToolbarRect(getLogoInteractionRect(logoEl));
15519
+ setToolbarShowEditLink(false);
15520
+ setActiveCommands(/* @__PURE__ */ new Set());
15521
+ },
15522
+ [deactivate, markSelected]
15523
+ );
15524
+ const openLogoSizePanel = useCallback7((logoEl) => {
15525
+ const placement = getLogoPlacement(logoEl);
15526
+ const draft = readLogoSizeState(editContentRef.current, placement);
15527
+ setLogoSizeDraft(draft);
15528
+ setParentScrollSnap(parentScrollRef.current);
15529
+ setFloatingPanel({
15530
+ key: `logo-size:${placement}`,
15531
+ title: "Logo",
15532
+ context: placement === "navbar" ? "Navbar" : "Footer",
15533
+ kind: "logo-size",
15534
+ placement
15535
+ });
15536
+ }, []);
14639
15537
  const openSocialsDisplayPanel = useCallback7((row) => {
14640
15538
  setParentScrollSnap(parentScrollRef.current);
14641
15539
  setFloatingPanel({
@@ -14671,13 +15569,53 @@ function OhhwellsBridge() {
14671
15569
  );
14672
15570
  const closeFloatingPanelOnly = useCallback7(() => {
14673
15571
  setFloatingPanel(null);
15572
+ setLogoSizeDraft(null);
14674
15573
  }, []);
14675
- const closeFloatingPanelOnlyRef = useRef9(closeFloatingPanelOnly);
14676
- closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
14677
15574
  const closeFloatingPanelAndDeselect = useCallback7(() => {
14678
15575
  setFloatingPanel(null);
15576
+ setLogoSizeDraft(null);
14679
15577
  deselectRef.current();
14680
15578
  }, []);
15579
+ const persistLogoSizeDraft = useCallback7(
15580
+ (placement, draft) => {
15581
+ const desktopKey = LOGO_SIZE_DESKTOP_KEYS[placement];
15582
+ const mobileKey = LOGO_SIZE_MOBILE_KEYS[placement];
15583
+ const nodes = [
15584
+ { key: desktopKey, text: String(draft.desktopPx) }
15585
+ ];
15586
+ if (draft.mobileFollowing) {
15587
+ nodes.push({ key: mobileKey, text: "" });
15588
+ } else {
15589
+ nodes.push({ key: mobileKey, text: String(draft.mobilePx) });
15590
+ }
15591
+ editContentRef.current = {
15592
+ ...editContentRef.current,
15593
+ [desktopKey]: String(draft.desktopPx),
15594
+ [mobileKey]: draft.mobileFollowing ? "" : String(draft.mobilePx)
15595
+ };
15596
+ applyLogoSizeToPlacement(
15597
+ placement,
15598
+ draft.desktopPx,
15599
+ draft.mobileFollowing ? draft.desktopPx : draft.mobilePx,
15600
+ draft.mobileFollowing
15601
+ );
15602
+ postToParent2({ type: "ow:change", nodes });
15603
+ requestAnimationFrame(() => {
15604
+ const selected = selectedElRef.current;
15605
+ if (!selected || toolbarVariantRef.current !== "logo") return;
15606
+ const rect = getLogoInteractionRect(selected);
15607
+ setToolbarRect(rect);
15608
+ if (glowElRef.current) {
15609
+ const GAP = SELECTION_CHROME_GAP2;
15610
+ glowElRef.current.style.top = `${rect.top - GAP}px`;
15611
+ glowElRef.current.style.left = `${rect.left - GAP}px`;
15612
+ glowElRef.current.style.width = `${rect.width + GAP * 2}px`;
15613
+ glowElRef.current.style.height = `${rect.height + GAP * 2}px`;
15614
+ }
15615
+ });
15616
+ },
15617
+ [postToParent2]
15618
+ );
14681
15619
  const activate = useCallback7((el, options) => {
14682
15620
  if (activeElRef.current === el) return;
14683
15621
  if (isIconEditable(el)) return;
@@ -14758,7 +15696,37 @@ function OhhwellsBridge() {
14758
15696
  deactivateRef.current = deactivate;
14759
15697
  selectRef.current = select;
14760
15698
  selectFrameRef.current = selectFrame;
15699
+ selectLogoRef.current = selectLogo;
15700
+ openLogoSizePanelRef.current = openLogoSizePanel;
14761
15701
  deselectRef.current = deselect;
15702
+ closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
15703
+ const lastSiteWideScopeRef = useRef9(null);
15704
+ useEffect12(() => {
15705
+ if (!isEditMode) {
15706
+ if (lastSiteWideScopeRef.current !== false) {
15707
+ lastSiteWideScopeRef.current = false;
15708
+ postToParent2({ type: "ow:site-wide-scope", active: false });
15709
+ }
15710
+ return;
15711
+ }
15712
+ const active = isSiteWideScopeActive({
15713
+ selected: selectedElRef.current,
15714
+ hoveredItem: hoveredItemElRef.current,
15715
+ hoveredNavContainer: hoveredNavContainerRef.current,
15716
+ active: activeElRef.current
15717
+ });
15718
+ if (lastSiteWideScopeRef.current === active) return;
15719
+ lastSiteWideScopeRef.current = active;
15720
+ postToParent2({ type: "ow:site-wide-scope", active });
15721
+ }, [
15722
+ isEditMode,
15723
+ hoveredItemRect,
15724
+ hoveredNavContainerRect,
15725
+ toolbarVariant,
15726
+ toolbarRect,
15727
+ isFooterFrameSelection,
15728
+ postToParent2
15729
+ ]);
14762
15730
  useLayoutEffect4(() => {
14763
15731
  if (!subdomain || isEditMode) {
14764
15732
  setFetchState("done");
@@ -14770,9 +15738,23 @@ function OhhwellsBridge() {
14770
15738
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
14771
15739
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
14772
15740
  }
15741
+ if (typeof content[BRAND_KIT_KEY] === "string") {
15742
+ brandKitRef.current = content[BRAND_KIT_KEY];
15743
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
15744
+ }
15745
+ if (typeof content[STYLE_STORE_KEY] === "string") {
15746
+ stylesRef.current = content[STYLE_STORE_KEY];
15747
+ applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
15748
+ }
15749
+ applyBrandChrome(content);
14773
15750
  for (const [key, val] of Object.entries(content)) {
14774
15751
  if (key === "__ohw_sections") continue;
14775
15752
  if (key === AI_SECTIONS_KEY) continue;
15753
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
15754
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
15755
+ if (key === BRAND_KIT_KEY) continue;
15756
+ if (key === STYLE_STORE_KEY) continue;
15757
+ if (BRAND_CHROME_KEYS.has(key)) continue;
14776
15758
  if (applyVideoSettingNode(key, val)) continue;
14777
15759
  if (applyCarouselNode(key, val)) continue;
14778
15760
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -14807,6 +15789,8 @@ function OhhwellsBridge() {
14807
15789
  });
14808
15790
  applyLinkByKey(key, val);
14809
15791
  }
15792
+ applyLogoFromContent(content);
15793
+ applyLogoSizes(content);
14810
15794
  reconcileNavbarItemsFromContent(content);
14811
15795
  reconcileFooterOrderFromContent(content);
14812
15796
  reconcileSocialsFromContent(content);
@@ -14827,7 +15811,9 @@ function OhhwellsBridge() {
14827
15811
  let cancelled = false;
14828
15812
  setFetchState("loading");
14829
15813
  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) => {
15814
+ const initialPath = pathname;
15815
+ fetchedContentPaths.add(`${subdomain}::${initialPath}`);
15816
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
14831
15817
  if (cancelled) return;
14832
15818
  const content = data?.content ?? {};
14833
15819
  contentCache.set(subdomain, content);
@@ -14851,8 +15837,21 @@ function OhhwellsBridge() {
14851
15837
  initSectionInstancesFromContent(content, window.location.pathname);
14852
15838
  observer?.disconnect();
14853
15839
  try {
15840
+ applyBrandChrome(content);
15841
+ if (typeof content[BRAND_KIT_KEY] === "string") {
15842
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
15843
+ }
15844
+ if (typeof content[STYLE_STORE_KEY] === "string") {
15845
+ applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
15846
+ }
14854
15847
  for (const [key, val] of Object.entries(content)) {
14855
15848
  if (key === "__ohw_sections") continue;
15849
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
15850
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
15851
+ if (key === BRAND_KIT_KEY) continue;
15852
+ if (key === STYLE_STORE_KEY) continue;
15853
+ if (key === STYLE_STORE_KEY) continue;
15854
+ if (BRAND_CHROME_KEYS.has(key)) continue;
14856
15855
  if (applyVideoSettingNode(key, val)) continue;
14857
15856
  if (applyCarouselNode(key, val)) continue;
14858
15857
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -14873,6 +15872,7 @@ function OhhwellsBridge() {
14873
15872
  });
14874
15873
  applyLinkByKey(key, val);
14875
15874
  }
15875
+ applyLogoFromContent(content);
14876
15876
  reconcileNavbarItemsFromContent(content);
14877
15877
  reconcileFooterOrderFromContent(content);
14878
15878
  reconcileSocialsFromContent(content);
@@ -14887,6 +15887,17 @@ function OhhwellsBridge() {
14887
15887
  debounceTimer = setTimeout(applyFromCache, 150);
14888
15888
  };
14889
15889
  applyFromCache();
15890
+ const pathCacheKey = `${subdomain}::${pathname}`;
15891
+ if (!fetchedContentPaths.has(pathCacheKey)) {
15892
+ fetchedContentPaths.add(pathCacheKey);
15893
+ const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
15894
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
15895
+ if (!data?.content) return;
15896
+ contentCache.set(subdomain, data.content);
15897
+ applyFromCache();
15898
+ }).catch(() => {
15899
+ });
15900
+ }
14890
15901
  observer = new MutationObserver(scheduleApply);
14891
15902
  observer.observe(document.body, { childList: true, subtree: true });
14892
15903
  return () => {
@@ -14980,26 +15991,31 @@ function OhhwellsBridge() {
14980
15991
  }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
14981
15992
  useEffect12(() => {
14982
15993
  if (!isEditMode) return;
15994
+ let lastPosted = 0;
14983
15995
  const measure = () => {
14984
15996
  const h = document.body.scrollHeight;
14985
- if (h > 50) postToParent2({ type: "ow:height", height: h });
15997
+ if (h > 50 && Math.abs(h - lastPosted) > 1) {
15998
+ lastPosted = h;
15999
+ postToParent2({ type: "ow:height", height: h });
16000
+ }
16001
+ };
16002
+ let raf = null;
16003
+ const schedule = () => {
16004
+ if (raf != null) return;
16005
+ raf = requestAnimationFrame(() => {
16006
+ raf = null;
16007
+ measure();
16008
+ });
14986
16009
  };
14987
16010
  const t1 = setTimeout(measure, 50);
14988
16011
  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);
16012
+ const ro = new ResizeObserver(schedule);
16013
+ ro.observe(document.body);
14998
16014
  return () => {
14999
16015
  clearTimeout(t1);
15000
16016
  clearTimeout(t2);
15001
- if (resizeTimer) clearTimeout(resizeTimer);
15002
- window.removeEventListener("resize", handleResize);
16017
+ if (raf != null) cancelAnimationFrame(raf);
16018
+ ro.disconnect();
15003
16019
  };
15004
16020
  }, [pathname, isEditMode, postToParent2]);
15005
16021
  useEffect12(() => {
@@ -15149,10 +16165,12 @@ function OhhwellsBridge() {
15149
16165
  return;
15150
16166
  }
15151
16167
  const target = e.target;
16168
+ if (target.closest("[data-ohw-ai-review]")) return;
15152
16169
  if (target.closest("[data-ohw-toolbar]")) return;
15153
16170
  if (target.closest("[data-ohw-state-toggle]")) return;
15154
16171
  if (target.closest("[data-ohw-max-badge]")) return;
15155
16172
  if (isInsideLinkEditor(target)) return;
16173
+ if (isInsideFloatingPanel(target)) return;
15156
16174
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
15157
16175
  const beneath = document.elementsFromPoint(e.clientX, e.clientY).find(
15158
16176
  (el) => el instanceof HTMLElement && !el.closest("[data-ohw-bridge-root]") && el.closest("[data-ohw-section]") != null
@@ -15216,6 +16234,21 @@ function OhhwellsBridge() {
15216
16234
  return;
15217
16235
  }
15218
16236
  }
16237
+ const logoEl = getLogoElement(target);
16238
+ if (logoEl) {
16239
+ e.preventDefault();
16240
+ e.stopPropagation();
16241
+ if (!logoHasUploadedImage(logoEl)) {
16242
+ deselectRef.current();
16243
+ deactivateRef.current();
16244
+ const identity = readLogoIdentityFromDom();
16245
+ postToParentRef.current({ type: "ow:open-logo-settings", ...identity });
16246
+ return;
16247
+ }
16248
+ selectLogoRef.current(logoEl);
16249
+ openLogoSizePanelRef.current(logoEl);
16250
+ return;
16251
+ }
15219
16252
  const editable = target.closest("[data-ohw-editable]");
15220
16253
  if (editable) {
15221
16254
  if (editable.dataset.ohwEditable === "link") {
@@ -15368,10 +16401,12 @@ function OhhwellsBridge() {
15368
16401
  };
15369
16402
  const handleDblClick = (e) => {
15370
16403
  const target = e.target;
16404
+ if (target.closest("[data-ohw-ai-review]")) return;
15371
16405
  if (target.closest("[data-ohw-toolbar]")) return;
15372
16406
  if (target.closest("[data-ohw-state-toggle]")) return;
15373
16407
  if (target.closest("[data-ohw-max-badge]")) return;
15374
16408
  if (isInsideLinkEditor(target)) return;
16409
+ if (isInsideFloatingPanel(target)) return;
15375
16410
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
15376
16411
  return;
15377
16412
  }
@@ -15399,11 +16434,14 @@ function OhhwellsBridge() {
15399
16434
  setHoveredNavContainerRect(null);
15400
16435
  return;
15401
16436
  }
15402
- if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || target.closest("[data-ohw-floating-panel]")) {
16437
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || isInsideFloatingPanel(target) || isPointOverFloatingPanel(e.clientX, e.clientY)) {
15403
16438
  hoveredItemElRef.current = null;
15404
16439
  setHoveredItemRect(null);
15405
16440
  hoveredNavContainerRef.current = null;
15406
16441
  setHoveredNavContainerRect(null);
16442
+ siblingHintElRef.current = null;
16443
+ setSiblingHintRect(null);
16444
+ setSiblingHintRects([]);
15407
16445
  return;
15408
16446
  }
15409
16447
  {
@@ -15413,7 +16451,7 @@ function OhhwellsBridge() {
15413
16451
  const allowFooterLinksHover = toolbarVariantRef.current !== "select-frame" || selectedIsFooterColumn;
15414
16452
  if (allowNavContainerHover) {
15415
16453
  const navContainer = target.closest("[data-ohw-nav-container]");
15416
- if (navContainer && !getNavigationItemAnchor(target)) {
16454
+ if (navContainer && !getNavigationItemAnchor(target) && !getLogoElement(target)) {
15417
16455
  hoveredNavContainerRef.current = navContainer;
15418
16456
  setHoveredNavContainerRect(navContainer.getBoundingClientRect());
15419
16457
  hoveredItemElRef.current = null;
@@ -15442,6 +16480,15 @@ function OhhwellsBridge() {
15442
16480
  setHoveredNavContainerRect(null);
15443
16481
  }
15444
16482
  }
16483
+ const logoEl = getLogoElement(target);
16484
+ if (logoEl) {
16485
+ hoveredNavContainerRef.current = null;
16486
+ setHoveredNavContainerRect(null);
16487
+ if (selectedElRef.current === logoEl) return;
16488
+ hoveredItemElRef.current = logoEl;
16489
+ setHoveredItemRect(getLogoInteractionRect(logoEl));
16490
+ return;
16491
+ }
15445
16492
  const navAnchor = getNavigationItemAnchor(target);
15446
16493
  if (navAnchor) {
15447
16494
  hoveredNavContainerRef.current = null;
@@ -15479,6 +16526,11 @@ function OhhwellsBridge() {
15479
16526
  setHoveredItemRect(hoverTarget.getBoundingClientRect());
15480
16527
  } else if (!isInsideNavigationItem(editable)) {
15481
16528
  hoverTarget.setAttribute("data-ohw-hovered", "");
16529
+ if (editable.closest("footer") || editable.closest('[data-ohw-section="footer"]')) {
16530
+ hoveredNavContainerRef.current = null;
16531
+ setHoveredNavContainerRect(null);
16532
+ hoveredItemElRef.current = editable;
16533
+ }
15482
16534
  }
15483
16535
  }
15484
16536
  };
@@ -15514,6 +16566,18 @@ function OhhwellsBridge() {
15514
16566
  }
15515
16567
  return;
15516
16568
  }
16569
+ const logoEl = getLogoElement(target);
16570
+ if (logoEl) {
16571
+ const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
16572
+ if (related2 && (logoEl === related2 || logoEl.contains(related2) || related2.closest?.('[data-ohw-role="logo"], [data-ohw-logo]'))) {
16573
+ return;
16574
+ }
16575
+ if (hoveredItemElRef.current === logoEl) {
16576
+ hoveredItemElRef.current = null;
16577
+ setHoveredItemRect(null);
16578
+ }
16579
+ return;
16580
+ }
15517
16581
  const editable = target.closest("[data-ohw-editable]");
15518
16582
  if (!editable) return;
15519
16583
  const related = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -15534,6 +16598,13 @@ function OhhwellsBridge() {
15534
16598
  }
15535
16599
  } else {
15536
16600
  hoverTarget.removeAttribute("data-ohw-hovered");
16601
+ if (hoveredItemElRef.current === editable) {
16602
+ const stillOnEditable = related instanceof Element && related.closest("[data-ohw-editable]") === editable;
16603
+ if (!stillOnEditable) {
16604
+ hoveredItemElRef.current = null;
16605
+ setHoveredItemRect(null);
16606
+ }
16607
+ }
15537
16608
  }
15538
16609
  }
15539
16610
  };
@@ -15650,6 +16721,26 @@ function OhhwellsBridge() {
15650
16721
  hoveredNavContainerRef.current = null;
15651
16722
  setHoveredNavContainerRect(null);
15652
16723
  }
16724
+ const logoCandidates = [
16725
+ ...document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]'),
16726
+ ...document.querySelectorAll("nav a:not([data-ohw-href-key]), [data-ohw-nav-root] a:not([data-ohw-href-key])"),
16727
+ ...document.querySelectorAll("footer img")
16728
+ ];
16729
+ const seenLogos = /* @__PURE__ */ new Set();
16730
+ for (const candidate of logoCandidates) {
16731
+ const logo = getLogoElement(candidate);
16732
+ if (!logo || seenLogos.has(logo)) continue;
16733
+ seenLogos.add(logo);
16734
+ const r2 = logo.getBoundingClientRect();
16735
+ if (x < r2.left || x > r2.right || y < r2.top || y > r2.bottom) continue;
16736
+ hoveredNavContainerRef.current = null;
16737
+ setHoveredNavContainerRect(null);
16738
+ if (selectedElRef.current !== logo) {
16739
+ hoveredItemElRef.current = logo;
16740
+ setHoveredItemRect(getLogoInteractionRect(logo));
16741
+ }
16742
+ return;
16743
+ }
15653
16744
  const navContainers = Array.from(
15654
16745
  document.querySelectorAll("[data-ohw-nav-container]")
15655
16746
  );
@@ -15735,7 +16826,7 @@ function OhhwellsBridge() {
15735
16826
  }
15736
16827
  };
15737
16828
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
15738
- if (linkPopoverOpenRef.current) {
16829
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
15739
16830
  if (hoveredImageRef.current) {
15740
16831
  hoveredImageRef.current = null;
15741
16832
  hoveredImageHasTextOverlapRef.current = false;
@@ -15989,7 +17080,7 @@ function OhhwellsBridge() {
15989
17080
  }
15990
17081
  };
15991
17082
  const probeHoverCardsAt = (clientX, clientY, fromParentViewport = false) => {
15992
- if (linkPopoverOpenRef.current || document.documentElement.hasAttribute("data-ohw-section-picking")) {
17083
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || document.documentElement.hasAttribute("data-ohw-section-picking")) {
15993
17084
  if (activeStateElRef.current) {
15994
17085
  activeStateElRef.current.removeAttribute("data-ohw-state-hovered");
15995
17086
  activeStateElRef.current = null;
@@ -16055,16 +17146,21 @@ function OhhwellsBridge() {
16055
17146
  setSectionGap(null);
16056
17147
  }
16057
17148
  };
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
17149
  const handleMouseMove = (e) => {
16066
17150
  const { clientX, clientY } = e;
16067
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
17151
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
17152
+ hoveredItemElRef.current = null;
17153
+ setHoveredItemRect(null);
17154
+ hoveredNavContainerRef.current = null;
17155
+ setHoveredNavContainerRect(null);
17156
+ siblingHintElRef.current = null;
17157
+ setSiblingHintRect(null);
17158
+ setSiblingHintRects([]);
17159
+ dismissImageHover();
17160
+ clearImageHover();
17161
+ setSectionGap(null);
17162
+ return;
17163
+ }
16068
17164
  probeSectionGapAt(clientX, clientY);
16069
17165
  probeImageAt(clientX, clientY);
16070
17166
  probeHoverCardsAt(clientX, clientY);
@@ -16073,7 +17169,11 @@ function OhhwellsBridge() {
16073
17169
  if (e.data?.type !== "ow:pointer-sync") return;
16074
17170
  const { clientX, clientY } = e.data;
16075
17171
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
16076
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
17172
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
17173
+ dismissImageHover();
17174
+ clearImageHover();
17175
+ return;
17176
+ }
16077
17177
  probeSectionGapAt(clientX, clientY);
16078
17178
  probeImageAt(clientX, clientY);
16079
17179
  probeHoverCardsAt(clientX, clientY);
@@ -16323,6 +17423,15 @@ function OhhwellsBridge() {
16323
17423
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
16324
17424
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
16325
17425
  }
17426
+ if (typeof content[BRAND_KIT_KEY] === "string") {
17427
+ brandKitRef.current = content[BRAND_KIT_KEY];
17428
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17429
+ }
17430
+ if (typeof content[STYLE_STORE_KEY] === "string") {
17431
+ stylesRef.current = content[STYLE_STORE_KEY];
17432
+ applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
17433
+ }
17434
+ applyBrandChrome(content);
16326
17435
  let sectionsJson = null;
16327
17436
  for (const [key, val] of Object.entries(content)) {
16328
17437
  if (key === "__ohw_sections") {
@@ -16330,6 +17439,11 @@ function OhhwellsBridge() {
16330
17439
  continue;
16331
17440
  }
16332
17441
  if (key === AI_SECTIONS_KEY) continue;
17442
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
17443
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
17444
+ if (key === BRAND_KIT_KEY) continue;
17445
+ if (key === STYLE_STORE_KEY) continue;
17446
+ if (BRAND_CHROME_KEYS.has(key)) continue;
16333
17447
  if (applyVideoSettingNode(key, val)) continue;
16334
17448
  if (applyCarouselNode(key, val)) continue;
16335
17449
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -16349,6 +17463,8 @@ function OhhwellsBridge() {
16349
17463
  });
16350
17464
  applyLinkByKey(key, val);
16351
17465
  }
17466
+ applyLogoFromContent(content);
17467
+ applyLogoSizes(content);
16352
17468
  if (sectionsJson) {
16353
17469
  initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
16354
17470
  sectionsLoadedRef.current = true;
@@ -16364,6 +17480,58 @@ function OhhwellsBridge() {
16364
17480
  if (hydratedHeight > 50) postToParentRef.current({ type: "ow:height", height: hydratedHeight });
16365
17481
  postToParentRef.current({ type: "ow:hydrate-done" });
16366
17482
  };
17483
+ const handleUpdateLogoIdentity = (e) => {
17484
+ if (e.data?.type !== "ow:update-logo-identity") return;
17485
+ const rawText = typeof e.data.text === "string" ? e.data.text : "";
17486
+ const alt = typeof e.data.alt === "string" ? e.data.alt : rawText;
17487
+ const href = typeof e.data.href === "string" ? e.data.href : void 0;
17488
+ const imageProvided = "image" in e.data;
17489
+ const imageUrl = imageProvided && typeof e.data.image === "string" && e.data.image.trim() ? e.data.image.trim() : imageProvided ? null : void 0;
17490
+ let isPlaceholder = e.data.isPlaceholder !== false;
17491
+ if (imageUrl) isPlaceholder = false;
17492
+ else if (imageProvided && imageUrl === null) {
17493
+ isPlaceholder = e.data.isPlaceholder === true || !rawText.trim() || resolveLogoDisplayText(rawText) === PLACEHOLDER_BUSINESS_NAME;
17494
+ }
17495
+ const display = applyLogoIdentity(rawText, isPlaceholder);
17496
+ const displayAlt = resolveLogoDisplayText(alt || display);
17497
+ if (imageUrl !== void 0) {
17498
+ applyLogoImage(imageUrl, displayAlt);
17499
+ } else {
17500
+ for (const key of LOGO_IMAGE_KEYS) {
17501
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
17502
+ const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
17503
+ if (img) img.alt = displayAlt;
17504
+ });
17505
+ }
17506
+ }
17507
+ if (href !== void 0) {
17508
+ applyLogoHref(href);
17509
+ applyLinkByKey("nav-logo-href", href);
17510
+ applyLinkByKey("footer-logo-href", href);
17511
+ applyLinkByKey("logo-href", href);
17512
+ }
17513
+ const nodes = [
17514
+ ...LOGO_TEXT_KEYS.map((key) => ({ key, text: display })),
17515
+ { key: LOGO_PLACEHOLDER_KEY, text: isPlaceholder ? "true" : "false" },
17516
+ { key: LOGO_ALT_KEY, text: displayAlt }
17517
+ ];
17518
+ if (imageUrl !== void 0) {
17519
+ if (imageUrl) {
17520
+ for (const key of LOGO_IMAGE_KEYS) nodes.push({ key, text: imageUrl });
17521
+ } else {
17522
+ for (const key of LOGO_IMAGE_KEYS) nodes.push({ key, text: "" });
17523
+ }
17524
+ }
17525
+ if (href !== void 0) {
17526
+ for (const key of LOGO_HREF_KEYS) nodes.push({ key, text: href.trim() || "/" });
17527
+ }
17528
+ editContentRef.current = {
17529
+ ...editContentRef.current,
17530
+ ...Object.fromEntries(nodes.map(({ key, text }) => [key, text]))
17531
+ };
17532
+ applyLogoSizes(editContentRef.current);
17533
+ postToParentRef.current({ type: "ow:change", nodes });
17534
+ };
16367
17535
  window.addEventListener("message", handleHydrate);
16368
17536
  const postAiSectionsChanged = () => {
16369
17537
  postToParentRef.current({
@@ -16377,7 +17545,10 @@ function OhhwellsBridge() {
16377
17545
  const payload = e.data.payload;
16378
17546
  if (!payload || typeof payload.id !== "string" || !isRenderableTree(payload.tree)) return;
16379
17547
  const previous = aiSectionsRef.current;
16380
- const nextState = applyTreeToState(parseAiSectionsState(previous), payload);
17548
+ const nextState = applyTreeToState(parseAiSectionsState(previous), {
17549
+ ...payload,
17550
+ path: payload.path ?? window.location.pathname
17551
+ });
16381
17552
  const nextValue = serializeAiSectionsState(nextState);
16382
17553
  aiSectionsRef.current = nextValue;
16383
17554
  applyAiSectionsToDom(nextState);
@@ -16414,12 +17585,42 @@ function OhhwellsBridge() {
16414
17585
  const value = typeof e.data.value === "string" ? e.data.value : "";
16415
17586
  aiSectionsRef.current = value;
16416
17587
  applyAiSectionsToDom(parseAiSectionsState(value));
17588
+ applyStylesToDom(parseStyleStore(stylesRef.current));
16417
17589
  const restoredHeight = document.documentElement.scrollHeight;
16418
17590
  if (restoredHeight > 50) postToParentRef.current({ type: "ow:height", height: restoredHeight });
16419
17591
  postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: value }] });
16420
17592
  postAiSectionsChanged();
16421
17593
  };
16422
17594
  window.addEventListener("message", handleAiSetSections);
17595
+ const handleAiSetBrand = (e) => {
17596
+ if (e.data?.type !== "ow:ai-set-brand") return;
17597
+ const value = typeof e.data.value === "string" ? e.data.value : "";
17598
+ const previous = brandKitRef.current;
17599
+ brandKitRef.current = value;
17600
+ applyBrandToDom(parseBrandKit(value));
17601
+ if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
17602
+ applyStylesToDom(parseStyleStore(stylesRef.current));
17603
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
17604
+ postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
17605
+ };
17606
+ window.addEventListener("message", handleAiSetBrand);
17607
+ const handleAiSetStyles = (e) => {
17608
+ if (e.data?.type !== "ow:ai-set-styles") return;
17609
+ const value = typeof e.data.value === "string" ? e.data.value : "";
17610
+ const previous = stylesRef.current;
17611
+ stylesRef.current = value;
17612
+ applyStylesToDom(parseStyleStore(value));
17613
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
17614
+ postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
17615
+ };
17616
+ window.addEventListener("message", handleAiSetStyles);
17617
+ const handleGetBrand = (e) => {
17618
+ if (e.data?.type !== "ow:get-brand") return;
17619
+ const template = deriveTemplateBrand();
17620
+ const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
17621
+ postToParentRef.current({ type: "ow:brand-value", value });
17622
+ };
17623
+ window.addEventListener("message", handleGetBrand);
16423
17624
  const handleDeactivate = (e) => {
16424
17625
  if (e.data?.type !== "ow:deactivate") return;
16425
17626
  if (Date.now() < linkPopoverGraceUntilRef.current) return;
@@ -16428,6 +17629,12 @@ function OhhwellsBridge() {
16428
17629
  closeLinkPopoverRef.current();
16429
17630
  return;
16430
17631
  }
17632
+ if (floatingPanelOpenRef.current) {
17633
+ setFloatingPanelRef.current(null);
17634
+ deselectRef.current();
17635
+ deactivateRef.current();
17636
+ return;
17637
+ }
16431
17638
  deselectRef.current();
16432
17639
  deactivateRef.current();
16433
17640
  };
@@ -16481,6 +17688,10 @@ function OhhwellsBridge() {
16481
17688
  return;
16482
17689
  }
16483
17690
  if (selectedElRef.current) {
17691
+ if (toolbarVariantRef.current === "logo") {
17692
+ deselectRef.current();
17693
+ return;
17694
+ }
16484
17695
  if (toolbarVariantRef.current === "select-frame") {
16485
17696
  const parent2 = getNavigationSelectionParent(selectedElRef.current);
16486
17697
  if (parent2 && isNavigationContainer(parent2) && isFooterLinksContainer(parent2) && !isFooterLinksContainer(selectedElRef.current)) {
@@ -16520,6 +17731,10 @@ function OhhwellsBridge() {
16520
17731
  return;
16521
17732
  }
16522
17733
  if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
17734
+ if (toolbarVariantRef.current === "logo") {
17735
+ deselectRef.current();
17736
+ return;
17737
+ }
16523
17738
  if (toolbarVariantRef.current === "select-frame") {
16524
17739
  const parent2 = getNavigationSelectionParent(selectedElRef.current);
16525
17740
  if (parent2 && isNavigationContainer(parent2) && isFooterLinksContainer(parent2) && !isFooterLinksContainer(selectedElRef.current)) {
@@ -16597,7 +17812,8 @@ function OhhwellsBridge() {
16597
17812
  const handleScroll = () => {
16598
17813
  const focusEl = activeElRef.current ?? selectedElRef.current;
16599
17814
  if (focusEl) {
16600
- const r2 = activeElRef.current ? getEditMeasureEl(activeElRef.current).getBoundingClientRect() : focusEl.getBoundingClientRect();
17815
+ const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : toolbarVariantRef.current === "logo" ? null : focusEl;
17816
+ const r2 = measureEl ? measureEl.getBoundingClientRect() : toolbarVariantRef.current === "logo" ? getLogoInteractionRect(focusEl) : focusEl.getBoundingClientRect();
16601
17817
  applyToolbarPos(r2);
16602
17818
  setToolbarRect(r2);
16603
17819
  setMaxBadge((prev) => prev && activeElRef.current ? { ...prev, rect: r2 } : prev);
@@ -16607,7 +17823,9 @@ function OhhwellsBridge() {
16607
17823
  setToggleState((prev) => prev ? { ...prev, rect } : null);
16608
17824
  }
16609
17825
  if (hoveredItemElRef.current) {
16610
- setHoveredItemRect(hoveredItemElRef.current.getBoundingClientRect());
17826
+ const hoverEl = hoveredItemElRef.current;
17827
+ const logo = getLogoElement(hoverEl);
17828
+ setHoveredItemRect(logo ? getLogoInteractionRect(logo) : hoverEl.getBoundingClientRect());
16611
17829
  }
16612
17830
  if (hoveredNavContainerRef.current) {
16613
17831
  setHoveredNavContainerRect(hoveredNavContainerRef.current.getBoundingClientRect());
@@ -16651,6 +17869,12 @@ function OhhwellsBridge() {
16651
17869
  if (aiSectionsRef.current) {
16652
17870
  nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
16653
17871
  }
17872
+ if (stylesRef.current) {
17873
+ nodes.push({ key: STYLE_STORE_KEY, type: "styles", text: stylesRef.current });
17874
+ }
17875
+ if (brandKitRef.current) {
17876
+ nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
17877
+ }
16654
17878
  postToParentRef.current({ type: "ow:save-result", nodes });
16655
17879
  };
16656
17880
  const handleInsertSection = (e) => {
@@ -16661,8 +17885,12 @@ function OhhwellsBridge() {
16661
17885
  if (inserted) {
16662
17886
  const tracker = getSectionsTracker();
16663
17887
  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 });
17888
+ const reportHeight = () => {
17889
+ const h = document.body.scrollHeight;
17890
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
17891
+ };
17892
+ reportHeight();
17893
+ setTimeout(reportHeight, 500);
16666
17894
  }
16667
17895
  };
16668
17896
  const handleSwitchSchedule = (e) => {
@@ -16855,13 +18083,17 @@ function OhhwellsBridge() {
16855
18083
  if (e.data?.type !== "ow:parent-scroll") return;
16856
18084
  const { iframeOffsetTop, headerH, canvasH } = e.data;
16857
18085
  parentScrollRef.current = { iframeOffsetTop, headerH, canvasH };
18086
+ if (floatingPanelOpenRef.current) {
18087
+ setParentScrollSnap({ iframeOffsetTop, headerH, canvasH });
18088
+ }
16858
18089
  if (visibleViewportRef.current) {
16859
18090
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
16860
18091
  }
16861
18092
  const focusEl = activeElRef.current ?? selectedElRef.current;
16862
18093
  if (focusEl) {
16863
- const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : focusEl;
16864
- applyToolbarPos(measureEl.getBoundingClientRect());
18094
+ const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : toolbarVariantRef.current === "logo" ? null : focusEl;
18095
+ const r2 = measureEl ? measureEl.getBoundingClientRect() : toolbarVariantRef.current === "logo" ? getLogoInteractionRect(focusEl) : focusEl.getBoundingClientRect();
18096
+ applyToolbarPos(r2);
16865
18097
  }
16866
18098
  };
16867
18099
  const handleClickAt = (e) => {
@@ -16886,6 +18118,25 @@ function OhhwellsBridge() {
16886
18118
  postToParentRef.current({ type: "ow:image-pick", key: stateCardImage.dataset.ohwKey ?? "", elementType: stateCardImage.dataset.ohwEditable ?? "image" });
16887
18119
  return;
16888
18120
  }
18121
+ const logoAtPoint = Array.from(
18122
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]')
18123
+ ).map((el) => getLogoElement(el)).find((logo) => {
18124
+ if (!logo) return false;
18125
+ const r2 = logo.getBoundingClientRect();
18126
+ return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
18127
+ });
18128
+ if (logoAtPoint) {
18129
+ if (!logoHasUploadedImage(logoAtPoint)) {
18130
+ deselectRef.current();
18131
+ deactivateRef.current();
18132
+ const identity = readLogoIdentityFromDom();
18133
+ postToParentRef.current({ type: "ow:open-logo-settings", ...identity });
18134
+ return;
18135
+ }
18136
+ selectLogoRef.current(logoAtPoint);
18137
+ openLogoSizePanelRef.current(logoAtPoint);
18138
+ return;
18139
+ }
16889
18140
  const textEditable = Array.from(
16890
18141
  document.querySelectorAll(NON_MEDIA_SELECTOR)
16891
18142
  ).find((el) => {
@@ -16957,6 +18208,14 @@ function OhhwellsBridge() {
16957
18208
  window.addEventListener("message", handleParentScroll);
16958
18209
  window.addEventListener("message", handlePointerSync);
16959
18210
  window.addEventListener("message", handleClickAt);
18211
+ window.addEventListener("message", handleUpdateLogoIdentity);
18212
+ const handleViewMode = (e) => {
18213
+ if (e.data?.type !== "ow:view-mode") return;
18214
+ const mode = e.data.mode === "Mobile" || e.data.mode === "mobile" ? "mobile" : "desktop";
18215
+ setEditorViewport(mode);
18216
+ applyLogoSizes(editContentRef.current);
18217
+ };
18218
+ window.addEventListener("message", handleViewMode);
16960
18219
  const handleViewportResize = () => {
16961
18220
  if (visibleViewportRef.current) {
16962
18221
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
@@ -17012,10 +18271,15 @@ function OhhwellsBridge() {
17012
18271
  window.removeEventListener("resize", handleViewportResize);
17013
18272
  window.removeEventListener("message", handlePointerSync);
17014
18273
  window.removeEventListener("message", handleClickAt);
18274
+ window.removeEventListener("message", handleUpdateLogoIdentity);
18275
+ window.removeEventListener("message", handleViewMode);
17015
18276
  window.removeEventListener("message", handleHydrate);
17016
18277
  window.removeEventListener("message", handleAiApplyTree);
17017
18278
  window.removeEventListener("message", handleAiDeleteSection);
17018
18279
  window.removeEventListener("message", handleAiSetSections);
18280
+ window.removeEventListener("message", handleAiSetBrand);
18281
+ window.removeEventListener("message", handleAiSetStyles);
18282
+ window.removeEventListener("message", handleGetBrand);
17019
18283
  window.removeEventListener("message", handleDeactivate);
17020
18284
  window.removeEventListener("message", handleToastAction);
17021
18285
  window.removeEventListener("message", handleUiEscape);
@@ -17219,7 +18483,7 @@ function OhhwellsBridge() {
17219
18483
  postToParent2({
17220
18484
  type: "ow:ready",
17221
18485
  version: "1",
17222
- bridgeVersion: "0.1.59",
18486
+ bridgeVersion: "0.1.61",
17223
18487
  path: pathname,
17224
18488
  nodes: collectEditableNodes(editContentRef.current),
17225
18489
  sections
@@ -17614,10 +18878,10 @@ function OhhwellsBridge() {
17614
18878
  [postToParent2]
17615
18879
  );
17616
18880
  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(
18881
+ /* @__PURE__ */ jsxs20(Fragment8, { children: [
18882
+ /* @__PURE__ */ jsx33("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
18883
+ isEditMode && /* @__PURE__ */ jsx33(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
18884
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx33(
17621
18885
  MediaOverlay,
17622
18886
  {
17623
18887
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -17628,7 +18892,7 @@ function OhhwellsBridge() {
17628
18892
  },
17629
18893
  `uploading-${key}`
17630
18894
  )),
17631
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx32(
18895
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx33(
17632
18896
  MediaOverlay,
17633
18897
  {
17634
18898
  hover: mediaHover,
@@ -17637,11 +18901,11 @@ function OhhwellsBridge() {
17637
18901
  onVideoSettingsChange: handleVideoSettingsChange
17638
18902
  }
17639
18903
  ),
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(
18904
+ carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
18905
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
18906
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
18907
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
18908
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
17645
18909
  "div",
17646
18910
  {
17647
18911
  className: "pointer-events-none fixed z-2147483646",
@@ -17651,7 +18915,7 @@ function OhhwellsBridge() {
17651
18915
  width: slot.width,
17652
18916
  height: slot.height
17653
18917
  },
17654
- children: /* @__PURE__ */ jsx32(
18918
+ children: /* @__PURE__ */ jsx33(
17655
18919
  DropIndicator,
17656
18920
  {
17657
18921
  direction: slot.direction,
@@ -17662,7 +18926,7 @@ function OhhwellsBridge() {
17662
18926
  },
17663
18927
  `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
17664
18928
  )),
17665
- isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx32(
18929
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
17666
18930
  "div",
17667
18931
  {
17668
18932
  className: "pointer-events-none fixed z-2147483646",
@@ -17672,7 +18936,7 @@ function OhhwellsBridge() {
17672
18936
  width: slot.width,
17673
18937
  height: slot.height
17674
18938
  },
17675
- children: /* @__PURE__ */ jsx32(
18939
+ children: /* @__PURE__ */ jsx33(
17676
18940
  DropIndicator,
17677
18941
  {
17678
18942
  direction: slot.direction,
@@ -17683,11 +18947,11 @@ function OhhwellsBridge() {
17683
18947
  },
17684
18948
  `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
17685
18949
  )),
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(
18950
+ hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
18951
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
18952
+ hoveredTextRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
18953
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx33(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
18954
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx33(
17691
18955
  FooterContainerChrome,
17692
18956
  {
17693
18957
  rect: toolbarRect,
@@ -17695,7 +18959,7 @@ function OhhwellsBridge() {
17695
18959
  addDisabled: !canAddFooterColumn()
17696
18960
  }
17697
18961
  ),
17698
- toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ jsx32(
18962
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ jsx33(
17699
18963
  ItemInteractionLayer,
17700
18964
  {
17701
18965
  rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
@@ -17707,10 +18971,10 @@ function OhhwellsBridge() {
17707
18971
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
17708
18972
  onDragHandleDragStart: handleItemDragStart,
17709
18973
  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(
18974
+ onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
18975
+ onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
18976
+ itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection,
18977
+ toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ jsx33(
17714
18978
  ItemActionToolbar,
17715
18979
  {
17716
18980
  onEditLink: openLinkPopoverForSelected,
@@ -17746,8 +19010,8 @@ function OhhwellsBridge() {
17746
19010
  ) : void 0
17747
19011
  }
17748
19012
  ),
17749
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs19(Fragment8, { children: [
17750
- /* @__PURE__ */ jsx32(
19013
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs20(Fragment8, { children: [
19014
+ /* @__PURE__ */ jsx33(
17751
19015
  EditGlowChrome,
17752
19016
  {
17753
19017
  rect: toolbarRect,
@@ -17757,7 +19021,7 @@ function OhhwellsBridge() {
17757
19021
  hideHandle: isItemDragging
17758
19022
  }
17759
19023
  ),
17760
- /* @__PURE__ */ jsx32(
19024
+ /* @__PURE__ */ jsx33(
17761
19025
  FloatingToolbar,
17762
19026
  {
17763
19027
  rect: toolbarRect,
@@ -17770,7 +19034,7 @@ function OhhwellsBridge() {
17770
19034
  }
17771
19035
  )
17772
19036
  ] }),
17773
- maxBadge && /* @__PURE__ */ jsxs19(
19037
+ maxBadge && /* @__PURE__ */ jsxs20(
17774
19038
  "div",
17775
19039
  {
17776
19040
  "data-ohw-max-badge": "",
@@ -17796,7 +19060,7 @@ function OhhwellsBridge() {
17796
19060
  ]
17797
19061
  }
17798
19062
  ),
17799
- toggleState && !linkPopover && /* @__PURE__ */ jsx32(
19063
+ toggleState && !linkPopover && /* @__PURE__ */ jsx33(
17800
19064
  StateToggle,
17801
19065
  {
17802
19066
  rect: toggleState.rect,
@@ -17805,15 +19069,15 @@ function OhhwellsBridge() {
17805
19069
  onStateChange: handleStateChange
17806
19070
  }
17807
19071
  ),
17808
- sectionGap && !linkPopover && /* @__PURE__ */ jsxs19(
19072
+ sectionGap && !linkPopover && /* @__PURE__ */ jsxs20(
17809
19073
  "div",
17810
19074
  {
17811
19075
  "data-ohw-section-insert-line": "",
17812
19076
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
17813
19077
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
17814
19078
  children: [
17815
- /* @__PURE__ */ jsx32("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
17816
- /* @__PURE__ */ jsx32(
19079
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
19080
+ /* @__PURE__ */ jsx33(
17817
19081
  Badge,
17818
19082
  {
17819
19083
  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 +19094,11 @@ function OhhwellsBridge() {
17830
19094
  children: "Add Section"
17831
19095
  }
17832
19096
  ),
17833
- /* @__PURE__ */ jsx32("div", { className: "flex-1 bg-primary", style: { height: 3 } })
19097
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } })
17834
19098
  ]
17835
19099
  }
17836
19100
  ),
17837
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx32(
19101
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx33(
17838
19102
  LinkPopover,
17839
19103
  {
17840
19104
  panelRef: linkPopoverPanelRef,
@@ -17851,7 +19115,7 @@ function OhhwellsBridge() {
17851
19115
  },
17852
19116
  linkPopover.key
17853
19117
  ) : null,
17854
- floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx32(
19118
+ floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx33(
17855
19119
  FloatingPanel,
17856
19120
  {
17857
19121
  open: true,
@@ -17861,7 +19125,7 @@ function OhhwellsBridge() {
17861
19125
  onPositionChange: setFloatingPanelPos,
17862
19126
  parentScroll: parentScrollSnap ?? parentScrollRef.current,
17863
19127
  onClose: closeFloatingPanelOnly,
17864
- children: /* @__PURE__ */ jsx32(
19128
+ children: /* @__PURE__ */ jsx33(
17865
19129
  SocialsDisplayPanel,
17866
19130
  {
17867
19131
  display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
@@ -17872,11 +19136,115 @@ function OhhwellsBridge() {
17872
19136
  }
17873
19137
  )
17874
19138
  }
19139
+ ) : null,
19140
+ floatingPanel && floatingPanel.kind === "logo-size" && logoSizeDraft ? /* @__PURE__ */ jsx33(
19141
+ FloatingPanel,
19142
+ {
19143
+ open: true,
19144
+ title: floatingPanel.title,
19145
+ context: floatingPanel.context,
19146
+ position: floatingPanelPos,
19147
+ onPositionChange: setFloatingPanelPos,
19148
+ parentScroll: parentScrollSnap ?? parentScrollRef.current,
19149
+ onClose: closeFloatingPanelAndDeselect,
19150
+ children: /* @__PURE__ */ jsx33(
19151
+ LogoSizePanel,
19152
+ {
19153
+ viewport: editorViewport,
19154
+ sizePx: editorViewport === "mobile" && !logoSizeDraft.mobileFollowing ? logoSizeDraft.mobilePx : logoSizeDraft.desktopPx,
19155
+ mobileFollowing: logoSizeDraft.mobileFollowing,
19156
+ onSizeChange: (px) => {
19157
+ const next = editorViewport === "mobile" ? { ...logoSizeDraft, mobilePx: px, mobileFollowing: false } : {
19158
+ ...logoSizeDraft,
19159
+ desktopPx: px,
19160
+ mobilePx: logoSizeDraft.mobileFollowing ? px : logoSizeDraft.mobilePx
19161
+ };
19162
+ setLogoSizeDraft(next);
19163
+ persistLogoSizeDraft(floatingPanel.placement, next);
19164
+ },
19165
+ onCustomizeMobile: () => {
19166
+ const next = {
19167
+ ...logoSizeDraft,
19168
+ mobileFollowing: false,
19169
+ mobilePx: logoSizeDraft.desktopPx
19170
+ };
19171
+ setLogoSizeDraft(next);
19172
+ persistLogoSizeDraft(floatingPanel.placement, next);
19173
+ },
19174
+ onResetMobile: () => {
19175
+ const next = {
19176
+ ...logoSizeDraft,
19177
+ mobileFollowing: true,
19178
+ mobilePx: logoSizeDraft.desktopPx
19179
+ };
19180
+ setLogoSizeDraft(next);
19181
+ persistLogoSizeDraft(floatingPanel.placement, next);
19182
+ },
19183
+ onUpdateEverywhere: () => {
19184
+ const identity = readLogoIdentityFromDom();
19185
+ postToParent2({ type: "ow:open-logo-settings", ...identity });
19186
+ }
19187
+ }
19188
+ )
19189
+ }
17875
19190
  ) : null
17876
19191
  ] }),
17877
19192
  bridgeRoot
17878
19193
  ) : null;
17879
19194
  }
19195
+
19196
+ // src/ui/EmptySection.tsx
19197
+ import Link4 from "next/link";
19198
+ import { Fragment as Fragment9, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
19199
+ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
19200
+ return /* @__PURE__ */ jsxs21(Fragment9, { children: [
19201
+ /* @__PURE__ */ jsx34(
19202
+ "p",
19203
+ {
19204
+ style: {
19205
+ fontFamily: "var(--brand-font-body)",
19206
+ fontSize: "0.75rem",
19207
+ fontWeight: 500,
19208
+ letterSpacing: "0.15em",
19209
+ textTransform: "uppercase",
19210
+ color: "var(--brand-accent)",
19211
+ marginBottom: "1.5rem"
19212
+ },
19213
+ children: /* @__PURE__ */ jsx34(Link4, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx34("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
19214
+ }
19215
+ ),
19216
+ /* @__PURE__ */ jsx34(
19217
+ "h1",
19218
+ {
19219
+ style: {
19220
+ fontFamily: "var(--brand-font-heading)",
19221
+ fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
19222
+ lineHeight: 1.1,
19223
+ letterSpacing: "-0.025em",
19224
+ color: "var(--brand-text)",
19225
+ marginBottom: "1rem"
19226
+ },
19227
+ ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
19228
+ children: title
19229
+ }
19230
+ ),
19231
+ /* @__PURE__ */ jsx34(
19232
+ "p",
19233
+ {
19234
+ style: {
19235
+ fontFamily: "var(--brand-font-body)",
19236
+ fontSize: "1rem",
19237
+ lineHeight: 1.7,
19238
+ fontWeight: 300,
19239
+ color: "var(--brand-text-muted)",
19240
+ maxWidth: "340px"
19241
+ },
19242
+ ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
19243
+ children: "This page doesn't have any content yet."
19244
+ }
19245
+ )
19246
+ ] });
19247
+ }
17880
19248
  export {
17881
19249
  AI_DEFAULT_BRAND,
17882
19250
  AI_TREE_SCHEMA_VERSIONS,
@@ -17893,6 +19261,7 @@ export {
17893
19261
  DropdownMenuItem,
17894
19262
  DropdownMenuSeparator,
17895
19263
  DropdownMenuTrigger,
19264
+ EmptySection,
17896
19265
  ItemActionToolbar,
17897
19266
  ItemInteractionLayer,
17898
19267
  LinkEditorPanel,