@ohhwells/bridge 0.1.59 → 0.1.60-next.171

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -46,6 +46,7 @@ __export(index_exports, {
46
46
  DropdownMenuItem: () => DropdownMenuItem,
47
47
  DropdownMenuSeparator: () => DropdownMenuSeparator,
48
48
  DropdownMenuTrigger: () => DropdownMenuTrigger,
49
+ EmptySection: () => EmptySection,
49
50
  ItemActionToolbar: () => ItemActionToolbar,
50
51
  ItemInteractionLayer: () => ItemInteractionLayer,
51
52
  LinkEditorPanel: () => LinkEditorPanel,
@@ -169,6 +170,7 @@ function applyTreeToState(state, payload) {
169
170
  const entry = {
170
171
  id: payload.id,
171
172
  label: payload.label ?? "Generated section",
173
+ ...typeof payload.path === "string" && payload.path ? { path: payload.path } : {},
172
174
  afterSection: payload.mode === "insert" && !insertBefore ? payload.targetSectionId ?? null : null,
173
175
  ...insertBefore && payload.targetSectionId ? { beforeSection: payload.targetSectionId } : {},
174
176
  ...payload.mode === "replace" && payload.targetSectionId ? { replaces: payload.targetSectionId } : {},
@@ -191,6 +193,272 @@ function deleteSectionFromState(state, sectionId) {
191
193
  return { ...state, v: 1, sections: state.sections, removed: [...removed, sectionId] };
192
194
  }
193
195
 
196
+ // src/lib/brand-chrome.ts
197
+ var BRAND_NAME_KEY = "__ohw_brand_name";
198
+ var BRAND_TITLE_KEY = "__ohw_site_title";
199
+ var BRAND_FAVICON_LETTER_KEY = "__ohw_favicon_letter";
200
+ var BRAND_CHROME_KEYS = /* @__PURE__ */ new Set([
201
+ BRAND_NAME_KEY,
202
+ BRAND_TITLE_KEY,
203
+ BRAND_FAVICON_LETTER_KEY
204
+ ]);
205
+ function upsertMeta(selector, attr, token, value) {
206
+ let el = document.head.querySelector(selector);
207
+ if (!el) {
208
+ el = document.createElement("meta");
209
+ el.setAttribute(attr, token);
210
+ document.head.appendChild(el);
211
+ }
212
+ if (el.getAttribute("content") !== value) el.setAttribute("content", value);
213
+ }
214
+ function escapeXml(value) {
215
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
216
+ }
217
+ function applyLetterFavicon(letter) {
218
+ 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>`;
219
+ const href = `data:image/svg+xml,${encodeURIComponent(svg)}`;
220
+ let link = document.head.querySelector('link[rel="icon"]');
221
+ if (!link) {
222
+ link = document.createElement("link");
223
+ link.rel = "icon";
224
+ document.head.appendChild(link);
225
+ }
226
+ link.type = "image/svg+xml";
227
+ if (link.href !== href) link.href = href;
228
+ }
229
+ function applyBrandChrome(content) {
230
+ const name = content[BRAND_NAME_KEY];
231
+ if (typeof name === "string" && name.length > 0) {
232
+ document.querySelectorAll("[data-ohw-wordmark]").forEach((el) => {
233
+ if (el.textContent !== name) el.textContent = name;
234
+ if (el.getAttribute("title") !== name) el.setAttribute("title", name);
235
+ });
236
+ }
237
+ const title = content[BRAND_TITLE_KEY];
238
+ if (typeof title === "string" && title.length > 0) {
239
+ if (document.title !== title) document.title = title;
240
+ upsertMeta('meta[property="og:title"]', "property", "og:title", title);
241
+ upsertMeta('meta[name="twitter:title"]', "name", "twitter:title", title);
242
+ }
243
+ const letter = content[BRAND_FAVICON_LETTER_KEY];
244
+ if (typeof letter === "string" && letter.length > 0) {
245
+ applyLetterFavicon(letter);
246
+ }
247
+ }
248
+
249
+ // src/lib/brand-kit.ts
250
+ var BRAND_KIT_KEY = "__ohw_brand";
251
+ var BRAND_VAR_PREFIX = "--ohw-brand-";
252
+ var BRAND_VAR_NAMES = ["primary", "accent", "light", "dark", "surface", "border", "muted"].map(
253
+ (role) => `${BRAND_VAR_PREFIX}${role}`
254
+ );
255
+ var FONT_VARS = { heading: ["--font-heading", "--font-display"], body: ["--font-body"] };
256
+ var BRAND_FONT_LINK_ID = "ohw-brand-fonts";
257
+ function brandColorVars(kit) {
258
+ const { dark, primary, accent, light } = kit.palette;
259
+ const mix = (a, aPct, b) => `color-mix(in srgb, ${a} ${aPct}%, ${b})`;
260
+ return {
261
+ [`${BRAND_VAR_PREFIX}primary`]: primary,
262
+ [`${BRAND_VAR_PREFIX}accent`]: accent,
263
+ [`${BRAND_VAR_PREFIX}light`]: light,
264
+ [`${BRAND_VAR_PREFIX}dark`]: dark,
265
+ [`${BRAND_VAR_PREFIX}surface`]: mix(light, 95, dark),
266
+ [`${BRAND_VAR_PREFIX}border`]: mix(light, 85, dark),
267
+ [`${BRAND_VAR_PREFIX}muted`]: mix(dark, 62, light)
268
+ };
269
+ }
270
+ function parseBrandKit(raw) {
271
+ if (!raw) return null;
272
+ try {
273
+ const parsed = JSON.parse(raw);
274
+ const p = parsed?.palette;
275
+ const f = parsed?.fonts;
276
+ 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") {
277
+ return null;
278
+ }
279
+ return {
280
+ palette: { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light },
281
+ fonts: { heading: f.heading, body: f.body }
282
+ };
283
+ } catch {
284
+ return null;
285
+ }
286
+ }
287
+ function familyOf(stack) {
288
+ const first = stack.split(",")[0]?.trim() ?? "";
289
+ return first.replace(/^['"]|['"]$/g, "");
290
+ }
291
+ function loadBrandFonts(families) {
292
+ const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
293
+ if (unique.length === 0) return;
294
+ const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
295
+ const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
296
+ let link = document.getElementById(BRAND_FONT_LINK_ID);
297
+ if (!link) {
298
+ link = document.createElement("link");
299
+ link.id = BRAND_FONT_LINK_ID;
300
+ link.rel = "stylesheet";
301
+ document.head.appendChild(link);
302
+ }
303
+ if (link.href !== href) link.href = href;
304
+ }
305
+ function applyBrandToDom(kit) {
306
+ const root = document.documentElement;
307
+ if (!kit) {
308
+ for (const name of BRAND_VAR_NAMES) root.style.removeProperty(name);
309
+ for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
310
+ document.getElementById(BRAND_FONT_LINK_ID)?.remove();
311
+ return;
312
+ }
313
+ for (const [name, value] of Object.entries(brandColorVars(kit))) root.style.setProperty(name, value);
314
+ for (const name of FONT_VARS.heading) root.style.setProperty(name, kit.fonts.heading);
315
+ for (const name of FONT_VARS.body) root.style.setProperty(name, kit.fonts.body);
316
+ loadBrandFonts([familyOf(kit.fonts.heading), familyOf(kit.fonts.body)]);
317
+ }
318
+
319
+ // src/lib/section-styles.ts
320
+ var STYLE_STORE_KEY = "__ohw_styles";
321
+ var STYLE_SHEET_ID = "ohw-section-styles";
322
+ function parseStyleStore(raw) {
323
+ if (!raw) return null;
324
+ try {
325
+ const parsed = JSON.parse(raw);
326
+ if (parsed?.v !== 1) return null;
327
+ return {
328
+ v: 1,
329
+ sections: typeof parsed.sections === "object" && parsed.sections ? parsed.sections : {},
330
+ nodes: typeof parsed.nodes === "object" && parsed.nodes ? parsed.nodes : {}
331
+ };
332
+ } catch {
333
+ return null;
334
+ }
335
+ }
336
+ var BG_VALUES = {
337
+ surface: "color-mix(in srgb, var(--ohw-brand-light, var(--color-light, #ECECEB)) 94%, var(--ohw-brand-dark, var(--color-dark, #0C0A09)))",
338
+ accent: "var(--ohw-brand-primary, var(--color-primary, #0078E5))",
339
+ "accent-soft": "color-mix(in srgb, var(--ohw-brand-primary, var(--color-primary, #0078E5)) 12%, var(--ohw-brand-light, var(--color-light, #FFFFFF)))"
340
+ };
341
+ var HEADLINE_SIZES = { md: 24, lg: 32, xl: 40, display: 56 };
342
+ function styleSheetCss() {
343
+ const rules = [];
344
+ for (const [tone, value] of Object.entries(BG_VALUES)) {
345
+ rules.push(`[data-ohw-style-bg="${tone}"] { background: ${value} !important; }`);
346
+ }
347
+ rules.push(
348
+ `[data-ohw-style-bg="accent"] { color: var(--ohw-brand-light, var(--color-light, #FFFFFF)) !important; }`
349
+ );
350
+ rules.push(
351
+ `[data-ohw-style-distribution="space-between"] { display: flex !important; flex-direction: column; justify-content: space-between; }`,
352
+ `[data-ohw-style-distribution="center"] { display: flex !important; flex-direction: column; justify-content: center; }`
353
+ );
354
+ for (const [scale, size] of Object.entries(HEADLINE_SIZES)) {
355
+ rules.push(
356
+ `[data-ohw-style-headline="${scale}"] :is(h1, h2, h3) { font-size: ${size}px !important; line-height: 1.15 !important; }`
357
+ );
358
+ }
359
+ rules.push(
360
+ `[data-ohw-style-aspect="fill-height"] img { height: 100% !important; aspect-ratio: auto !important; object-fit: cover; }`
361
+ );
362
+ for (const aspect of ["1:1", "4:5", "3:4", "3:2", "16:9", "2:1", "3:1"]) {
363
+ rules.push(
364
+ `[data-ohw-style-aspect="${aspect.replace(":", "-")}"] img { aspect-ratio: ${aspect.replace(":", " / ")} !important; height: auto !important; object-fit: cover; }`
365
+ );
366
+ }
367
+ const pad = { tight: 40, balanced: 64, airy: 96 };
368
+ for (const [spacing, px] of Object.entries(pad)) {
369
+ rules.push(
370
+ `[data-ohw-style-spacing="${spacing}"] { padding-top: ${px}px !important; padding-bottom: ${px}px !important; }`
371
+ );
372
+ }
373
+ return rules.join("\n");
374
+ }
375
+ var SECTION_ATTRS = {
376
+ sectionBackground: "data-ohw-style-bg",
377
+ textDistribution: "data-ohw-style-distribution",
378
+ headlineScale: "data-ohw-style-headline",
379
+ imageAspect: "data-ohw-style-aspect",
380
+ spacing: "data-ohw-style-spacing"
381
+ };
382
+ var NODE_WROTE_ATTR = "data-ohw-style-node";
383
+ function ensureStyleSheet() {
384
+ let el = document.getElementById(STYLE_SHEET_ID);
385
+ if (!el) {
386
+ el = document.createElement("style");
387
+ el.id = STYLE_SHEET_ID;
388
+ document.head.appendChild(el);
389
+ }
390
+ const css = styleSheetCss();
391
+ if (el.textContent !== css) el.textContent = css;
392
+ }
393
+ function clearSectionAttrs(root) {
394
+ for (const attr of Object.values(SECTION_ATTRS)) {
395
+ for (const el of Array.from(root.querySelectorAll(`[${attr}]`))) el.removeAttribute(attr);
396
+ }
397
+ for (const el of Array.from(root.querySelectorAll("[data-ohw-style-bgcolor]"))) {
398
+ ;
399
+ el.style.removeProperty("background");
400
+ el.removeAttribute("data-ohw-style-bgcolor");
401
+ }
402
+ }
403
+ function clearNodeProps(root) {
404
+ for (const el of Array.from(root.querySelectorAll(`[${NODE_WROTE_ATTR}]`))) {
405
+ const h = el;
406
+ h.style.removeProperty("color");
407
+ h.style.removeProperty("font-size");
408
+ h.style.removeProperty("background");
409
+ h.removeAttribute(NODE_WROTE_ATTR);
410
+ }
411
+ }
412
+ function buttonSurfaceOf(el) {
413
+ return el.closest("a, button") ?? el;
414
+ }
415
+ function applyStylesToDom(store) {
416
+ ensureStyleSheet();
417
+ clearSectionAttrs(document);
418
+ clearNodeProps(document);
419
+ if (!store) return;
420
+ for (const [sectionId, override] of Object.entries(store.sections)) {
421
+ const sections = document.querySelectorAll(
422
+ `[data-ohw-section="${CSS.escape(sectionId)}"]`
423
+ );
424
+ for (const section of Array.from(sections)) {
425
+ for (const [prop, attr] of Object.entries(SECTION_ATTRS)) {
426
+ const value = override[prop];
427
+ if (value === void 0) continue;
428
+ if (prop === "sectionBackground" && override.sectionBackgroundColor !== void 0) continue;
429
+ section.setAttribute(attr, String(value).replace(":", "-"));
430
+ }
431
+ if (override.sectionBackgroundColor !== void 0) {
432
+ section.style.setProperty("background", override.sectionBackgroundColor, "important");
433
+ section.setAttribute("data-ohw-style-bgcolor", "");
434
+ }
435
+ }
436
+ }
437
+ for (const [key, override] of Object.entries(store.nodes)) {
438
+ const nodes = document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`);
439
+ for (const el of Array.from(nodes)) {
440
+ if (override.color !== void 0) {
441
+ el.style.setProperty("color", override.color, "important");
442
+ el.setAttribute(NODE_WROTE_ATTR, "");
443
+ }
444
+ if (override.fontSize !== void 0) {
445
+ el.style.setProperty("font-size", `${override.fontSize}px`, "important");
446
+ el.setAttribute(NODE_WROTE_ATTR, "");
447
+ }
448
+ if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
449
+ const surface = buttonSurfaceOf(el);
450
+ if (override.buttonBackground !== void 0) {
451
+ surface.style.setProperty("background", override.buttonBackground, "important");
452
+ }
453
+ if (override.buttonText !== void 0) {
454
+ surface.style.setProperty("color", override.buttonText, "important");
455
+ }
456
+ surface.setAttribute(NODE_WROTE_ATTR, "");
457
+ }
458
+ }
459
+ }
460
+ }
461
+
194
462
  // src/ui/ai-tree/aiSectionsManager.tsx
195
463
  var import_react_dom = require("react-dom");
196
464
  var import_client = require("react-dom/client");
@@ -205,7 +473,8 @@ function lucideByName(name) {
205
473
  }
206
474
  var typeStyle = (spec, font) => ({
207
475
  fontFamily: font,
208
- fontSize: spec.size,
476
+ // Headings shrink with the viewport (reaching full size around ~900px wide); body copy stays put.
477
+ 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,
209
478
  lineHeight: spec.line,
210
479
  fontWeight: spec.weight
211
480
  });
@@ -223,6 +492,19 @@ var FEATURE_LINE_CSS = [
223
492
  function textAttrs(ctx, path) {
224
493
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
225
494
  }
495
+ var AI_RESPONSIVE_CSS = [
496
+ "@media (max-width: 960px) {",
497
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
498
+ ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
499
+ "}",
500
+ "@media (max-width: 640px) {",
501
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
502
+ " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
503
+ " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
504
+ " [data-ai-responsive] { overflow-x: hidden; }",
505
+ " [data-ai-responsive] img { max-width: 100%; }",
506
+ "}"
507
+ ].join("\n");
226
508
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
227
509
  function MediaBox({
228
510
  refValue,
@@ -965,6 +1247,7 @@ function Carousel({ items, itemsPerRow, ctx }) {
965
1247
  children: pageGroups.map((group, p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
966
1248
  "div",
967
1249
  {
1250
+ "data-ai-grid": String(itemsPerRow),
968
1251
  style: {
969
1252
  flex: "0 0 100%",
970
1253
  display: "grid",
@@ -1068,6 +1351,7 @@ function CollectionBlock({ node, ctx, path }) {
1068
1351
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1069
1352
  "div",
1070
1353
  {
1354
+ "data-ai-grid": String(itemsPerRow),
1071
1355
  style: {
1072
1356
  display: "grid",
1073
1357
  gridTemplateColumns: `repeat(${itemsPerRow}, 1fr)`,
@@ -1226,24 +1510,42 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1226
1510
  const overlayAlpha = { low: 0.25, medium: 0.45, high: 0.65 }[settings.overlayIntensity ?? "medium"];
1227
1511
  const backgroundUrl = isOverlay && settings.backgroundMedia ? ctx.resolveMedia(settings.backgroundMedia) : null;
1228
1512
  const bgAttrs = ctx.keyFor && isOverlay && settings.backgroundMedia ? { "data-ohw-key": ctx.keyFor("settings.backgroundMedia"), "data-ohw-editable": "bg-image" } : {};
1513
+ const toneBackground = (() => {
1514
+ const { dark, primary, light } = resolvedBrand.palette;
1515
+ switch (settings.sectionBackground) {
1516
+ case "surface":
1517
+ return `color-mix(in srgb, ${light} 94%, ${dark})`;
1518
+ case "accent":
1519
+ return primary;
1520
+ case "accent-soft":
1521
+ return `color-mix(in srgb, ${primary} 12%, ${light})`;
1522
+ default:
1523
+ return void 0;
1524
+ }
1525
+ })();
1526
+ const distributed = !isOverlay && settings.textDistribution;
1229
1527
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1230
1528
  "section",
1231
1529
  {
1232
1530
  "data-ai-section": tree.tag ?? "",
1233
1531
  ...bgAttrs,
1532
+ "data-ai-responsive": "",
1234
1533
  style: {
1235
1534
  position: "relative",
1236
1535
  padding: `${pad}px 0`,
1237
- background: isOverlay && !backgroundUrl ? "#DBEAFE" : void 0,
1536
+ background: toneBackground ?? (isOverlay && !backgroundUrl ? "#DBEAFE" : void 0),
1238
1537
  backgroundImage: backgroundUrl ? `url(${backgroundUrl})` : void 0,
1239
1538
  backgroundSize: "cover",
1240
- backgroundPosition: "center"
1539
+ backgroundPosition: "center",
1540
+ color: settings.sectionBackground === "accent" ? resolvedBrand.palette.light : void 0
1241
1541
  },
1242
1542
  children: [
1543
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
1243
1544
  isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
1244
1545
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1245
1546
  "div",
1246
1547
  {
1548
+ "data-ai-section-inner": "",
1247
1549
  style: {
1248
1550
  position: "relative",
1249
1551
  maxWidth: AI_TREE_TOKENS.sectionMaxWidth,
@@ -1254,14 +1556,29 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1254
1556
  children: tree.rows.map((row, r2) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1255
1557
  "div",
1256
1558
  {
1559
+ "data-ai-columns": "",
1257
1560
  style: {
1258
1561
  display: "grid",
1259
1562
  gridTemplateColumns: "repeat(12, 1fr)",
1260
1563
  gap: AI_TREE_TOKENS.spacing6,
1261
- alignItems: settings.verticalPosition === "top" ? "start" : "center",
1564
+ alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
1262
1565
  marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
1263
1566
  },
1264
- children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`, minWidth: 0 }, children: renderNode(block, ctx, `r${r2}.b${b}`) }, b))
1567
+ children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1568
+ "div",
1569
+ {
1570
+ "data-ai-cell": "",
1571
+ style: {
1572
+ gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
1573
+ minWidth: 0,
1574
+ // space-between: each column becomes a flex column whose content spreads over
1575
+ // the full row height instead of clumping at the top.
1576
+ ...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
1577
+ },
1578
+ children: renderNode(block, ctx, `r${r2}.b${b}`)
1579
+ },
1580
+ b
1581
+ ))
1265
1582
  },
1266
1583
  r2
1267
1584
  ))
@@ -1277,17 +1594,34 @@ var import_jsx_runtime2 = require("react/jsx-runtime");
1277
1594
  var CONTAINER_ATTR = "data-ohw-ai-generated";
1278
1595
  var REPLACED_ATTR = "data-ohw-ai-replaced-by";
1279
1596
  var REMOVED_ATTR = "data-ohw-ai-removed";
1597
+ function readRootVar(name) {
1598
+ if (typeof document === "undefined") return "";
1599
+ return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
1600
+ }
1601
+ function deriveBrandOverride() {
1602
+ const dark = readRootVar("--ohw-brand-dark");
1603
+ const primary = readRootVar("--ohw-brand-primary");
1604
+ const light = readRootVar("--ohw-brand-light");
1605
+ if (!dark || !primary || !light) return null;
1606
+ const accent = readRootVar("--ohw-brand-accent");
1607
+ const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1608
+ const body = readRootVar("--font-body");
1609
+ return {
1610
+ palette: { dark, primary, accent: accent || dark, light },
1611
+ fonts: {
1612
+ heading: heading || AI_DEFAULT_BRAND.fonts.heading,
1613
+ body: body || AI_DEFAULT_BRAND.fonts.body
1614
+ }
1615
+ };
1616
+ }
1280
1617
  function deriveTemplateBrand() {
1281
- if (typeof document === "undefined") return null;
1282
- const cs = getComputedStyle(document.documentElement);
1283
- const read = (name) => cs.getPropertyValue(name).trim();
1284
- const dark = read("--color-dark");
1285
- const primary = read("--color-primary");
1286
- const light = read("--color-light");
1618
+ const dark = readRootVar("--color-dark");
1619
+ const primary = readRootVar("--color-primary");
1620
+ const light = readRootVar("--color-light");
1287
1621
  if (!dark || !primary || !light) return null;
1288
- const accent = read("--color-accent");
1289
- const heading = read("--font-heading") || read("--font-display");
1290
- const body = read("--font-body");
1622
+ const accent = readRootVar("--color-accent");
1623
+ const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1624
+ const body = readRootVar("--font-body");
1291
1625
  return {
1292
1626
  palette: { dark, primary, accent: accent || dark, light },
1293
1627
  fonts: {
@@ -1379,8 +1713,12 @@ function syncReplacedOriginals(state) {
1379
1713
  }
1380
1714
  function applyAiSectionsToDom(state, options) {
1381
1715
  if (typeof document === "undefined") return;
1716
+ const brandOverride = deriveBrandOverride();
1382
1717
  const templateBrand = deriveTemplateBrand();
1383
- const activeIds = new Set(state.sections.map((entry) => entry.id));
1718
+ const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
1719
+ const pagePath = window.location.pathname;
1720
+ const pageSections = state.sections.filter((entry) => !entry.path || entry.path === pagePath);
1721
+ const activeIds = new Set(pageSections.map((entry) => entry.id));
1384
1722
  for (const [id, section] of mounted) {
1385
1723
  if (!activeIds.has(id)) {
1386
1724
  section.root.unmount();
@@ -1388,8 +1726,8 @@ function applyAiSectionsToDom(state, options) {
1388
1726
  mounted.delete(id);
1389
1727
  }
1390
1728
  }
1391
- for (const entry of state.sections) {
1392
- const serialized = JSON.stringify(entry);
1729
+ for (const entry of pageSections) {
1730
+ const serialized = JSON.stringify(entry) + brandKey;
1393
1731
  const existing = mounted.get(entry.id);
1394
1732
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
1395
1733
  continue;
@@ -1403,6 +1741,7 @@ function applyAiSectionsToDom(state, options) {
1403
1741
  mounted.delete(entry.id);
1404
1742
  }
1405
1743
  container.setAttribute("data-ohw-section", entry.id);
1744
+ container.setAttribute("data-ohw-instance", entry.id);
1406
1745
  container.setAttribute("data-ohw-section-label", entry.label);
1407
1746
  placeContainer(container, entry);
1408
1747
  const root = mounted.get(entry.id)?.root ?? (0, import_client.createRoot)(container);
@@ -1413,7 +1752,7 @@ function applyAiSectionsToDom(state, options) {
1413
1752
  AiTreeRenderer,
1414
1753
  {
1415
1754
  tree: entry.tree,
1416
- brand: entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1755
+ brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1417
1756
  resolveMedia,
1418
1757
  editKeyPrefix: `ai.${entry.id}`
1419
1758
  }
@@ -2030,7 +2369,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2030
2369
  const autoId = (0, import_react5.useId)();
2031
2370
  const insertAfter = insertAfterProp ?? autoId;
2032
2371
  const [schedule, setSchedule] = (0, import_react5.useState)(null);
2033
- const [loading, setLoading] = (0, import_react5.useState)(true);
2372
+ const [loading, setLoading] = (0, import_react5.useState)(initialScheduleId !== null);
2034
2373
  const [inEditor, setInEditor] = (0, import_react5.useState)(false);
2035
2374
  const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
2036
2375
  const [modalState, setModalState] = (0, import_react5.useState)(null);
@@ -2204,8 +2543,10 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2204
2543
  "*"
2205
2544
  );
2206
2545
  };
2207
- if (!inEditor && !loading && !schedule) return null;
2208
2546
  const sectionId = `scheduling-${insertAfter}`;
2547
+ if (!inEditor && !loading && !schedule) {
2548
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
2549
+ }
2209
2550
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2210
2551
  "section",
2211
2552
  {
@@ -6159,6 +6500,7 @@ var FOCUS_RING = `0 0 0 4px color-mix(in srgb, ${PRIMARY} 12%, transparent)`;
6159
6500
  var DRAG_SHADOW = "0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0, 0, 0, 0.1)";
6160
6501
  var TOOLBAR_EDGE_MARGIN = 4;
6161
6502
  var SELECTION_CHROME_GAP = 4;
6503
+ var HOVER_CHROME_GAP = 2;
6162
6504
  var TOOLBAR_STROKE_GAP = 4;
6163
6505
  function getChromeZIndex(state) {
6164
6506
  switch (state) {
@@ -6314,10 +6656,11 @@ function ItemInteractionLayer({
6314
6656
  onItemPointerDown,
6315
6657
  onItemClick,
6316
6658
  itemDragSurface = true,
6317
- chromeGap = SELECTION_CHROME_GAP,
6659
+ chromeGap,
6318
6660
  className
6319
6661
  }) {
6320
6662
  if (state === "default") return null;
6663
+ const gap = chromeGap ?? (state === "hover" ? HOVER_CHROME_GAP : SELECTION_CHROME_GAP);
6321
6664
  const isActive = state === "active-top" || state === "active-bottom";
6322
6665
  const isDragging = state === "dragging";
6323
6666
  const showToolbar = isActive && toolbar;
@@ -6333,10 +6676,10 @@ function ItemInteractionLayer({
6333
6676
  className: cn("pointer-events-none", className),
6334
6677
  style: {
6335
6678
  position: "fixed",
6336
- top: rect.top - chromeGap,
6337
- left: rect.left - chromeGap,
6338
- width: rect.width + chromeGap * 2,
6339
- height: rect.height + chromeGap * 2,
6679
+ top: rect.top - gap,
6680
+ left: rect.left - gap,
6681
+ width: rect.width + gap * 2,
6682
+ height: rect.height + gap * 2,
6340
6683
  zIndex: getChromeZIndex(state)
6341
6684
  },
6342
6685
  children: [
@@ -6722,8 +7065,12 @@ function parseSectionsFromHtml(html) {
6722
7065
 
6723
7066
  // src/ui/ai-section/AiSectionOverlay.tsx
6724
7067
  var import_jsx_runtime16 = require("react/jsx-runtime");
6725
- function readRect(sectionId) {
6726
- const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
7068
+ function findSectionElement(instanceId) {
7069
+ const escaped = CSS.escape(instanceId);
7070
+ return document.querySelector(`[data-ohw-instance="${escaped}"]`) ?? document.querySelector(`[data-ohw-section="${escaped}"]:not([data-ohw-instance])`);
7071
+ }
7072
+ function readRect(instanceId) {
7073
+ const el = findSectionElement(instanceId);
6727
7074
  if (!el) return null;
6728
7075
  const r2 = el.getBoundingClientRect();
6729
7076
  if (r2.width <= 0 || r2.height <= 0) return null;
@@ -6746,7 +7093,7 @@ function useLiveSectionRect(sectionId) {
6746
7093
  const opts = { capture: true, passive: true };
6747
7094
  window.addEventListener("scroll", update, opts);
6748
7095
  window.addEventListener("resize", update);
6749
- const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
7096
+ const el = findSectionElement(sectionId);
6750
7097
  const ro = el ? new ResizeObserver(update) : null;
6751
7098
  if (el && ro) ro.observe(el);
6752
7099
  const interval = setInterval(update, 500);
@@ -6759,6 +7106,14 @@ function useLiveSectionRect(sectionId) {
6759
7106
  }, [sectionId]);
6760
7107
  return rect;
6761
7108
  }
7109
+ function computeSectionBoundaryFlags(instanceId) {
7110
+ const topLevel = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
7111
+ (el) => !el.parentElement?.closest("[data-ohw-section]")
7112
+ );
7113
+ const index = topLevel.findIndex((el) => (el.dataset.ohwInstance ?? el.dataset.ohwSection) === instanceId);
7114
+ if (index === -1) return { isFirst: true, isLast: true };
7115
+ return { isFirst: index === 0, isLast: index === topLevel.length - 1 };
7116
+ }
6762
7117
  var PRIMARY2 = "#0885FE";
6763
7118
  function edgeAwareRadius(rect) {
6764
7119
  const container = window.innerWidth <= 480 ? 16 : 24;
@@ -6840,7 +7195,7 @@ function AiSectionOverlay({
6840
7195
  (el) => {
6841
7196
  postToParent2({
6842
7197
  type: "ow:section-selected",
6843
- sectionId: el?.dataset.ohwSection ?? null,
7198
+ sectionId: el ? el.dataset.ohwInstance ?? el.dataset.ohwSection ?? null : null,
6844
7199
  sectionLabel: el ? el.dataset.ohwSectionLabel ?? titleCaseSectionId(el.dataset.ohwSection ?? "") : null
6845
7200
  });
6846
7201
  },
@@ -6849,7 +7204,7 @@ function AiSectionOverlay({
6849
7204
  const selectFromElement = (0, import_react8.useCallback)(
6850
7205
  (el, options) => {
6851
7206
  const sectionEl = el?.closest("[data-ohw-section]") ?? null;
6852
- const id = sectionEl?.dataset.ohwSection ?? null;
7207
+ const id = sectionEl ? sectionEl.dataset.ohwInstance ?? sectionEl.dataset.ohwSection ?? null : null;
6853
7208
  if (id === selectedIdRef.current) return;
6854
7209
  setSelectedId(id);
6855
7210
  if (options?.report !== false) report(sectionEl);
@@ -6892,7 +7247,7 @@ function AiSectionOverlay({
6892
7247
  setReviewId(found ? sectionId : null);
6893
7248
  postToParent2({ type: "ow:ai-review-started", sectionId, found });
6894
7249
  if (found) {
6895
- document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
7250
+ document.querySelector(`[data-ohw-instance="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
6896
7251
  }
6897
7252
  }
6898
7253
  };
@@ -6911,7 +7266,7 @@ function AiSectionOverlay({
6911
7266
  return;
6912
7267
  }
6913
7268
  const sec = t.closest("[data-ohw-section]");
6914
- setHoveredId(sec?.dataset.ohwSection ?? null);
7269
+ setHoveredId(sec ? sec.dataset.ohwInstance ?? sec.dataset.ohwSection ?? null : null);
6915
7270
  };
6916
7271
  const onLeave = () => setHoveredId(null);
6917
7272
  document.addEventListener("mousemove", onMove, { passive: true });
@@ -6943,9 +7298,29 @@ function AiSectionOverlay({
6943
7298
  },
6944
7299
  [postToParent2]
6945
7300
  );
6946
- const selectionRect = useLiveSectionRect(reviewId ? null : selectedId);
7301
+ const activeSelectionId = reviewId ? null : selectedId;
7302
+ const selectionRect = useLiveSectionRect(activeSelectionId);
6947
7303
  const reviewRect = useLiveSectionRect(reviewId);
6948
7304
  const hoverRect = useLiveSectionRect(reviewId || hoveredId === selectedId ? null : hoveredId);
7305
+ (0, import_react8.useEffect)(() => {
7306
+ if (!activeSelectionId || !selectionRect) {
7307
+ postToParent2({ type: "ow:section-rect", instanceId: null, rect: null });
7308
+ return;
7309
+ }
7310
+ const { isFirst, isLast } = computeSectionBoundaryFlags(activeSelectionId);
7311
+ postToParent2({
7312
+ type: "ow:section-rect",
7313
+ instanceId: activeSelectionId,
7314
+ rect: {
7315
+ top: selectionRect.top + window.scrollY,
7316
+ left: selectionRect.left + window.scrollX,
7317
+ width: selectionRect.width,
7318
+ height: selectionRect.height
7319
+ },
7320
+ isFirst,
7321
+ isLast
7322
+ });
7323
+ }, [activeSelectionId, selectionRect, postToParent2]);
6949
7324
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
6950
7325
  hoverRect && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
6951
7326
  "div",
@@ -6996,7 +7371,10 @@ function AiSectionOverlay({
6996
7371
  border: `2px solid ${PRIMARY2}`,
6997
7372
  borderRadius: edgeAwareRadius(reviewRect),
6998
7373
  zIndex: 2147483200,
6999
- // The veil itself: swallows clicks so the section stays locked until decided.
7374
+ // The veil itself: swallows clicks so the section stays locked until decided. This
7375
+ // stopPropagation only guards the bubble phase; the bridge's capture-phase click
7376
+ // handlers additionally bail on `[data-ohw-ai-review]`, without which a click on
7377
+ // Accept/Discard resolves to the media beneath and opens the file picker.
7000
7378
  background: "rgba(8, 133, 254, 0.04)",
7001
7379
  pointerEvents: "auto",
7002
7380
  cursor: "default"
@@ -9690,6 +10068,10 @@ function isSocialsRow(el) {
9690
10068
  const anchors = Array.from(el.querySelectorAll("a"));
9691
10069
  return anchors.length > 0 && anchors.every((anchor) => isSocialItem(anchor));
9692
10070
  }
10071
+ var MAX_SOCIAL_ITEMS_PER_ROW = 7;
10072
+ function canAddSocialItem(row) {
10073
+ return listSocialItems(row).length < MAX_SOCIAL_ITEMS_PER_ROW;
10074
+ }
9693
10075
  function listSocialItems(row) {
9694
10076
  return Array.from(row.children).map((child) => {
9695
10077
  if (!(child instanceof HTMLElement)) return null;
@@ -10049,6 +10431,7 @@ function ensureIconSlot(item) {
10049
10431
  // src/lib/footer-items.ts
10050
10432
  var FOOTER_ORDER_KEY = "__ohw_footer_order";
10051
10433
  var MAX_FOOTER_COLUMNS = 18;
10434
+ var MAX_FOOTER_ITEMS_PER_COLUMN = 7;
10052
10435
  var FOOTER_HREF_RE = /^footer-(\d+)-(\d+)-href$/;
10053
10436
  function parseFooterHrefKey(key) {
10054
10437
  if (!key) return null;
@@ -10266,6 +10649,13 @@ function getNextFooterColumnIndex() {
10266
10649
  function canAddFooterColumn() {
10267
10650
  return listFooterColumns().length < MAX_FOOTER_COLUMNS;
10268
10651
  }
10652
+ function canAddFooterItem(column) {
10653
+ return listFooterLinksInColumn(column).length < MAX_FOOTER_ITEMS_PER_COLUMN;
10654
+ }
10655
+ function resolveFooterColumnForAdd(selected) {
10656
+ if (selected.hasAttribute("data-ohw-footer-col")) return selected;
10657
+ return selected.closest("[data-ohw-footer-col]") ?? (listFooterColumns().includes(selected) ? selected : null);
10658
+ }
10269
10659
  function buildFooterHeading(colIndex, text) {
10270
10660
  const heading = document.createElement("p");
10271
10661
  heading.setAttribute("data-ohw-editable", "text");
@@ -10890,6 +11280,329 @@ function deleteFooterColumn(column) {
10890
11280
  };
10891
11281
  }
10892
11282
 
11283
+ // src/lib/logo-identity.ts
11284
+ var LOGO_TEXT_KEYS = ["nav-logo-text", "footer-logo-text", "logo-text"];
11285
+ var LOGO_IMAGE_KEYS = ["nav-logo-image", "footer-logo", "footer-logo-image"];
11286
+ var LOGO_HREF_KEYS = ["nav-logo-href", "footer-logo-href", "logo-href"];
11287
+ var LOGO_PLACEHOLDER_KEY = "logo-is-placeholder";
11288
+ var LOGO_ALT_KEY = "logo-alt";
11289
+ var LOGO_IMAGE_URL_KEY = "nav-logo-image";
11290
+ var PLACEHOLDER_BUSINESS_NAME = "Business name";
11291
+ function resolveLogoDisplayText(text) {
11292
+ const trimmed = (text ?? "").trim();
11293
+ return trimmed || PLACEHOLDER_BUSINESS_NAME;
11294
+ }
11295
+ function isFooterLogoRoot(root) {
11296
+ return Boolean(root.closest("footer") || root.closest('[data-ohw-section="footer"]'));
11297
+ }
11298
+ function imageKeyForRoot(root) {
11299
+ return isFooterLogoRoot(root) ? "footer-logo" : "nav-logo-image";
11300
+ }
11301
+ function textKeyForRoot(root) {
11302
+ return isFooterLogoRoot(root) ? "footer-logo-text" : "nav-logo-text";
11303
+ }
11304
+ function ensureLogoHrefKey(root) {
11305
+ if (!(root instanceof HTMLAnchorElement)) return;
11306
+ if (root.hasAttribute("data-ohw-href-key")) return;
11307
+ root.setAttribute("data-ohw-href-key", isFooterLogoRoot(root) ? "footer-logo-href" : "nav-logo-href");
11308
+ }
11309
+ function applyLogoIdentity(text, isPlaceholder) {
11310
+ const display = resolveLogoDisplayText(text);
11311
+ const placeholder = isPlaceholder || !text.trim() || display === PLACEHOLDER_BUSINESS_NAME;
11312
+ for (const key of LOGO_TEXT_KEYS) {
11313
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
11314
+ if (el.textContent !== display) el.textContent = display;
11315
+ });
11316
+ }
11317
+ for (const key of LOGO_IMAGE_KEYS) {
11318
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
11319
+ const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
11320
+ if (img) img.alt = display;
11321
+ });
11322
+ }
11323
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((el) => {
11324
+ if (placeholder) el.setAttribute("data-ohw-placeholder", "");
11325
+ else el.removeAttribute("data-ohw-placeholder");
11326
+ });
11327
+ return display;
11328
+ }
11329
+ function applyLogoImage(url, alt) {
11330
+ const displayAlt = resolveLogoDisplayText(alt);
11331
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11332
+ ensureLogoHrefKey(root);
11333
+ const imageKey = imageKeyForRoot(root);
11334
+ const textKey = textKeyForRoot(root);
11335
+ 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");
11336
+ let textEl = root.querySelector(`[data-ohw-key="${textKey}"]`) ?? root.querySelector('[data-ohw-key="logo-text"]');
11337
+ if (url) {
11338
+ if (!img) {
11339
+ img = document.createElement("img");
11340
+ img.setAttribute("data-ohw-editable", "image");
11341
+ img.setAttribute("data-ohw-key", imageKey);
11342
+ img.alt = displayAlt;
11343
+ img.style.height = "";
11344
+ img.style.maxHeight = "none";
11345
+ img.style.width = "auto";
11346
+ img.style.display = "block";
11347
+ img.style.objectFit = "contain";
11348
+ root.insertBefore(img, root.firstChild);
11349
+ } else {
11350
+ img.setAttribute("data-ohw-editable", "image");
11351
+ img.setAttribute("data-ohw-key", imageKey);
11352
+ }
11353
+ img.removeAttribute("srcset");
11354
+ img.removeAttribute("sizes");
11355
+ img.src = url;
11356
+ img.alt = displayAlt;
11357
+ img.style.display = "block";
11358
+ if (textEl) textEl.style.display = "none";
11359
+ root.removeAttribute("data-ohw-placeholder");
11360
+ return;
11361
+ }
11362
+ if (img) {
11363
+ img.removeAttribute("src");
11364
+ img.removeAttribute("srcset");
11365
+ img.removeAttribute("sizes");
11366
+ img.alt = displayAlt;
11367
+ img.style.display = "none";
11368
+ }
11369
+ if (!textEl) {
11370
+ textEl = document.createElement("span");
11371
+ textEl.setAttribute("data-ohw-editable", "plain");
11372
+ textEl.setAttribute("data-ohw-key", textKey);
11373
+ root.appendChild(textEl);
11374
+ }
11375
+ textEl.style.display = "";
11376
+ if (textEl.textContent !== displayAlt) textEl.textContent = displayAlt;
11377
+ if (!displayAlt.trim() || displayAlt === PLACEHOLDER_BUSINESS_NAME) {
11378
+ root.setAttribute("data-ohw-placeholder", "");
11379
+ } else {
11380
+ root.removeAttribute("data-ohw-placeholder");
11381
+ }
11382
+ });
11383
+ }
11384
+ function applyLogoHref(href) {
11385
+ const target = href.trim() || "/";
11386
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11387
+ ensureLogoHrefKey(root);
11388
+ if (root instanceof HTMLAnchorElement) {
11389
+ root.setAttribute("href", target);
11390
+ }
11391
+ });
11392
+ for (const key of LOGO_HREF_KEYS) setStoredLinkHref(key, target);
11393
+ }
11394
+ function readLogoIdentityFromDom() {
11395
+ let imageUrl = null;
11396
+ for (const key of LOGO_IMAGE_KEYS) {
11397
+ const el = document.querySelector(`[data-ohw-key="${key}"]`);
11398
+ const img = el instanceof HTMLImageElement ? el : el?.querySelector("img");
11399
+ const attrSrc = img?.getAttribute("src")?.trim() ?? "";
11400
+ if (attrSrc && !attrSrc.startsWith("data:") && img && img.style.display !== "none") {
11401
+ imageUrl = img.currentSrc || img.src;
11402
+ break;
11403
+ }
11404
+ }
11405
+ let text = PLACEHOLDER_BUSINESS_NAME;
11406
+ let isPlaceholder = true;
11407
+ for (const key of LOGO_TEXT_KEYS) {
11408
+ const el = document.querySelector(`[data-ohw-key="${key}"]`);
11409
+ if (el?.textContent?.trim()) {
11410
+ text = el.textContent.trim();
11411
+ const logoRoot2 = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
11412
+ isPlaceholder = logoRoot2?.hasAttribute("data-ohw-placeholder") === true || text === PLACEHOLDER_BUSINESS_NAME;
11413
+ break;
11414
+ }
11415
+ }
11416
+ if (imageUrl) {
11417
+ const logoImg = document.querySelector(
11418
+ '[data-ohw-key="nav-logo-image"], [data-ohw-key="footer-logo"]'
11419
+ );
11420
+ const alt = logoImg?.alt?.trim() || text;
11421
+ isPlaceholder = false;
11422
+ const hrefEl = document.querySelector(
11423
+ 'a[data-ohw-role="logo"], a[data-ohw-logo], [data-ohw-role="logo"]'
11424
+ );
11425
+ const href2 = (hrefEl instanceof HTMLAnchorElement ? hrefEl.getAttribute("href") : null) || hrefEl?.closest("a")?.getAttribute("href") || "/";
11426
+ return { text, isPlaceholder, imageUrl, href: href2, alt };
11427
+ }
11428
+ const logoRoot = document.querySelector('[data-ohw-role="logo"], [data-ohw-logo]');
11429
+ const href = (logoRoot instanceof HTMLAnchorElement ? logoRoot.getAttribute("href") : null) || logoRoot?.closest("a")?.getAttribute("href") || "/";
11430
+ return { text, isPlaceholder, imageUrl: null, href, alt: text };
11431
+ }
11432
+ function applyLogoFromContent(content) {
11433
+ 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);
11434
+ if (!hasLogoIdentity) return false;
11435
+ const logoText = content[LOGO_TEXT_KEYS[0]] ?? content[LOGO_TEXT_KEYS[1]] ?? readLogoIdentityFromDom().text;
11436
+ const logoAlt = content[LOGO_ALT_KEY] ?? logoText;
11437
+ const rawLogoImage = content[LOGO_IMAGE_URL_KEY] ?? content["footer-logo"] ?? content["footer-logo-image"] ?? null;
11438
+ const logoImageUrl = typeof rawLogoImage === "string" && rawLogoImage.trim() ? rawLogoImage.trim() : null;
11439
+ const imageExplicitlyCleared = LOGO_IMAGE_KEYS.some((key) => key in content) && !logoImageUrl;
11440
+ const logoIsPlaceholder = LOGO_PLACEHOLDER_KEY in content ? content[LOGO_PLACEHOLDER_KEY] !== "false" : !logoImageUrl && (!logoText.trim() || logoText === PLACEHOLDER_BUSINESS_NAME);
11441
+ if (logoImageUrl) {
11442
+ applyLogoImage(logoImageUrl, logoAlt);
11443
+ } else {
11444
+ if (imageExplicitlyCleared) applyLogoImage(null, logoAlt);
11445
+ applyLogoIdentity(logoText, logoIsPlaceholder);
11446
+ }
11447
+ const logoHref = content["nav-logo-href"] ?? content["footer-logo-href"] ?? content["logo-href"];
11448
+ if (typeof logoHref === "string" && logoHref.trim()) {
11449
+ applyLogoHref(logoHref);
11450
+ }
11451
+ return true;
11452
+ }
11453
+
11454
+ // src/lib/logo-size.ts
11455
+ var LOGO_SIZE_DEFAULTS = {
11456
+ navbar: 28,
11457
+ footer: 32
11458
+ };
11459
+ var LOGO_SIZE_MIN = 16;
11460
+ var LOGO_SIZE_MAX = 80;
11461
+ var LOGO_SIZE_DESKTOP_KEYS = {
11462
+ navbar: "nav-logo-size",
11463
+ footer: "footer-logo-size"
11464
+ };
11465
+ var LOGO_SIZE_MOBILE_KEYS = {
11466
+ navbar: "nav-logo-size-mobile",
11467
+ footer: "footer-logo-size-mobile"
11468
+ };
11469
+ var LOGO_SIZE_KEYS = [
11470
+ LOGO_SIZE_DESKTOP_KEYS.navbar,
11471
+ LOGO_SIZE_DESKTOP_KEYS.footer,
11472
+ LOGO_SIZE_MOBILE_KEYS.navbar,
11473
+ LOGO_SIZE_MOBILE_KEYS.footer
11474
+ ];
11475
+ function isFooterLogoRoot2(root) {
11476
+ return Boolean(root.closest("footer") || root.closest('[data-ohw-section="footer"]'));
11477
+ }
11478
+ function getLogoPlacement(root) {
11479
+ return isFooterLogoRoot2(root) ? "footer" : "navbar";
11480
+ }
11481
+ function parseLogoSizePx(raw, fallback) {
11482
+ if (raw == null || raw === "") return fallback;
11483
+ const n = Number.parseFloat(raw);
11484
+ if (!Number.isFinite(n)) return fallback;
11485
+ return Math.min(LOGO_SIZE_MAX, Math.max(LOGO_SIZE_MIN, Math.round(n)));
11486
+ }
11487
+ function isMobileLogoSizeFollowing(content, placement) {
11488
+ const raw = content[LOGO_SIZE_MOBILE_KEYS[placement]];
11489
+ return raw == null || raw.trim() === "";
11490
+ }
11491
+ function resolveDesktopLogoSize(content, placement) {
11492
+ return parseLogoSizePx(content[LOGO_SIZE_DESKTOP_KEYS[placement]], LOGO_SIZE_DEFAULTS[placement]);
11493
+ }
11494
+ function resolveMobileLogoSize(content, placement) {
11495
+ if (isMobileLogoSizeFollowing(content, placement)) {
11496
+ return resolveDesktopLogoSize(content, placement);
11497
+ }
11498
+ return parseLogoSizePx(
11499
+ content[LOGO_SIZE_MOBILE_KEYS[placement]],
11500
+ resolveDesktopLogoSize(content, placement)
11501
+ );
11502
+ }
11503
+ function setRootSizeVars(root, desktopPx, mobilePx, following) {
11504
+ root.style.setProperty("--ohw-logo-size", `${desktopPx}px`);
11505
+ if (following) {
11506
+ root.style.removeProperty("--ohw-logo-size-mobile");
11507
+ } else {
11508
+ root.style.setProperty("--ohw-logo-size-mobile", `${mobilePx}px`);
11509
+ }
11510
+ root.querySelectorAll("img").forEach((img) => {
11511
+ img.style.height = "";
11512
+ img.style.maxHeight = "none";
11513
+ img.style.width = "auto";
11514
+ img.style.objectFit = "contain";
11515
+ });
11516
+ }
11517
+ function applyLogoSizes(content) {
11518
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11519
+ const placement = getLogoPlacement(root);
11520
+ const desktop = resolveDesktopLogoSize(content, placement);
11521
+ const following = isMobileLogoSizeFollowing(content, placement);
11522
+ const mobile = following ? desktop : resolveMobileLogoSize(content, placement);
11523
+ setRootSizeVars(root, desktop, mobile, following);
11524
+ });
11525
+ }
11526
+ function applyLogoSizeToPlacement(placement, desktopPx, mobilePx, following) {
11527
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11528
+ if (getLogoPlacement(root) !== placement) return;
11529
+ setRootSizeVars(root, desktopPx, mobilePx, following);
11530
+ });
11531
+ }
11532
+ function logoHasUploadedImage(logoEl) {
11533
+ if (logoEl.hasAttribute("data-ohw-placeholder")) return false;
11534
+ 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");
11535
+ if (!img) return false;
11536
+ const src = img.getAttribute("src")?.trim() ?? "";
11537
+ if (!src || src.startsWith("data:")) return false;
11538
+ if (img.style.display === "none") return false;
11539
+ return true;
11540
+ }
11541
+ function getLogoInteractionRect(logoEl) {
11542
+ if (logoHasUploadedImage(logoEl)) {
11543
+ 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");
11544
+ if (img) {
11545
+ const r2 = img.getBoundingClientRect();
11546
+ if (r2.width > 0 && r2.height > 0) return r2;
11547
+ }
11548
+ }
11549
+ const text = logoEl.querySelector(
11550
+ '[data-ohw-key="nav-logo-text"], [data-ohw-key="footer-logo-text"]'
11551
+ );
11552
+ if (text) {
11553
+ const style = window.getComputedStyle(text);
11554
+ if (style.display !== "none" && style.visibility !== "hidden") {
11555
+ const r2 = text.getBoundingClientRect();
11556
+ if (r2.width > 0 && r2.height > 0) return r2;
11557
+ }
11558
+ }
11559
+ return logoEl.getBoundingClientRect();
11560
+ }
11561
+ function readLogoSizeState(content, placement) {
11562
+ const desktopPx = resolveDesktopLogoSize(content, placement);
11563
+ const mobileFollowing = isMobileLogoSizeFollowing(content, placement);
11564
+ const mobilePx = mobileFollowing ? desktopPx : resolveMobileLogoSize(content, placement);
11565
+ return { desktopPx, mobilePx, mobileFollowing };
11566
+ }
11567
+
11568
+ // src/lib/site-wide-scope.ts
11569
+ function getLogoElement(el) {
11570
+ const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
11571
+ if (marked) return marked;
11572
+ if (el.closest('[data-ohw-editable="icon"]')) return null;
11573
+ const root = el.closest("nav, [data-ohw-nav-root], footer");
11574
+ if (!root) return null;
11575
+ const anchor = el.closest("a");
11576
+ if (anchor && root.contains(anchor) && !anchor.hasAttribute("data-ohw-href-key") && !anchor.closest("[data-ohw-nav-container]") && Boolean(anchor.querySelector("img") || anchor.matches("img"))) {
11577
+ return anchor;
11578
+ }
11579
+ const img = el.matches("img") ? el : null;
11580
+ if (img && !img.closest("[data-ohw-href-key]") && !img.closest("[data-ohw-nav-container]") && (img.closest("footer") || img.closest("nav, [data-ohw-nav-root]"))) {
11581
+ return img;
11582
+ }
11583
+ return null;
11584
+ }
11585
+ function isInFooter(el) {
11586
+ if (!el) return false;
11587
+ return Boolean(el.closest("footer") || el.closest('[data-ohw-section="footer"]'));
11588
+ }
11589
+ function isSiteWideElement(el) {
11590
+ if (!el) return false;
11591
+ if (getLogoElement(el)) return true;
11592
+ if (isInFooter(el)) return true;
11593
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-root")) {
11594
+ return true;
11595
+ }
11596
+ if (el.hasAttribute("data-ohw-href-key") && el.closest("nav, [data-ohw-nav-root], [data-ohw-nav-drawer], aside")) {
11597
+ return true;
11598
+ }
11599
+ if (el.closest('[data-ohw-role="navbar-button"]')) return true;
11600
+ return false;
11601
+ }
11602
+ function isSiteWideScopeActive(args) {
11603
+ return isSiteWideElement(args.selected) || isSiteWideElement(args.hoveredItem) || isSiteWideElement(args.hoveredNavContainer) || isSiteWideElement(args.active);
11604
+ }
11605
+
10893
11606
  // src/lib/add-footer-column.ts
10894
11607
  function buildFooterColumnEditContentPatch(result) {
10895
11608
  return {
@@ -10995,6 +11708,7 @@ function FloatingPanel({
10995
11708
  e.stopPropagation();
10996
11709
  const el = e.currentTarget;
10997
11710
  el.setPointerCapture(e.pointerId);
11711
+ document.documentElement.setAttribute("data-ohw-panel-dragging", "");
10998
11712
  dragRef.current = {
10999
11713
  pointerId: e.pointerId,
11000
11714
  startX: e.clientX,
@@ -11027,11 +11741,17 @@ function FloatingPanel({
11027
11741
  const drag = dragRef.current;
11028
11742
  if (!drag || drag.pointerId !== e.pointerId) return;
11029
11743
  dragRef.current = null;
11744
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
11030
11745
  try {
11031
11746
  e.currentTarget.releasePointerCapture(e.pointerId);
11032
11747
  } catch {
11033
11748
  }
11034
11749
  }, []);
11750
+ (0, import_react13.useEffect)(() => {
11751
+ if (open) return;
11752
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
11753
+ }, [open]);
11754
+ (0, import_react13.useEffect)(() => () => document.documentElement.removeAttribute("data-ohw-panel-dragging"), []);
11035
11755
  if (!open) return null;
11036
11756
  return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
11037
11757
  "div",
@@ -11098,16 +11818,127 @@ function FloatingPanel({
11098
11818
  );
11099
11819
  }
11100
11820
 
11101
- // src/ui/socials-display-panel.tsx
11821
+ // src/ui/logo-size-panel.tsx
11822
+ var import_lucide_react14 = require("lucide-react");
11102
11823
  var import_jsx_runtime27 = require("react/jsx-runtime");
11824
+ function SizeSlider({
11825
+ value,
11826
+ onChange
11827
+ }) {
11828
+ const pct = (value - LOGO_SIZE_MIN) / (LOGO_SIZE_MAX - LOGO_SIZE_MIN) * 100;
11829
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex w-full flex-col gap-3", children: [
11830
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex w-full items-center gap-2 text-sm font-medium leading-5", children: [
11831
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "min-w-0 flex-1 text-foreground", children: "Size" }),
11832
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "shrink-0 whitespace-nowrap text-muted-foreground", children: [
11833
+ value,
11834
+ " px"
11835
+ ] })
11836
+ ] }),
11837
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "relative h-2 w-full rounded-full bg-primary-50", children: [
11838
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
11839
+ "div",
11840
+ {
11841
+ className: "absolute inset-y-0 left-0 rounded-full bg-primary",
11842
+ style: { width: `${pct}%` }
11843
+ }
11844
+ ),
11845
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
11846
+ "input",
11847
+ {
11848
+ type: "range",
11849
+ min: LOGO_SIZE_MIN,
11850
+ max: LOGO_SIZE_MAX,
11851
+ step: 1,
11852
+ value,
11853
+ "aria-label": "Logo size",
11854
+ className: cn(
11855
+ "absolute inset-0 h-full w-full cursor-pointer appearance-none bg-transparent",
11856
+ "[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:size-5",
11857
+ "[&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2",
11858
+ "[&::-webkit-slider-thumb]:border-primary [&::-webkit-slider-thumb]:bg-background",
11859
+ "[&::-moz-range-thumb]:size-5 [&::-moz-range-thumb]:rounded-full",
11860
+ "[&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-primary",
11861
+ "[&::-moz-range-thumb]:bg-background"
11862
+ ),
11863
+ onChange: (e) => onChange(Number(e.target.value))
11864
+ }
11865
+ )
11866
+ ] })
11867
+ ] });
11868
+ }
11869
+ function LogoSizePanel({
11870
+ viewport,
11871
+ sizePx,
11872
+ mobileFollowing = true,
11873
+ onSizeChange,
11874
+ onCustomizeMobile,
11875
+ onResetMobile,
11876
+ onUpdateEverywhere,
11877
+ className
11878
+ }) {
11879
+ const showFollowing = viewport === "mobile" && mobileFollowing;
11880
+ const showMobileSlider = viewport === "mobile" && !mobileFollowing;
11881
+ const showDesktopSlider = viewport === "desktop";
11882
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: cn("flex w-full flex-col gap-4", className), children: [
11883
+ showFollowing ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex w-full flex-col gap-2", children: [
11884
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-start gap-1", children: [
11885
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react14.Link, { size: 16, className: "mt-0.5 shrink-0 text-foreground", "aria-hidden": true }),
11886
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-sm font-semibold leading-5 text-foreground", children: "Following desktop size" })
11887
+ ] }),
11888
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("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." }),
11889
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
11890
+ Button,
11891
+ {
11892
+ type: "button",
11893
+ variant: "outline",
11894
+ size: "sm",
11895
+ className: "h-9 w-full min-w-0 cursor-pointer",
11896
+ onClick: onCustomizeMobile,
11897
+ children: "Customize for mobile"
11898
+ }
11899
+ )
11900
+ ] }) : null,
11901
+ showDesktopSlider || showMobileSlider ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(SizeSlider, { value: sizePx, onChange: onSizeChange }) : null,
11902
+ showMobileSlider ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
11903
+ Button,
11904
+ {
11905
+ type: "button",
11906
+ variant: "outline",
11907
+ size: "sm",
11908
+ className: "h-9 w-full min-w-0 cursor-pointer",
11909
+ onClick: onResetMobile,
11910
+ children: "Reset to desktop size"
11911
+ }
11912
+ ) : null,
11913
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "h-px w-full bg-border", role: "separator" }),
11914
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
11915
+ Button,
11916
+ {
11917
+ type: "button",
11918
+ variant: "outline",
11919
+ size: "sm",
11920
+ className: "h-9 w-full min-w-0 cursor-pointer gap-1",
11921
+ onClick: onUpdateEverywhere,
11922
+ children: [
11923
+ "Update logo everywhere",
11924
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react14.ArrowUpRight, { size: 16, "aria-hidden": true })
11925
+ ]
11926
+ }
11927
+ ),
11928
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-sm leading-5 text-muted-foreground", children: "Matches the right version to your background." })
11929
+ ] });
11930
+ }
11931
+
11932
+ // src/ui/socials-display-panel.tsx
11933
+ var import_jsx_runtime28 = require("react/jsx-runtime");
11103
11934
  function DisplaySwitch({
11104
11935
  label,
11105
11936
  checked,
11106
11937
  disabled,
11107
11938
  onChange
11108
11939
  }) {
11109
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex w-full items-center gap-2", children: [
11110
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
11940
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex w-full items-center gap-2", children: [
11941
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
11111
11942
  "span",
11112
11943
  {
11113
11944
  className: cn(
@@ -11117,7 +11948,7 @@ function DisplaySwitch({
11117
11948
  children: label
11118
11949
  }
11119
11950
  ),
11120
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
11951
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
11121
11952
  "button",
11122
11953
  {
11123
11954
  type: "button",
@@ -11131,7 +11962,7 @@ function DisplaySwitch({
11131
11962
  checked ? "bg-primary" : "bg-primary-50",
11132
11963
  disabled ? "cursor-default opacity-50" : "cursor-pointer"
11133
11964
  ),
11134
- children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
11965
+ children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
11135
11966
  "span",
11136
11967
  {
11137
11968
  className: cn(
@@ -11145,8 +11976,8 @@ function DisplaySwitch({
11145
11976
  ] });
11146
11977
  }
11147
11978
  function SocialsDisplayPanel({ display, onChange, className }) {
11148
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: cn("flex w-full flex-col gap-3", className), children: [
11149
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
11979
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: cn("flex w-full flex-col gap-3", className), children: [
11980
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
11150
11981
  DisplaySwitch,
11151
11982
  {
11152
11983
  label: "Text",
@@ -11155,7 +11986,7 @@ function SocialsDisplayPanel({ display, onChange, className }) {
11155
11986
  onChange: (text) => onChange({ ...display, text })
11156
11987
  }
11157
11988
  ),
11158
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
11989
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
11159
11990
  DisplaySwitch,
11160
11991
  {
11161
11992
  label: "Icon",
@@ -11715,8 +12546,8 @@ function useNavItemDrag({
11715
12546
  }
11716
12547
 
11717
12548
  // src/ui/footer-container-chrome.tsx
11718
- var import_lucide_react14 = require("lucide-react");
11719
- var import_jsx_runtime28 = require("react/jsx-runtime");
12549
+ var import_lucide_react15 = require("lucide-react");
12550
+ var import_jsx_runtime29 = require("react/jsx-runtime");
11720
12551
  function FooterContainerChrome({
11721
12552
  rect,
11722
12553
  onAdd,
@@ -11724,7 +12555,7 @@ function FooterContainerChrome({
11724
12555
  }) {
11725
12556
  const chromeGap = 6;
11726
12557
  const buttonMargin = 7;
11727
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
12558
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
11728
12559
  "div",
11729
12560
  {
11730
12561
  "data-ohw-footer-container-chrome": "",
@@ -11736,8 +12567,8 @@ function FooterContainerChrome({
11736
12567
  width: rect.width + chromeGap * 2,
11737
12568
  height: rect.height + chromeGap * 2
11738
12569
  },
11739
- children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Tooltip, { children: [
11740
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
12570
+ children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(Tooltip, { children: [
12571
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
11741
12572
  "button",
11742
12573
  {
11743
12574
  type: "button",
@@ -11756,10 +12587,10 @@ function FooterContainerChrome({
11756
12587
  if (addDisabled) return;
11757
12588
  onAdd();
11758
12589
  },
11759
- children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_lucide_react14.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
12590
+ children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react15.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
11760
12591
  }
11761
12592
  ) }),
11762
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
12593
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
11763
12594
  ] })
11764
12595
  }
11765
12596
  ) });
@@ -11942,6 +12773,18 @@ function collectEditableNodes(extraContent, root = document) {
11942
12773
  }
11943
12774
  if (extraContent && !isScoped) {
11944
12775
  applyNavFooterDeleteOverrides(byKey, extraContent);
12776
+ for (const key of LOGO_IMAGE_KEYS) {
12777
+ if (!(key in extraContent)) continue;
12778
+ byKey.set(key, { key, type: "image", text: extraContent[key] ?? "" });
12779
+ }
12780
+ for (const key of [LOGO_PLACEHOLDER_KEY, LOGO_ALT_KEY]) {
12781
+ if (!(key in extraContent)) continue;
12782
+ byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
12783
+ }
12784
+ for (const key of LOGO_SIZE_KEYS) {
12785
+ if (!(key in extraContent)) continue;
12786
+ byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
12787
+ }
11945
12788
  }
11946
12789
  return Array.from(byKey.values());
11947
12790
  }
@@ -12207,14 +13050,14 @@ function deleteSelectedNavFooterItem(deps) {
12207
13050
  }
12208
13051
 
12209
13052
  // src/ui/navbar-container-chrome.tsx
12210
- var import_lucide_react15 = require("lucide-react");
12211
- var import_jsx_runtime29 = require("react/jsx-runtime");
13053
+ var import_lucide_react16 = require("lucide-react");
13054
+ var import_jsx_runtime30 = require("react/jsx-runtime");
12212
13055
  function NavbarContainerChrome({
12213
13056
  rect,
12214
13057
  onAdd
12215
13058
  }) {
12216
13059
  const chromeGap = 6;
12217
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
13060
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
12218
13061
  "div",
12219
13062
  {
12220
13063
  "data-ohw-navbar-container-chrome": "",
@@ -12226,7 +13069,7 @@ function NavbarContainerChrome({
12226
13069
  width: rect.width + chromeGap * 2,
12227
13070
  height: rect.height + chromeGap * 2
12228
13071
  },
12229
- children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
13072
+ children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
12230
13073
  "button",
12231
13074
  {
12232
13075
  type: "button",
@@ -12243,7 +13086,7 @@ function NavbarContainerChrome({
12243
13086
  e.stopPropagation();
12244
13087
  onAdd();
12245
13088
  },
12246
- children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react15.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
13089
+ children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_lucide_react16.Plus, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
12247
13090
  }
12248
13091
  )
12249
13092
  }
@@ -12252,7 +13095,7 @@ function NavbarContainerChrome({
12252
13095
 
12253
13096
  // src/ui/drop-indicator.tsx
12254
13097
  var React10 = __toESM(require("react"), 1);
12255
- var import_jsx_runtime30 = require("react/jsx-runtime");
13098
+ var import_jsx_runtime31 = require("react/jsx-runtime");
12256
13099
  var dropIndicatorVariants = cva(
12257
13100
  "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
12258
13101
  {
@@ -12276,7 +13119,7 @@ var dropIndicatorVariants = cva(
12276
13119
  );
12277
13120
  var DropIndicator = React10.forwardRef(
12278
13121
  ({ className, direction, state, ...props }, ref) => {
12279
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
13122
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
12280
13123
  "div",
12281
13124
  {
12282
13125
  ref,
@@ -12293,7 +13136,7 @@ var DropIndicator = React10.forwardRef(
12293
13136
  DropIndicator.displayName = "DropIndicator";
12294
13137
 
12295
13138
  // src/ui/badge.tsx
12296
- var import_jsx_runtime31 = require("react/jsx-runtime");
13139
+ var import_jsx_runtime32 = require("react/jsx-runtime");
12297
13140
  var badgeVariants = cva(
12298
13141
  "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",
12299
13142
  {
@@ -12311,12 +13154,12 @@ var badgeVariants = cva(
12311
13154
  }
12312
13155
  );
12313
13156
  function Badge({ className, variant, ...props }) {
12314
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
13157
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: cn(badgeVariants({ variant }), className), ...props });
12315
13158
  }
12316
13159
 
12317
13160
  // src/OhhwellsBridge.tsx
12318
- var import_lucide_react16 = require("lucide-react");
12319
- var import_jsx_runtime32 = require("react/jsx-runtime");
13161
+ var import_lucide_react17 = require("lucide-react");
13162
+ var import_jsx_runtime33 = require("react/jsx-runtime");
12320
13163
  var PRIMARY3 = "#0885FE";
12321
13164
  var IMAGE_FADE_MS = 300;
12322
13165
  function runOpacityFade(el, onDone) {
@@ -12410,21 +13253,10 @@ function parseSchedulingInsertAfter(insertAfter) {
12410
13253
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
12411
13254
  };
12412
13255
  }
12413
- function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
12414
- const parsed = parseSchedulingInsertAfter(insertAfter);
12415
- const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
12416
- const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
12417
- return { effectiveInsertAfter, insertBefore };
12418
- }
12419
- function getSchedulingMountPoint(insertAfter) {
12420
- const { anchor } = parseSchedulingInsertAfter(insertAfter);
12421
- let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
12422
- if (!anchorEl && anchor === "scheduling") {
12423
- const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
12424
- anchorEl = widgets.at(-1) ?? null;
12425
- }
12426
- if (!anchorEl) return null;
12427
- return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
13256
+ function resolveEntryAnchor(entry) {
13257
+ if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
13258
+ const parsed = parseSchedulingInsertAfter(entry.insertAfter);
13259
+ return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
12428
13260
  }
12429
13261
  function schedulingMountDepth(insertAfter) {
12430
13262
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -12441,8 +13273,7 @@ function getPageSchedulingEntries(raw) {
12441
13273
  }
12442
13274
  }
12443
13275
  function isSchedulingWidgetMissing(entry) {
12444
- const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
12445
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
13276
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
12446
13277
  }
12447
13278
  function hasMissingSchedulingWidgets(entries) {
12448
13279
  return entries.some(isSchedulingWidgetMissing);
@@ -12472,16 +13303,17 @@ function initSectionsFromContent(content, removeExisting = false) {
12472
13303
  } catch {
12473
13304
  }
12474
13305
  }
12475
- function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
12476
- const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
12477
- const sectionId = schedulingSectionId(effectiveInsertAfter);
13306
+ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
13307
+ const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
13308
+ const sectionId = schedulingSectionId(widgetId);
12478
13309
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
12479
- const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
12480
- if (!mountPoint) return false;
13310
+ const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
13311
+ if (!anchorEl) return false;
13312
+ const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
12481
13313
  const container = document.createElement("div");
12482
13314
  container.dataset.ohwSectionContainer = "scheduling";
12483
- if (insertBefore) {
12484
- const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
13315
+ if (beforeId) {
13316
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
12485
13317
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
12486
13318
  if (!beforePoint) return false;
12487
13319
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -12492,19 +13324,25 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
12492
13324
  }
12493
13325
  tail.insertAdjacentElement("afterend", container);
12494
13326
  }
12495
- const root = (0, import_client2.createRoot)(container);
12496
- (0, import_react_dom3.flushSync)(() => {
12497
- root.render(
12498
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
12499
- SchedulingWidget,
12500
- {
12501
- notifyOnConnect,
12502
- initialScheduleId: scheduleId,
12503
- insertAfter: effectiveInsertAfter
12504
- }
12505
- )
12506
- );
12507
- });
13327
+ try {
13328
+ const root = (0, import_client2.createRoot)(container);
13329
+ (0, import_react_dom3.flushSync)(() => {
13330
+ root.render(
13331
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
13332
+ SchedulingWidget,
13333
+ {
13334
+ notifyOnConnect,
13335
+ initialScheduleId: scheduleId,
13336
+ insertAfter: widgetId
13337
+ }
13338
+ )
13339
+ );
13340
+ });
13341
+ } catch (err) {
13342
+ console.error("[ow:scheduling] render threw", err);
13343
+ container.remove();
13344
+ return false;
13345
+ }
12508
13346
  const tracker = getSectionsTracker();
12509
13347
  let sections = [];
12510
13348
  try {
@@ -12512,10 +13350,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
12512
13350
  } catch {
12513
13351
  }
12514
13352
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
12515
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
13353
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
12516
13354
  sections.push({
12517
13355
  type: "scheduling",
12518
- insertAfter: effectiveInsertAfter,
13356
+ insertAfter: widgetId,
13357
+ anchorId,
13358
+ beforeId: beforeId ?? null,
12519
13359
  pagePath: window.location.pathname,
12520
13360
  ...scheduleId ? { scheduleId } : {}
12521
13361
  });
@@ -12529,7 +13369,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
12529
13369
  for (let i = pending.length - 1; i >= 0; i--) {
12530
13370
  const entry = pending[i];
12531
13371
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
12532
- if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
13372
+ const { anchorId, beforeId } = resolveEntryAnchor(entry);
13373
+ if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
12533
13374
  pending.splice(i, 1);
12534
13375
  }
12535
13376
  }
@@ -12673,6 +13514,13 @@ function isInsideLinkEditor(target) {
12673
13514
  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"]')
12674
13515
  );
12675
13516
  }
13517
+ function isInsideFloatingPanel(target) {
13518
+ return Boolean(target.closest("[data-ohw-floating-panel]"));
13519
+ }
13520
+ function isPointOverFloatingPanel(clientX, clientY) {
13521
+ const el = document.elementFromPoint(clientX, clientY);
13522
+ return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
13523
+ }
12676
13524
  function getHrefKeyFromElement(el) {
12677
13525
  if (!el) return null;
12678
13526
  const anchor = el.closest("[data-ohw-href-key]");
@@ -12810,6 +13658,11 @@ function isNavbarLinksContainer2(el) {
12810
13658
  function getFooterColumn(el) {
12811
13659
  return el.closest("[data-ohw-footer-col], [data-ohw-footer-column]");
12812
13660
  }
13661
+ function isFooterAddItemDisabled(selected) {
13662
+ if (!selected) return false;
13663
+ const column = resolveFooterColumnForAdd(selected);
13664
+ return column ? !canAddFooterItem(column) : false;
13665
+ }
12813
13666
  function resolveFooterColumnSelectionTarget(target, clientX, clientY) {
12814
13667
  if (getNavigationItemAnchor(target)) return null;
12815
13668
  const column = getFooterColumn(target);
@@ -12905,7 +13758,7 @@ function getNavigationSelectionParent(el) {
12905
13758
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
12906
13759
  return getFooterLinksContainer();
12907
13760
  }
12908
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
13761
+ 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)) {
12909
13762
  return getNavigationRoot(el);
12910
13763
  }
12911
13764
  return null;
@@ -13123,6 +13976,9 @@ var ICONS = {
13123
13976
  var SELECTION_CHROME_GAP2 = 4;
13124
13977
  var TOOLBAR_STROKE_GAP2 = 4;
13125
13978
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
13979
+ var HOVER_STROKE_WIDTH = 1.5;
13980
+ var TEXT_HOVER_SIDE_PAD = 4;
13981
+ var CHROME_PRIMARY = `var(--ohw-primary, ${PRIMARY3})`;
13126
13982
  var TOOLBAR_GROUPS = [
13127
13983
  [
13128
13984
  { cmd: "bold", title: "Bold" },
@@ -13148,7 +14004,7 @@ function EditGlowChrome({
13148
14004
  hideHandle = false
13149
14005
  }) {
13150
14006
  const GAP = SELECTION_CHROME_GAP2;
13151
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
14007
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
13152
14008
  "div",
13153
14009
  {
13154
14010
  ref: elRef,
@@ -13163,7 +14019,7 @@ function EditGlowChrome({
13163
14019
  zIndex: 2147483646
13164
14020
  },
13165
14021
  children: [
13166
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
14022
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
13167
14023
  "div",
13168
14024
  {
13169
14025
  style: {
@@ -13176,7 +14032,7 @@ function EditGlowChrome({
13176
14032
  }
13177
14033
  }
13178
14034
  ),
13179
- reorderHrefKey && !hideHandle && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
14035
+ reorderHrefKey && !hideHandle && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
13180
14036
  "div",
13181
14037
  {
13182
14038
  "data-ohw-drag-handle-container": "",
@@ -13188,7 +14044,7 @@ function EditGlowChrome({
13188
14044
  transform: "translate(calc(-100% - 7px), -50%)",
13189
14045
  pointerEvents: dragDisabled ? "none" : "auto"
13190
14046
  },
13191
- children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
14047
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
13192
14048
  DragHandle,
13193
14049
  {
13194
14050
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -13398,7 +14254,7 @@ function FloatingToolbar({
13398
14254
  return () => ro.disconnect();
13399
14255
  }, [showEditLink, activeCommands]);
13400
14256
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, measuredW);
13401
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
14257
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
13402
14258
  "div",
13403
14259
  {
13404
14260
  ref: setRefs,
@@ -13410,12 +14266,12 @@ function FloatingToolbar({
13410
14266
  zIndex: 2147483647,
13411
14267
  pointerEvents: "auto"
13412
14268
  },
13413
- children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(CustomToolbar, { children: [
13414
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_react16.default.Fragment, { children: [
13415
- gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(CustomToolbarDivider, {}),
14269
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(CustomToolbar, { children: [
14270
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_react16.default.Fragment, { children: [
14271
+ gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(CustomToolbarDivider, {}),
13416
14272
  btns.map((btn) => {
13417
14273
  const isActive = activeCommands.has(btn.cmd);
13418
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
14274
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
13419
14275
  CustomToolbarButton,
13420
14276
  {
13421
14277
  title: btn.title,
@@ -13424,7 +14280,7 @@ function FloatingToolbar({
13424
14280
  e.preventDefault();
13425
14281
  onCommand(btn.cmd);
13426
14282
  },
13427
- children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
14283
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
13428
14284
  "svg",
13429
14285
  {
13430
14286
  width: "16",
@@ -13445,7 +14301,7 @@ function FloatingToolbar({
13445
14301
  );
13446
14302
  })
13447
14303
  ] }, gi)),
13448
- showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
14304
+ showEditLink ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
13449
14305
  CustomToolbarButton,
13450
14306
  {
13451
14307
  type: "button",
@@ -13459,7 +14315,7 @@ function FloatingToolbar({
13459
14315
  e.preventDefault();
13460
14316
  e.stopPropagation();
13461
14317
  },
13462
- children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react16.Link, { className: "size-4 shrink-0", "aria-hidden": true })
14318
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react17.Link, { className: "size-4 shrink-0", "aria-hidden": true })
13463
14319
  }
13464
14320
  ) : null
13465
14321
  ] })
@@ -13476,7 +14332,7 @@ function StateToggle({
13476
14332
  states,
13477
14333
  onStateChange
13478
14334
  }) {
13479
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
14335
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
13480
14336
  ToggleGroup,
13481
14337
  {
13482
14338
  "data-ohw-state-toggle": "",
@@ -13490,11 +14346,12 @@ function StateToggle({
13490
14346
  left: rect.right - 8,
13491
14347
  transform: "translateX(-100%)"
13492
14348
  },
13493
- children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
14349
+ children: states.map((state) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
13494
14350
  }
13495
14351
  );
13496
14352
  }
13497
14353
  var contentCache = /* @__PURE__ */ new Map();
14354
+ var fetchedContentPaths = /* @__PURE__ */ new Set();
13498
14355
  function resolveSubdomain(subdomainFromQuery) {
13499
14356
  if (subdomainFromQuery) return subdomainFromQuery;
13500
14357
  if (typeof window !== "undefined") {
@@ -13589,8 +14446,14 @@ function OhhwellsBridge() {
13589
14446
  });
13590
14447
  const selectFrameRef = (0, import_react16.useRef)(() => {
13591
14448
  });
14449
+ const selectLogoRef = (0, import_react16.useRef)(() => {
14450
+ });
14451
+ const openLogoSizePanelRef = (0, import_react16.useRef)(() => {
14452
+ });
13592
14453
  const deselectRef = (0, import_react16.useRef)(() => {
13593
14454
  });
14455
+ const closeFloatingPanelOnlyRef = (0, import_react16.useRef)(() => {
14456
+ });
13594
14457
  const reselectNavigationItemRef = (0, import_react16.useRef)(() => {
13595
14458
  });
13596
14459
  const commitNavigationTextEditRef = (0, import_react16.useRef)(() => {
@@ -13623,17 +14486,34 @@ function OhhwellsBridge() {
13623
14486
  const [hoveredNavContainerRect, setHoveredNavContainerRect] = (0, import_react16.useState)(null);
13624
14487
  const hoveredItemElRef = (0, import_react16.useRef)(null);
13625
14488
  const [hoveredItemRect, setHoveredItemRect] = (0, import_react16.useState)(null);
14489
+ const [hoveredTextRect, setHoveredTextRect] = (0, import_react16.useState)(null);
14490
+ (0, import_react16.useEffect)(() => {
14491
+ const sync = () => {
14492
+ const el = document.querySelector(
14493
+ "[data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key])"
14494
+ );
14495
+ const target = el && !el.closest("[data-ohw-href-key]") ? el : null;
14496
+ if (!target) {
14497
+ setHoveredTextRect(null);
14498
+ return;
14499
+ }
14500
+ const r2 = target.getBoundingClientRect();
14501
+ setHoveredTextRect(new DOMRect(r2.x - TEXT_HOVER_SIDE_PAD, r2.y, r2.width + TEXT_HOVER_SIDE_PAD * 2, r2.height));
14502
+ };
14503
+ const observer = new MutationObserver(sync);
14504
+ observer.observe(document.documentElement, {
14505
+ attributes: true,
14506
+ attributeFilter: ["data-ohw-hovered"],
14507
+ subtree: true
14508
+ });
14509
+ return () => observer.disconnect();
14510
+ }, []);
13626
14511
  const siblingHintElRef = (0, import_react16.useRef)(null);
13627
14512
  const [siblingHintRect, setSiblingHintRect] = (0, import_react16.useState)(null);
13628
14513
  const [siblingHintRects, setSiblingHintRects] = (0, import_react16.useState)([]);
13629
14514
  const [isItemDragging, setIsItemDragging] = (0, import_react16.useState)(false);
13630
14515
  const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react16.useState)(false);
13631
14516
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
13632
- const [floatingPanel, setFloatingPanel] = (0, import_react16.useState)(null);
13633
- const floatingPanelOpenRef = (0, import_react16.useRef)(false);
13634
- floatingPanelOpenRef.current = floatingPanel !== null;
13635
- const [floatingPanelPos, setFloatingPanelPos] = (0, import_react16.useState)(null);
13636
- const [parentScrollSnap, setParentScrollSnap] = (0, import_react16.useState)(null);
13637
14517
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react16.useState)(null);
13638
14518
  const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react16.useState)(null);
13639
14519
  const footerDragRef = (0, import_react16.useRef)(null);
@@ -13648,7 +14528,16 @@ function OhhwellsBridge() {
13648
14528
  const addNavAfterAnchorRef = (0, import_react16.useRef)(null);
13649
14529
  const editContentRef = (0, import_react16.useRef)({});
13650
14530
  const aiSectionsRef = (0, import_react16.useRef)("");
14531
+ const brandKitRef = (0, import_react16.useRef)("");
14532
+ const stylesRef = (0, import_react16.useRef)("");
13651
14533
  const pendingDeleteUndoRef = (0, import_react16.useRef)(null);
14534
+ const [floatingPanel, setFloatingPanel] = (0, import_react16.useState)(null);
14535
+ const floatingPanelOpenRef = (0, import_react16.useRef)(false);
14536
+ const setFloatingPanelRef = (0, import_react16.useRef)(setFloatingPanel);
14537
+ const [floatingPanelPos, setFloatingPanelPos] = (0, import_react16.useState)(null);
14538
+ const [logoSizeDraft, setLogoSizeDraft] = (0, import_react16.useState)(null);
14539
+ const [editorViewport, setEditorViewport] = (0, import_react16.useState)("desktop");
14540
+ const [parentScrollSnap, setParentScrollSnap] = (0, import_react16.useState)(null);
13652
14541
  const [sitePages, setSitePages] = (0, import_react16.useState)([]);
13653
14542
  const [sectionsByPath, setSectionsByPath] = (0, import_react16.useState)({});
13654
14543
  const sectionsPrefetchGenRef = (0, import_react16.useRef)(0);
@@ -13657,7 +14546,18 @@ function OhhwellsBridge() {
13657
14546
  const linkPopoverOpenRef = (0, import_react16.useRef)(false);
13658
14547
  const linkPopoverGraceUntilRef = (0, import_react16.useRef)(0);
13659
14548
  setLinkPopoverRef.current = setLinkPopover;
14549
+ setFloatingPanelRef.current = setFloatingPanel;
13660
14550
  linkPopoverSessionRef.current = linkPopover;
14551
+ floatingPanelOpenRef.current = Boolean(floatingPanel);
14552
+ (0, import_react16.useEffect)(() => {
14553
+ const syncViewport = () => {
14554
+ const next = window.innerWidth <= 480 ? "mobile" : "desktop";
14555
+ setEditorViewport((prev) => prev === next ? prev : next);
14556
+ };
14557
+ syncViewport();
14558
+ window.addEventListener("resize", syncViewport);
14559
+ return () => window.removeEventListener("resize", syncViewport);
14560
+ }, []);
13661
14561
  const {
13662
14562
  navDragRef,
13663
14563
  navDropSlots,
@@ -13880,6 +14780,10 @@ function OhhwellsBridge() {
13880
14780
  setIsItemDragging(false);
13881
14781
  hoveredNavContainerRef.current = null;
13882
14782
  setHoveredNavContainerRect(null);
14783
+ hoveredItemElRef.current = null;
14784
+ setHoveredItemRect(null);
14785
+ setFloatingPanel(null);
14786
+ setLogoSizeDraft(null);
13883
14787
  if (!activeElRef.current) {
13884
14788
  setNavGroupForceOpen(null, false);
13885
14789
  setToolbarRect(null);
@@ -14076,6 +14980,14 @@ function OhhwellsBridge() {
14076
14980
  if (!selected) return;
14077
14981
  const socialsRow = isSocialsRow(selected) ? selected : findSocialsRow(selected);
14078
14982
  if (socialsRow) {
14983
+ if (!canAddSocialItem(socialsRow)) {
14984
+ postToParent2({
14985
+ type: "ow:toast",
14986
+ title: `Maximum ${MAX_SOCIAL_ITEMS_PER_ROW} social icons`,
14987
+ toastType: "error"
14988
+ });
14989
+ return;
14990
+ }
14079
14991
  const after = getSocialItem(selected);
14080
14992
  const result2 = insertSocialItem(socialsRow, after, editContentRef.current);
14081
14993
  if (!result2) return;
@@ -14097,10 +15009,16 @@ function OhhwellsBridge() {
14097
15009
  return;
14098
15010
  }
14099
15011
  if (toolbarVariantRef.current === "select-frame" && isFooterFrameSelection) {
14100
- if (!selected.hasAttribute("data-ohw-footer-col") && !selected.closest("[data-ohw-footer-col]")) {
14101
- }
14102
- const column = (selected.hasAttribute("data-ohw-footer-col") ? selected : null) ?? selected.closest("[data-ohw-footer-col]") ?? (listFooterColumns().includes(selected) ? selected : null);
15012
+ const column = resolveFooterColumnForAdd(selected);
14103
15013
  if (!column) return;
15014
+ if (!canAddFooterItem(column)) {
15015
+ postToParent2({
15016
+ type: "ow:toast",
15017
+ title: `Maximum ${MAX_FOOTER_ITEMS_PER_COLUMN} items per column`,
15018
+ toastType: "error"
15019
+ });
15020
+ return;
15021
+ }
14104
15022
  const result2 = insertFooterItem(column, "/", "New link", null);
14105
15023
  applyLinkByKey(result2.hrefKey, result2.href);
14106
15024
  document.querySelectorAll(`[data-ohw-key="${result2.labelKey}"]`).forEach((el) => {
@@ -14571,6 +15489,8 @@ function OhhwellsBridge() {
14571
15489
  setToolbarRect(anchor.getBoundingClientRect());
14572
15490
  setToolbarShowEditLink(false);
14573
15491
  setActiveCommands(/* @__PURE__ */ new Set());
15492
+ setFloatingPanel(null);
15493
+ setLogoSizeDraft(null);
14574
15494
  }, [deactivate, markSelected]);
14575
15495
  const selectFrame = (0, import_react16.useCallback)((el) => {
14576
15496
  if (!isNavigationContainer(el)) return;
@@ -14620,7 +15540,51 @@ function OhhwellsBridge() {
14620
15540
  setToolbarRect(el.getBoundingClientRect());
14621
15541
  setToolbarShowEditLink(false);
14622
15542
  setActiveCommands(/* @__PURE__ */ new Set());
15543
+ setFloatingPanel(null);
15544
+ setLogoSizeDraft(null);
14623
15545
  }, [deactivate, markSelected, postToParent2]);
15546
+ const selectLogo = (0, import_react16.useCallback)(
15547
+ (logoEl) => {
15548
+ if (activeElRef.current) deactivate();
15549
+ selectedElRef.current = logoEl;
15550
+ selectedHrefKeyRef.current = null;
15551
+ selectedFooterColAttrRef.current = null;
15552
+ markSelected(logoEl);
15553
+ setSelectedIsCta(false);
15554
+ setSelectedIsSocial(false);
15555
+ setSelectedIsSocialsRow(false);
15556
+ clearHrefKeyHover(logoEl);
15557
+ hoveredNavContainerRef.current = null;
15558
+ setHoveredNavContainerRect(null);
15559
+ setHoveredItemRect(null);
15560
+ hoveredItemElRef.current = null;
15561
+ siblingHintElRef.current = null;
15562
+ setSiblingHintRect(null);
15563
+ setSiblingHintRects([]);
15564
+ setIsItemDragging(false);
15565
+ setReorderHrefKey(null);
15566
+ setReorderDragDisabled(false);
15567
+ setIsFooterFrameSelection(false);
15568
+ setToolbarVariant("logo");
15569
+ setToolbarRect(getLogoInteractionRect(logoEl));
15570
+ setToolbarShowEditLink(false);
15571
+ setActiveCommands(/* @__PURE__ */ new Set());
15572
+ },
15573
+ [deactivate, markSelected]
15574
+ );
15575
+ const openLogoSizePanel = (0, import_react16.useCallback)((logoEl) => {
15576
+ const placement = getLogoPlacement(logoEl);
15577
+ const draft = readLogoSizeState(editContentRef.current, placement);
15578
+ setLogoSizeDraft(draft);
15579
+ setParentScrollSnap(parentScrollRef.current);
15580
+ setFloatingPanel({
15581
+ key: `logo-size:${placement}`,
15582
+ title: "Logo",
15583
+ context: placement === "navbar" ? "Navbar" : "Footer",
15584
+ kind: "logo-size",
15585
+ placement
15586
+ });
15587
+ }, []);
14624
15588
  const openSocialsDisplayPanel = (0, import_react16.useCallback)((row) => {
14625
15589
  setParentScrollSnap(parentScrollRef.current);
14626
15590
  setFloatingPanel({
@@ -14656,13 +15620,53 @@ function OhhwellsBridge() {
14656
15620
  );
14657
15621
  const closeFloatingPanelOnly = (0, import_react16.useCallback)(() => {
14658
15622
  setFloatingPanel(null);
15623
+ setLogoSizeDraft(null);
14659
15624
  }, []);
14660
- const closeFloatingPanelOnlyRef = (0, import_react16.useRef)(closeFloatingPanelOnly);
14661
- closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
14662
15625
  const closeFloatingPanelAndDeselect = (0, import_react16.useCallback)(() => {
14663
15626
  setFloatingPanel(null);
15627
+ setLogoSizeDraft(null);
14664
15628
  deselectRef.current();
14665
15629
  }, []);
15630
+ const persistLogoSizeDraft = (0, import_react16.useCallback)(
15631
+ (placement, draft) => {
15632
+ const desktopKey = LOGO_SIZE_DESKTOP_KEYS[placement];
15633
+ const mobileKey = LOGO_SIZE_MOBILE_KEYS[placement];
15634
+ const nodes = [
15635
+ { key: desktopKey, text: String(draft.desktopPx) }
15636
+ ];
15637
+ if (draft.mobileFollowing) {
15638
+ nodes.push({ key: mobileKey, text: "" });
15639
+ } else {
15640
+ nodes.push({ key: mobileKey, text: String(draft.mobilePx) });
15641
+ }
15642
+ editContentRef.current = {
15643
+ ...editContentRef.current,
15644
+ [desktopKey]: String(draft.desktopPx),
15645
+ [mobileKey]: draft.mobileFollowing ? "" : String(draft.mobilePx)
15646
+ };
15647
+ applyLogoSizeToPlacement(
15648
+ placement,
15649
+ draft.desktopPx,
15650
+ draft.mobileFollowing ? draft.desktopPx : draft.mobilePx,
15651
+ draft.mobileFollowing
15652
+ );
15653
+ postToParent2({ type: "ow:change", nodes });
15654
+ requestAnimationFrame(() => {
15655
+ const selected = selectedElRef.current;
15656
+ if (!selected || toolbarVariantRef.current !== "logo") return;
15657
+ const rect = getLogoInteractionRect(selected);
15658
+ setToolbarRect(rect);
15659
+ if (glowElRef.current) {
15660
+ const GAP = SELECTION_CHROME_GAP2;
15661
+ glowElRef.current.style.top = `${rect.top - GAP}px`;
15662
+ glowElRef.current.style.left = `${rect.left - GAP}px`;
15663
+ glowElRef.current.style.width = `${rect.width + GAP * 2}px`;
15664
+ glowElRef.current.style.height = `${rect.height + GAP * 2}px`;
15665
+ }
15666
+ });
15667
+ },
15668
+ [postToParent2]
15669
+ );
14666
15670
  const activate = (0, import_react16.useCallback)((el, options) => {
14667
15671
  if (activeElRef.current === el) return;
14668
15672
  if (isIconEditable(el)) return;
@@ -14743,7 +15747,37 @@ function OhhwellsBridge() {
14743
15747
  deactivateRef.current = deactivate;
14744
15748
  selectRef.current = select;
14745
15749
  selectFrameRef.current = selectFrame;
15750
+ selectLogoRef.current = selectLogo;
15751
+ openLogoSizePanelRef.current = openLogoSizePanel;
14746
15752
  deselectRef.current = deselect;
15753
+ closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
15754
+ const lastSiteWideScopeRef = (0, import_react16.useRef)(null);
15755
+ (0, import_react16.useEffect)(() => {
15756
+ if (!isEditMode) {
15757
+ if (lastSiteWideScopeRef.current !== false) {
15758
+ lastSiteWideScopeRef.current = false;
15759
+ postToParent2({ type: "ow:site-wide-scope", active: false });
15760
+ }
15761
+ return;
15762
+ }
15763
+ const active = isSiteWideScopeActive({
15764
+ selected: selectedElRef.current,
15765
+ hoveredItem: hoveredItemElRef.current,
15766
+ hoveredNavContainer: hoveredNavContainerRef.current,
15767
+ active: activeElRef.current
15768
+ });
15769
+ if (lastSiteWideScopeRef.current === active) return;
15770
+ lastSiteWideScopeRef.current = active;
15771
+ postToParent2({ type: "ow:site-wide-scope", active });
15772
+ }, [
15773
+ isEditMode,
15774
+ hoveredItemRect,
15775
+ hoveredNavContainerRect,
15776
+ toolbarVariant,
15777
+ toolbarRect,
15778
+ isFooterFrameSelection,
15779
+ postToParent2
15780
+ ]);
14747
15781
  (0, import_react16.useLayoutEffect)(() => {
14748
15782
  if (!subdomain || isEditMode) {
14749
15783
  setFetchState("done");
@@ -14755,9 +15789,23 @@ function OhhwellsBridge() {
14755
15789
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
14756
15790
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
14757
15791
  }
15792
+ if (typeof content[BRAND_KIT_KEY] === "string") {
15793
+ brandKitRef.current = content[BRAND_KIT_KEY];
15794
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
15795
+ }
15796
+ if (typeof content[STYLE_STORE_KEY] === "string") {
15797
+ stylesRef.current = content[STYLE_STORE_KEY];
15798
+ applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
15799
+ }
15800
+ applyBrandChrome(content);
14758
15801
  for (const [key, val] of Object.entries(content)) {
14759
15802
  if (key === "__ohw_sections") continue;
14760
15803
  if (key === AI_SECTIONS_KEY) continue;
15804
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
15805
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
15806
+ if (key === BRAND_KIT_KEY) continue;
15807
+ if (key === STYLE_STORE_KEY) continue;
15808
+ if (BRAND_CHROME_KEYS.has(key)) continue;
14761
15809
  if (applyVideoSettingNode(key, val)) continue;
14762
15810
  if (applyCarouselNode(key, val)) continue;
14763
15811
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -14792,6 +15840,8 @@ function OhhwellsBridge() {
14792
15840
  });
14793
15841
  applyLinkByKey(key, val);
14794
15842
  }
15843
+ applyLogoFromContent(content);
15844
+ applyLogoSizes(content);
14795
15845
  reconcileNavbarItemsFromContent(content);
14796
15846
  reconcileFooterOrderFromContent(content);
14797
15847
  reconcileSocialsFromContent(content);
@@ -14812,7 +15862,9 @@ function OhhwellsBridge() {
14812
15862
  let cancelled = false;
14813
15863
  setFetchState("loading");
14814
15864
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
14815
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
15865
+ const initialPath = pathname;
15866
+ fetchedContentPaths.add(`${subdomain}::${initialPath}`);
15867
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
14816
15868
  if (cancelled) return;
14817
15869
  const content = data?.content ?? {};
14818
15870
  contentCache.set(subdomain, content);
@@ -14836,8 +15888,21 @@ function OhhwellsBridge() {
14836
15888
  initSectionInstancesFromContent(content, window.location.pathname);
14837
15889
  observer?.disconnect();
14838
15890
  try {
15891
+ applyBrandChrome(content);
15892
+ if (typeof content[BRAND_KIT_KEY] === "string") {
15893
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
15894
+ }
15895
+ if (typeof content[STYLE_STORE_KEY] === "string") {
15896
+ applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
15897
+ }
14839
15898
  for (const [key, val] of Object.entries(content)) {
14840
15899
  if (key === "__ohw_sections") continue;
15900
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
15901
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
15902
+ if (key === BRAND_KIT_KEY) continue;
15903
+ if (key === STYLE_STORE_KEY) continue;
15904
+ if (key === STYLE_STORE_KEY) continue;
15905
+ if (BRAND_CHROME_KEYS.has(key)) continue;
14841
15906
  if (applyVideoSettingNode(key, val)) continue;
14842
15907
  if (applyCarouselNode(key, val)) continue;
14843
15908
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -14858,6 +15923,7 @@ function OhhwellsBridge() {
14858
15923
  });
14859
15924
  applyLinkByKey(key, val);
14860
15925
  }
15926
+ applyLogoFromContent(content);
14861
15927
  reconcileNavbarItemsFromContent(content);
14862
15928
  reconcileFooterOrderFromContent(content);
14863
15929
  reconcileSocialsFromContent(content);
@@ -14872,6 +15938,17 @@ function OhhwellsBridge() {
14872
15938
  debounceTimer = setTimeout(applyFromCache, 150);
14873
15939
  };
14874
15940
  applyFromCache();
15941
+ const pathCacheKey = `${subdomain}::${pathname}`;
15942
+ if (!fetchedContentPaths.has(pathCacheKey)) {
15943
+ fetchedContentPaths.add(pathCacheKey);
15944
+ const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
15945
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
15946
+ if (!data?.content) return;
15947
+ contentCache.set(subdomain, data.content);
15948
+ applyFromCache();
15949
+ }).catch(() => {
15950
+ });
15951
+ }
14875
15952
  observer = new MutationObserver(scheduleApply);
14876
15953
  observer.observe(document.body, { childList: true, subtree: true });
14877
15954
  return () => {
@@ -14965,26 +16042,31 @@ function OhhwellsBridge() {
14965
16042
  }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
14966
16043
  (0, import_react16.useEffect)(() => {
14967
16044
  if (!isEditMode) return;
16045
+ let lastPosted = 0;
14968
16046
  const measure = () => {
14969
16047
  const h = document.body.scrollHeight;
14970
- if (h > 50) postToParent2({ type: "ow:height", height: h });
16048
+ if (h > 50 && Math.abs(h - lastPosted) > 1) {
16049
+ lastPosted = h;
16050
+ postToParent2({ type: "ow:height", height: h });
16051
+ }
16052
+ };
16053
+ let raf = null;
16054
+ const schedule = () => {
16055
+ if (raf != null) return;
16056
+ raf = requestAnimationFrame(() => {
16057
+ raf = null;
16058
+ measure();
16059
+ });
14971
16060
  };
14972
16061
  const t1 = setTimeout(measure, 50);
14973
16062
  const t2 = setTimeout(measure, 500);
14974
- let lastWidth = window.innerWidth;
14975
- let resizeTimer = null;
14976
- const handleResize = () => {
14977
- if (window.innerWidth === lastWidth) return;
14978
- lastWidth = window.innerWidth;
14979
- if (resizeTimer) clearTimeout(resizeTimer);
14980
- resizeTimer = setTimeout(measure, 150);
14981
- };
14982
- window.addEventListener("resize", handleResize);
16063
+ const ro = new ResizeObserver(schedule);
16064
+ ro.observe(document.body);
14983
16065
  return () => {
14984
16066
  clearTimeout(t1);
14985
16067
  clearTimeout(t2);
14986
- if (resizeTimer) clearTimeout(resizeTimer);
14987
- window.removeEventListener("resize", handleResize);
16068
+ if (raf != null) cancelAnimationFrame(raf);
16069
+ ro.disconnect();
14988
16070
  };
14989
16071
  }, [pathname, isEditMode, postToParent2]);
14990
16072
  (0, import_react16.useEffect)(() => {
@@ -15054,9 +16136,12 @@ function OhhwellsBridge() {
15054
16136
  [data-ohw-editable="video"], [data-ohw-editable="video"] *,
15055
16137
  [data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
15056
16138
  [data-ohw-editable="link"], [data-ohw-editable="link"] * { cursor: pointer !important; }
16139
+ /* Text hover chrome is drawn by the overlay (see hoveredTextRect) \u2014 the CSS outline
16140
+ that used to draw it dashes denser than the overlay border, so identical specs
16141
+ still read as two different frames (OHH-695). The attribute stays: hover paths
16142
+ and suppression rules key off it. */
15057
16143
  [data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]) {
15058
- outline: 2px dashed ${PRIMARY3} !important;
15059
- outline-offset: 4px;
16144
+ outline: none !important;
15060
16145
  }
15061
16146
  [data-ohw-href-key] [data-ohw-hovered],
15062
16147
  [data-ohw-href-key][data-ohw-hovered],
@@ -15109,8 +16194,11 @@ function OhhwellsBridge() {
15109
16194
  stateViews.textContent = `
15110
16195
  [data-ohw-state-view]:not([data-ohw-state-view="default"]) { display: none; }
15111
16196
  [data-ohw-state-view="default"] [data-ohw-editable] { pointer-events: auto !important; }
15112
- [data-ohw-state-hovered] { outline: 2px dashed ${PRIMARY3} !important; outline-offset: 4px; }
16197
+ [data-ohw-state-hovered] { outline: ${HOVER_STROKE_WIDTH}px dashed ${CHROME_PRIMARY} !important; outline-offset: ${HOVER_CHROME_GAP}px; }
15113
16198
  [data-ohw-state-hovered]:has([data-ohw-hovered]) { outline: none !important; }
16199
+ /* :has() only sees descendants \u2014 when the card itself is the hovered text, the overlay
16200
+ already frames it, and the card outline doubled it (OHH-695). */
16201
+ [data-ohw-state-hovered][data-ohw-hovered] { outline: none !important; }
15114
16202
  `;
15115
16203
  document.head.appendChild(base);
15116
16204
  document.head.appendChild(forceHover);
@@ -15128,10 +16216,12 @@ function OhhwellsBridge() {
15128
16216
  return;
15129
16217
  }
15130
16218
  const target = e.target;
16219
+ if (target.closest("[data-ohw-ai-review]")) return;
15131
16220
  if (target.closest("[data-ohw-toolbar]")) return;
15132
16221
  if (target.closest("[data-ohw-state-toggle]")) return;
15133
16222
  if (target.closest("[data-ohw-max-badge]")) return;
15134
16223
  if (isInsideLinkEditor(target)) return;
16224
+ if (isInsideFloatingPanel(target)) return;
15135
16225
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
15136
16226
  const beneath = document.elementsFromPoint(e.clientX, e.clientY).find(
15137
16227
  (el) => el instanceof HTMLElement && !el.closest("[data-ohw-bridge-root]") && el.closest("[data-ohw-section]") != null
@@ -15195,6 +16285,21 @@ function OhhwellsBridge() {
15195
16285
  return;
15196
16286
  }
15197
16287
  }
16288
+ const logoEl = getLogoElement(target);
16289
+ if (logoEl) {
16290
+ e.preventDefault();
16291
+ e.stopPropagation();
16292
+ if (!logoHasUploadedImage(logoEl)) {
16293
+ deselectRef.current();
16294
+ deactivateRef.current();
16295
+ const identity = readLogoIdentityFromDom();
16296
+ postToParentRef.current({ type: "ow:open-logo-settings", ...identity });
16297
+ return;
16298
+ }
16299
+ selectLogoRef.current(logoEl);
16300
+ openLogoSizePanelRef.current(logoEl);
16301
+ return;
16302
+ }
15198
16303
  const editable = target.closest("[data-ohw-editable]");
15199
16304
  if (editable) {
15200
16305
  if (editable.dataset.ohwEditable === "link") {
@@ -15347,10 +16452,12 @@ function OhhwellsBridge() {
15347
16452
  };
15348
16453
  const handleDblClick = (e) => {
15349
16454
  const target = e.target;
16455
+ if (target.closest("[data-ohw-ai-review]")) return;
15350
16456
  if (target.closest("[data-ohw-toolbar]")) return;
15351
16457
  if (target.closest("[data-ohw-state-toggle]")) return;
15352
16458
  if (target.closest("[data-ohw-max-badge]")) return;
15353
16459
  if (isInsideLinkEditor(target)) return;
16460
+ if (isInsideFloatingPanel(target)) return;
15354
16461
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
15355
16462
  return;
15356
16463
  }
@@ -15378,6 +16485,16 @@ function OhhwellsBridge() {
15378
16485
  setHoveredNavContainerRect(null);
15379
16486
  return;
15380
16487
  }
16488
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || isInsideFloatingPanel(target) || isPointOverFloatingPanel(e.clientX, e.clientY)) {
16489
+ hoveredItemElRef.current = null;
16490
+ setHoveredItemRect(null);
16491
+ hoveredNavContainerRef.current = null;
16492
+ setHoveredNavContainerRect(null);
16493
+ siblingHintElRef.current = null;
16494
+ setSiblingHintRect(null);
16495
+ setSiblingHintRects([]);
16496
+ return;
16497
+ }
15381
16498
  {
15382
16499
  const selected2 = selectedElRef.current;
15383
16500
  const selectedIsFooterColumn = Boolean(selected2) && !isFooterLinksContainer(selected2) && (selected2.hasAttribute("data-ohw-footer-col") || selected2.hasAttribute("data-ohw-footer-column") || Boolean(selected2.closest("footer") && isInferredFooterGroup2(selected2)));
@@ -15385,7 +16502,7 @@ function OhhwellsBridge() {
15385
16502
  const allowFooterLinksHover = toolbarVariantRef.current !== "select-frame" || selectedIsFooterColumn;
15386
16503
  if (allowNavContainerHover) {
15387
16504
  const navContainer = target.closest("[data-ohw-nav-container]");
15388
- if (navContainer && !getNavigationItemAnchor(target)) {
16505
+ if (navContainer && !getNavigationItemAnchor(target) && !getLogoElement(target)) {
15389
16506
  hoveredNavContainerRef.current = navContainer;
15390
16507
  setHoveredNavContainerRect(navContainer.getBoundingClientRect());
15391
16508
  hoveredItemElRef.current = null;
@@ -15414,6 +16531,15 @@ function OhhwellsBridge() {
15414
16531
  setHoveredNavContainerRect(null);
15415
16532
  }
15416
16533
  }
16534
+ const logoEl = getLogoElement(target);
16535
+ if (logoEl) {
16536
+ hoveredNavContainerRef.current = null;
16537
+ setHoveredNavContainerRect(null);
16538
+ if (selectedElRef.current === logoEl) return;
16539
+ hoveredItemElRef.current = logoEl;
16540
+ setHoveredItemRect(getLogoInteractionRect(logoEl));
16541
+ return;
16542
+ }
15417
16543
  const navAnchor = getNavigationItemAnchor(target);
15418
16544
  if (navAnchor) {
15419
16545
  hoveredNavContainerRef.current = null;
@@ -15451,6 +16577,11 @@ function OhhwellsBridge() {
15451
16577
  setHoveredItemRect(hoverTarget.getBoundingClientRect());
15452
16578
  } else if (!isInsideNavigationItem(editable)) {
15453
16579
  hoverTarget.setAttribute("data-ohw-hovered", "");
16580
+ if (editable.closest("footer") || editable.closest('[data-ohw-section="footer"]')) {
16581
+ hoveredNavContainerRef.current = null;
16582
+ setHoveredNavContainerRect(null);
16583
+ hoveredItemElRef.current = editable;
16584
+ }
15454
16585
  }
15455
16586
  }
15456
16587
  };
@@ -15486,6 +16617,18 @@ function OhhwellsBridge() {
15486
16617
  }
15487
16618
  return;
15488
16619
  }
16620
+ const logoEl = getLogoElement(target);
16621
+ if (logoEl) {
16622
+ const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
16623
+ if (related2 && (logoEl === related2 || logoEl.contains(related2) || related2.closest?.('[data-ohw-role="logo"], [data-ohw-logo]'))) {
16624
+ return;
16625
+ }
16626
+ if (hoveredItemElRef.current === logoEl) {
16627
+ hoveredItemElRef.current = null;
16628
+ setHoveredItemRect(null);
16629
+ }
16630
+ return;
16631
+ }
15489
16632
  const editable = target.closest("[data-ohw-editable]");
15490
16633
  if (!editable) return;
15491
16634
  const related = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -15506,6 +16649,13 @@ function OhhwellsBridge() {
15506
16649
  }
15507
16650
  } else {
15508
16651
  hoverTarget.removeAttribute("data-ohw-hovered");
16652
+ if (hoveredItemElRef.current === editable) {
16653
+ const stillOnEditable = related instanceof Element && related.closest("[data-ohw-editable]") === editable;
16654
+ if (!stillOnEditable) {
16655
+ hoveredItemElRef.current = null;
16656
+ setHoveredItemRect(null);
16657
+ }
16658
+ }
15509
16659
  }
15510
16660
  }
15511
16661
  };
@@ -15622,6 +16772,26 @@ function OhhwellsBridge() {
15622
16772
  hoveredNavContainerRef.current = null;
15623
16773
  setHoveredNavContainerRect(null);
15624
16774
  }
16775
+ const logoCandidates = [
16776
+ ...document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]'),
16777
+ ...document.querySelectorAll("nav a:not([data-ohw-href-key]), [data-ohw-nav-root] a:not([data-ohw-href-key])"),
16778
+ ...document.querySelectorAll("footer img")
16779
+ ];
16780
+ const seenLogos = /* @__PURE__ */ new Set();
16781
+ for (const candidate of logoCandidates) {
16782
+ const logo = getLogoElement(candidate);
16783
+ if (!logo || seenLogos.has(logo)) continue;
16784
+ seenLogos.add(logo);
16785
+ const r2 = logo.getBoundingClientRect();
16786
+ if (x < r2.left || x > r2.right || y < r2.top || y > r2.bottom) continue;
16787
+ hoveredNavContainerRef.current = null;
16788
+ setHoveredNavContainerRect(null);
16789
+ if (selectedElRef.current !== logo) {
16790
+ hoveredItemElRef.current = logo;
16791
+ setHoveredItemRect(getLogoInteractionRect(logo));
16792
+ }
16793
+ return;
16794
+ }
15625
16795
  const navContainers = Array.from(
15626
16796
  document.querySelectorAll("[data-ohw-nav-container]")
15627
16797
  );
@@ -15707,7 +16877,7 @@ function OhhwellsBridge() {
15707
16877
  }
15708
16878
  };
15709
16879
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
15710
- if (linkPopoverOpenRef.current) {
16880
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
15711
16881
  if (hoveredImageRef.current) {
15712
16882
  hoveredImageRef.current = null;
15713
16883
  hoveredImageHasTextOverlapRef.current = false;
@@ -15961,7 +17131,7 @@ function OhhwellsBridge() {
15961
17131
  }
15962
17132
  };
15963
17133
  const probeHoverCardsAt = (clientX, clientY, fromParentViewport = false) => {
15964
- if (linkPopoverOpenRef.current || document.documentElement.hasAttribute("data-ohw-section-picking")) {
17134
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || document.documentElement.hasAttribute("data-ohw-section-picking")) {
15965
17135
  if (activeStateElRef.current) {
15966
17136
  activeStateElRef.current.removeAttribute("data-ohw-state-hovered");
15967
17137
  activeStateElRef.current = null;
@@ -16029,6 +17199,19 @@ function OhhwellsBridge() {
16029
17199
  };
16030
17200
  const handleMouseMove = (e) => {
16031
17201
  const { clientX, clientY } = e;
17202
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
17203
+ hoveredItemElRef.current = null;
17204
+ setHoveredItemRect(null);
17205
+ hoveredNavContainerRef.current = null;
17206
+ setHoveredNavContainerRect(null);
17207
+ siblingHintElRef.current = null;
17208
+ setSiblingHintRect(null);
17209
+ setSiblingHintRects([]);
17210
+ dismissImageHover();
17211
+ clearImageHover();
17212
+ setSectionGap(null);
17213
+ return;
17214
+ }
16032
17215
  probeSectionGapAt(clientX, clientY);
16033
17216
  probeImageAt(clientX, clientY);
16034
17217
  probeHoverCardsAt(clientX, clientY);
@@ -16037,6 +17220,11 @@ function OhhwellsBridge() {
16037
17220
  if (e.data?.type !== "ow:pointer-sync") return;
16038
17221
  const { clientX, clientY } = e.data;
16039
17222
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
17223
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
17224
+ dismissImageHover();
17225
+ clearImageHover();
17226
+ return;
17227
+ }
16040
17228
  probeSectionGapAt(clientX, clientY);
16041
17229
  probeImageAt(clientX, clientY);
16042
17230
  probeHoverCardsAt(clientX, clientY);
@@ -16286,6 +17474,15 @@ function OhhwellsBridge() {
16286
17474
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
16287
17475
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
16288
17476
  }
17477
+ if (typeof content[BRAND_KIT_KEY] === "string") {
17478
+ brandKitRef.current = content[BRAND_KIT_KEY];
17479
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17480
+ }
17481
+ if (typeof content[STYLE_STORE_KEY] === "string") {
17482
+ stylesRef.current = content[STYLE_STORE_KEY];
17483
+ applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
17484
+ }
17485
+ applyBrandChrome(content);
16289
17486
  let sectionsJson = null;
16290
17487
  for (const [key, val] of Object.entries(content)) {
16291
17488
  if (key === "__ohw_sections") {
@@ -16293,6 +17490,11 @@ function OhhwellsBridge() {
16293
17490
  continue;
16294
17491
  }
16295
17492
  if (key === AI_SECTIONS_KEY) continue;
17493
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
17494
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
17495
+ if (key === BRAND_KIT_KEY) continue;
17496
+ if (key === STYLE_STORE_KEY) continue;
17497
+ if (BRAND_CHROME_KEYS.has(key)) continue;
16296
17498
  if (applyVideoSettingNode(key, val)) continue;
16297
17499
  if (applyCarouselNode(key, val)) continue;
16298
17500
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -16312,6 +17514,8 @@ function OhhwellsBridge() {
16312
17514
  });
16313
17515
  applyLinkByKey(key, val);
16314
17516
  }
17517
+ applyLogoFromContent(content);
17518
+ applyLogoSizes(content);
16315
17519
  if (sectionsJson) {
16316
17520
  initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
16317
17521
  sectionsLoadedRef.current = true;
@@ -16327,6 +17531,58 @@ function OhhwellsBridge() {
16327
17531
  if (hydratedHeight > 50) postToParentRef.current({ type: "ow:height", height: hydratedHeight });
16328
17532
  postToParentRef.current({ type: "ow:hydrate-done" });
16329
17533
  };
17534
+ const handleUpdateLogoIdentity = (e) => {
17535
+ if (e.data?.type !== "ow:update-logo-identity") return;
17536
+ const rawText = typeof e.data.text === "string" ? e.data.text : "";
17537
+ const alt = typeof e.data.alt === "string" ? e.data.alt : rawText;
17538
+ const href = typeof e.data.href === "string" ? e.data.href : void 0;
17539
+ const imageProvided = "image" in e.data;
17540
+ const imageUrl = imageProvided && typeof e.data.image === "string" && e.data.image.trim() ? e.data.image.trim() : imageProvided ? null : void 0;
17541
+ let isPlaceholder = e.data.isPlaceholder !== false;
17542
+ if (imageUrl) isPlaceholder = false;
17543
+ else if (imageProvided && imageUrl === null) {
17544
+ isPlaceholder = e.data.isPlaceholder === true || !rawText.trim() || resolveLogoDisplayText(rawText) === PLACEHOLDER_BUSINESS_NAME;
17545
+ }
17546
+ const display = applyLogoIdentity(rawText, isPlaceholder);
17547
+ const displayAlt = resolveLogoDisplayText(alt || display);
17548
+ if (imageUrl !== void 0) {
17549
+ applyLogoImage(imageUrl, displayAlt);
17550
+ } else {
17551
+ for (const key of LOGO_IMAGE_KEYS) {
17552
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
17553
+ const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
17554
+ if (img) img.alt = displayAlt;
17555
+ });
17556
+ }
17557
+ }
17558
+ if (href !== void 0) {
17559
+ applyLogoHref(href);
17560
+ applyLinkByKey("nav-logo-href", href);
17561
+ applyLinkByKey("footer-logo-href", href);
17562
+ applyLinkByKey("logo-href", href);
17563
+ }
17564
+ const nodes = [
17565
+ ...LOGO_TEXT_KEYS.map((key) => ({ key, text: display })),
17566
+ { key: LOGO_PLACEHOLDER_KEY, text: isPlaceholder ? "true" : "false" },
17567
+ { key: LOGO_ALT_KEY, text: displayAlt }
17568
+ ];
17569
+ if (imageUrl !== void 0) {
17570
+ if (imageUrl) {
17571
+ for (const key of LOGO_IMAGE_KEYS) nodes.push({ key, text: imageUrl });
17572
+ } else {
17573
+ for (const key of LOGO_IMAGE_KEYS) nodes.push({ key, text: "" });
17574
+ }
17575
+ }
17576
+ if (href !== void 0) {
17577
+ for (const key of LOGO_HREF_KEYS) nodes.push({ key, text: href.trim() || "/" });
17578
+ }
17579
+ editContentRef.current = {
17580
+ ...editContentRef.current,
17581
+ ...Object.fromEntries(nodes.map(({ key, text }) => [key, text]))
17582
+ };
17583
+ applyLogoSizes(editContentRef.current);
17584
+ postToParentRef.current({ type: "ow:change", nodes });
17585
+ };
16330
17586
  window.addEventListener("message", handleHydrate);
16331
17587
  const postAiSectionsChanged = () => {
16332
17588
  postToParentRef.current({
@@ -16340,7 +17596,10 @@ function OhhwellsBridge() {
16340
17596
  const payload = e.data.payload;
16341
17597
  if (!payload || typeof payload.id !== "string" || !isRenderableTree(payload.tree)) return;
16342
17598
  const previous = aiSectionsRef.current;
16343
- const nextState = applyTreeToState(parseAiSectionsState(previous), payload);
17599
+ const nextState = applyTreeToState(parseAiSectionsState(previous), {
17600
+ ...payload,
17601
+ path: payload.path ?? window.location.pathname
17602
+ });
16344
17603
  const nextValue = serializeAiSectionsState(nextState);
16345
17604
  aiSectionsRef.current = nextValue;
16346
17605
  applyAiSectionsToDom(nextState);
@@ -16377,12 +17636,42 @@ function OhhwellsBridge() {
16377
17636
  const value = typeof e.data.value === "string" ? e.data.value : "";
16378
17637
  aiSectionsRef.current = value;
16379
17638
  applyAiSectionsToDom(parseAiSectionsState(value));
17639
+ applyStylesToDom(parseStyleStore(stylesRef.current));
16380
17640
  const restoredHeight = document.documentElement.scrollHeight;
16381
17641
  if (restoredHeight > 50) postToParentRef.current({ type: "ow:height", height: restoredHeight });
16382
17642
  postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: value }] });
16383
17643
  postAiSectionsChanged();
16384
17644
  };
16385
17645
  window.addEventListener("message", handleAiSetSections);
17646
+ const handleAiSetBrand = (e) => {
17647
+ if (e.data?.type !== "ow:ai-set-brand") return;
17648
+ const value = typeof e.data.value === "string" ? e.data.value : "";
17649
+ const previous = brandKitRef.current;
17650
+ brandKitRef.current = value;
17651
+ applyBrandToDom(parseBrandKit(value));
17652
+ if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
17653
+ applyStylesToDom(parseStyleStore(stylesRef.current));
17654
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
17655
+ postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
17656
+ };
17657
+ window.addEventListener("message", handleAiSetBrand);
17658
+ const handleAiSetStyles = (e) => {
17659
+ if (e.data?.type !== "ow:ai-set-styles") return;
17660
+ const value = typeof e.data.value === "string" ? e.data.value : "";
17661
+ const previous = stylesRef.current;
17662
+ stylesRef.current = value;
17663
+ applyStylesToDom(parseStyleStore(value));
17664
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
17665
+ postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
17666
+ };
17667
+ window.addEventListener("message", handleAiSetStyles);
17668
+ const handleGetBrand = (e) => {
17669
+ if (e.data?.type !== "ow:get-brand") return;
17670
+ const template = deriveTemplateBrand();
17671
+ const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
17672
+ postToParentRef.current({ type: "ow:brand-value", value });
17673
+ };
17674
+ window.addEventListener("message", handleGetBrand);
16386
17675
  const handleDeactivate = (e) => {
16387
17676
  if (e.data?.type !== "ow:deactivate") return;
16388
17677
  if (Date.now() < linkPopoverGraceUntilRef.current) return;
@@ -16391,6 +17680,12 @@ function OhhwellsBridge() {
16391
17680
  closeLinkPopoverRef.current();
16392
17681
  return;
16393
17682
  }
17683
+ if (floatingPanelOpenRef.current) {
17684
+ setFloatingPanelRef.current(null);
17685
+ deselectRef.current();
17686
+ deactivateRef.current();
17687
+ return;
17688
+ }
16394
17689
  deselectRef.current();
16395
17690
  deactivateRef.current();
16396
17691
  };
@@ -16444,6 +17739,10 @@ function OhhwellsBridge() {
16444
17739
  return;
16445
17740
  }
16446
17741
  if (selectedElRef.current) {
17742
+ if (toolbarVariantRef.current === "logo") {
17743
+ deselectRef.current();
17744
+ return;
17745
+ }
16447
17746
  if (toolbarVariantRef.current === "select-frame") {
16448
17747
  const parent2 = getNavigationSelectionParent(selectedElRef.current);
16449
17748
  if (parent2 && isNavigationContainer(parent2) && isFooterLinksContainer(parent2) && !isFooterLinksContainer(selectedElRef.current)) {
@@ -16483,6 +17782,10 @@ function OhhwellsBridge() {
16483
17782
  return;
16484
17783
  }
16485
17784
  if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
17785
+ if (toolbarVariantRef.current === "logo") {
17786
+ deselectRef.current();
17787
+ return;
17788
+ }
16486
17789
  if (toolbarVariantRef.current === "select-frame") {
16487
17790
  const parent2 = getNavigationSelectionParent(selectedElRef.current);
16488
17791
  if (parent2 && isNavigationContainer(parent2) && isFooterLinksContainer(parent2) && !isFooterLinksContainer(selectedElRef.current)) {
@@ -16560,7 +17863,8 @@ function OhhwellsBridge() {
16560
17863
  const handleScroll = () => {
16561
17864
  const focusEl = activeElRef.current ?? selectedElRef.current;
16562
17865
  if (focusEl) {
16563
- const r2 = activeElRef.current ? getEditMeasureEl(activeElRef.current).getBoundingClientRect() : focusEl.getBoundingClientRect();
17866
+ const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : toolbarVariantRef.current === "logo" ? null : focusEl;
17867
+ const r2 = measureEl ? measureEl.getBoundingClientRect() : toolbarVariantRef.current === "logo" ? getLogoInteractionRect(focusEl) : focusEl.getBoundingClientRect();
16564
17868
  applyToolbarPos(r2);
16565
17869
  setToolbarRect(r2);
16566
17870
  setMaxBadge((prev) => prev && activeElRef.current ? { ...prev, rect: r2 } : prev);
@@ -16570,7 +17874,9 @@ function OhhwellsBridge() {
16570
17874
  setToggleState((prev) => prev ? { ...prev, rect } : null);
16571
17875
  }
16572
17876
  if (hoveredItemElRef.current) {
16573
- setHoveredItemRect(hoveredItemElRef.current.getBoundingClientRect());
17877
+ const hoverEl = hoveredItemElRef.current;
17878
+ const logo = getLogoElement(hoverEl);
17879
+ setHoveredItemRect(logo ? getLogoInteractionRect(logo) : hoverEl.getBoundingClientRect());
16574
17880
  }
16575
17881
  if (hoveredNavContainerRef.current) {
16576
17882
  setHoveredNavContainerRect(hoveredNavContainerRef.current.getBoundingClientRect());
@@ -16614,6 +17920,12 @@ function OhhwellsBridge() {
16614
17920
  if (aiSectionsRef.current) {
16615
17921
  nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
16616
17922
  }
17923
+ if (stylesRef.current) {
17924
+ nodes.push({ key: STYLE_STORE_KEY, type: "styles", text: stylesRef.current });
17925
+ }
17926
+ if (brandKitRef.current) {
17927
+ nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
17928
+ }
16617
17929
  postToParentRef.current({ type: "ow:save-result", nodes });
16618
17930
  };
16619
17931
  const handleInsertSection = (e) => {
@@ -16624,8 +17936,12 @@ function OhhwellsBridge() {
16624
17936
  if (inserted) {
16625
17937
  const tracker = getSectionsTracker();
16626
17938
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
16627
- const h = document.documentElement.scrollHeight;
16628
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
17939
+ const reportHeight = () => {
17940
+ const h = document.body.scrollHeight;
17941
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
17942
+ };
17943
+ reportHeight();
17944
+ setTimeout(reportHeight, 500);
16629
17945
  }
16630
17946
  };
16631
17947
  const handleSwitchSchedule = (e) => {
@@ -16818,13 +18134,17 @@ function OhhwellsBridge() {
16818
18134
  if (e.data?.type !== "ow:parent-scroll") return;
16819
18135
  const { iframeOffsetTop, headerH, canvasH } = e.data;
16820
18136
  parentScrollRef.current = { iframeOffsetTop, headerH, canvasH };
18137
+ if (floatingPanelOpenRef.current) {
18138
+ setParentScrollSnap({ iframeOffsetTop, headerH, canvasH });
18139
+ }
16821
18140
  if (visibleViewportRef.current) {
16822
18141
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
16823
18142
  }
16824
18143
  const focusEl = activeElRef.current ?? selectedElRef.current;
16825
18144
  if (focusEl) {
16826
- const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : focusEl;
16827
- applyToolbarPos(measureEl.getBoundingClientRect());
18145
+ const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : toolbarVariantRef.current === "logo" ? null : focusEl;
18146
+ const r2 = measureEl ? measureEl.getBoundingClientRect() : toolbarVariantRef.current === "logo" ? getLogoInteractionRect(focusEl) : focusEl.getBoundingClientRect();
18147
+ applyToolbarPos(r2);
16828
18148
  }
16829
18149
  };
16830
18150
  const handleClickAt = (e) => {
@@ -16849,6 +18169,25 @@ function OhhwellsBridge() {
16849
18169
  postToParentRef.current({ type: "ow:image-pick", key: stateCardImage.dataset.ohwKey ?? "", elementType: stateCardImage.dataset.ohwEditable ?? "image" });
16850
18170
  return;
16851
18171
  }
18172
+ const logoAtPoint = Array.from(
18173
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]')
18174
+ ).map((el) => getLogoElement(el)).find((logo) => {
18175
+ if (!logo) return false;
18176
+ const r2 = logo.getBoundingClientRect();
18177
+ return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
18178
+ });
18179
+ if (logoAtPoint) {
18180
+ if (!logoHasUploadedImage(logoAtPoint)) {
18181
+ deselectRef.current();
18182
+ deactivateRef.current();
18183
+ const identity = readLogoIdentityFromDom();
18184
+ postToParentRef.current({ type: "ow:open-logo-settings", ...identity });
18185
+ return;
18186
+ }
18187
+ selectLogoRef.current(logoAtPoint);
18188
+ openLogoSizePanelRef.current(logoAtPoint);
18189
+ return;
18190
+ }
16852
18191
  const textEditable = Array.from(
16853
18192
  document.querySelectorAll(NON_MEDIA_SELECTOR)
16854
18193
  ).find((el) => {
@@ -16920,6 +18259,14 @@ function OhhwellsBridge() {
16920
18259
  window.addEventListener("message", handleParentScroll);
16921
18260
  window.addEventListener("message", handlePointerSync);
16922
18261
  window.addEventListener("message", handleClickAt);
18262
+ window.addEventListener("message", handleUpdateLogoIdentity);
18263
+ const handleViewMode = (e) => {
18264
+ if (e.data?.type !== "ow:view-mode") return;
18265
+ const mode = e.data.mode === "Mobile" || e.data.mode === "mobile" ? "mobile" : "desktop";
18266
+ setEditorViewport(mode);
18267
+ applyLogoSizes(editContentRef.current);
18268
+ };
18269
+ window.addEventListener("message", handleViewMode);
16923
18270
  const handleViewportResize = () => {
16924
18271
  if (visibleViewportRef.current) {
16925
18272
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
@@ -16975,10 +18322,15 @@ function OhhwellsBridge() {
16975
18322
  window.removeEventListener("resize", handleViewportResize);
16976
18323
  window.removeEventListener("message", handlePointerSync);
16977
18324
  window.removeEventListener("message", handleClickAt);
18325
+ window.removeEventListener("message", handleUpdateLogoIdentity);
18326
+ window.removeEventListener("message", handleViewMode);
16978
18327
  window.removeEventListener("message", handleHydrate);
16979
18328
  window.removeEventListener("message", handleAiApplyTree);
16980
18329
  window.removeEventListener("message", handleAiDeleteSection);
16981
18330
  window.removeEventListener("message", handleAiSetSections);
18331
+ window.removeEventListener("message", handleAiSetBrand);
18332
+ window.removeEventListener("message", handleAiSetStyles);
18333
+ window.removeEventListener("message", handleGetBrand);
16982
18334
  window.removeEventListener("message", handleDeactivate);
16983
18335
  window.removeEventListener("message", handleToastAction);
16984
18336
  window.removeEventListener("message", handleUiEscape);
@@ -17004,7 +18356,7 @@ function OhhwellsBridge() {
17004
18356
  if (footerDragRef.current) return;
17005
18357
  const target = e.target;
17006
18358
  if (!target) return;
17007
- if (target.closest('[data-ohw-drag-handle-container], [data-slot="drag-handle"], [data-ohw-toolbar], [data-ohw-item-toolbar-anchor], [data-ohw-link-popover-root]')) {
18359
+ if (target.closest('[data-ohw-drag-handle-container], [data-slot="drag-handle"], [data-ohw-toolbar], [data-ohw-item-toolbar-anchor], [data-ohw-link-popover-root], [data-ohw-floating-panel]')) {
17008
18360
  return;
17009
18361
  }
17010
18362
  if (target.closest("[data-ohw-item-drag-surface]")) return;
@@ -17182,7 +18534,7 @@ function OhhwellsBridge() {
17182
18534
  postToParent2({
17183
18535
  type: "ow:ready",
17184
18536
  version: "1",
17185
- bridgeVersion: "0.1.58",
18537
+ bridgeVersion: "0.1.60",
17186
18538
  path: pathname,
17187
18539
  nodes: collectEditableNodes(editContentRef.current),
17188
18540
  sections
@@ -17577,10 +18929,10 @@ function OhhwellsBridge() {
17577
18929
  [postToParent2]
17578
18930
  );
17579
18931
  return bridgeRoot ? (0, import_react_dom4.createPortal)(
17580
- /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_jsx_runtime32.Fragment, { children: [
17581
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
17582
- isEditMode && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
17583
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
18932
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
18933
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
18934
+ isEditMode && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
18935
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17584
18936
  MediaOverlay,
17585
18937
  {
17586
18938
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -17591,7 +18943,7 @@ function OhhwellsBridge() {
17591
18943
  },
17592
18944
  `uploading-${key}`
17593
18945
  )),
17594
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
18946
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17595
18947
  MediaOverlay,
17596
18948
  {
17597
18949
  hover: mediaHover,
@@ -17600,11 +18952,11 @@ function OhhwellsBridge() {
17600
18952
  onVideoSettingsChange: handleVideoSettingsChange
17601
18953
  }
17602
18954
  ),
17603
- carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
17604
- siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
17605
- siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
17606
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
17607
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
18955
+ carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
18956
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
18957
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
18958
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
18959
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17608
18960
  "div",
17609
18961
  {
17610
18962
  className: "pointer-events-none fixed z-2147483646",
@@ -17614,7 +18966,7 @@ function OhhwellsBridge() {
17614
18966
  width: slot.width,
17615
18967
  height: slot.height
17616
18968
  },
17617
- children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
18969
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17618
18970
  DropIndicator,
17619
18971
  {
17620
18972
  direction: slot.direction,
@@ -17625,7 +18977,7 @@ function OhhwellsBridge() {
17625
18977
  },
17626
18978
  `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
17627
18979
  )),
17628
- isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
18980
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17629
18981
  "div",
17630
18982
  {
17631
18983
  className: "pointer-events-none fixed z-2147483646",
@@ -17635,7 +18987,7 @@ function OhhwellsBridge() {
17635
18987
  width: slot.width,
17636
18988
  height: slot.height
17637
18989
  },
17638
- children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
18990
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17639
18991
  DropIndicator,
17640
18992
  {
17641
18993
  direction: slot.direction,
@@ -17646,10 +18998,11 @@ function OhhwellsBridge() {
17646
18998
  },
17647
18999
  `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
17648
19000
  )),
17649
- hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
17650
- hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
17651
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
17652
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
19001
+ hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
19002
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
19003
+ hoveredTextRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
19004
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
19005
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17653
19006
  FooterContainerChrome,
17654
19007
  {
17655
19008
  rect: toolbarRect,
@@ -17657,7 +19010,7 @@ function OhhwellsBridge() {
17657
19010
  addDisabled: !canAddFooterColumn()
17658
19011
  }
17659
19012
  ),
17660
- toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
19013
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17661
19014
  ItemInteractionLayer,
17662
19015
  {
17663
19016
  rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
@@ -17669,10 +19022,10 @@ function OhhwellsBridge() {
17669
19022
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
17670
19023
  onDragHandleDragStart: handleItemDragStart,
17671
19024
  onDragHandleDragEnd: handleItemDragEnd,
17672
- onItemPointerDown: handleItemChromePointerDown,
17673
- onItemClick: handleItemChromeClick,
17674
- itemDragSurface: !isFooterFrameSelection,
17675
- toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
19025
+ onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
19026
+ onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
19027
+ itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection,
19028
+ toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17676
19029
  ItemActionToolbar,
17677
19030
  {
17678
19031
  onEditLink: openLinkPopoverForSelected,
@@ -17688,7 +19041,10 @@ function OhhwellsBridge() {
17688
19041
  onSelectParent: handleSelectParent,
17689
19042
  onDuplicate: handleDuplicateSelected,
17690
19043
  onDelete: handleDeleteSelected,
17691
- addItemDisabled: false,
19044
+ addItemDisabled: isFooterFrameSelection && isFooterAddItemDisabled(selectedElRef.current) || selectedElRef.current !== null && (() => {
19045
+ const row = isSocialsRow(selectedElRef.current) ? selectedElRef.current : findSocialsRow(selectedElRef.current);
19046
+ return row ? !canAddSocialItem(row) : false;
19047
+ })(),
17692
19048
  editLinkDisabled: false,
17693
19049
  moreDisabled: false,
17694
19050
  duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow,
@@ -17705,8 +19061,8 @@ function OhhwellsBridge() {
17705
19061
  ) : void 0
17706
19062
  }
17707
19063
  ),
17708
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_jsx_runtime32.Fragment, { children: [
17709
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
19064
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
19065
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17710
19066
  EditGlowChrome,
17711
19067
  {
17712
19068
  rect: toolbarRect,
@@ -17716,7 +19072,7 @@ function OhhwellsBridge() {
17716
19072
  hideHandle: isItemDragging
17717
19073
  }
17718
19074
  ),
17719
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
19075
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17720
19076
  FloatingToolbar,
17721
19077
  {
17722
19078
  rect: toolbarRect,
@@ -17729,7 +19085,7 @@ function OhhwellsBridge() {
17729
19085
  }
17730
19086
  )
17731
19087
  ] }),
17732
- maxBadge && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
19088
+ maxBadge && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
17733
19089
  "div",
17734
19090
  {
17735
19091
  "data-ohw-max-badge": "",
@@ -17755,7 +19111,7 @@ function OhhwellsBridge() {
17755
19111
  ]
17756
19112
  }
17757
19113
  ),
17758
- toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
19114
+ toggleState && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17759
19115
  StateToggle,
17760
19116
  {
17761
19117
  rect: toggleState.rect,
@@ -17764,15 +19120,15 @@ function OhhwellsBridge() {
17764
19120
  onStateChange: handleStateChange
17765
19121
  }
17766
19122
  ),
17767
- sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
19123
+ sectionGap && !linkPopover && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
17768
19124
  "div",
17769
19125
  {
17770
19126
  "data-ohw-section-insert-line": "",
17771
19127
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
17772
19128
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
17773
19129
  children: [
17774
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
17775
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
19130
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
19131
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17776
19132
  Badge,
17777
19133
  {
17778
19134
  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",
@@ -17789,11 +19145,11 @@ function OhhwellsBridge() {
17789
19145
  children: "Add Section"
17790
19146
  }
17791
19147
  ),
17792
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
19148
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex-1 bg-primary", style: { height: 3 } })
17793
19149
  ]
17794
19150
  }
17795
19151
  ),
17796
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
19152
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17797
19153
  LinkPopover,
17798
19154
  {
17799
19155
  panelRef: linkPopoverPanelRef,
@@ -17810,7 +19166,7 @@ function OhhwellsBridge() {
17810
19166
  },
17811
19167
  linkPopover.key
17812
19168
  ) : null,
17813
- floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
19169
+ floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17814
19170
  FloatingPanel,
17815
19171
  {
17816
19172
  open: true,
@@ -17820,7 +19176,7 @@ function OhhwellsBridge() {
17820
19176
  onPositionChange: setFloatingPanelPos,
17821
19177
  parentScroll: parentScrollSnap ?? parentScrollRef.current,
17822
19178
  onClose: closeFloatingPanelOnly,
17823
- children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
19179
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
17824
19180
  SocialsDisplayPanel,
17825
19181
  {
17826
19182
  display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
@@ -17831,11 +19187,115 @@ function OhhwellsBridge() {
17831
19187
  }
17832
19188
  )
17833
19189
  }
19190
+ ) : null,
19191
+ floatingPanel && floatingPanel.kind === "logo-size" && logoSizeDraft ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19192
+ FloatingPanel,
19193
+ {
19194
+ open: true,
19195
+ title: floatingPanel.title,
19196
+ context: floatingPanel.context,
19197
+ position: floatingPanelPos,
19198
+ onPositionChange: setFloatingPanelPos,
19199
+ parentScroll: parentScrollSnap ?? parentScrollRef.current,
19200
+ onClose: closeFloatingPanelAndDeselect,
19201
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
19202
+ LogoSizePanel,
19203
+ {
19204
+ viewport: editorViewport,
19205
+ sizePx: editorViewport === "mobile" && !logoSizeDraft.mobileFollowing ? logoSizeDraft.mobilePx : logoSizeDraft.desktopPx,
19206
+ mobileFollowing: logoSizeDraft.mobileFollowing,
19207
+ onSizeChange: (px) => {
19208
+ const next = editorViewport === "mobile" ? { ...logoSizeDraft, mobilePx: px, mobileFollowing: false } : {
19209
+ ...logoSizeDraft,
19210
+ desktopPx: px,
19211
+ mobilePx: logoSizeDraft.mobileFollowing ? px : logoSizeDraft.mobilePx
19212
+ };
19213
+ setLogoSizeDraft(next);
19214
+ persistLogoSizeDraft(floatingPanel.placement, next);
19215
+ },
19216
+ onCustomizeMobile: () => {
19217
+ const next = {
19218
+ ...logoSizeDraft,
19219
+ mobileFollowing: false,
19220
+ mobilePx: logoSizeDraft.desktopPx
19221
+ };
19222
+ setLogoSizeDraft(next);
19223
+ persistLogoSizeDraft(floatingPanel.placement, next);
19224
+ },
19225
+ onResetMobile: () => {
19226
+ const next = {
19227
+ ...logoSizeDraft,
19228
+ mobileFollowing: true,
19229
+ mobilePx: logoSizeDraft.desktopPx
19230
+ };
19231
+ setLogoSizeDraft(next);
19232
+ persistLogoSizeDraft(floatingPanel.placement, next);
19233
+ },
19234
+ onUpdateEverywhere: () => {
19235
+ const identity = readLogoIdentityFromDom();
19236
+ postToParent2({ type: "ow:open-logo-settings", ...identity });
19237
+ }
19238
+ }
19239
+ )
19240
+ }
17834
19241
  ) : null
17835
19242
  ] }),
17836
19243
  bridgeRoot
17837
19244
  ) : null;
17838
19245
  }
19246
+
19247
+ // src/ui/EmptySection.tsx
19248
+ var import_link = __toESM(require("next/link"), 1);
19249
+ var import_jsx_runtime34 = require("react/jsx-runtime");
19250
+ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
19251
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
19252
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
19253
+ "p",
19254
+ {
19255
+ style: {
19256
+ fontFamily: "var(--brand-font-body)",
19257
+ fontSize: "0.75rem",
19258
+ fontWeight: 500,
19259
+ letterSpacing: "0.15em",
19260
+ textTransform: "uppercase",
19261
+ color: "var(--brand-accent)",
19262
+ marginBottom: "1.5rem"
19263
+ },
19264
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_link.default, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
19265
+ }
19266
+ ),
19267
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
19268
+ "h1",
19269
+ {
19270
+ style: {
19271
+ fontFamily: "var(--brand-font-heading)",
19272
+ fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
19273
+ lineHeight: 1.1,
19274
+ letterSpacing: "-0.025em",
19275
+ color: "var(--brand-text)",
19276
+ marginBottom: "1rem"
19277
+ },
19278
+ ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
19279
+ children: title
19280
+ }
19281
+ ),
19282
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
19283
+ "p",
19284
+ {
19285
+ style: {
19286
+ fontFamily: "var(--brand-font-body)",
19287
+ fontSize: "1rem",
19288
+ lineHeight: 1.7,
19289
+ fontWeight: 300,
19290
+ color: "var(--brand-text-muted)",
19291
+ maxWidth: "340px"
19292
+ },
19293
+ ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
19294
+ children: "This page doesn't have any content yet."
19295
+ }
19296
+ )
19297
+ ] });
19298
+ }
17839
19299
  // Annotate the CommonJS export names for ESM import in node:
17840
19300
  0 && (module.exports = {
17841
19301
  AI_DEFAULT_BRAND,
@@ -17853,6 +19313,7 @@ function OhhwellsBridge() {
17853
19313
  DropdownMenuItem,
17854
19314
  DropdownMenuSeparator,
17855
19315
  DropdownMenuTrigger,
19316
+ EmptySection,
17856
19317
  ItemActionToolbar,
17857
19318
  ItemInteractionLayer,
17858
19319
  LinkEditorPanel,