@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.js CHANGED
@@ -96,6 +96,7 @@ function applyTreeToState(state, payload) {
96
96
  const entry = {
97
97
  id: payload.id,
98
98
  label: payload.label ?? "Generated section",
99
+ ...typeof payload.path === "string" && payload.path ? { path: payload.path } : {},
99
100
  afterSection: payload.mode === "insert" && !insertBefore ? payload.targetSectionId ?? null : null,
100
101
  ...insertBefore && payload.targetSectionId ? { beforeSection: payload.targetSectionId } : {},
101
102
  ...payload.mode === "replace" && payload.targetSectionId ? { replaces: payload.targetSectionId } : {},
@@ -118,6 +119,272 @@ function deleteSectionFromState(state, sectionId) {
118
119
  return { ...state, v: 1, sections: state.sections, removed: [...removed, sectionId] };
119
120
  }
120
121
 
122
+ // src/lib/brand-chrome.ts
123
+ var BRAND_NAME_KEY = "__ohw_brand_name";
124
+ var BRAND_TITLE_KEY = "__ohw_site_title";
125
+ var BRAND_FAVICON_LETTER_KEY = "__ohw_favicon_letter";
126
+ var BRAND_CHROME_KEYS = /* @__PURE__ */ new Set([
127
+ BRAND_NAME_KEY,
128
+ BRAND_TITLE_KEY,
129
+ BRAND_FAVICON_LETTER_KEY
130
+ ]);
131
+ function upsertMeta(selector, attr, token, value) {
132
+ let el = document.head.querySelector(selector);
133
+ if (!el) {
134
+ el = document.createElement("meta");
135
+ el.setAttribute(attr, token);
136
+ document.head.appendChild(el);
137
+ }
138
+ if (el.getAttribute("content") !== value) el.setAttribute("content", value);
139
+ }
140
+ function escapeXml(value) {
141
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
142
+ }
143
+ function applyLetterFavicon(letter) {
144
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="12" fill="#111827"/><text x="32" y="46" font-family="system-ui,-apple-system,Segoe UI,sans-serif" font-size="40" font-weight="700" text-anchor="middle" fill="#ffffff">${escapeXml(letter)}</text></svg>`;
145
+ const href = `data:image/svg+xml,${encodeURIComponent(svg)}`;
146
+ let link = document.head.querySelector('link[rel="icon"]');
147
+ if (!link) {
148
+ link = document.createElement("link");
149
+ link.rel = "icon";
150
+ document.head.appendChild(link);
151
+ }
152
+ link.type = "image/svg+xml";
153
+ if (link.href !== href) link.href = href;
154
+ }
155
+ function applyBrandChrome(content) {
156
+ const name = content[BRAND_NAME_KEY];
157
+ if (typeof name === "string" && name.length > 0) {
158
+ document.querySelectorAll("[data-ohw-wordmark]").forEach((el) => {
159
+ if (el.textContent !== name) el.textContent = name;
160
+ if (el.getAttribute("title") !== name) el.setAttribute("title", name);
161
+ });
162
+ }
163
+ const title = content[BRAND_TITLE_KEY];
164
+ if (typeof title === "string" && title.length > 0) {
165
+ if (document.title !== title) document.title = title;
166
+ upsertMeta('meta[property="og:title"]', "property", "og:title", title);
167
+ upsertMeta('meta[name="twitter:title"]', "name", "twitter:title", title);
168
+ }
169
+ const letter = content[BRAND_FAVICON_LETTER_KEY];
170
+ if (typeof letter === "string" && letter.length > 0) {
171
+ applyLetterFavicon(letter);
172
+ }
173
+ }
174
+
175
+ // src/lib/brand-kit.ts
176
+ var BRAND_KIT_KEY = "__ohw_brand";
177
+ var BRAND_VAR_PREFIX = "--ohw-brand-";
178
+ var BRAND_VAR_NAMES = ["primary", "accent", "light", "dark", "surface", "border", "muted"].map(
179
+ (role) => `${BRAND_VAR_PREFIX}${role}`
180
+ );
181
+ var FONT_VARS = { heading: ["--font-heading", "--font-display"], body: ["--font-body"] };
182
+ var BRAND_FONT_LINK_ID = "ohw-brand-fonts";
183
+ function brandColorVars(kit) {
184
+ const { dark, primary, accent, light } = kit.palette;
185
+ const mix = (a, aPct, b) => `color-mix(in srgb, ${a} ${aPct}%, ${b})`;
186
+ return {
187
+ [`${BRAND_VAR_PREFIX}primary`]: primary,
188
+ [`${BRAND_VAR_PREFIX}accent`]: accent,
189
+ [`${BRAND_VAR_PREFIX}light`]: light,
190
+ [`${BRAND_VAR_PREFIX}dark`]: dark,
191
+ [`${BRAND_VAR_PREFIX}surface`]: mix(light, 95, dark),
192
+ [`${BRAND_VAR_PREFIX}border`]: mix(light, 85, dark),
193
+ [`${BRAND_VAR_PREFIX}muted`]: mix(dark, 62, light)
194
+ };
195
+ }
196
+ function parseBrandKit(raw) {
197
+ if (!raw) return null;
198
+ try {
199
+ const parsed = JSON.parse(raw);
200
+ const p = parsed?.palette;
201
+ const f = parsed?.fonts;
202
+ if (!p || !f || typeof p.dark !== "string" || typeof p.primary !== "string" || typeof p.accent !== "string" || typeof p.light !== "string" || typeof f.heading !== "string" || typeof f.body !== "string") {
203
+ return null;
204
+ }
205
+ return {
206
+ palette: { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light },
207
+ fonts: { heading: f.heading, body: f.body }
208
+ };
209
+ } catch {
210
+ return null;
211
+ }
212
+ }
213
+ function familyOf(stack) {
214
+ const first = stack.split(",")[0]?.trim() ?? "";
215
+ return first.replace(/^['"]|['"]$/g, "");
216
+ }
217
+ function loadBrandFonts(families) {
218
+ const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
219
+ if (unique.length === 0) return;
220
+ const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
221
+ const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
222
+ let link = document.getElementById(BRAND_FONT_LINK_ID);
223
+ if (!link) {
224
+ link = document.createElement("link");
225
+ link.id = BRAND_FONT_LINK_ID;
226
+ link.rel = "stylesheet";
227
+ document.head.appendChild(link);
228
+ }
229
+ if (link.href !== href) link.href = href;
230
+ }
231
+ function applyBrandToDom(kit) {
232
+ const root = document.documentElement;
233
+ if (!kit) {
234
+ for (const name of BRAND_VAR_NAMES) root.style.removeProperty(name);
235
+ for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
236
+ document.getElementById(BRAND_FONT_LINK_ID)?.remove();
237
+ return;
238
+ }
239
+ for (const [name, value] of Object.entries(brandColorVars(kit))) root.style.setProperty(name, value);
240
+ for (const name of FONT_VARS.heading) root.style.setProperty(name, kit.fonts.heading);
241
+ for (const name of FONT_VARS.body) root.style.setProperty(name, kit.fonts.body);
242
+ loadBrandFonts([familyOf(kit.fonts.heading), familyOf(kit.fonts.body)]);
243
+ }
244
+
245
+ // src/lib/section-styles.ts
246
+ var STYLE_STORE_KEY = "__ohw_styles";
247
+ var STYLE_SHEET_ID = "ohw-section-styles";
248
+ function parseStyleStore(raw) {
249
+ if (!raw) return null;
250
+ try {
251
+ const parsed = JSON.parse(raw);
252
+ if (parsed?.v !== 1) return null;
253
+ return {
254
+ v: 1,
255
+ sections: typeof parsed.sections === "object" && parsed.sections ? parsed.sections : {},
256
+ nodes: typeof parsed.nodes === "object" && parsed.nodes ? parsed.nodes : {}
257
+ };
258
+ } catch {
259
+ return null;
260
+ }
261
+ }
262
+ var BG_VALUES = {
263
+ surface: "color-mix(in srgb, var(--ohw-brand-light, var(--color-light, #ECECEB)) 94%, var(--ohw-brand-dark, var(--color-dark, #0C0A09)))",
264
+ accent: "var(--ohw-brand-primary, var(--color-primary, #0078E5))",
265
+ "accent-soft": "color-mix(in srgb, var(--ohw-brand-primary, var(--color-primary, #0078E5)) 12%, var(--ohw-brand-light, var(--color-light, #FFFFFF)))"
266
+ };
267
+ var HEADLINE_SIZES = { md: 24, lg: 32, xl: 40, display: 56 };
268
+ function styleSheetCss() {
269
+ const rules = [];
270
+ for (const [tone, value] of Object.entries(BG_VALUES)) {
271
+ rules.push(`[data-ohw-style-bg="${tone}"] { background: ${value} !important; }`);
272
+ }
273
+ rules.push(
274
+ `[data-ohw-style-bg="accent"] { color: var(--ohw-brand-light, var(--color-light, #FFFFFF)) !important; }`
275
+ );
276
+ rules.push(
277
+ `[data-ohw-style-distribution="space-between"] { display: flex !important; flex-direction: column; justify-content: space-between; }`,
278
+ `[data-ohw-style-distribution="center"] { display: flex !important; flex-direction: column; justify-content: center; }`
279
+ );
280
+ for (const [scale, size] of Object.entries(HEADLINE_SIZES)) {
281
+ rules.push(
282
+ `[data-ohw-style-headline="${scale}"] :is(h1, h2, h3) { font-size: ${size}px !important; line-height: 1.15 !important; }`
283
+ );
284
+ }
285
+ rules.push(
286
+ `[data-ohw-style-aspect="fill-height"] img { height: 100% !important; aspect-ratio: auto !important; object-fit: cover; }`
287
+ );
288
+ for (const aspect of ["1:1", "4:5", "3:4", "3:2", "16:9", "2:1", "3:1"]) {
289
+ rules.push(
290
+ `[data-ohw-style-aspect="${aspect.replace(":", "-")}"] img { aspect-ratio: ${aspect.replace(":", " / ")} !important; height: auto !important; object-fit: cover; }`
291
+ );
292
+ }
293
+ const pad = { tight: 40, balanced: 64, airy: 96 };
294
+ for (const [spacing, px] of Object.entries(pad)) {
295
+ rules.push(
296
+ `[data-ohw-style-spacing="${spacing}"] { padding-top: ${px}px !important; padding-bottom: ${px}px !important; }`
297
+ );
298
+ }
299
+ return rules.join("\n");
300
+ }
301
+ var SECTION_ATTRS = {
302
+ sectionBackground: "data-ohw-style-bg",
303
+ textDistribution: "data-ohw-style-distribution",
304
+ headlineScale: "data-ohw-style-headline",
305
+ imageAspect: "data-ohw-style-aspect",
306
+ spacing: "data-ohw-style-spacing"
307
+ };
308
+ var NODE_WROTE_ATTR = "data-ohw-style-node";
309
+ function ensureStyleSheet() {
310
+ let el = document.getElementById(STYLE_SHEET_ID);
311
+ if (!el) {
312
+ el = document.createElement("style");
313
+ el.id = STYLE_SHEET_ID;
314
+ document.head.appendChild(el);
315
+ }
316
+ const css = styleSheetCss();
317
+ if (el.textContent !== css) el.textContent = css;
318
+ }
319
+ function clearSectionAttrs(root) {
320
+ for (const attr of Object.values(SECTION_ATTRS)) {
321
+ for (const el of Array.from(root.querySelectorAll(`[${attr}]`))) el.removeAttribute(attr);
322
+ }
323
+ for (const el of Array.from(root.querySelectorAll("[data-ohw-style-bgcolor]"))) {
324
+ ;
325
+ el.style.removeProperty("background");
326
+ el.removeAttribute("data-ohw-style-bgcolor");
327
+ }
328
+ }
329
+ function clearNodeProps(root) {
330
+ for (const el of Array.from(root.querySelectorAll(`[${NODE_WROTE_ATTR}]`))) {
331
+ const h = el;
332
+ h.style.removeProperty("color");
333
+ h.style.removeProperty("font-size");
334
+ h.style.removeProperty("background");
335
+ h.removeAttribute(NODE_WROTE_ATTR);
336
+ }
337
+ }
338
+ function buttonSurfaceOf(el) {
339
+ return el.closest("a, button") ?? el;
340
+ }
341
+ function applyStylesToDom(store) {
342
+ ensureStyleSheet();
343
+ clearSectionAttrs(document);
344
+ clearNodeProps(document);
345
+ if (!store) return;
346
+ for (const [sectionId, override] of Object.entries(store.sections)) {
347
+ const sections = document.querySelectorAll(
348
+ `[data-ohw-section="${CSS.escape(sectionId)}"]`
349
+ );
350
+ for (const section of Array.from(sections)) {
351
+ for (const [prop, attr] of Object.entries(SECTION_ATTRS)) {
352
+ const value = override[prop];
353
+ if (value === void 0) continue;
354
+ if (prop === "sectionBackground" && override.sectionBackgroundColor !== void 0) continue;
355
+ section.setAttribute(attr, String(value).replace(":", "-"));
356
+ }
357
+ if (override.sectionBackgroundColor !== void 0) {
358
+ section.style.setProperty("background", override.sectionBackgroundColor, "important");
359
+ section.setAttribute("data-ohw-style-bgcolor", "");
360
+ }
361
+ }
362
+ }
363
+ for (const [key, override] of Object.entries(store.nodes)) {
364
+ const nodes = document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`);
365
+ for (const el of Array.from(nodes)) {
366
+ if (override.color !== void 0) {
367
+ el.style.setProperty("color", override.color, "important");
368
+ el.setAttribute(NODE_WROTE_ATTR, "");
369
+ }
370
+ if (override.fontSize !== void 0) {
371
+ el.style.setProperty("font-size", `${override.fontSize}px`, "important");
372
+ el.setAttribute(NODE_WROTE_ATTR, "");
373
+ }
374
+ if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
375
+ const surface = buttonSurfaceOf(el);
376
+ if (override.buttonBackground !== void 0) {
377
+ surface.style.setProperty("background", override.buttonBackground, "important");
378
+ }
379
+ if (override.buttonText !== void 0) {
380
+ surface.style.setProperty("color", override.buttonText, "important");
381
+ }
382
+ surface.setAttribute(NODE_WROTE_ATTR, "");
383
+ }
384
+ }
385
+ }
386
+ }
387
+
121
388
  // src/ui/ai-tree/aiSectionsManager.tsx
122
389
  import { flushSync } from "react-dom";
123
390
  import { createRoot } from "react-dom/client";
@@ -132,7 +399,8 @@ function lucideByName(name) {
132
399
  }
133
400
  var typeStyle = (spec, font) => ({
134
401
  fontFamily: font,
135
- fontSize: spec.size,
402
+ // Headings shrink with the viewport (reaching full size around ~900px wide); body copy stays put.
403
+ fontSize: spec.size >= 24 ? `clamp(${Math.max(18, Math.round(spec.size * 0.6))}px, ${(spec.size / 9).toFixed(2)}vw, ${spec.size}px)` : spec.size,
136
404
  lineHeight: spec.line,
137
405
  fontWeight: spec.weight
138
406
  });
@@ -150,6 +418,19 @@ var FEATURE_LINE_CSS = [
150
418
  function textAttrs(ctx, path) {
151
419
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
152
420
  }
421
+ var AI_RESPONSIVE_CSS = [
422
+ "@media (max-width: 960px) {",
423
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
424
+ ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
425
+ "}",
426
+ "@media (max-width: 640px) {",
427
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
428
+ " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
429
+ " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
430
+ " [data-ai-responsive] { overflow-x: hidden; }",
431
+ " [data-ai-responsive] img { max-width: 100%; }",
432
+ "}"
433
+ ].join("\n");
153
434
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
154
435
  function MediaBox({
155
436
  refValue,
@@ -892,6 +1173,7 @@ function Carousel({ items, itemsPerRow, ctx }) {
892
1173
  children: pageGroups.map((group, p) => /* @__PURE__ */ jsx(
893
1174
  "div",
894
1175
  {
1176
+ "data-ai-grid": String(itemsPerRow),
895
1177
  style: {
896
1178
  flex: "0 0 100%",
897
1179
  display: "grid",
@@ -995,6 +1277,7 @@ function CollectionBlock({ node, ctx, path }) {
995
1277
  return /* @__PURE__ */ jsx(
996
1278
  "div",
997
1279
  {
1280
+ "data-ai-grid": String(itemsPerRow),
998
1281
  style: {
999
1282
  display: "grid",
1000
1283
  gridTemplateColumns: `repeat(${itemsPerRow}, 1fr)`,
@@ -1153,24 +1436,42 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1153
1436
  const overlayAlpha = { low: 0.25, medium: 0.45, high: 0.65 }[settings.overlayIntensity ?? "medium"];
1154
1437
  const backgroundUrl = isOverlay && settings.backgroundMedia ? ctx.resolveMedia(settings.backgroundMedia) : null;
1155
1438
  const bgAttrs = ctx.keyFor && isOverlay && settings.backgroundMedia ? { "data-ohw-key": ctx.keyFor("settings.backgroundMedia"), "data-ohw-editable": "bg-image" } : {};
1439
+ const toneBackground = (() => {
1440
+ const { dark, primary, light } = resolvedBrand.palette;
1441
+ switch (settings.sectionBackground) {
1442
+ case "surface":
1443
+ return `color-mix(in srgb, ${light} 94%, ${dark})`;
1444
+ case "accent":
1445
+ return primary;
1446
+ case "accent-soft":
1447
+ return `color-mix(in srgb, ${primary} 12%, ${light})`;
1448
+ default:
1449
+ return void 0;
1450
+ }
1451
+ })();
1452
+ const distributed = !isOverlay && settings.textDistribution;
1156
1453
  return /* @__PURE__ */ jsxs(
1157
1454
  "section",
1158
1455
  {
1159
1456
  "data-ai-section": tree.tag ?? "",
1160
1457
  ...bgAttrs,
1458
+ "data-ai-responsive": "",
1161
1459
  style: {
1162
1460
  position: "relative",
1163
1461
  padding: `${pad}px 0`,
1164
- background: isOverlay && !backgroundUrl ? "#DBEAFE" : void 0,
1462
+ background: toneBackground ?? (isOverlay && !backgroundUrl ? "#DBEAFE" : void 0),
1165
1463
  backgroundImage: backgroundUrl ? `url(${backgroundUrl})` : void 0,
1166
1464
  backgroundSize: "cover",
1167
- backgroundPosition: "center"
1465
+ backgroundPosition: "center",
1466
+ color: settings.sectionBackground === "accent" ? resolvedBrand.palette.light : void 0
1168
1467
  },
1169
1468
  children: [
1469
+ /* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
1170
1470
  isOverlay && backgroundUrl && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
1171
1471
  /* @__PURE__ */ jsx(
1172
1472
  "div",
1173
1473
  {
1474
+ "data-ai-section-inner": "",
1174
1475
  style: {
1175
1476
  position: "relative",
1176
1477
  maxWidth: AI_TREE_TOKENS.sectionMaxWidth,
@@ -1181,14 +1482,29 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1181
1482
  children: tree.rows.map((row, r2) => /* @__PURE__ */ jsx(
1182
1483
  "div",
1183
1484
  {
1485
+ "data-ai-columns": "",
1184
1486
  style: {
1185
1487
  display: "grid",
1186
1488
  gridTemplateColumns: "repeat(12, 1fr)",
1187
1489
  gap: AI_TREE_TOKENS.spacing6,
1188
- alignItems: settings.verticalPosition === "top" ? "start" : "center",
1490
+ alignItems: distributed === "space-between" ? "stretch" : (distributed ?? settings.verticalPosition) === "top" ? "start" : "center",
1189
1491
  marginTop: r2 > 0 ? AI_TREE_TOKENS.spacing8 : 0
1190
1492
  },
1191
- children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ jsx("div", { style: { gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`, minWidth: 0 }, children: renderNode(block, ctx, `r${r2}.b${b}`) }, b))
1493
+ children: (row.blocks ?? []).map((block, b) => /* @__PURE__ */ jsx(
1494
+ "div",
1495
+ {
1496
+ "data-ai-cell": "",
1497
+ style: {
1498
+ gridColumn: `span ${Math.min(12, Math.max(1, block.span))}`,
1499
+ minWidth: 0,
1500
+ // space-between: each column becomes a flex column whose content spreads over
1501
+ // the full row height instead of clumping at the top.
1502
+ ...distributed === "space-between" ? { display: "flex", flexDirection: "column", justifyContent: "space-between" } : {}
1503
+ },
1504
+ children: renderNode(block, ctx, `r${r2}.b${b}`)
1505
+ },
1506
+ b
1507
+ ))
1192
1508
  },
1193
1509
  r2
1194
1510
  ))
@@ -1204,17 +1520,34 @@ import { jsx as jsx2 } from "react/jsx-runtime";
1204
1520
  var CONTAINER_ATTR = "data-ohw-ai-generated";
1205
1521
  var REPLACED_ATTR = "data-ohw-ai-replaced-by";
1206
1522
  var REMOVED_ATTR = "data-ohw-ai-removed";
1523
+ function readRootVar(name) {
1524
+ if (typeof document === "undefined") return "";
1525
+ return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
1526
+ }
1527
+ function deriveBrandOverride() {
1528
+ const dark = readRootVar("--ohw-brand-dark");
1529
+ const primary = readRootVar("--ohw-brand-primary");
1530
+ const light = readRootVar("--ohw-brand-light");
1531
+ if (!dark || !primary || !light) return null;
1532
+ const accent = readRootVar("--ohw-brand-accent");
1533
+ const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1534
+ const body = readRootVar("--font-body");
1535
+ return {
1536
+ palette: { dark, primary, accent: accent || dark, light },
1537
+ fonts: {
1538
+ heading: heading || AI_DEFAULT_BRAND.fonts.heading,
1539
+ body: body || AI_DEFAULT_BRAND.fonts.body
1540
+ }
1541
+ };
1542
+ }
1207
1543
  function deriveTemplateBrand() {
1208
- if (typeof document === "undefined") return null;
1209
- const cs = getComputedStyle(document.documentElement);
1210
- const read = (name) => cs.getPropertyValue(name).trim();
1211
- const dark = read("--color-dark");
1212
- const primary = read("--color-primary");
1213
- const light = read("--color-light");
1544
+ const dark = readRootVar("--color-dark");
1545
+ const primary = readRootVar("--color-primary");
1546
+ const light = readRootVar("--color-light");
1214
1547
  if (!dark || !primary || !light) return null;
1215
- const accent = read("--color-accent");
1216
- const heading = read("--font-heading") || read("--font-display");
1217
- const body = read("--font-body");
1548
+ const accent = readRootVar("--color-accent");
1549
+ const heading = readRootVar("--font-heading") || readRootVar("--font-display");
1550
+ const body = readRootVar("--font-body");
1218
1551
  return {
1219
1552
  palette: { dark, primary, accent: accent || dark, light },
1220
1553
  fonts: {
@@ -1306,8 +1639,12 @@ function syncReplacedOriginals(state) {
1306
1639
  }
1307
1640
  function applyAiSectionsToDom(state, options) {
1308
1641
  if (typeof document === "undefined") return;
1642
+ const brandOverride = deriveBrandOverride();
1309
1643
  const templateBrand = deriveTemplateBrand();
1310
- const activeIds = new Set(state.sections.map((entry) => entry.id));
1644
+ const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
1645
+ const pagePath = window.location.pathname;
1646
+ const pageSections = state.sections.filter((entry) => !entry.path || entry.path === pagePath);
1647
+ const activeIds = new Set(pageSections.map((entry) => entry.id));
1311
1648
  for (const [id, section] of mounted) {
1312
1649
  if (!activeIds.has(id)) {
1313
1650
  section.root.unmount();
@@ -1315,8 +1652,8 @@ function applyAiSectionsToDom(state, options) {
1315
1652
  mounted.delete(id);
1316
1653
  }
1317
1654
  }
1318
- for (const entry of state.sections) {
1319
- const serialized = JSON.stringify(entry);
1655
+ for (const entry of pageSections) {
1656
+ const serialized = JSON.stringify(entry) + brandKey;
1320
1657
  const existing = mounted.get(entry.id);
1321
1658
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
1322
1659
  continue;
@@ -1330,6 +1667,7 @@ function applyAiSectionsToDom(state, options) {
1330
1667
  mounted.delete(entry.id);
1331
1668
  }
1332
1669
  container.setAttribute("data-ohw-section", entry.id);
1670
+ container.setAttribute("data-ohw-instance", entry.id);
1333
1671
  container.setAttribute("data-ohw-section-label", entry.label);
1334
1672
  placeContainer(container, entry);
1335
1673
  const root = mounted.get(entry.id)?.root ?? createRoot(container);
@@ -1340,7 +1678,7 @@ function applyAiSectionsToDom(state, options) {
1340
1678
  AiTreeRenderer,
1341
1679
  {
1342
1680
  tree: entry.tree,
1343
- brand: entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1681
+ brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
1344
1682
  resolveMedia,
1345
1683
  editKeyPrefix: `ai.${entry.id}`
1346
1684
  }
@@ -1957,7 +2295,7 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
1957
2295
  const autoId = useId();
1958
2296
  const insertAfter = insertAfterProp ?? autoId;
1959
2297
  const [schedule, setSchedule] = useState2(null);
1960
- const [loading, setLoading] = useState2(true);
2298
+ const [loading, setLoading] = useState2(initialScheduleId !== null);
1961
2299
  const [inEditor, setInEditor] = useState2(false);
1962
2300
  const [isHovered, setIsHovered] = useState2(false);
1963
2301
  const [modalState, setModalState] = useState2(null);
@@ -2131,8 +2469,10 @@ function SchedulingWidget({ notifyOnConnect = false, initialScheduleId, insertAf
2131
2469
  "*"
2132
2470
  );
2133
2471
  };
2134
- if (!inEditor && !loading && !schedule) return null;
2135
2472
  const sectionId = `scheduling-${insertAfter}`;
2473
+ if (!inEditor && !loading && !schedule) {
2474
+ return /* @__PURE__ */ jsx4("section", { "data-ohw-section": sectionId, "data-ohw-scheduling-anchor": insertAfter, style: { display: "none" } });
2475
+ }
2136
2476
  return /* @__PURE__ */ jsxs3(
2137
2477
  "section",
2138
2478
  {
@@ -6086,6 +6426,7 @@ var FOCUS_RING = `0 0 0 4px color-mix(in srgb, ${PRIMARY} 12%, transparent)`;
6086
6426
  var DRAG_SHADOW = "0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0, 0, 0, 0.1)";
6087
6427
  var TOOLBAR_EDGE_MARGIN = 4;
6088
6428
  var SELECTION_CHROME_GAP = 4;
6429
+ var HOVER_CHROME_GAP = 2;
6089
6430
  var TOOLBAR_STROKE_GAP = 4;
6090
6431
  function getChromeZIndex(state) {
6091
6432
  switch (state) {
@@ -6241,10 +6582,11 @@ function ItemInteractionLayer({
6241
6582
  onItemPointerDown,
6242
6583
  onItemClick,
6243
6584
  itemDragSurface = true,
6244
- chromeGap = SELECTION_CHROME_GAP,
6585
+ chromeGap,
6245
6586
  className
6246
6587
  }) {
6247
6588
  if (state === "default") return null;
6589
+ const gap = chromeGap ?? (state === "hover" ? HOVER_CHROME_GAP : SELECTION_CHROME_GAP);
6248
6590
  const isActive = state === "active-top" || state === "active-bottom";
6249
6591
  const isDragging = state === "dragging";
6250
6592
  const showToolbar = isActive && toolbar;
@@ -6260,10 +6602,10 @@ function ItemInteractionLayer({
6260
6602
  className: cn("pointer-events-none", className),
6261
6603
  style: {
6262
6604
  position: "fixed",
6263
- top: rect.top - chromeGap,
6264
- left: rect.left - chromeGap,
6265
- width: rect.width + chromeGap * 2,
6266
- height: rect.height + chromeGap * 2,
6605
+ top: rect.top - gap,
6606
+ left: rect.left - gap,
6607
+ width: rect.width + gap * 2,
6608
+ height: rect.height + gap * 2,
6267
6609
  zIndex: getChromeZIndex(state)
6268
6610
  },
6269
6611
  children: [
@@ -6649,8 +6991,12 @@ function parseSectionsFromHtml(html) {
6649
6991
 
6650
6992
  // src/ui/ai-section/AiSectionOverlay.tsx
6651
6993
  import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
6652
- function readRect(sectionId) {
6653
- const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
6994
+ function findSectionElement(instanceId) {
6995
+ const escaped = CSS.escape(instanceId);
6996
+ return document.querySelector(`[data-ohw-instance="${escaped}"]`) ?? document.querySelector(`[data-ohw-section="${escaped}"]:not([data-ohw-instance])`);
6997
+ }
6998
+ function readRect(instanceId) {
6999
+ const el = findSectionElement(instanceId);
6654
7000
  if (!el) return null;
6655
7001
  const r2 = el.getBoundingClientRect();
6656
7002
  if (r2.width <= 0 || r2.height <= 0) return null;
@@ -6673,7 +7019,7 @@ function useLiveSectionRect(sectionId) {
6673
7019
  const opts = { capture: true, passive: true };
6674
7020
  window.addEventListener("scroll", update, opts);
6675
7021
  window.addEventListener("resize", update);
6676
- const el = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
7022
+ const el = findSectionElement(sectionId);
6677
7023
  const ro = el ? new ResizeObserver(update) : null;
6678
7024
  if (el && ro) ro.observe(el);
6679
7025
  const interval = setInterval(update, 500);
@@ -6686,6 +7032,14 @@ function useLiveSectionRect(sectionId) {
6686
7032
  }, [sectionId]);
6687
7033
  return rect;
6688
7034
  }
7035
+ function computeSectionBoundaryFlags(instanceId) {
7036
+ const topLevel = Array.from(document.querySelectorAll("[data-ohw-section]")).filter(
7037
+ (el) => !el.parentElement?.closest("[data-ohw-section]")
7038
+ );
7039
+ const index = topLevel.findIndex((el) => (el.dataset.ohwInstance ?? el.dataset.ohwSection) === instanceId);
7040
+ if (index === -1) return { isFirst: true, isLast: true };
7041
+ return { isFirst: index === 0, isLast: index === topLevel.length - 1 };
7042
+ }
6689
7043
  var PRIMARY2 = "#0885FE";
6690
7044
  function edgeAwareRadius(rect) {
6691
7045
  const container = window.innerWidth <= 480 ? 16 : 24;
@@ -6767,7 +7121,7 @@ function AiSectionOverlay({
6767
7121
  (el) => {
6768
7122
  postToParent2({
6769
7123
  type: "ow:section-selected",
6770
- sectionId: el?.dataset.ohwSection ?? null,
7124
+ sectionId: el ? el.dataset.ohwInstance ?? el.dataset.ohwSection ?? null : null,
6771
7125
  sectionLabel: el ? el.dataset.ohwSectionLabel ?? titleCaseSectionId(el.dataset.ohwSection ?? "") : null
6772
7126
  });
6773
7127
  },
@@ -6776,7 +7130,7 @@ function AiSectionOverlay({
6776
7130
  const selectFromElement = useCallback2(
6777
7131
  (el, options) => {
6778
7132
  const sectionEl = el?.closest("[data-ohw-section]") ?? null;
6779
- const id = sectionEl?.dataset.ohwSection ?? null;
7133
+ const id = sectionEl ? sectionEl.dataset.ohwInstance ?? sectionEl.dataset.ohwSection ?? null : null;
6780
7134
  if (id === selectedIdRef.current) return;
6781
7135
  setSelectedId(id);
6782
7136
  if (options?.report !== false) report(sectionEl);
@@ -6819,7 +7173,7 @@ function AiSectionOverlay({
6819
7173
  setReviewId(found ? sectionId : null);
6820
7174
  postToParent2({ type: "ow:ai-review-started", sectionId, found });
6821
7175
  if (found) {
6822
- document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
7176
+ document.querySelector(`[data-ohw-instance="${CSS.escape(sectionId)}"]`)?.scrollIntoView({ behavior: "smooth", block: "nearest" });
6823
7177
  }
6824
7178
  }
6825
7179
  };
@@ -6838,7 +7192,7 @@ function AiSectionOverlay({
6838
7192
  return;
6839
7193
  }
6840
7194
  const sec = t.closest("[data-ohw-section]");
6841
- setHoveredId(sec?.dataset.ohwSection ?? null);
7195
+ setHoveredId(sec ? sec.dataset.ohwInstance ?? sec.dataset.ohwSection ?? null : null);
6842
7196
  };
6843
7197
  const onLeave = () => setHoveredId(null);
6844
7198
  document.addEventListener("mousemove", onMove, { passive: true });
@@ -6870,9 +7224,29 @@ function AiSectionOverlay({
6870
7224
  },
6871
7225
  [postToParent2]
6872
7226
  );
6873
- const selectionRect = useLiveSectionRect(reviewId ? null : selectedId);
7227
+ const activeSelectionId = reviewId ? null : selectedId;
7228
+ const selectionRect = useLiveSectionRect(activeSelectionId);
6874
7229
  const reviewRect = useLiveSectionRect(reviewId);
6875
7230
  const hoverRect = useLiveSectionRect(reviewId || hoveredId === selectedId ? null : hoveredId);
7231
+ useEffect4(() => {
7232
+ if (!activeSelectionId || !selectionRect) {
7233
+ postToParent2({ type: "ow:section-rect", instanceId: null, rect: null });
7234
+ return;
7235
+ }
7236
+ const { isFirst, isLast } = computeSectionBoundaryFlags(activeSelectionId);
7237
+ postToParent2({
7238
+ type: "ow:section-rect",
7239
+ instanceId: activeSelectionId,
7240
+ rect: {
7241
+ top: selectionRect.top + window.scrollY,
7242
+ left: selectionRect.left + window.scrollX,
7243
+ width: selectionRect.width,
7244
+ height: selectionRect.height
7245
+ },
7246
+ isFirst,
7247
+ isLast
7248
+ });
7249
+ }, [activeSelectionId, selectionRect, postToParent2]);
6876
7250
  return /* @__PURE__ */ jsxs8(Fragment5, { children: [
6877
7251
  hoverRect && /* @__PURE__ */ jsx16(
6878
7252
  "div",
@@ -6923,7 +7297,10 @@ function AiSectionOverlay({
6923
7297
  border: `2px solid ${PRIMARY2}`,
6924
7298
  borderRadius: edgeAwareRadius(reviewRect),
6925
7299
  zIndex: 2147483200,
6926
- // The veil itself: swallows clicks so the section stays locked until decided.
7300
+ // The veil itself: swallows clicks so the section stays locked until decided. This
7301
+ // stopPropagation only guards the bubble phase; the bridge's capture-phase click
7302
+ // handlers additionally bail on `[data-ohw-ai-review]`, without which a click on
7303
+ // Accept/Discard resolves to the media beneath and opens the file picker.
6927
7304
  background: "rgba(8, 133, 254, 0.04)",
6928
7305
  pointerEvents: "auto",
6929
7306
  cursor: "default"
@@ -9617,6 +9994,10 @@ function isSocialsRow(el) {
9617
9994
  const anchors = Array.from(el.querySelectorAll("a"));
9618
9995
  return anchors.length > 0 && anchors.every((anchor) => isSocialItem(anchor));
9619
9996
  }
9997
+ var MAX_SOCIAL_ITEMS_PER_ROW = 7;
9998
+ function canAddSocialItem(row) {
9999
+ return listSocialItems(row).length < MAX_SOCIAL_ITEMS_PER_ROW;
10000
+ }
9620
10001
  function listSocialItems(row) {
9621
10002
  return Array.from(row.children).map((child) => {
9622
10003
  if (!(child instanceof HTMLElement)) return null;
@@ -9976,6 +10357,7 @@ function ensureIconSlot(item) {
9976
10357
  // src/lib/footer-items.ts
9977
10358
  var FOOTER_ORDER_KEY = "__ohw_footer_order";
9978
10359
  var MAX_FOOTER_COLUMNS = 18;
10360
+ var MAX_FOOTER_ITEMS_PER_COLUMN = 7;
9979
10361
  var FOOTER_HREF_RE = /^footer-(\d+)-(\d+)-href$/;
9980
10362
  function parseFooterHrefKey(key) {
9981
10363
  if (!key) return null;
@@ -10193,6 +10575,13 @@ function getNextFooterColumnIndex() {
10193
10575
  function canAddFooterColumn() {
10194
10576
  return listFooterColumns().length < MAX_FOOTER_COLUMNS;
10195
10577
  }
10578
+ function canAddFooterItem(column) {
10579
+ return listFooterLinksInColumn(column).length < MAX_FOOTER_ITEMS_PER_COLUMN;
10580
+ }
10581
+ function resolveFooterColumnForAdd(selected) {
10582
+ if (selected.hasAttribute("data-ohw-footer-col")) return selected;
10583
+ return selected.closest("[data-ohw-footer-col]") ?? (listFooterColumns().includes(selected) ? selected : null);
10584
+ }
10196
10585
  function buildFooterHeading(colIndex, text) {
10197
10586
  const heading = document.createElement("p");
10198
10587
  heading.setAttribute("data-ohw-editable", "text");
@@ -10817,6 +11206,329 @@ function deleteFooterColumn(column) {
10817
11206
  };
10818
11207
  }
10819
11208
 
11209
+ // src/lib/logo-identity.ts
11210
+ var LOGO_TEXT_KEYS = ["nav-logo-text", "footer-logo-text", "logo-text"];
11211
+ var LOGO_IMAGE_KEYS = ["nav-logo-image", "footer-logo", "footer-logo-image"];
11212
+ var LOGO_HREF_KEYS = ["nav-logo-href", "footer-logo-href", "logo-href"];
11213
+ var LOGO_PLACEHOLDER_KEY = "logo-is-placeholder";
11214
+ var LOGO_ALT_KEY = "logo-alt";
11215
+ var LOGO_IMAGE_URL_KEY = "nav-logo-image";
11216
+ var PLACEHOLDER_BUSINESS_NAME = "Business name";
11217
+ function resolveLogoDisplayText(text) {
11218
+ const trimmed = (text ?? "").trim();
11219
+ return trimmed || PLACEHOLDER_BUSINESS_NAME;
11220
+ }
11221
+ function isFooterLogoRoot(root) {
11222
+ return Boolean(root.closest("footer") || root.closest('[data-ohw-section="footer"]'));
11223
+ }
11224
+ function imageKeyForRoot(root) {
11225
+ return isFooterLogoRoot(root) ? "footer-logo" : "nav-logo-image";
11226
+ }
11227
+ function textKeyForRoot(root) {
11228
+ return isFooterLogoRoot(root) ? "footer-logo-text" : "nav-logo-text";
11229
+ }
11230
+ function ensureLogoHrefKey(root) {
11231
+ if (!(root instanceof HTMLAnchorElement)) return;
11232
+ if (root.hasAttribute("data-ohw-href-key")) return;
11233
+ root.setAttribute("data-ohw-href-key", isFooterLogoRoot(root) ? "footer-logo-href" : "nav-logo-href");
11234
+ }
11235
+ function applyLogoIdentity(text, isPlaceholder) {
11236
+ const display = resolveLogoDisplayText(text);
11237
+ const placeholder = isPlaceholder || !text.trim() || display === PLACEHOLDER_BUSINESS_NAME;
11238
+ for (const key of LOGO_TEXT_KEYS) {
11239
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
11240
+ if (el.textContent !== display) el.textContent = display;
11241
+ });
11242
+ }
11243
+ for (const key of LOGO_IMAGE_KEYS) {
11244
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
11245
+ const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
11246
+ if (img) img.alt = display;
11247
+ });
11248
+ }
11249
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((el) => {
11250
+ if (placeholder) el.setAttribute("data-ohw-placeholder", "");
11251
+ else el.removeAttribute("data-ohw-placeholder");
11252
+ });
11253
+ return display;
11254
+ }
11255
+ function applyLogoImage(url, alt) {
11256
+ const displayAlt = resolveLogoDisplayText(alt);
11257
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11258
+ ensureLogoHrefKey(root);
11259
+ const imageKey = imageKeyForRoot(root);
11260
+ const textKey = textKeyForRoot(root);
11261
+ 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");
11262
+ let textEl = root.querySelector(`[data-ohw-key="${textKey}"]`) ?? root.querySelector('[data-ohw-key="logo-text"]');
11263
+ if (url) {
11264
+ if (!img) {
11265
+ img = document.createElement("img");
11266
+ img.setAttribute("data-ohw-editable", "image");
11267
+ img.setAttribute("data-ohw-key", imageKey);
11268
+ img.alt = displayAlt;
11269
+ img.style.height = "";
11270
+ img.style.maxHeight = "none";
11271
+ img.style.width = "auto";
11272
+ img.style.display = "block";
11273
+ img.style.objectFit = "contain";
11274
+ root.insertBefore(img, root.firstChild);
11275
+ } else {
11276
+ img.setAttribute("data-ohw-editable", "image");
11277
+ img.setAttribute("data-ohw-key", imageKey);
11278
+ }
11279
+ img.removeAttribute("srcset");
11280
+ img.removeAttribute("sizes");
11281
+ img.src = url;
11282
+ img.alt = displayAlt;
11283
+ img.style.display = "block";
11284
+ if (textEl) textEl.style.display = "none";
11285
+ root.removeAttribute("data-ohw-placeholder");
11286
+ return;
11287
+ }
11288
+ if (img) {
11289
+ img.removeAttribute("src");
11290
+ img.removeAttribute("srcset");
11291
+ img.removeAttribute("sizes");
11292
+ img.alt = displayAlt;
11293
+ img.style.display = "none";
11294
+ }
11295
+ if (!textEl) {
11296
+ textEl = document.createElement("span");
11297
+ textEl.setAttribute("data-ohw-editable", "plain");
11298
+ textEl.setAttribute("data-ohw-key", textKey);
11299
+ root.appendChild(textEl);
11300
+ }
11301
+ textEl.style.display = "";
11302
+ if (textEl.textContent !== displayAlt) textEl.textContent = displayAlt;
11303
+ if (!displayAlt.trim() || displayAlt === PLACEHOLDER_BUSINESS_NAME) {
11304
+ root.setAttribute("data-ohw-placeholder", "");
11305
+ } else {
11306
+ root.removeAttribute("data-ohw-placeholder");
11307
+ }
11308
+ });
11309
+ }
11310
+ function applyLogoHref(href) {
11311
+ const target = href.trim() || "/";
11312
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11313
+ ensureLogoHrefKey(root);
11314
+ if (root instanceof HTMLAnchorElement) {
11315
+ root.setAttribute("href", target);
11316
+ }
11317
+ });
11318
+ for (const key of LOGO_HREF_KEYS) setStoredLinkHref(key, target);
11319
+ }
11320
+ function readLogoIdentityFromDom() {
11321
+ let imageUrl = null;
11322
+ for (const key of LOGO_IMAGE_KEYS) {
11323
+ const el = document.querySelector(`[data-ohw-key="${key}"]`);
11324
+ const img = el instanceof HTMLImageElement ? el : el?.querySelector("img");
11325
+ const attrSrc = img?.getAttribute("src")?.trim() ?? "";
11326
+ if (attrSrc && !attrSrc.startsWith("data:") && img && img.style.display !== "none") {
11327
+ imageUrl = img.currentSrc || img.src;
11328
+ break;
11329
+ }
11330
+ }
11331
+ let text = PLACEHOLDER_BUSINESS_NAME;
11332
+ let isPlaceholder = true;
11333
+ for (const key of LOGO_TEXT_KEYS) {
11334
+ const el = document.querySelector(`[data-ohw-key="${key}"]`);
11335
+ if (el?.textContent?.trim()) {
11336
+ text = el.textContent.trim();
11337
+ const logoRoot2 = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
11338
+ isPlaceholder = logoRoot2?.hasAttribute("data-ohw-placeholder") === true || text === PLACEHOLDER_BUSINESS_NAME;
11339
+ break;
11340
+ }
11341
+ }
11342
+ if (imageUrl) {
11343
+ const logoImg = document.querySelector(
11344
+ '[data-ohw-key="nav-logo-image"], [data-ohw-key="footer-logo"]'
11345
+ );
11346
+ const alt = logoImg?.alt?.trim() || text;
11347
+ isPlaceholder = false;
11348
+ const hrefEl = document.querySelector(
11349
+ 'a[data-ohw-role="logo"], a[data-ohw-logo], [data-ohw-role="logo"]'
11350
+ );
11351
+ const href2 = (hrefEl instanceof HTMLAnchorElement ? hrefEl.getAttribute("href") : null) || hrefEl?.closest("a")?.getAttribute("href") || "/";
11352
+ return { text, isPlaceholder, imageUrl, href: href2, alt };
11353
+ }
11354
+ const logoRoot = document.querySelector('[data-ohw-role="logo"], [data-ohw-logo]');
11355
+ const href = (logoRoot instanceof HTMLAnchorElement ? logoRoot.getAttribute("href") : null) || logoRoot?.closest("a")?.getAttribute("href") || "/";
11356
+ return { text, isPlaceholder, imageUrl: null, href, alt: text };
11357
+ }
11358
+ function applyLogoFromContent(content) {
11359
+ 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);
11360
+ if (!hasLogoIdentity) return false;
11361
+ const logoText = content[LOGO_TEXT_KEYS[0]] ?? content[LOGO_TEXT_KEYS[1]] ?? readLogoIdentityFromDom().text;
11362
+ const logoAlt = content[LOGO_ALT_KEY] ?? logoText;
11363
+ const rawLogoImage = content[LOGO_IMAGE_URL_KEY] ?? content["footer-logo"] ?? content["footer-logo-image"] ?? null;
11364
+ const logoImageUrl = typeof rawLogoImage === "string" && rawLogoImage.trim() ? rawLogoImage.trim() : null;
11365
+ const imageExplicitlyCleared = LOGO_IMAGE_KEYS.some((key) => key in content) && !logoImageUrl;
11366
+ const logoIsPlaceholder = LOGO_PLACEHOLDER_KEY in content ? content[LOGO_PLACEHOLDER_KEY] !== "false" : !logoImageUrl && (!logoText.trim() || logoText === PLACEHOLDER_BUSINESS_NAME);
11367
+ if (logoImageUrl) {
11368
+ applyLogoImage(logoImageUrl, logoAlt);
11369
+ } else {
11370
+ if (imageExplicitlyCleared) applyLogoImage(null, logoAlt);
11371
+ applyLogoIdentity(logoText, logoIsPlaceholder);
11372
+ }
11373
+ const logoHref = content["nav-logo-href"] ?? content["footer-logo-href"] ?? content["logo-href"];
11374
+ if (typeof logoHref === "string" && logoHref.trim()) {
11375
+ applyLogoHref(logoHref);
11376
+ }
11377
+ return true;
11378
+ }
11379
+
11380
+ // src/lib/logo-size.ts
11381
+ var LOGO_SIZE_DEFAULTS = {
11382
+ navbar: 28,
11383
+ footer: 32
11384
+ };
11385
+ var LOGO_SIZE_MIN = 16;
11386
+ var LOGO_SIZE_MAX = 80;
11387
+ var LOGO_SIZE_DESKTOP_KEYS = {
11388
+ navbar: "nav-logo-size",
11389
+ footer: "footer-logo-size"
11390
+ };
11391
+ var LOGO_SIZE_MOBILE_KEYS = {
11392
+ navbar: "nav-logo-size-mobile",
11393
+ footer: "footer-logo-size-mobile"
11394
+ };
11395
+ var LOGO_SIZE_KEYS = [
11396
+ LOGO_SIZE_DESKTOP_KEYS.navbar,
11397
+ LOGO_SIZE_DESKTOP_KEYS.footer,
11398
+ LOGO_SIZE_MOBILE_KEYS.navbar,
11399
+ LOGO_SIZE_MOBILE_KEYS.footer
11400
+ ];
11401
+ function isFooterLogoRoot2(root) {
11402
+ return Boolean(root.closest("footer") || root.closest('[data-ohw-section="footer"]'));
11403
+ }
11404
+ function getLogoPlacement(root) {
11405
+ return isFooterLogoRoot2(root) ? "footer" : "navbar";
11406
+ }
11407
+ function parseLogoSizePx(raw, fallback) {
11408
+ if (raw == null || raw === "") return fallback;
11409
+ const n = Number.parseFloat(raw);
11410
+ if (!Number.isFinite(n)) return fallback;
11411
+ return Math.min(LOGO_SIZE_MAX, Math.max(LOGO_SIZE_MIN, Math.round(n)));
11412
+ }
11413
+ function isMobileLogoSizeFollowing(content, placement) {
11414
+ const raw = content[LOGO_SIZE_MOBILE_KEYS[placement]];
11415
+ return raw == null || raw.trim() === "";
11416
+ }
11417
+ function resolveDesktopLogoSize(content, placement) {
11418
+ return parseLogoSizePx(content[LOGO_SIZE_DESKTOP_KEYS[placement]], LOGO_SIZE_DEFAULTS[placement]);
11419
+ }
11420
+ function resolveMobileLogoSize(content, placement) {
11421
+ if (isMobileLogoSizeFollowing(content, placement)) {
11422
+ return resolveDesktopLogoSize(content, placement);
11423
+ }
11424
+ return parseLogoSizePx(
11425
+ content[LOGO_SIZE_MOBILE_KEYS[placement]],
11426
+ resolveDesktopLogoSize(content, placement)
11427
+ );
11428
+ }
11429
+ function setRootSizeVars(root, desktopPx, mobilePx, following) {
11430
+ root.style.setProperty("--ohw-logo-size", `${desktopPx}px`);
11431
+ if (following) {
11432
+ root.style.removeProperty("--ohw-logo-size-mobile");
11433
+ } else {
11434
+ root.style.setProperty("--ohw-logo-size-mobile", `${mobilePx}px`);
11435
+ }
11436
+ root.querySelectorAll("img").forEach((img) => {
11437
+ img.style.height = "";
11438
+ img.style.maxHeight = "none";
11439
+ img.style.width = "auto";
11440
+ img.style.objectFit = "contain";
11441
+ });
11442
+ }
11443
+ function applyLogoSizes(content) {
11444
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11445
+ const placement = getLogoPlacement(root);
11446
+ const desktop = resolveDesktopLogoSize(content, placement);
11447
+ const following = isMobileLogoSizeFollowing(content, placement);
11448
+ const mobile = following ? desktop : resolveMobileLogoSize(content, placement);
11449
+ setRootSizeVars(root, desktop, mobile, following);
11450
+ });
11451
+ }
11452
+ function applyLogoSizeToPlacement(placement, desktopPx, mobilePx, following) {
11453
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]').forEach((root) => {
11454
+ if (getLogoPlacement(root) !== placement) return;
11455
+ setRootSizeVars(root, desktopPx, mobilePx, following);
11456
+ });
11457
+ }
11458
+ function logoHasUploadedImage(logoEl) {
11459
+ if (logoEl.hasAttribute("data-ohw-placeholder")) return false;
11460
+ 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");
11461
+ if (!img) return false;
11462
+ const src = img.getAttribute("src")?.trim() ?? "";
11463
+ if (!src || src.startsWith("data:")) return false;
11464
+ if (img.style.display === "none") return false;
11465
+ return true;
11466
+ }
11467
+ function getLogoInteractionRect(logoEl) {
11468
+ if (logoHasUploadedImage(logoEl)) {
11469
+ 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");
11470
+ if (img) {
11471
+ const r2 = img.getBoundingClientRect();
11472
+ if (r2.width > 0 && r2.height > 0) return r2;
11473
+ }
11474
+ }
11475
+ const text = logoEl.querySelector(
11476
+ '[data-ohw-key="nav-logo-text"], [data-ohw-key="footer-logo-text"]'
11477
+ );
11478
+ if (text) {
11479
+ const style = window.getComputedStyle(text);
11480
+ if (style.display !== "none" && style.visibility !== "hidden") {
11481
+ const r2 = text.getBoundingClientRect();
11482
+ if (r2.width > 0 && r2.height > 0) return r2;
11483
+ }
11484
+ }
11485
+ return logoEl.getBoundingClientRect();
11486
+ }
11487
+ function readLogoSizeState(content, placement) {
11488
+ const desktopPx = resolveDesktopLogoSize(content, placement);
11489
+ const mobileFollowing = isMobileLogoSizeFollowing(content, placement);
11490
+ const mobilePx = mobileFollowing ? desktopPx : resolveMobileLogoSize(content, placement);
11491
+ return { desktopPx, mobilePx, mobileFollowing };
11492
+ }
11493
+
11494
+ // src/lib/site-wide-scope.ts
11495
+ function getLogoElement(el) {
11496
+ const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
11497
+ if (marked) return marked;
11498
+ if (el.closest('[data-ohw-editable="icon"]')) return null;
11499
+ const root = el.closest("nav, [data-ohw-nav-root], footer");
11500
+ if (!root) return null;
11501
+ const anchor = el.closest("a");
11502
+ if (anchor && root.contains(anchor) && !anchor.hasAttribute("data-ohw-href-key") && !anchor.closest("[data-ohw-nav-container]") && Boolean(anchor.querySelector("img") || anchor.matches("img"))) {
11503
+ return anchor;
11504
+ }
11505
+ const img = el.matches("img") ? el : null;
11506
+ if (img && !img.closest("[data-ohw-href-key]") && !img.closest("[data-ohw-nav-container]") && (img.closest("footer") || img.closest("nav, [data-ohw-nav-root]"))) {
11507
+ return img;
11508
+ }
11509
+ return null;
11510
+ }
11511
+ function isInFooter(el) {
11512
+ if (!el) return false;
11513
+ return Boolean(el.closest("footer") || el.closest('[data-ohw-section="footer"]'));
11514
+ }
11515
+ function isSiteWideElement(el) {
11516
+ if (!el) return false;
11517
+ if (getLogoElement(el)) return true;
11518
+ if (isInFooter(el)) return true;
11519
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-root")) {
11520
+ return true;
11521
+ }
11522
+ if (el.hasAttribute("data-ohw-href-key") && el.closest("nav, [data-ohw-nav-root], [data-ohw-nav-drawer], aside")) {
11523
+ return true;
11524
+ }
11525
+ if (el.closest('[data-ohw-role="navbar-button"]')) return true;
11526
+ return false;
11527
+ }
11528
+ function isSiteWideScopeActive(args) {
11529
+ return isSiteWideElement(args.selected) || isSiteWideElement(args.hoveredItem) || isSiteWideElement(args.hoveredNavContainer) || isSiteWideElement(args.active);
11530
+ }
11531
+
10820
11532
  // src/lib/add-footer-column.ts
10821
11533
  function buildFooterColumnEditContentPatch(result) {
10822
11534
  return {
@@ -10928,6 +11640,7 @@ function FloatingPanel({
10928
11640
  e.stopPropagation();
10929
11641
  const el = e.currentTarget;
10930
11642
  el.setPointerCapture(e.pointerId);
11643
+ document.documentElement.setAttribute("data-ohw-panel-dragging", "");
10931
11644
  dragRef.current = {
10932
11645
  pointerId: e.pointerId,
10933
11646
  startX: e.clientX,
@@ -10960,11 +11673,17 @@ function FloatingPanel({
10960
11673
  const drag = dragRef.current;
10961
11674
  if (!drag || drag.pointerId !== e.pointerId) return;
10962
11675
  dragRef.current = null;
11676
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
10963
11677
  try {
10964
11678
  e.currentTarget.releasePointerCapture(e.pointerId);
10965
11679
  } catch {
10966
11680
  }
10967
11681
  }, []);
11682
+ useEffect9(() => {
11683
+ if (open) return;
11684
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
11685
+ }, [open]);
11686
+ useEffect9(() => () => document.documentElement.removeAttribute("data-ohw-panel-dragging"), []);
10968
11687
  if (!open) return null;
10969
11688
  return /* @__PURE__ */ jsxs16(
10970
11689
  "div",
@@ -11031,16 +11750,127 @@ function FloatingPanel({
11031
11750
  );
11032
11751
  }
11033
11752
 
11034
- // src/ui/socials-display-panel.tsx
11753
+ // src/ui/logo-size-panel.tsx
11754
+ import { ArrowUpRight, Link as Link2 } from "lucide-react";
11035
11755
  import { jsx as jsx27, jsxs as jsxs17 } from "react/jsx-runtime";
11756
+ function SizeSlider({
11757
+ value,
11758
+ onChange
11759
+ }) {
11760
+ const pct = (value - LOGO_SIZE_MIN) / (LOGO_SIZE_MAX - LOGO_SIZE_MIN) * 100;
11761
+ return /* @__PURE__ */ jsxs17("div", { className: "flex w-full flex-col gap-3", children: [
11762
+ /* @__PURE__ */ jsxs17("div", { className: "flex w-full items-center gap-2 text-sm font-medium leading-5", children: [
11763
+ /* @__PURE__ */ jsx27("span", { className: "min-w-0 flex-1 text-foreground", children: "Size" }),
11764
+ /* @__PURE__ */ jsxs17("span", { className: "shrink-0 whitespace-nowrap text-muted-foreground", children: [
11765
+ value,
11766
+ " px"
11767
+ ] })
11768
+ ] }),
11769
+ /* @__PURE__ */ jsxs17("div", { className: "relative h-2 w-full rounded-full bg-primary-50", children: [
11770
+ /* @__PURE__ */ jsx27(
11771
+ "div",
11772
+ {
11773
+ className: "absolute inset-y-0 left-0 rounded-full bg-primary",
11774
+ style: { width: `${pct}%` }
11775
+ }
11776
+ ),
11777
+ /* @__PURE__ */ jsx27(
11778
+ "input",
11779
+ {
11780
+ type: "range",
11781
+ min: LOGO_SIZE_MIN,
11782
+ max: LOGO_SIZE_MAX,
11783
+ step: 1,
11784
+ value,
11785
+ "aria-label": "Logo size",
11786
+ className: cn(
11787
+ "absolute inset-0 h-full w-full cursor-pointer appearance-none bg-transparent",
11788
+ "[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:size-5",
11789
+ "[&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2",
11790
+ "[&::-webkit-slider-thumb]:border-primary [&::-webkit-slider-thumb]:bg-background",
11791
+ "[&::-moz-range-thumb]:size-5 [&::-moz-range-thumb]:rounded-full",
11792
+ "[&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-primary",
11793
+ "[&::-moz-range-thumb]:bg-background"
11794
+ ),
11795
+ onChange: (e) => onChange(Number(e.target.value))
11796
+ }
11797
+ )
11798
+ ] })
11799
+ ] });
11800
+ }
11801
+ function LogoSizePanel({
11802
+ viewport,
11803
+ sizePx,
11804
+ mobileFollowing = true,
11805
+ onSizeChange,
11806
+ onCustomizeMobile,
11807
+ onResetMobile,
11808
+ onUpdateEverywhere,
11809
+ className
11810
+ }) {
11811
+ const showFollowing = viewport === "mobile" && mobileFollowing;
11812
+ const showMobileSlider = viewport === "mobile" && !mobileFollowing;
11813
+ const showDesktopSlider = viewport === "desktop";
11814
+ return /* @__PURE__ */ jsxs17("div", { className: cn("flex w-full flex-col gap-4", className), children: [
11815
+ showFollowing ? /* @__PURE__ */ jsxs17("div", { className: "flex w-full flex-col gap-2", children: [
11816
+ /* @__PURE__ */ jsxs17("div", { className: "flex items-start gap-1", children: [
11817
+ /* @__PURE__ */ jsx27(Link2, { size: 16, className: "mt-0.5 shrink-0 text-foreground", "aria-hidden": true }),
11818
+ /* @__PURE__ */ jsx27("p", { className: "text-sm font-semibold leading-5 text-foreground", children: "Following desktop size" })
11819
+ ] }),
11820
+ /* @__PURE__ */ jsx27("p", { className: "text-sm leading-5 text-muted-foreground", children: "Mobile uses the desktop size until you customize it. Change the desktop size and it follows automatically." }),
11821
+ /* @__PURE__ */ jsx27(
11822
+ Button,
11823
+ {
11824
+ type: "button",
11825
+ variant: "outline",
11826
+ size: "sm",
11827
+ className: "h-9 w-full min-w-0 cursor-pointer",
11828
+ onClick: onCustomizeMobile,
11829
+ children: "Customize for mobile"
11830
+ }
11831
+ )
11832
+ ] }) : null,
11833
+ showDesktopSlider || showMobileSlider ? /* @__PURE__ */ jsx27(SizeSlider, { value: sizePx, onChange: onSizeChange }) : null,
11834
+ showMobileSlider ? /* @__PURE__ */ jsx27(
11835
+ Button,
11836
+ {
11837
+ type: "button",
11838
+ variant: "outline",
11839
+ size: "sm",
11840
+ className: "h-9 w-full min-w-0 cursor-pointer",
11841
+ onClick: onResetMobile,
11842
+ children: "Reset to desktop size"
11843
+ }
11844
+ ) : null,
11845
+ /* @__PURE__ */ jsx27("div", { className: "h-px w-full bg-border", role: "separator" }),
11846
+ /* @__PURE__ */ jsxs17(
11847
+ Button,
11848
+ {
11849
+ type: "button",
11850
+ variant: "outline",
11851
+ size: "sm",
11852
+ className: "h-9 w-full min-w-0 cursor-pointer gap-1",
11853
+ onClick: onUpdateEverywhere,
11854
+ children: [
11855
+ "Update logo everywhere",
11856
+ /* @__PURE__ */ jsx27(ArrowUpRight, { size: 16, "aria-hidden": true })
11857
+ ]
11858
+ }
11859
+ ),
11860
+ /* @__PURE__ */ jsx27("p", { className: "text-sm leading-5 text-muted-foreground", children: "Matches the right version to your background." })
11861
+ ] });
11862
+ }
11863
+
11864
+ // src/ui/socials-display-panel.tsx
11865
+ import { jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
11036
11866
  function DisplaySwitch({
11037
11867
  label,
11038
11868
  checked,
11039
11869
  disabled,
11040
11870
  onChange
11041
11871
  }) {
11042
- return /* @__PURE__ */ jsxs17("div", { className: "flex w-full items-center gap-2", children: [
11043
- /* @__PURE__ */ jsx27(
11872
+ return /* @__PURE__ */ jsxs18("div", { className: "flex w-full items-center gap-2", children: [
11873
+ /* @__PURE__ */ jsx28(
11044
11874
  "span",
11045
11875
  {
11046
11876
  className: cn(
@@ -11050,7 +11880,7 @@ function DisplaySwitch({
11050
11880
  children: label
11051
11881
  }
11052
11882
  ),
11053
- /* @__PURE__ */ jsx27(
11883
+ /* @__PURE__ */ jsx28(
11054
11884
  "button",
11055
11885
  {
11056
11886
  type: "button",
@@ -11064,7 +11894,7 @@ function DisplaySwitch({
11064
11894
  checked ? "bg-primary" : "bg-primary-50",
11065
11895
  disabled ? "cursor-default opacity-50" : "cursor-pointer"
11066
11896
  ),
11067
- children: /* @__PURE__ */ jsx27(
11897
+ children: /* @__PURE__ */ jsx28(
11068
11898
  "span",
11069
11899
  {
11070
11900
  className: cn(
@@ -11078,8 +11908,8 @@ function DisplaySwitch({
11078
11908
  ] });
11079
11909
  }
11080
11910
  function SocialsDisplayPanel({ display, onChange, className }) {
11081
- return /* @__PURE__ */ jsxs17("div", { className: cn("flex w-full flex-col gap-3", className), children: [
11082
- /* @__PURE__ */ jsx27(
11911
+ return /* @__PURE__ */ jsxs18("div", { className: cn("flex w-full flex-col gap-3", className), children: [
11912
+ /* @__PURE__ */ jsx28(
11083
11913
  DisplaySwitch,
11084
11914
  {
11085
11915
  label: "Text",
@@ -11088,7 +11918,7 @@ function SocialsDisplayPanel({ display, onChange, className }) {
11088
11918
  onChange: (text) => onChange({ ...display, text })
11089
11919
  }
11090
11920
  ),
11091
- /* @__PURE__ */ jsx27(
11921
+ /* @__PURE__ */ jsx28(
11092
11922
  DisplaySwitch,
11093
11923
  {
11094
11924
  label: "Icon",
@@ -11649,7 +12479,7 @@ function useNavItemDrag({
11649
12479
 
11650
12480
  // src/ui/footer-container-chrome.tsx
11651
12481
  import { Plus as Plus2 } from "lucide-react";
11652
- import { jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
12482
+ import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
11653
12483
  function FooterContainerChrome({
11654
12484
  rect,
11655
12485
  onAdd,
@@ -11657,7 +12487,7 @@ function FooterContainerChrome({
11657
12487
  }) {
11658
12488
  const chromeGap = 6;
11659
12489
  const buttonMargin = 7;
11660
- return /* @__PURE__ */ jsx28(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx28(
12490
+ return /* @__PURE__ */ jsx29(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx29(
11661
12491
  "div",
11662
12492
  {
11663
12493
  "data-ohw-footer-container-chrome": "",
@@ -11669,8 +12499,8 @@ function FooterContainerChrome({
11669
12499
  width: rect.width + chromeGap * 2,
11670
12500
  height: rect.height + chromeGap * 2
11671
12501
  },
11672
- children: /* @__PURE__ */ jsxs18(Tooltip, { children: [
11673
- /* @__PURE__ */ jsx28(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx28(
12502
+ children: /* @__PURE__ */ jsxs19(Tooltip, { children: [
12503
+ /* @__PURE__ */ jsx29(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx29(
11674
12504
  "button",
11675
12505
  {
11676
12506
  type: "button",
@@ -11689,10 +12519,10 @@ function FooterContainerChrome({
11689
12519
  if (addDisabled) return;
11690
12520
  onAdd();
11691
12521
  },
11692
- children: /* @__PURE__ */ jsx28(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
12522
+ children: /* @__PURE__ */ jsx29(Plus2, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
11693
12523
  }
11694
12524
  ) }),
11695
- /* @__PURE__ */ jsx28(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
12525
+ /* @__PURE__ */ jsx29(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
11696
12526
  ] })
11697
12527
  }
11698
12528
  ) });
@@ -11875,6 +12705,18 @@ function collectEditableNodes(extraContent, root = document) {
11875
12705
  }
11876
12706
  if (extraContent && !isScoped) {
11877
12707
  applyNavFooterDeleteOverrides(byKey, extraContent);
12708
+ for (const key of LOGO_IMAGE_KEYS) {
12709
+ if (!(key in extraContent)) continue;
12710
+ byKey.set(key, { key, type: "image", text: extraContent[key] ?? "" });
12711
+ }
12712
+ for (const key of [LOGO_PLACEHOLDER_KEY, LOGO_ALT_KEY]) {
12713
+ if (!(key in extraContent)) continue;
12714
+ byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
12715
+ }
12716
+ for (const key of LOGO_SIZE_KEYS) {
12717
+ if (!(key in extraContent)) continue;
12718
+ byKey.set(key, { key, type: "meta", text: extraContent[key] ?? "" });
12719
+ }
11878
12720
  }
11879
12721
  return Array.from(byKey.values());
11880
12722
  }
@@ -12141,13 +12983,13 @@ function deleteSelectedNavFooterItem(deps) {
12141
12983
 
12142
12984
  // src/ui/navbar-container-chrome.tsx
12143
12985
  import { Plus as Plus3 } from "lucide-react";
12144
- import { jsx as jsx29 } from "react/jsx-runtime";
12986
+ import { jsx as jsx30 } from "react/jsx-runtime";
12145
12987
  function NavbarContainerChrome({
12146
12988
  rect,
12147
12989
  onAdd
12148
12990
  }) {
12149
12991
  const chromeGap = 6;
12150
- return /* @__PURE__ */ jsx29(
12992
+ return /* @__PURE__ */ jsx30(
12151
12993
  "div",
12152
12994
  {
12153
12995
  "data-ohw-navbar-container-chrome": "",
@@ -12159,7 +13001,7 @@ function NavbarContainerChrome({
12159
13001
  width: rect.width + chromeGap * 2,
12160
13002
  height: rect.height + chromeGap * 2
12161
13003
  },
12162
- children: /* @__PURE__ */ jsx29(
13004
+ children: /* @__PURE__ */ jsx30(
12163
13005
  "button",
12164
13006
  {
12165
13007
  type: "button",
@@ -12176,7 +13018,7 @@ function NavbarContainerChrome({
12176
13018
  e.stopPropagation();
12177
13019
  onAdd();
12178
13020
  },
12179
- children: /* @__PURE__ */ jsx29(Plus3, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
13021
+ children: /* @__PURE__ */ jsx30(Plus3, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
12180
13022
  }
12181
13023
  )
12182
13024
  }
@@ -12185,7 +13027,7 @@ function NavbarContainerChrome({
12185
13027
 
12186
13028
  // src/ui/drop-indicator.tsx
12187
13029
  import * as React10 from "react";
12188
- import { jsx as jsx30 } from "react/jsx-runtime";
13030
+ import { jsx as jsx31 } from "react/jsx-runtime";
12189
13031
  var dropIndicatorVariants = cva(
12190
13032
  "ov-gap-line pointer-events-none shrink-0 transition-opacity duration-150",
12191
13033
  {
@@ -12209,7 +13051,7 @@ var dropIndicatorVariants = cva(
12209
13051
  );
12210
13052
  var DropIndicator = React10.forwardRef(
12211
13053
  ({ className, direction, state, ...props }, ref) => {
12212
- return /* @__PURE__ */ jsx30(
13054
+ return /* @__PURE__ */ jsx31(
12213
13055
  "div",
12214
13056
  {
12215
13057
  ref,
@@ -12226,7 +13068,7 @@ var DropIndicator = React10.forwardRef(
12226
13068
  DropIndicator.displayName = "DropIndicator";
12227
13069
 
12228
13070
  // src/ui/badge.tsx
12229
- import { jsx as jsx31 } from "react/jsx-runtime";
13071
+ import { jsx as jsx32 } from "react/jsx-runtime";
12230
13072
  var badgeVariants = cva(
12231
13073
  "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",
12232
13074
  {
@@ -12244,12 +13086,12 @@ var badgeVariants = cva(
12244
13086
  }
12245
13087
  );
12246
13088
  function Badge({ className, variant, ...props }) {
12247
- return /* @__PURE__ */ jsx31("div", { className: cn(badgeVariants({ variant }), className), ...props });
13089
+ return /* @__PURE__ */ jsx32("div", { className: cn(badgeVariants({ variant }), className), ...props });
12248
13090
  }
12249
13091
 
12250
13092
  // src/OhhwellsBridge.tsx
12251
- import { Link as Link2 } from "lucide-react";
12252
- import { Fragment as Fragment8, jsx as jsx32, jsxs as jsxs19 } from "react/jsx-runtime";
13093
+ import { Link as Link3 } from "lucide-react";
13094
+ import { Fragment as Fragment8, jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
12253
13095
  var PRIMARY3 = "#0885FE";
12254
13096
  var IMAGE_FADE_MS = 300;
12255
13097
  function runOpacityFade(el, onDone) {
@@ -12343,21 +13185,10 @@ function parseSchedulingInsertAfter(insertAfter) {
12343
13185
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
12344
13186
  };
12345
13187
  }
12346
- function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
12347
- const parsed = parseSchedulingInsertAfter(insertAfter);
12348
- const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
12349
- const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
12350
- return { effectiveInsertAfter, insertBefore };
12351
- }
12352
- function getSchedulingMountPoint(insertAfter) {
12353
- const { anchor } = parseSchedulingInsertAfter(insertAfter);
12354
- let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
12355
- if (!anchorEl && anchor === "scheduling") {
12356
- const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
12357
- anchorEl = widgets.at(-1) ?? null;
12358
- }
12359
- if (!anchorEl) return null;
12360
- return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
13188
+ function resolveEntryAnchor(entry) {
13189
+ if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
13190
+ const parsed = parseSchedulingInsertAfter(entry.insertAfter);
13191
+ return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
12361
13192
  }
12362
13193
  function schedulingMountDepth(insertAfter) {
12363
13194
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -12374,8 +13205,7 @@ function getPageSchedulingEntries(raw) {
12374
13205
  }
12375
13206
  }
12376
13207
  function isSchedulingWidgetMissing(entry) {
12377
- const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
12378
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
13208
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
12379
13209
  }
12380
13210
  function hasMissingSchedulingWidgets(entries) {
12381
13211
  return entries.some(isSchedulingWidgetMissing);
@@ -12405,16 +13235,17 @@ function initSectionsFromContent(content, removeExisting = false) {
12405
13235
  } catch {
12406
13236
  }
12407
13237
  }
12408
- function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
12409
- const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
12410
- const sectionId = schedulingSectionId(effectiveInsertAfter);
13238
+ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
13239
+ const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
13240
+ const sectionId = schedulingSectionId(widgetId);
12411
13241
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
12412
- const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
12413
- if (!mountPoint) return false;
13242
+ const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
13243
+ if (!anchorEl) return false;
13244
+ const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
12414
13245
  const container = document.createElement("div");
12415
13246
  container.dataset.ohwSectionContainer = "scheduling";
12416
- if (insertBefore) {
12417
- const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
13247
+ if (beforeId) {
13248
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
12418
13249
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
12419
13250
  if (!beforePoint) return false;
12420
13251
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -12425,19 +13256,25 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
12425
13256
  }
12426
13257
  tail.insertAdjacentElement("afterend", container);
12427
13258
  }
12428
- const root = createRoot2(container);
12429
- flushSync2(() => {
12430
- root.render(
12431
- /* @__PURE__ */ jsx32(
12432
- SchedulingWidget,
12433
- {
12434
- notifyOnConnect,
12435
- initialScheduleId: scheduleId,
12436
- insertAfter: effectiveInsertAfter
12437
- }
12438
- )
12439
- );
12440
- });
13259
+ try {
13260
+ const root = createRoot2(container);
13261
+ flushSync2(() => {
13262
+ root.render(
13263
+ /* @__PURE__ */ jsx33(
13264
+ SchedulingWidget,
13265
+ {
13266
+ notifyOnConnect,
13267
+ initialScheduleId: scheduleId,
13268
+ insertAfter: widgetId
13269
+ }
13270
+ )
13271
+ );
13272
+ });
13273
+ } catch (err) {
13274
+ console.error("[ow:scheduling] render threw", err);
13275
+ container.remove();
13276
+ return false;
13277
+ }
12441
13278
  const tracker = getSectionsTracker();
12442
13279
  let sections = [];
12443
13280
  try {
@@ -12445,10 +13282,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
12445
13282
  } catch {
12446
13283
  }
12447
13284
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
12448
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
13285
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
12449
13286
  sections.push({
12450
13287
  type: "scheduling",
12451
- insertAfter: effectiveInsertAfter,
13288
+ insertAfter: widgetId,
13289
+ anchorId,
13290
+ beforeId: beforeId ?? null,
12452
13291
  pagePath: window.location.pathname,
12453
13292
  ...scheduleId ? { scheduleId } : {}
12454
13293
  });
@@ -12462,7 +13301,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
12462
13301
  for (let i = pending.length - 1; i >= 0; i--) {
12463
13302
  const entry = pending[i];
12464
13303
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
12465
- if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId)) {
13304
+ const { anchorId, beforeId } = resolveEntryAnchor(entry);
13305
+ if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
12466
13306
  pending.splice(i, 1);
12467
13307
  }
12468
13308
  }
@@ -12606,6 +13446,13 @@ function isInsideLinkEditor(target) {
12606
13446
  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"]')
12607
13447
  );
12608
13448
  }
13449
+ function isInsideFloatingPanel(target) {
13450
+ return Boolean(target.closest("[data-ohw-floating-panel]"));
13451
+ }
13452
+ function isPointOverFloatingPanel(clientX, clientY) {
13453
+ const el = document.elementFromPoint(clientX, clientY);
13454
+ return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
13455
+ }
12609
13456
  function getHrefKeyFromElement(el) {
12610
13457
  if (!el) return null;
12611
13458
  const anchor = el.closest("[data-ohw-href-key]");
@@ -12743,6 +13590,11 @@ function isNavbarLinksContainer2(el) {
12743
13590
  function getFooterColumn(el) {
12744
13591
  return el.closest("[data-ohw-footer-col], [data-ohw-footer-column]");
12745
13592
  }
13593
+ function isFooterAddItemDisabled(selected) {
13594
+ if (!selected) return false;
13595
+ const column = resolveFooterColumnForAdd(selected);
13596
+ return column ? !canAddFooterItem(column) : false;
13597
+ }
12746
13598
  function resolveFooterColumnSelectionTarget(target, clientX, clientY) {
12747
13599
  if (getNavigationItemAnchor(target)) return null;
12748
13600
  const column = getFooterColumn(target);
@@ -12838,7 +13690,7 @@ function getNavigationSelectionParent(el) {
12838
13690
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
12839
13691
  return getFooterLinksContainer();
12840
13692
  }
12841
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
13693
+ 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)) {
12842
13694
  return getNavigationRoot(el);
12843
13695
  }
12844
13696
  return null;
@@ -13056,6 +13908,9 @@ var ICONS = {
13056
13908
  var SELECTION_CHROME_GAP2 = 4;
13057
13909
  var TOOLBAR_STROKE_GAP2 = 4;
13058
13910
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
13911
+ var HOVER_STROKE_WIDTH = 1.5;
13912
+ var TEXT_HOVER_SIDE_PAD = 4;
13913
+ var CHROME_PRIMARY = `var(--ohw-primary, ${PRIMARY3})`;
13059
13914
  var TOOLBAR_GROUPS = [
13060
13915
  [
13061
13916
  { cmd: "bold", title: "Bold" },
@@ -13081,7 +13936,7 @@ function EditGlowChrome({
13081
13936
  hideHandle = false
13082
13937
  }) {
13083
13938
  const GAP = SELECTION_CHROME_GAP2;
13084
- return /* @__PURE__ */ jsxs19(
13939
+ return /* @__PURE__ */ jsxs20(
13085
13940
  "div",
13086
13941
  {
13087
13942
  ref: elRef,
@@ -13096,7 +13951,7 @@ function EditGlowChrome({
13096
13951
  zIndex: 2147483646
13097
13952
  },
13098
13953
  children: [
13099
- /* @__PURE__ */ jsx32(
13954
+ /* @__PURE__ */ jsx33(
13100
13955
  "div",
13101
13956
  {
13102
13957
  style: {
@@ -13109,7 +13964,7 @@ function EditGlowChrome({
13109
13964
  }
13110
13965
  }
13111
13966
  ),
13112
- reorderHrefKey && !hideHandle && /* @__PURE__ */ jsx32(
13967
+ reorderHrefKey && !hideHandle && /* @__PURE__ */ jsx33(
13113
13968
  "div",
13114
13969
  {
13115
13970
  "data-ohw-drag-handle-container": "",
@@ -13121,7 +13976,7 @@ function EditGlowChrome({
13121
13976
  transform: "translate(calc(-100% - 7px), -50%)",
13122
13977
  pointerEvents: dragDisabled ? "none" : "auto"
13123
13978
  },
13124
- children: /* @__PURE__ */ jsx32(
13979
+ children: /* @__PURE__ */ jsx33(
13125
13980
  DragHandle,
13126
13981
  {
13127
13982
  "aria-label": `Reorder ${reorderHrefKey}`,
@@ -13331,7 +14186,7 @@ function FloatingToolbar({
13331
14186
  return () => ro.disconnect();
13332
14187
  }, [showEditLink, activeCommands]);
13333
14188
  const { top, left, transform } = calcToolbarPos(rect, parentScroll, measuredW);
13334
- return /* @__PURE__ */ jsx32(
14189
+ return /* @__PURE__ */ jsx33(
13335
14190
  "div",
13336
14191
  {
13337
14192
  ref: setRefs,
@@ -13343,12 +14198,12 @@ function FloatingToolbar({
13343
14198
  zIndex: 2147483647,
13344
14199
  pointerEvents: "auto"
13345
14200
  },
13346
- children: /* @__PURE__ */ jsxs19(CustomToolbar, { children: [
13347
- TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs19(React11.Fragment, { children: [
13348
- gi > 0 && /* @__PURE__ */ jsx32(CustomToolbarDivider, {}),
14201
+ children: /* @__PURE__ */ jsxs20(CustomToolbar, { children: [
14202
+ TOOLBAR_GROUPS.map((btns, gi) => /* @__PURE__ */ jsxs20(React11.Fragment, { children: [
14203
+ gi > 0 && /* @__PURE__ */ jsx33(CustomToolbarDivider, {}),
13349
14204
  btns.map((btn) => {
13350
14205
  const isActive = activeCommands.has(btn.cmd);
13351
- return /* @__PURE__ */ jsx32(
14206
+ return /* @__PURE__ */ jsx33(
13352
14207
  CustomToolbarButton,
13353
14208
  {
13354
14209
  title: btn.title,
@@ -13357,7 +14212,7 @@ function FloatingToolbar({
13357
14212
  e.preventDefault();
13358
14213
  onCommand(btn.cmd);
13359
14214
  },
13360
- children: /* @__PURE__ */ jsx32(
14215
+ children: /* @__PURE__ */ jsx33(
13361
14216
  "svg",
13362
14217
  {
13363
14218
  width: "16",
@@ -13378,7 +14233,7 @@ function FloatingToolbar({
13378
14233
  );
13379
14234
  })
13380
14235
  ] }, gi)),
13381
- showEditLink ? /* @__PURE__ */ jsx32(
14236
+ showEditLink ? /* @__PURE__ */ jsx33(
13382
14237
  CustomToolbarButton,
13383
14238
  {
13384
14239
  type: "button",
@@ -13392,7 +14247,7 @@ function FloatingToolbar({
13392
14247
  e.preventDefault();
13393
14248
  e.stopPropagation();
13394
14249
  },
13395
- children: /* @__PURE__ */ jsx32(Link2, { className: "size-4 shrink-0", "aria-hidden": true })
14250
+ children: /* @__PURE__ */ jsx33(Link3, { className: "size-4 shrink-0", "aria-hidden": true })
13396
14251
  }
13397
14252
  ) : null
13398
14253
  ] })
@@ -13409,7 +14264,7 @@ function StateToggle({
13409
14264
  states,
13410
14265
  onStateChange
13411
14266
  }) {
13412
- return /* @__PURE__ */ jsx32(
14267
+ return /* @__PURE__ */ jsx33(
13413
14268
  ToggleGroup,
13414
14269
  {
13415
14270
  "data-ohw-state-toggle": "",
@@ -13423,11 +14278,12 @@ function StateToggle({
13423
14278
  left: rect.right - 8,
13424
14279
  transform: "translateX(-100%)"
13425
14280
  },
13426
- children: states.map((state) => /* @__PURE__ */ jsx32(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
14281
+ children: states.map((state) => /* @__PURE__ */ jsx33(ToggleGroupItem, { value: state, size: "sm", children: state }, state))
13427
14282
  }
13428
14283
  );
13429
14284
  }
13430
14285
  var contentCache = /* @__PURE__ */ new Map();
14286
+ var fetchedContentPaths = /* @__PURE__ */ new Set();
13431
14287
  function resolveSubdomain(subdomainFromQuery) {
13432
14288
  if (subdomainFromQuery) return subdomainFromQuery;
13433
14289
  if (typeof window !== "undefined") {
@@ -13522,8 +14378,14 @@ function OhhwellsBridge() {
13522
14378
  });
13523
14379
  const selectFrameRef = useRef9(() => {
13524
14380
  });
14381
+ const selectLogoRef = useRef9(() => {
14382
+ });
14383
+ const openLogoSizePanelRef = useRef9(() => {
14384
+ });
13525
14385
  const deselectRef = useRef9(() => {
13526
14386
  });
14387
+ const closeFloatingPanelOnlyRef = useRef9(() => {
14388
+ });
13527
14389
  const reselectNavigationItemRef = useRef9(() => {
13528
14390
  });
13529
14391
  const commitNavigationTextEditRef = useRef9(() => {
@@ -13556,17 +14418,34 @@ function OhhwellsBridge() {
13556
14418
  const [hoveredNavContainerRect, setHoveredNavContainerRect] = useState11(null);
13557
14419
  const hoveredItemElRef = useRef9(null);
13558
14420
  const [hoveredItemRect, setHoveredItemRect] = useState11(null);
14421
+ const [hoveredTextRect, setHoveredTextRect] = useState11(null);
14422
+ useEffect12(() => {
14423
+ const sync = () => {
14424
+ const el = document.querySelector(
14425
+ "[data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key])"
14426
+ );
14427
+ const target = el && !el.closest("[data-ohw-href-key]") ? el : null;
14428
+ if (!target) {
14429
+ setHoveredTextRect(null);
14430
+ return;
14431
+ }
14432
+ const r2 = target.getBoundingClientRect();
14433
+ setHoveredTextRect(new DOMRect(r2.x - TEXT_HOVER_SIDE_PAD, r2.y, r2.width + TEXT_HOVER_SIDE_PAD * 2, r2.height));
14434
+ };
14435
+ const observer = new MutationObserver(sync);
14436
+ observer.observe(document.documentElement, {
14437
+ attributes: true,
14438
+ attributeFilter: ["data-ohw-hovered"],
14439
+ subtree: true
14440
+ });
14441
+ return () => observer.disconnect();
14442
+ }, []);
13559
14443
  const siblingHintElRef = useRef9(null);
13560
14444
  const [siblingHintRect, setSiblingHintRect] = useState11(null);
13561
14445
  const [siblingHintRects, setSiblingHintRects] = useState11([]);
13562
14446
  const [isItemDragging, setIsItemDragging] = useState11(false);
13563
14447
  const [isFooterFrameSelection, setIsFooterFrameSelection] = useState11(false);
13564
14448
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
13565
- const [floatingPanel, setFloatingPanel] = useState11(null);
13566
- const floatingPanelOpenRef = useRef9(false);
13567
- floatingPanelOpenRef.current = floatingPanel !== null;
13568
- const [floatingPanelPos, setFloatingPanelPos] = useState11(null);
13569
- const [parentScrollSnap, setParentScrollSnap] = useState11(null);
13570
14449
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState11(null);
13571
14450
  const [footerHeadingVisible, setFooterHeadingVisible] = useState11(null);
13572
14451
  const footerDragRef = useRef9(null);
@@ -13581,7 +14460,16 @@ function OhhwellsBridge() {
13581
14460
  const addNavAfterAnchorRef = useRef9(null);
13582
14461
  const editContentRef = useRef9({});
13583
14462
  const aiSectionsRef = useRef9("");
14463
+ const brandKitRef = useRef9("");
14464
+ const stylesRef = useRef9("");
13584
14465
  const pendingDeleteUndoRef = useRef9(null);
14466
+ const [floatingPanel, setFloatingPanel] = useState11(null);
14467
+ const floatingPanelOpenRef = useRef9(false);
14468
+ const setFloatingPanelRef = useRef9(setFloatingPanel);
14469
+ const [floatingPanelPos, setFloatingPanelPos] = useState11(null);
14470
+ const [logoSizeDraft, setLogoSizeDraft] = useState11(null);
14471
+ const [editorViewport, setEditorViewport] = useState11("desktop");
14472
+ const [parentScrollSnap, setParentScrollSnap] = useState11(null);
13585
14473
  const [sitePages, setSitePages] = useState11([]);
13586
14474
  const [sectionsByPath, setSectionsByPath] = useState11({});
13587
14475
  const sectionsPrefetchGenRef = useRef9(0);
@@ -13590,7 +14478,18 @@ function OhhwellsBridge() {
13590
14478
  const linkPopoverOpenRef = useRef9(false);
13591
14479
  const linkPopoverGraceUntilRef = useRef9(0);
13592
14480
  setLinkPopoverRef.current = setLinkPopover;
14481
+ setFloatingPanelRef.current = setFloatingPanel;
13593
14482
  linkPopoverSessionRef.current = linkPopover;
14483
+ floatingPanelOpenRef.current = Boolean(floatingPanel);
14484
+ useEffect12(() => {
14485
+ const syncViewport = () => {
14486
+ const next = window.innerWidth <= 480 ? "mobile" : "desktop";
14487
+ setEditorViewport((prev) => prev === next ? prev : next);
14488
+ };
14489
+ syncViewport();
14490
+ window.addEventListener("resize", syncViewport);
14491
+ return () => window.removeEventListener("resize", syncViewport);
14492
+ }, []);
13594
14493
  const {
13595
14494
  navDragRef,
13596
14495
  navDropSlots,
@@ -13813,6 +14712,10 @@ function OhhwellsBridge() {
13813
14712
  setIsItemDragging(false);
13814
14713
  hoveredNavContainerRef.current = null;
13815
14714
  setHoveredNavContainerRect(null);
14715
+ hoveredItemElRef.current = null;
14716
+ setHoveredItemRect(null);
14717
+ setFloatingPanel(null);
14718
+ setLogoSizeDraft(null);
13816
14719
  if (!activeElRef.current) {
13817
14720
  setNavGroupForceOpen(null, false);
13818
14721
  setToolbarRect(null);
@@ -14009,6 +14912,14 @@ function OhhwellsBridge() {
14009
14912
  if (!selected) return;
14010
14913
  const socialsRow = isSocialsRow(selected) ? selected : findSocialsRow(selected);
14011
14914
  if (socialsRow) {
14915
+ if (!canAddSocialItem(socialsRow)) {
14916
+ postToParent2({
14917
+ type: "ow:toast",
14918
+ title: `Maximum ${MAX_SOCIAL_ITEMS_PER_ROW} social icons`,
14919
+ toastType: "error"
14920
+ });
14921
+ return;
14922
+ }
14012
14923
  const after = getSocialItem(selected);
14013
14924
  const result2 = insertSocialItem(socialsRow, after, editContentRef.current);
14014
14925
  if (!result2) return;
@@ -14030,10 +14941,16 @@ function OhhwellsBridge() {
14030
14941
  return;
14031
14942
  }
14032
14943
  if (toolbarVariantRef.current === "select-frame" && isFooterFrameSelection) {
14033
- if (!selected.hasAttribute("data-ohw-footer-col") && !selected.closest("[data-ohw-footer-col]")) {
14034
- }
14035
- const column = (selected.hasAttribute("data-ohw-footer-col") ? selected : null) ?? selected.closest("[data-ohw-footer-col]") ?? (listFooterColumns().includes(selected) ? selected : null);
14944
+ const column = resolveFooterColumnForAdd(selected);
14036
14945
  if (!column) return;
14946
+ if (!canAddFooterItem(column)) {
14947
+ postToParent2({
14948
+ type: "ow:toast",
14949
+ title: `Maximum ${MAX_FOOTER_ITEMS_PER_COLUMN} items per column`,
14950
+ toastType: "error"
14951
+ });
14952
+ return;
14953
+ }
14037
14954
  const result2 = insertFooterItem(column, "/", "New link", null);
14038
14955
  applyLinkByKey(result2.hrefKey, result2.href);
14039
14956
  document.querySelectorAll(`[data-ohw-key="${result2.labelKey}"]`).forEach((el) => {
@@ -14504,6 +15421,8 @@ function OhhwellsBridge() {
14504
15421
  setToolbarRect(anchor.getBoundingClientRect());
14505
15422
  setToolbarShowEditLink(false);
14506
15423
  setActiveCommands(/* @__PURE__ */ new Set());
15424
+ setFloatingPanel(null);
15425
+ setLogoSizeDraft(null);
14507
15426
  }, [deactivate, markSelected]);
14508
15427
  const selectFrame = useCallback7((el) => {
14509
15428
  if (!isNavigationContainer(el)) return;
@@ -14553,7 +15472,51 @@ function OhhwellsBridge() {
14553
15472
  setToolbarRect(el.getBoundingClientRect());
14554
15473
  setToolbarShowEditLink(false);
14555
15474
  setActiveCommands(/* @__PURE__ */ new Set());
15475
+ setFloatingPanel(null);
15476
+ setLogoSizeDraft(null);
14556
15477
  }, [deactivate, markSelected, postToParent2]);
15478
+ const selectLogo = useCallback7(
15479
+ (logoEl) => {
15480
+ if (activeElRef.current) deactivate();
15481
+ selectedElRef.current = logoEl;
15482
+ selectedHrefKeyRef.current = null;
15483
+ selectedFooterColAttrRef.current = null;
15484
+ markSelected(logoEl);
15485
+ setSelectedIsCta(false);
15486
+ setSelectedIsSocial(false);
15487
+ setSelectedIsSocialsRow(false);
15488
+ clearHrefKeyHover(logoEl);
15489
+ hoveredNavContainerRef.current = null;
15490
+ setHoveredNavContainerRect(null);
15491
+ setHoveredItemRect(null);
15492
+ hoveredItemElRef.current = null;
15493
+ siblingHintElRef.current = null;
15494
+ setSiblingHintRect(null);
15495
+ setSiblingHintRects([]);
15496
+ setIsItemDragging(false);
15497
+ setReorderHrefKey(null);
15498
+ setReorderDragDisabled(false);
15499
+ setIsFooterFrameSelection(false);
15500
+ setToolbarVariant("logo");
15501
+ setToolbarRect(getLogoInteractionRect(logoEl));
15502
+ setToolbarShowEditLink(false);
15503
+ setActiveCommands(/* @__PURE__ */ new Set());
15504
+ },
15505
+ [deactivate, markSelected]
15506
+ );
15507
+ const openLogoSizePanel = useCallback7((logoEl) => {
15508
+ const placement = getLogoPlacement(logoEl);
15509
+ const draft = readLogoSizeState(editContentRef.current, placement);
15510
+ setLogoSizeDraft(draft);
15511
+ setParentScrollSnap(parentScrollRef.current);
15512
+ setFloatingPanel({
15513
+ key: `logo-size:${placement}`,
15514
+ title: "Logo",
15515
+ context: placement === "navbar" ? "Navbar" : "Footer",
15516
+ kind: "logo-size",
15517
+ placement
15518
+ });
15519
+ }, []);
14557
15520
  const openSocialsDisplayPanel = useCallback7((row) => {
14558
15521
  setParentScrollSnap(parentScrollRef.current);
14559
15522
  setFloatingPanel({
@@ -14589,13 +15552,53 @@ function OhhwellsBridge() {
14589
15552
  );
14590
15553
  const closeFloatingPanelOnly = useCallback7(() => {
14591
15554
  setFloatingPanel(null);
15555
+ setLogoSizeDraft(null);
14592
15556
  }, []);
14593
- const closeFloatingPanelOnlyRef = useRef9(closeFloatingPanelOnly);
14594
- closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
14595
15557
  const closeFloatingPanelAndDeselect = useCallback7(() => {
14596
15558
  setFloatingPanel(null);
15559
+ setLogoSizeDraft(null);
14597
15560
  deselectRef.current();
14598
15561
  }, []);
15562
+ const persistLogoSizeDraft = useCallback7(
15563
+ (placement, draft) => {
15564
+ const desktopKey = LOGO_SIZE_DESKTOP_KEYS[placement];
15565
+ const mobileKey = LOGO_SIZE_MOBILE_KEYS[placement];
15566
+ const nodes = [
15567
+ { key: desktopKey, text: String(draft.desktopPx) }
15568
+ ];
15569
+ if (draft.mobileFollowing) {
15570
+ nodes.push({ key: mobileKey, text: "" });
15571
+ } else {
15572
+ nodes.push({ key: mobileKey, text: String(draft.mobilePx) });
15573
+ }
15574
+ editContentRef.current = {
15575
+ ...editContentRef.current,
15576
+ [desktopKey]: String(draft.desktopPx),
15577
+ [mobileKey]: draft.mobileFollowing ? "" : String(draft.mobilePx)
15578
+ };
15579
+ applyLogoSizeToPlacement(
15580
+ placement,
15581
+ draft.desktopPx,
15582
+ draft.mobileFollowing ? draft.desktopPx : draft.mobilePx,
15583
+ draft.mobileFollowing
15584
+ );
15585
+ postToParent2({ type: "ow:change", nodes });
15586
+ requestAnimationFrame(() => {
15587
+ const selected = selectedElRef.current;
15588
+ if (!selected || toolbarVariantRef.current !== "logo") return;
15589
+ const rect = getLogoInteractionRect(selected);
15590
+ setToolbarRect(rect);
15591
+ if (glowElRef.current) {
15592
+ const GAP = SELECTION_CHROME_GAP2;
15593
+ glowElRef.current.style.top = `${rect.top - GAP}px`;
15594
+ glowElRef.current.style.left = `${rect.left - GAP}px`;
15595
+ glowElRef.current.style.width = `${rect.width + GAP * 2}px`;
15596
+ glowElRef.current.style.height = `${rect.height + GAP * 2}px`;
15597
+ }
15598
+ });
15599
+ },
15600
+ [postToParent2]
15601
+ );
14599
15602
  const activate = useCallback7((el, options) => {
14600
15603
  if (activeElRef.current === el) return;
14601
15604
  if (isIconEditable(el)) return;
@@ -14676,7 +15679,37 @@ function OhhwellsBridge() {
14676
15679
  deactivateRef.current = deactivate;
14677
15680
  selectRef.current = select;
14678
15681
  selectFrameRef.current = selectFrame;
15682
+ selectLogoRef.current = selectLogo;
15683
+ openLogoSizePanelRef.current = openLogoSizePanel;
14679
15684
  deselectRef.current = deselect;
15685
+ closeFloatingPanelOnlyRef.current = closeFloatingPanelOnly;
15686
+ const lastSiteWideScopeRef = useRef9(null);
15687
+ useEffect12(() => {
15688
+ if (!isEditMode) {
15689
+ if (lastSiteWideScopeRef.current !== false) {
15690
+ lastSiteWideScopeRef.current = false;
15691
+ postToParent2({ type: "ow:site-wide-scope", active: false });
15692
+ }
15693
+ return;
15694
+ }
15695
+ const active = isSiteWideScopeActive({
15696
+ selected: selectedElRef.current,
15697
+ hoveredItem: hoveredItemElRef.current,
15698
+ hoveredNavContainer: hoveredNavContainerRef.current,
15699
+ active: activeElRef.current
15700
+ });
15701
+ if (lastSiteWideScopeRef.current === active) return;
15702
+ lastSiteWideScopeRef.current = active;
15703
+ postToParent2({ type: "ow:site-wide-scope", active });
15704
+ }, [
15705
+ isEditMode,
15706
+ hoveredItemRect,
15707
+ hoveredNavContainerRect,
15708
+ toolbarVariant,
15709
+ toolbarRect,
15710
+ isFooterFrameSelection,
15711
+ postToParent2
15712
+ ]);
14680
15713
  useLayoutEffect4(() => {
14681
15714
  if (!subdomain || isEditMode) {
14682
15715
  setFetchState("done");
@@ -14688,9 +15721,23 @@ function OhhwellsBridge() {
14688
15721
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
14689
15722
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
14690
15723
  }
15724
+ if (typeof content[BRAND_KIT_KEY] === "string") {
15725
+ brandKitRef.current = content[BRAND_KIT_KEY];
15726
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
15727
+ }
15728
+ if (typeof content[STYLE_STORE_KEY] === "string") {
15729
+ stylesRef.current = content[STYLE_STORE_KEY];
15730
+ applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
15731
+ }
15732
+ applyBrandChrome(content);
14691
15733
  for (const [key, val] of Object.entries(content)) {
14692
15734
  if (key === "__ohw_sections") continue;
14693
15735
  if (key === AI_SECTIONS_KEY) continue;
15736
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
15737
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
15738
+ if (key === BRAND_KIT_KEY) continue;
15739
+ if (key === STYLE_STORE_KEY) continue;
15740
+ if (BRAND_CHROME_KEYS.has(key)) continue;
14694
15741
  if (applyVideoSettingNode(key, val)) continue;
14695
15742
  if (applyCarouselNode(key, val)) continue;
14696
15743
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -14725,6 +15772,8 @@ function OhhwellsBridge() {
14725
15772
  });
14726
15773
  applyLinkByKey(key, val);
14727
15774
  }
15775
+ applyLogoFromContent(content);
15776
+ applyLogoSizes(content);
14728
15777
  reconcileNavbarItemsFromContent(content);
14729
15778
  reconcileFooterOrderFromContent(content);
14730
15779
  reconcileSocialsFromContent(content);
@@ -14745,7 +15794,9 @@ function OhhwellsBridge() {
14745
15794
  let cancelled = false;
14746
15795
  setFetchState("loading");
14747
15796
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
14748
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
15797
+ const initialPath = pathname;
15798
+ fetchedContentPaths.add(`${subdomain}::${initialPath}`);
15799
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
14749
15800
  if (cancelled) return;
14750
15801
  const content = data?.content ?? {};
14751
15802
  contentCache.set(subdomain, content);
@@ -14769,8 +15820,21 @@ function OhhwellsBridge() {
14769
15820
  initSectionInstancesFromContent(content, window.location.pathname);
14770
15821
  observer?.disconnect();
14771
15822
  try {
15823
+ applyBrandChrome(content);
15824
+ if (typeof content[BRAND_KIT_KEY] === "string") {
15825
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
15826
+ }
15827
+ if (typeof content[STYLE_STORE_KEY] === "string") {
15828
+ applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
15829
+ }
14772
15830
  for (const [key, val] of Object.entries(content)) {
14773
15831
  if (key === "__ohw_sections") continue;
15832
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
15833
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
15834
+ if (key === BRAND_KIT_KEY) continue;
15835
+ if (key === STYLE_STORE_KEY) continue;
15836
+ if (key === STYLE_STORE_KEY) continue;
15837
+ if (BRAND_CHROME_KEYS.has(key)) continue;
14774
15838
  if (applyVideoSettingNode(key, val)) continue;
14775
15839
  if (applyCarouselNode(key, val)) continue;
14776
15840
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -14791,6 +15855,7 @@ function OhhwellsBridge() {
14791
15855
  });
14792
15856
  applyLinkByKey(key, val);
14793
15857
  }
15858
+ applyLogoFromContent(content);
14794
15859
  reconcileNavbarItemsFromContent(content);
14795
15860
  reconcileFooterOrderFromContent(content);
14796
15861
  reconcileSocialsFromContent(content);
@@ -14805,6 +15870,17 @@ function OhhwellsBridge() {
14805
15870
  debounceTimer = setTimeout(applyFromCache, 150);
14806
15871
  };
14807
15872
  applyFromCache();
15873
+ const pathCacheKey = `${subdomain}::${pathname}`;
15874
+ if (!fetchedContentPaths.has(pathCacheKey)) {
15875
+ fetchedContentPaths.add(pathCacheKey);
15876
+ const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
15877
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
15878
+ if (!data?.content) return;
15879
+ contentCache.set(subdomain, data.content);
15880
+ applyFromCache();
15881
+ }).catch(() => {
15882
+ });
15883
+ }
14808
15884
  observer = new MutationObserver(scheduleApply);
14809
15885
  observer.observe(document.body, { childList: true, subtree: true });
14810
15886
  return () => {
@@ -14898,26 +15974,31 @@ function OhhwellsBridge() {
14898
15974
  }, [isEditMode, pathname, subdomain, fetchState, resyncSelectedNavigationItem]);
14899
15975
  useEffect12(() => {
14900
15976
  if (!isEditMode) return;
15977
+ let lastPosted = 0;
14901
15978
  const measure = () => {
14902
15979
  const h = document.body.scrollHeight;
14903
- if (h > 50) postToParent2({ type: "ow:height", height: h });
15980
+ if (h > 50 && Math.abs(h - lastPosted) > 1) {
15981
+ lastPosted = h;
15982
+ postToParent2({ type: "ow:height", height: h });
15983
+ }
15984
+ };
15985
+ let raf = null;
15986
+ const schedule = () => {
15987
+ if (raf != null) return;
15988
+ raf = requestAnimationFrame(() => {
15989
+ raf = null;
15990
+ measure();
15991
+ });
14904
15992
  };
14905
15993
  const t1 = setTimeout(measure, 50);
14906
15994
  const t2 = setTimeout(measure, 500);
14907
- let lastWidth = window.innerWidth;
14908
- let resizeTimer = null;
14909
- const handleResize = () => {
14910
- if (window.innerWidth === lastWidth) return;
14911
- lastWidth = window.innerWidth;
14912
- if (resizeTimer) clearTimeout(resizeTimer);
14913
- resizeTimer = setTimeout(measure, 150);
14914
- };
14915
- window.addEventListener("resize", handleResize);
15995
+ const ro = new ResizeObserver(schedule);
15996
+ ro.observe(document.body);
14916
15997
  return () => {
14917
15998
  clearTimeout(t1);
14918
15999
  clearTimeout(t2);
14919
- if (resizeTimer) clearTimeout(resizeTimer);
14920
- window.removeEventListener("resize", handleResize);
16000
+ if (raf != null) cancelAnimationFrame(raf);
16001
+ ro.disconnect();
14921
16002
  };
14922
16003
  }, [pathname, isEditMode, postToParent2]);
14923
16004
  useEffect12(() => {
@@ -14987,9 +16068,12 @@ function OhhwellsBridge() {
14987
16068
  [data-ohw-editable="video"], [data-ohw-editable="video"] *,
14988
16069
  [data-ohw-editable="bg-image"], [data-ohw-editable="bg-image"] * { cursor: pointer !important; }
14989
16070
  [data-ohw-editable="link"], [data-ohw-editable="link"] * { cursor: pointer !important; }
16071
+ /* Text hover chrome is drawn by the overlay (see hoveredTextRect) \u2014 the CSS outline
16072
+ that used to draw it dashes denser than the overlay border, so identical specs
16073
+ still read as two different frames (OHH-695). The attribute stays: hover paths
16074
+ and suppression rules key off it. */
14990
16075
  [data-ohw-hovered]:not([contenteditable]):not([data-ohw-href-key]) {
14991
- outline: 2px dashed ${PRIMARY3} !important;
14992
- outline-offset: 4px;
16076
+ outline: none !important;
14993
16077
  }
14994
16078
  [data-ohw-href-key] [data-ohw-hovered],
14995
16079
  [data-ohw-href-key][data-ohw-hovered],
@@ -15042,8 +16126,11 @@ function OhhwellsBridge() {
15042
16126
  stateViews.textContent = `
15043
16127
  [data-ohw-state-view]:not([data-ohw-state-view="default"]) { display: none; }
15044
16128
  [data-ohw-state-view="default"] [data-ohw-editable] { pointer-events: auto !important; }
15045
- [data-ohw-state-hovered] { outline: 2px dashed ${PRIMARY3} !important; outline-offset: 4px; }
16129
+ [data-ohw-state-hovered] { outline: ${HOVER_STROKE_WIDTH}px dashed ${CHROME_PRIMARY} !important; outline-offset: ${HOVER_CHROME_GAP}px; }
15046
16130
  [data-ohw-state-hovered]:has([data-ohw-hovered]) { outline: none !important; }
16131
+ /* :has() only sees descendants \u2014 when the card itself is the hovered text, the overlay
16132
+ already frames it, and the card outline doubled it (OHH-695). */
16133
+ [data-ohw-state-hovered][data-ohw-hovered] { outline: none !important; }
15047
16134
  `;
15048
16135
  document.head.appendChild(base);
15049
16136
  document.head.appendChild(forceHover);
@@ -15061,10 +16148,12 @@ function OhhwellsBridge() {
15061
16148
  return;
15062
16149
  }
15063
16150
  const target = e.target;
16151
+ if (target.closest("[data-ohw-ai-review]")) return;
15064
16152
  if (target.closest("[data-ohw-toolbar]")) return;
15065
16153
  if (target.closest("[data-ohw-state-toggle]")) return;
15066
16154
  if (target.closest("[data-ohw-max-badge]")) return;
15067
16155
  if (isInsideLinkEditor(target)) return;
16156
+ if (isInsideFloatingPanel(target)) return;
15068
16157
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
15069
16158
  const beneath = document.elementsFromPoint(e.clientX, e.clientY).find(
15070
16159
  (el) => el instanceof HTMLElement && !el.closest("[data-ohw-bridge-root]") && el.closest("[data-ohw-section]") != null
@@ -15128,6 +16217,21 @@ function OhhwellsBridge() {
15128
16217
  return;
15129
16218
  }
15130
16219
  }
16220
+ const logoEl = getLogoElement(target);
16221
+ if (logoEl) {
16222
+ e.preventDefault();
16223
+ e.stopPropagation();
16224
+ if (!logoHasUploadedImage(logoEl)) {
16225
+ deselectRef.current();
16226
+ deactivateRef.current();
16227
+ const identity = readLogoIdentityFromDom();
16228
+ postToParentRef.current({ type: "ow:open-logo-settings", ...identity });
16229
+ return;
16230
+ }
16231
+ selectLogoRef.current(logoEl);
16232
+ openLogoSizePanelRef.current(logoEl);
16233
+ return;
16234
+ }
15131
16235
  const editable = target.closest("[data-ohw-editable]");
15132
16236
  if (editable) {
15133
16237
  if (editable.dataset.ohwEditable === "link") {
@@ -15280,10 +16384,12 @@ function OhhwellsBridge() {
15280
16384
  };
15281
16385
  const handleDblClick = (e) => {
15282
16386
  const target = e.target;
16387
+ if (target.closest("[data-ohw-ai-review]")) return;
15283
16388
  if (target.closest("[data-ohw-toolbar]")) return;
15284
16389
  if (target.closest("[data-ohw-state-toggle]")) return;
15285
16390
  if (target.closest("[data-ohw-max-badge]")) return;
15286
16391
  if (isInsideLinkEditor(target)) return;
16392
+ if (isInsideFloatingPanel(target)) return;
15287
16393
  if (target.closest('[data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-drag-handle-container], [data-slot="drag-handle"]')) {
15288
16394
  return;
15289
16395
  }
@@ -15311,6 +16417,16 @@ function OhhwellsBridge() {
15311
16417
  setHoveredNavContainerRect(null);
15312
16418
  return;
15313
16419
  }
16420
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || isInsideFloatingPanel(target) || isPointOverFloatingPanel(e.clientX, e.clientY)) {
16421
+ hoveredItemElRef.current = null;
16422
+ setHoveredItemRect(null);
16423
+ hoveredNavContainerRef.current = null;
16424
+ setHoveredNavContainerRect(null);
16425
+ siblingHintElRef.current = null;
16426
+ setSiblingHintRect(null);
16427
+ setSiblingHintRects([]);
16428
+ return;
16429
+ }
15314
16430
  {
15315
16431
  const selected2 = selectedElRef.current;
15316
16432
  const selectedIsFooterColumn = Boolean(selected2) && !isFooterLinksContainer(selected2) && (selected2.hasAttribute("data-ohw-footer-col") || selected2.hasAttribute("data-ohw-footer-column") || Boolean(selected2.closest("footer") && isInferredFooterGroup2(selected2)));
@@ -15318,7 +16434,7 @@ function OhhwellsBridge() {
15318
16434
  const allowFooterLinksHover = toolbarVariantRef.current !== "select-frame" || selectedIsFooterColumn;
15319
16435
  if (allowNavContainerHover) {
15320
16436
  const navContainer = target.closest("[data-ohw-nav-container]");
15321
- if (navContainer && !getNavigationItemAnchor(target)) {
16437
+ if (navContainer && !getNavigationItemAnchor(target) && !getLogoElement(target)) {
15322
16438
  hoveredNavContainerRef.current = navContainer;
15323
16439
  setHoveredNavContainerRect(navContainer.getBoundingClientRect());
15324
16440
  hoveredItemElRef.current = null;
@@ -15347,6 +16463,15 @@ function OhhwellsBridge() {
15347
16463
  setHoveredNavContainerRect(null);
15348
16464
  }
15349
16465
  }
16466
+ const logoEl = getLogoElement(target);
16467
+ if (logoEl) {
16468
+ hoveredNavContainerRef.current = null;
16469
+ setHoveredNavContainerRect(null);
16470
+ if (selectedElRef.current === logoEl) return;
16471
+ hoveredItemElRef.current = logoEl;
16472
+ setHoveredItemRect(getLogoInteractionRect(logoEl));
16473
+ return;
16474
+ }
15350
16475
  const navAnchor = getNavigationItemAnchor(target);
15351
16476
  if (navAnchor) {
15352
16477
  hoveredNavContainerRef.current = null;
@@ -15384,6 +16509,11 @@ function OhhwellsBridge() {
15384
16509
  setHoveredItemRect(hoverTarget.getBoundingClientRect());
15385
16510
  } else if (!isInsideNavigationItem(editable)) {
15386
16511
  hoverTarget.setAttribute("data-ohw-hovered", "");
16512
+ if (editable.closest("footer") || editable.closest('[data-ohw-section="footer"]')) {
16513
+ hoveredNavContainerRef.current = null;
16514
+ setHoveredNavContainerRect(null);
16515
+ hoveredItemElRef.current = editable;
16516
+ }
15387
16517
  }
15388
16518
  }
15389
16519
  };
@@ -15419,6 +16549,18 @@ function OhhwellsBridge() {
15419
16549
  }
15420
16550
  return;
15421
16551
  }
16552
+ const logoEl = getLogoElement(target);
16553
+ if (logoEl) {
16554
+ const related2 = e.relatedTarget instanceof Element ? e.relatedTarget : null;
16555
+ if (related2 && (logoEl === related2 || logoEl.contains(related2) || related2.closest?.('[data-ohw-role="logo"], [data-ohw-logo]'))) {
16556
+ return;
16557
+ }
16558
+ if (hoveredItemElRef.current === logoEl) {
16559
+ hoveredItemElRef.current = null;
16560
+ setHoveredItemRect(null);
16561
+ }
16562
+ return;
16563
+ }
15422
16564
  const editable = target.closest("[data-ohw-editable]");
15423
16565
  if (!editable) return;
15424
16566
  const related = e.relatedTarget instanceof Element ? e.relatedTarget : null;
@@ -15439,6 +16581,13 @@ function OhhwellsBridge() {
15439
16581
  }
15440
16582
  } else {
15441
16583
  hoverTarget.removeAttribute("data-ohw-hovered");
16584
+ if (hoveredItemElRef.current === editable) {
16585
+ const stillOnEditable = related instanceof Element && related.closest("[data-ohw-editable]") === editable;
16586
+ if (!stillOnEditable) {
16587
+ hoveredItemElRef.current = null;
16588
+ setHoveredItemRect(null);
16589
+ }
16590
+ }
15442
16591
  }
15443
16592
  }
15444
16593
  };
@@ -15555,6 +16704,26 @@ function OhhwellsBridge() {
15555
16704
  hoveredNavContainerRef.current = null;
15556
16705
  setHoveredNavContainerRect(null);
15557
16706
  }
16707
+ const logoCandidates = [
16708
+ ...document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]'),
16709
+ ...document.querySelectorAll("nav a:not([data-ohw-href-key]), [data-ohw-nav-root] a:not([data-ohw-href-key])"),
16710
+ ...document.querySelectorAll("footer img")
16711
+ ];
16712
+ const seenLogos = /* @__PURE__ */ new Set();
16713
+ for (const candidate of logoCandidates) {
16714
+ const logo = getLogoElement(candidate);
16715
+ if (!logo || seenLogos.has(logo)) continue;
16716
+ seenLogos.add(logo);
16717
+ const r2 = logo.getBoundingClientRect();
16718
+ if (x < r2.left || x > r2.right || y < r2.top || y > r2.bottom) continue;
16719
+ hoveredNavContainerRef.current = null;
16720
+ setHoveredNavContainerRect(null);
16721
+ if (selectedElRef.current !== logo) {
16722
+ hoveredItemElRef.current = logo;
16723
+ setHoveredItemRect(getLogoInteractionRect(logo));
16724
+ }
16725
+ return;
16726
+ }
15558
16727
  const navContainers = Array.from(
15559
16728
  document.querySelectorAll("[data-ohw-nav-container]")
15560
16729
  );
@@ -15640,7 +16809,7 @@ function OhhwellsBridge() {
15640
16809
  }
15641
16810
  };
15642
16811
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
15643
- if (linkPopoverOpenRef.current) {
16812
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
15644
16813
  if (hoveredImageRef.current) {
15645
16814
  hoveredImageRef.current = null;
15646
16815
  hoveredImageHasTextOverlapRef.current = false;
@@ -15894,7 +17063,7 @@ function OhhwellsBridge() {
15894
17063
  }
15895
17064
  };
15896
17065
  const probeHoverCardsAt = (clientX, clientY, fromParentViewport = false) => {
15897
- if (linkPopoverOpenRef.current || document.documentElement.hasAttribute("data-ohw-section-picking")) {
17066
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || document.documentElement.hasAttribute("data-ohw-section-picking")) {
15898
17067
  if (activeStateElRef.current) {
15899
17068
  activeStateElRef.current.removeAttribute("data-ohw-state-hovered");
15900
17069
  activeStateElRef.current = null;
@@ -15962,6 +17131,19 @@ function OhhwellsBridge() {
15962
17131
  };
15963
17132
  const handleMouseMove = (e) => {
15964
17133
  const { clientX, clientY } = e;
17134
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
17135
+ hoveredItemElRef.current = null;
17136
+ setHoveredItemRect(null);
17137
+ hoveredNavContainerRef.current = null;
17138
+ setHoveredNavContainerRect(null);
17139
+ siblingHintElRef.current = null;
17140
+ setSiblingHintRect(null);
17141
+ setSiblingHintRects([]);
17142
+ dismissImageHover();
17143
+ clearImageHover();
17144
+ setSectionGap(null);
17145
+ return;
17146
+ }
15965
17147
  probeSectionGapAt(clientX, clientY);
15966
17148
  probeImageAt(clientX, clientY);
15967
17149
  probeHoverCardsAt(clientX, clientY);
@@ -15970,6 +17152,11 @@ function OhhwellsBridge() {
15970
17152
  if (e.data?.type !== "ow:pointer-sync") return;
15971
17153
  const { clientX, clientY } = e.data;
15972
17154
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
17155
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
17156
+ dismissImageHover();
17157
+ clearImageHover();
17158
+ return;
17159
+ }
15973
17160
  probeSectionGapAt(clientX, clientY);
15974
17161
  probeImageAt(clientX, clientY);
15975
17162
  probeHoverCardsAt(clientX, clientY);
@@ -16219,6 +17406,15 @@ function OhhwellsBridge() {
16219
17406
  aiSectionsRef.current = content[AI_SECTIONS_KEY];
16220
17407
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
16221
17408
  }
17409
+ if (typeof content[BRAND_KIT_KEY] === "string") {
17410
+ brandKitRef.current = content[BRAND_KIT_KEY];
17411
+ applyBrandToDom(parseBrandKit(content[BRAND_KIT_KEY]));
17412
+ }
17413
+ if (typeof content[STYLE_STORE_KEY] === "string") {
17414
+ stylesRef.current = content[STYLE_STORE_KEY];
17415
+ applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
17416
+ }
17417
+ applyBrandChrome(content);
16222
17418
  let sectionsJson = null;
16223
17419
  for (const [key, val] of Object.entries(content)) {
16224
17420
  if (key === "__ohw_sections") {
@@ -16226,6 +17422,11 @@ function OhhwellsBridge() {
16226
17422
  continue;
16227
17423
  }
16228
17424
  if (key === AI_SECTIONS_KEY) continue;
17425
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
17426
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
17427
+ if (key === BRAND_KIT_KEY) continue;
17428
+ if (key === STYLE_STORE_KEY) continue;
17429
+ if (BRAND_CHROME_KEYS.has(key)) continue;
16229
17430
  if (applyVideoSettingNode(key, val)) continue;
16230
17431
  if (applyCarouselNode(key, val)) continue;
16231
17432
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -16245,6 +17446,8 @@ function OhhwellsBridge() {
16245
17446
  });
16246
17447
  applyLinkByKey(key, val);
16247
17448
  }
17449
+ applyLogoFromContent(content);
17450
+ applyLogoSizes(content);
16248
17451
  if (sectionsJson) {
16249
17452
  initSectionsFromContent({ __ohw_sections: sectionsJson }, true);
16250
17453
  sectionsLoadedRef.current = true;
@@ -16260,6 +17463,58 @@ function OhhwellsBridge() {
16260
17463
  if (hydratedHeight > 50) postToParentRef.current({ type: "ow:height", height: hydratedHeight });
16261
17464
  postToParentRef.current({ type: "ow:hydrate-done" });
16262
17465
  };
17466
+ const handleUpdateLogoIdentity = (e) => {
17467
+ if (e.data?.type !== "ow:update-logo-identity") return;
17468
+ const rawText = typeof e.data.text === "string" ? e.data.text : "";
17469
+ const alt = typeof e.data.alt === "string" ? e.data.alt : rawText;
17470
+ const href = typeof e.data.href === "string" ? e.data.href : void 0;
17471
+ const imageProvided = "image" in e.data;
17472
+ const imageUrl = imageProvided && typeof e.data.image === "string" && e.data.image.trim() ? e.data.image.trim() : imageProvided ? null : void 0;
17473
+ let isPlaceholder = e.data.isPlaceholder !== false;
17474
+ if (imageUrl) isPlaceholder = false;
17475
+ else if (imageProvided && imageUrl === null) {
17476
+ isPlaceholder = e.data.isPlaceholder === true || !rawText.trim() || resolveLogoDisplayText(rawText) === PLACEHOLDER_BUSINESS_NAME;
17477
+ }
17478
+ const display = applyLogoIdentity(rawText, isPlaceholder);
17479
+ const displayAlt = resolveLogoDisplayText(alt || display);
17480
+ if (imageUrl !== void 0) {
17481
+ applyLogoImage(imageUrl, displayAlt);
17482
+ } else {
17483
+ for (const key of LOGO_IMAGE_KEYS) {
17484
+ document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
17485
+ const img = el instanceof HTMLImageElement ? el : el.querySelector("img");
17486
+ if (img) img.alt = displayAlt;
17487
+ });
17488
+ }
17489
+ }
17490
+ if (href !== void 0) {
17491
+ applyLogoHref(href);
17492
+ applyLinkByKey("nav-logo-href", href);
17493
+ applyLinkByKey("footer-logo-href", href);
17494
+ applyLinkByKey("logo-href", href);
17495
+ }
17496
+ const nodes = [
17497
+ ...LOGO_TEXT_KEYS.map((key) => ({ key, text: display })),
17498
+ { key: LOGO_PLACEHOLDER_KEY, text: isPlaceholder ? "true" : "false" },
17499
+ { key: LOGO_ALT_KEY, text: displayAlt }
17500
+ ];
17501
+ if (imageUrl !== void 0) {
17502
+ if (imageUrl) {
17503
+ for (const key of LOGO_IMAGE_KEYS) nodes.push({ key, text: imageUrl });
17504
+ } else {
17505
+ for (const key of LOGO_IMAGE_KEYS) nodes.push({ key, text: "" });
17506
+ }
17507
+ }
17508
+ if (href !== void 0) {
17509
+ for (const key of LOGO_HREF_KEYS) nodes.push({ key, text: href.trim() || "/" });
17510
+ }
17511
+ editContentRef.current = {
17512
+ ...editContentRef.current,
17513
+ ...Object.fromEntries(nodes.map(({ key, text }) => [key, text]))
17514
+ };
17515
+ applyLogoSizes(editContentRef.current);
17516
+ postToParentRef.current({ type: "ow:change", nodes });
17517
+ };
16263
17518
  window.addEventListener("message", handleHydrate);
16264
17519
  const postAiSectionsChanged = () => {
16265
17520
  postToParentRef.current({
@@ -16273,7 +17528,10 @@ function OhhwellsBridge() {
16273
17528
  const payload = e.data.payload;
16274
17529
  if (!payload || typeof payload.id !== "string" || !isRenderableTree(payload.tree)) return;
16275
17530
  const previous = aiSectionsRef.current;
16276
- const nextState = applyTreeToState(parseAiSectionsState(previous), payload);
17531
+ const nextState = applyTreeToState(parseAiSectionsState(previous), {
17532
+ ...payload,
17533
+ path: payload.path ?? window.location.pathname
17534
+ });
16277
17535
  const nextValue = serializeAiSectionsState(nextState);
16278
17536
  aiSectionsRef.current = nextValue;
16279
17537
  applyAiSectionsToDom(nextState);
@@ -16310,12 +17568,42 @@ function OhhwellsBridge() {
16310
17568
  const value = typeof e.data.value === "string" ? e.data.value : "";
16311
17569
  aiSectionsRef.current = value;
16312
17570
  applyAiSectionsToDom(parseAiSectionsState(value));
17571
+ applyStylesToDom(parseStyleStore(stylesRef.current));
16313
17572
  const restoredHeight = document.documentElement.scrollHeight;
16314
17573
  if (restoredHeight > 50) postToParentRef.current({ type: "ow:height", height: restoredHeight });
16315
17574
  postToParentRef.current({ type: "ow:change", nodes: [{ key: AI_SECTIONS_KEY, text: value }] });
16316
17575
  postAiSectionsChanged();
16317
17576
  };
16318
17577
  window.addEventListener("message", handleAiSetSections);
17578
+ const handleAiSetBrand = (e) => {
17579
+ if (e.data?.type !== "ow:ai-set-brand") return;
17580
+ const value = typeof e.data.value === "string" ? e.data.value : "";
17581
+ const previous = brandKitRef.current;
17582
+ brandKitRef.current = value;
17583
+ applyBrandToDom(parseBrandKit(value));
17584
+ if (aiSectionsRef.current) applyAiSectionsToDom(parseAiSectionsState(aiSectionsRef.current));
17585
+ applyStylesToDom(parseStyleStore(stylesRef.current));
17586
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: BRAND_KIT_KEY, text: value }] });
17587
+ postToParentRef.current({ type: "ow:ai-brand-applied", previous, value });
17588
+ };
17589
+ window.addEventListener("message", handleAiSetBrand);
17590
+ const handleAiSetStyles = (e) => {
17591
+ if (e.data?.type !== "ow:ai-set-styles") return;
17592
+ const value = typeof e.data.value === "string" ? e.data.value : "";
17593
+ const previous = stylesRef.current;
17594
+ stylesRef.current = value;
17595
+ applyStylesToDom(parseStyleStore(value));
17596
+ postToParentRef.current({ type: "ow:change", nodes: [{ key: STYLE_STORE_KEY, text: value }] });
17597
+ postToParentRef.current({ type: "ow:ai-styles-applied", previous, value });
17598
+ };
17599
+ window.addEventListener("message", handleAiSetStyles);
17600
+ const handleGetBrand = (e) => {
17601
+ if (e.data?.type !== "ow:get-brand") return;
17602
+ const template = deriveTemplateBrand();
17603
+ const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
17604
+ postToParentRef.current({ type: "ow:brand-value", value });
17605
+ };
17606
+ window.addEventListener("message", handleGetBrand);
16319
17607
  const handleDeactivate = (e) => {
16320
17608
  if (e.data?.type !== "ow:deactivate") return;
16321
17609
  if (Date.now() < linkPopoverGraceUntilRef.current) return;
@@ -16324,6 +17612,12 @@ function OhhwellsBridge() {
16324
17612
  closeLinkPopoverRef.current();
16325
17613
  return;
16326
17614
  }
17615
+ if (floatingPanelOpenRef.current) {
17616
+ setFloatingPanelRef.current(null);
17617
+ deselectRef.current();
17618
+ deactivateRef.current();
17619
+ return;
17620
+ }
16327
17621
  deselectRef.current();
16328
17622
  deactivateRef.current();
16329
17623
  };
@@ -16377,6 +17671,10 @@ function OhhwellsBridge() {
16377
17671
  return;
16378
17672
  }
16379
17673
  if (selectedElRef.current) {
17674
+ if (toolbarVariantRef.current === "logo") {
17675
+ deselectRef.current();
17676
+ return;
17677
+ }
16380
17678
  if (toolbarVariantRef.current === "select-frame") {
16381
17679
  const parent2 = getNavigationSelectionParent(selectedElRef.current);
16382
17680
  if (parent2 && isNavigationContainer(parent2) && isFooterLinksContainer(parent2) && !isFooterLinksContainer(selectedElRef.current)) {
@@ -16416,6 +17714,10 @@ function OhhwellsBridge() {
16416
17714
  return;
16417
17715
  }
16418
17716
  if (e.key === "Escape" && selectedElRef.current && !activeElRef.current) {
17717
+ if (toolbarVariantRef.current === "logo") {
17718
+ deselectRef.current();
17719
+ return;
17720
+ }
16419
17721
  if (toolbarVariantRef.current === "select-frame") {
16420
17722
  const parent2 = getNavigationSelectionParent(selectedElRef.current);
16421
17723
  if (parent2 && isNavigationContainer(parent2) && isFooterLinksContainer(parent2) && !isFooterLinksContainer(selectedElRef.current)) {
@@ -16493,7 +17795,8 @@ function OhhwellsBridge() {
16493
17795
  const handleScroll = () => {
16494
17796
  const focusEl = activeElRef.current ?? selectedElRef.current;
16495
17797
  if (focusEl) {
16496
- const r2 = activeElRef.current ? getEditMeasureEl(activeElRef.current).getBoundingClientRect() : focusEl.getBoundingClientRect();
17798
+ const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : toolbarVariantRef.current === "logo" ? null : focusEl;
17799
+ const r2 = measureEl ? measureEl.getBoundingClientRect() : toolbarVariantRef.current === "logo" ? getLogoInteractionRect(focusEl) : focusEl.getBoundingClientRect();
16497
17800
  applyToolbarPos(r2);
16498
17801
  setToolbarRect(r2);
16499
17802
  setMaxBadge((prev) => prev && activeElRef.current ? { ...prev, rect: r2 } : prev);
@@ -16503,7 +17806,9 @@ function OhhwellsBridge() {
16503
17806
  setToggleState((prev) => prev ? { ...prev, rect } : null);
16504
17807
  }
16505
17808
  if (hoveredItemElRef.current) {
16506
- setHoveredItemRect(hoveredItemElRef.current.getBoundingClientRect());
17809
+ const hoverEl = hoveredItemElRef.current;
17810
+ const logo = getLogoElement(hoverEl);
17811
+ setHoveredItemRect(logo ? getLogoInteractionRect(logo) : hoverEl.getBoundingClientRect());
16507
17812
  }
16508
17813
  if (hoveredNavContainerRef.current) {
16509
17814
  setHoveredNavContainerRect(hoveredNavContainerRef.current.getBoundingClientRect());
@@ -16547,6 +17852,12 @@ function OhhwellsBridge() {
16547
17852
  if (aiSectionsRef.current) {
16548
17853
  nodes.push({ key: AI_SECTIONS_KEY, type: "sections", text: aiSectionsRef.current });
16549
17854
  }
17855
+ if (stylesRef.current) {
17856
+ nodes.push({ key: STYLE_STORE_KEY, type: "styles", text: stylesRef.current });
17857
+ }
17858
+ if (brandKitRef.current) {
17859
+ nodes.push({ key: BRAND_KIT_KEY, type: "brand", text: brandKitRef.current });
17860
+ }
16550
17861
  postToParentRef.current({ type: "ow:save-result", nodes });
16551
17862
  };
16552
17863
  const handleInsertSection = (e) => {
@@ -16557,8 +17868,12 @@ function OhhwellsBridge() {
16557
17868
  if (inserted) {
16558
17869
  const tracker = getSectionsTracker();
16559
17870
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
16560
- const h = document.documentElement.scrollHeight;
16561
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
17871
+ const reportHeight = () => {
17872
+ const h = document.body.scrollHeight;
17873
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
17874
+ };
17875
+ reportHeight();
17876
+ setTimeout(reportHeight, 500);
16562
17877
  }
16563
17878
  };
16564
17879
  const handleSwitchSchedule = (e) => {
@@ -16751,13 +18066,17 @@ function OhhwellsBridge() {
16751
18066
  if (e.data?.type !== "ow:parent-scroll") return;
16752
18067
  const { iframeOffsetTop, headerH, canvasH } = e.data;
16753
18068
  parentScrollRef.current = { iframeOffsetTop, headerH, canvasH };
18069
+ if (floatingPanelOpenRef.current) {
18070
+ setParentScrollSnap({ iframeOffsetTop, headerH, canvasH });
18071
+ }
16754
18072
  if (visibleViewportRef.current) {
16755
18073
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
16756
18074
  }
16757
18075
  const focusEl = activeElRef.current ?? selectedElRef.current;
16758
18076
  if (focusEl) {
16759
- const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : focusEl;
16760
- applyToolbarPos(measureEl.getBoundingClientRect());
18077
+ const measureEl = activeElRef.current ? getEditMeasureEl(activeElRef.current) : toolbarVariantRef.current === "logo" ? null : focusEl;
18078
+ const r2 = measureEl ? measureEl.getBoundingClientRect() : toolbarVariantRef.current === "logo" ? getLogoInteractionRect(focusEl) : focusEl.getBoundingClientRect();
18079
+ applyToolbarPos(r2);
16761
18080
  }
16762
18081
  };
16763
18082
  const handleClickAt = (e) => {
@@ -16782,6 +18101,25 @@ function OhhwellsBridge() {
16782
18101
  postToParentRef.current({ type: "ow:image-pick", key: stateCardImage.dataset.ohwKey ?? "", elementType: stateCardImage.dataset.ohwEditable ?? "image" });
16783
18102
  return;
16784
18103
  }
18104
+ const logoAtPoint = Array.from(
18105
+ document.querySelectorAll('[data-ohw-role="logo"], [data-ohw-logo]')
18106
+ ).map((el) => getLogoElement(el)).find((logo) => {
18107
+ if (!logo) return false;
18108
+ const r2 = logo.getBoundingClientRect();
18109
+ return clientX >= r2.left && clientX <= r2.right && clientY >= r2.top && clientY <= r2.bottom;
18110
+ });
18111
+ if (logoAtPoint) {
18112
+ if (!logoHasUploadedImage(logoAtPoint)) {
18113
+ deselectRef.current();
18114
+ deactivateRef.current();
18115
+ const identity = readLogoIdentityFromDom();
18116
+ postToParentRef.current({ type: "ow:open-logo-settings", ...identity });
18117
+ return;
18118
+ }
18119
+ selectLogoRef.current(logoAtPoint);
18120
+ openLogoSizePanelRef.current(logoAtPoint);
18121
+ return;
18122
+ }
16785
18123
  const textEditable = Array.from(
16786
18124
  document.querySelectorAll(NON_MEDIA_SELECTOR)
16787
18125
  ).find((el) => {
@@ -16853,6 +18191,14 @@ function OhhwellsBridge() {
16853
18191
  window.addEventListener("message", handleParentScroll);
16854
18192
  window.addEventListener("message", handlePointerSync);
16855
18193
  window.addEventListener("message", handleClickAt);
18194
+ window.addEventListener("message", handleUpdateLogoIdentity);
18195
+ const handleViewMode = (e) => {
18196
+ if (e.data?.type !== "ow:view-mode") return;
18197
+ const mode = e.data.mode === "Mobile" || e.data.mode === "mobile" ? "mobile" : "desktop";
18198
+ setEditorViewport(mode);
18199
+ applyLogoSizes(editContentRef.current);
18200
+ };
18201
+ window.addEventListener("message", handleViewMode);
16856
18202
  const handleViewportResize = () => {
16857
18203
  if (visibleViewportRef.current) {
16858
18204
  applyVisibleViewport(visibleViewportRef.current, parentScrollRef.current);
@@ -16908,10 +18254,15 @@ function OhhwellsBridge() {
16908
18254
  window.removeEventListener("resize", handleViewportResize);
16909
18255
  window.removeEventListener("message", handlePointerSync);
16910
18256
  window.removeEventListener("message", handleClickAt);
18257
+ window.removeEventListener("message", handleUpdateLogoIdentity);
18258
+ window.removeEventListener("message", handleViewMode);
16911
18259
  window.removeEventListener("message", handleHydrate);
16912
18260
  window.removeEventListener("message", handleAiApplyTree);
16913
18261
  window.removeEventListener("message", handleAiDeleteSection);
16914
18262
  window.removeEventListener("message", handleAiSetSections);
18263
+ window.removeEventListener("message", handleAiSetBrand);
18264
+ window.removeEventListener("message", handleAiSetStyles);
18265
+ window.removeEventListener("message", handleGetBrand);
16915
18266
  window.removeEventListener("message", handleDeactivate);
16916
18267
  window.removeEventListener("message", handleToastAction);
16917
18268
  window.removeEventListener("message", handleUiEscape);
@@ -16937,7 +18288,7 @@ function OhhwellsBridge() {
16937
18288
  if (footerDragRef.current) return;
16938
18289
  const target = e.target;
16939
18290
  if (!target) return;
16940
- 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]')) {
18291
+ 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]')) {
16941
18292
  return;
16942
18293
  }
16943
18294
  if (target.closest("[data-ohw-item-drag-surface]")) return;
@@ -17115,7 +18466,7 @@ function OhhwellsBridge() {
17115
18466
  postToParent2({
17116
18467
  type: "ow:ready",
17117
18468
  version: "1",
17118
- bridgeVersion: "0.1.58",
18469
+ bridgeVersion: "0.1.60",
17119
18470
  path: pathname,
17120
18471
  nodes: collectEditableNodes(editContentRef.current),
17121
18472
  sections
@@ -17510,10 +18861,10 @@ function OhhwellsBridge() {
17510
18861
  [postToParent2]
17511
18862
  );
17512
18863
  return bridgeRoot ? createPortal2(
17513
- /* @__PURE__ */ jsxs19(Fragment8, { children: [
17514
- /* @__PURE__ */ jsx32("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
17515
- isEditMode && /* @__PURE__ */ jsx32(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
17516
- Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx32(
18864
+ /* @__PURE__ */ jsxs20(Fragment8, { children: [
18865
+ /* @__PURE__ */ jsx33("div", { ref: attachVisibleViewport, "data-ohw-visible-viewport": "", "aria-hidden": true }),
18866
+ isEditMode && /* @__PURE__ */ jsx33(AiSectionOverlay, { postToParent: postToParent2, apiRef: aiSectionApiRef }),
18867
+ Object.entries(uploadingRects).map(([key, { rect, fadingOut }]) => /* @__PURE__ */ jsx33(
17517
18868
  MediaOverlay,
17518
18869
  {
17519
18870
  hover: { key, rect, elementType: "image", isDragOver: false, hasTextOverlap: false },
@@ -17524,7 +18875,7 @@ function OhhwellsBridge() {
17524
18875
  },
17525
18876
  `uploading-${key}`
17526
18877
  )),
17527
- mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx32(
18878
+ mediaHover && !(mediaHover.key in uploadingRects) && /* @__PURE__ */ jsx33(
17528
18879
  MediaOverlay,
17529
18880
  {
17530
18881
  hover: mediaHover,
@@ -17533,11 +18884,11 @@ function OhhwellsBridge() {
17533
18884
  onVideoSettingsChange: handleVideoSettingsChange
17534
18885
  }
17535
18886
  ),
17536
- carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx32(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
17537
- siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx32(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
17538
- siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx32(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
17539
- isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx32(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
17540
- isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx32(
18887
+ carouselHover && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(CarouselOverlay, { hover: carouselHover, onEdit: handleEditCarousel }),
18888
+ siblingHintRect && !linkPopover && !isItemDragging && siblingHintRects.length === 0 && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: siblingHintRect, state: "sibling-hint" }),
18889
+ siblingHintRects.length > 0 && !linkPopover && !isItemDragging && siblingHintRects.map((rect, i) => /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect, state: "sibling-hint" }, `sibling-hint-${i}`)),
18890
+ isItemDragging && draggedItemRect && selectedElRef.current !== footerDragRef.current?.draggedEl && selectedElRef.current !== navDragRef.current?.draggedEl && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: draggedItemRect, state: "dragging" }),
18891
+ isItemDragging && footerDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
17541
18892
  "div",
17542
18893
  {
17543
18894
  className: "pointer-events-none fixed z-2147483646",
@@ -17547,7 +18898,7 @@ function OhhwellsBridge() {
17547
18898
  width: slot.width,
17548
18899
  height: slot.height
17549
18900
  },
17550
- children: /* @__PURE__ */ jsx32(
18901
+ children: /* @__PURE__ */ jsx33(
17551
18902
  DropIndicator,
17552
18903
  {
17553
18904
  direction: slot.direction,
@@ -17558,7 +18909,7 @@ function OhhwellsBridge() {
17558
18909
  },
17559
18910
  `footer-drop-${slot.direction}-${slot.columnIndex}-${slot.insertIndex}-${i}`
17560
18911
  )),
17561
- isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx32(
18912
+ isItemDragging && navDropSlots.map((slot, i) => /* @__PURE__ */ jsx33(
17562
18913
  "div",
17563
18914
  {
17564
18915
  className: "pointer-events-none fixed z-2147483646",
@@ -17568,7 +18919,7 @@ function OhhwellsBridge() {
17568
18919
  width: slot.width,
17569
18920
  height: slot.height
17570
18921
  },
17571
- children: /* @__PURE__ */ jsx32(
18922
+ children: /* @__PURE__ */ jsx33(
17572
18923
  DropIndicator,
17573
18924
  {
17574
18925
  direction: slot.direction,
@@ -17579,10 +18930,11 @@ function OhhwellsBridge() {
17579
18930
  },
17580
18931
  `nav-drop-${slot.direction}-${slot.parentId ?? "root"}-${slot.insertIndex}-${i}`
17581
18932
  )),
17582
- hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx32(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
17583
- hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx32(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
17584
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx32(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
17585
- toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx32(
18933
+ hoveredNavContainerRect && (toolbarVariant !== "select-frame" || isFooterFrameSelection) && !linkPopover && !isItemDragging && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredNavContainerRect, state: "hover" }),
18934
+ hoveredItemRect && !linkPopover && !hoveredNavContainerRect && !isItemDragging && hoveredItemElRef.current !== selectedElRef.current && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredItemRect, state: "hover" }),
18935
+ hoveredTextRect && !isItemDragging && toolbarVariant !== "rich-text" && /* @__PURE__ */ jsx33(ItemInteractionLayer, { rect: hoveredTextRect, state: "hover" }),
18936
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isNavbarLinksContainer2(selectedElRef.current) && /* @__PURE__ */ jsx33(NavbarContainerChrome, { rect: toolbarRect, onAdd: handleAddTopLevelNavItem }),
18937
+ toolbarVariant === "select-frame" && toolbarRect && !linkPopover && !isItemDragging && !isFooterFrameSelection && selectedElRef.current && isFooterLinksContainer(selectedElRef.current) && /* @__PURE__ */ jsx33(
17586
18938
  FooterContainerChrome,
17587
18939
  {
17588
18940
  rect: toolbarRect,
@@ -17590,7 +18942,7 @@ function OhhwellsBridge() {
17590
18942
  addDisabled: !canAddFooterColumn()
17591
18943
  }
17592
18944
  ),
17593
- toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame") && /* @__PURE__ */ jsx32(
18945
+ toolbarRect && !linkPopover && (toolbarVariant === "link-action" || toolbarVariant === "select-frame" || toolbarVariant === "logo") && /* @__PURE__ */ jsx33(
17594
18946
  ItemInteractionLayer,
17595
18947
  {
17596
18948
  rect: isItemDragging && draggedItemRect && (footerDragRef.current?.wasSelected || navDragRef.current?.wasSelected) ? draggedItemRect : toolbarRect,
@@ -17602,10 +18954,10 @@ function OhhwellsBridge() {
17602
18954
  dragHandleLabel: reorderHrefKey ? `Reorder ${reorderHrefKey}` : "Reorder item",
17603
18955
  onDragHandleDragStart: handleItemDragStart,
17604
18956
  onDragHandleDragEnd: handleItemDragEnd,
17605
- onItemPointerDown: handleItemChromePointerDown,
17606
- onItemClick: handleItemChromeClick,
17607
- itemDragSurface: !isFooterFrameSelection,
17608
- toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ jsx32(
18957
+ onItemPointerDown: toolbarVariant === "logo" ? void 0 : handleItemChromePointerDown,
18958
+ onItemClick: toolbarVariant === "logo" ? void 0 : handleItemChromeClick,
18959
+ itemDragSurface: toolbarVariant !== "logo" && !isFooterFrameSelection,
18960
+ toolbar: toolbarVariant === "link-action" && !isItemDragging || toolbarVariant === "select-frame" && (isFooterFrameSelection || selectedIsSocialsRow) && !isItemDragging ? /* @__PURE__ */ jsx33(
17609
18961
  ItemActionToolbar,
17610
18962
  {
17611
18963
  onEditLink: openLinkPopoverForSelected,
@@ -17621,7 +18973,10 @@ function OhhwellsBridge() {
17621
18973
  onSelectParent: handleSelectParent,
17622
18974
  onDuplicate: handleDuplicateSelected,
17623
18975
  onDelete: handleDeleteSelected,
17624
- addItemDisabled: false,
18976
+ addItemDisabled: isFooterFrameSelection && isFooterAddItemDisabled(selectedElRef.current) || selectedElRef.current !== null && (() => {
18977
+ const row = isSocialsRow(selectedElRef.current) ? selectedElRef.current : findSocialsRow(selectedElRef.current);
18978
+ return row ? !canAddSocialItem(row) : false;
18979
+ })(),
17625
18980
  editLinkDisabled: false,
17626
18981
  moreDisabled: false,
17627
18982
  duplicateDisabled: isFooterFrameSelection || selectedIsSocialsRow,
@@ -17638,8 +18993,8 @@ function OhhwellsBridge() {
17638
18993
  ) : void 0
17639
18994
  }
17640
18995
  ),
17641
- toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs19(Fragment8, { children: [
17642
- /* @__PURE__ */ jsx32(
18996
+ toolbarRect && toolbarVariant === "rich-text" && !linkPopover && /* @__PURE__ */ jsxs20(Fragment8, { children: [
18997
+ /* @__PURE__ */ jsx33(
17643
18998
  EditGlowChrome,
17644
18999
  {
17645
19000
  rect: toolbarRect,
@@ -17649,7 +19004,7 @@ function OhhwellsBridge() {
17649
19004
  hideHandle: isItemDragging
17650
19005
  }
17651
19006
  ),
17652
- /* @__PURE__ */ jsx32(
19007
+ /* @__PURE__ */ jsx33(
17653
19008
  FloatingToolbar,
17654
19009
  {
17655
19010
  rect: toolbarRect,
@@ -17662,7 +19017,7 @@ function OhhwellsBridge() {
17662
19017
  }
17663
19018
  )
17664
19019
  ] }),
17665
- maxBadge && /* @__PURE__ */ jsxs19(
19020
+ maxBadge && /* @__PURE__ */ jsxs20(
17666
19021
  "div",
17667
19022
  {
17668
19023
  "data-ohw-max-badge": "",
@@ -17688,7 +19043,7 @@ function OhhwellsBridge() {
17688
19043
  ]
17689
19044
  }
17690
19045
  ),
17691
- toggleState && !linkPopover && /* @__PURE__ */ jsx32(
19046
+ toggleState && !linkPopover && /* @__PURE__ */ jsx33(
17692
19047
  StateToggle,
17693
19048
  {
17694
19049
  rect: toggleState.rect,
@@ -17697,15 +19052,15 @@ function OhhwellsBridge() {
17697
19052
  onStateChange: handleStateChange
17698
19053
  }
17699
19054
  ),
17700
- sectionGap && !linkPopover && /* @__PURE__ */ jsxs19(
19055
+ sectionGap && !linkPopover && /* @__PURE__ */ jsxs20(
17701
19056
  "div",
17702
19057
  {
17703
19058
  "data-ohw-section-insert-line": "",
17704
19059
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
17705
19060
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
17706
19061
  children: [
17707
- /* @__PURE__ */ jsx32("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
17708
- /* @__PURE__ */ jsx32(
19062
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
19063
+ /* @__PURE__ */ jsx33(
17709
19064
  Badge,
17710
19065
  {
17711
19066
  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",
@@ -17722,11 +19077,11 @@ function OhhwellsBridge() {
17722
19077
  children: "Add Section"
17723
19078
  }
17724
19079
  ),
17725
- /* @__PURE__ */ jsx32("div", { className: "flex-1 bg-primary", style: { height: 3 } })
19080
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } })
17726
19081
  ]
17727
19082
  }
17728
19083
  ),
17729
- linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx32(
19084
+ linkPopover && dialogPortalContainer ? /* @__PURE__ */ jsx33(
17730
19085
  LinkPopover,
17731
19086
  {
17732
19087
  panelRef: linkPopoverPanelRef,
@@ -17743,7 +19098,7 @@ function OhhwellsBridge() {
17743
19098
  },
17744
19099
  linkPopover.key
17745
19100
  ) : null,
17746
- floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx32(
19101
+ floatingPanel && floatingPanel.kind === "socials-display" ? /* @__PURE__ */ jsx33(
17747
19102
  FloatingPanel,
17748
19103
  {
17749
19104
  open: true,
@@ -17753,7 +19108,7 @@ function OhhwellsBridge() {
17753
19108
  onPositionChange: setFloatingPanelPos,
17754
19109
  parentScroll: parentScrollSnap ?? parentScrollRef.current,
17755
19110
  onClose: closeFloatingPanelOnly,
17756
- children: /* @__PURE__ */ jsx32(
19111
+ children: /* @__PURE__ */ jsx33(
17757
19112
  SocialsDisplayPanel,
17758
19113
  {
17759
19114
  display: socialsDisplayFor(floatingPanel.row, editContentRef.current),
@@ -17764,11 +19119,115 @@ function OhhwellsBridge() {
17764
19119
  }
17765
19120
  )
17766
19121
  }
19122
+ ) : null,
19123
+ floatingPanel && floatingPanel.kind === "logo-size" && logoSizeDraft ? /* @__PURE__ */ jsx33(
19124
+ FloatingPanel,
19125
+ {
19126
+ open: true,
19127
+ title: floatingPanel.title,
19128
+ context: floatingPanel.context,
19129
+ position: floatingPanelPos,
19130
+ onPositionChange: setFloatingPanelPos,
19131
+ parentScroll: parentScrollSnap ?? parentScrollRef.current,
19132
+ onClose: closeFloatingPanelAndDeselect,
19133
+ children: /* @__PURE__ */ jsx33(
19134
+ LogoSizePanel,
19135
+ {
19136
+ viewport: editorViewport,
19137
+ sizePx: editorViewport === "mobile" && !logoSizeDraft.mobileFollowing ? logoSizeDraft.mobilePx : logoSizeDraft.desktopPx,
19138
+ mobileFollowing: logoSizeDraft.mobileFollowing,
19139
+ onSizeChange: (px) => {
19140
+ const next = editorViewport === "mobile" ? { ...logoSizeDraft, mobilePx: px, mobileFollowing: false } : {
19141
+ ...logoSizeDraft,
19142
+ desktopPx: px,
19143
+ mobilePx: logoSizeDraft.mobileFollowing ? px : logoSizeDraft.mobilePx
19144
+ };
19145
+ setLogoSizeDraft(next);
19146
+ persistLogoSizeDraft(floatingPanel.placement, next);
19147
+ },
19148
+ onCustomizeMobile: () => {
19149
+ const next = {
19150
+ ...logoSizeDraft,
19151
+ mobileFollowing: false,
19152
+ mobilePx: logoSizeDraft.desktopPx
19153
+ };
19154
+ setLogoSizeDraft(next);
19155
+ persistLogoSizeDraft(floatingPanel.placement, next);
19156
+ },
19157
+ onResetMobile: () => {
19158
+ const next = {
19159
+ ...logoSizeDraft,
19160
+ mobileFollowing: true,
19161
+ mobilePx: logoSizeDraft.desktopPx
19162
+ };
19163
+ setLogoSizeDraft(next);
19164
+ persistLogoSizeDraft(floatingPanel.placement, next);
19165
+ },
19166
+ onUpdateEverywhere: () => {
19167
+ const identity = readLogoIdentityFromDom();
19168
+ postToParent2({ type: "ow:open-logo-settings", ...identity });
19169
+ }
19170
+ }
19171
+ )
19172
+ }
17767
19173
  ) : null
17768
19174
  ] }),
17769
19175
  bridgeRoot
17770
19176
  ) : null;
17771
19177
  }
19178
+
19179
+ // src/ui/EmptySection.tsx
19180
+ import Link4 from "next/link";
19181
+ import { Fragment as Fragment9, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
19182
+ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
19183
+ return /* @__PURE__ */ jsxs21(Fragment9, { children: [
19184
+ /* @__PURE__ */ jsx34(
19185
+ "p",
19186
+ {
19187
+ style: {
19188
+ fontFamily: "var(--brand-font-body)",
19189
+ fontSize: "0.75rem",
19190
+ fontWeight: 500,
19191
+ letterSpacing: "0.15em",
19192
+ textTransform: "uppercase",
19193
+ color: "var(--brand-accent)",
19194
+ marginBottom: "1.5rem"
19195
+ },
19196
+ children: /* @__PURE__ */ jsx34(Link4, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx34("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
19197
+ }
19198
+ ),
19199
+ /* @__PURE__ */ jsx34(
19200
+ "h1",
19201
+ {
19202
+ style: {
19203
+ fontFamily: "var(--brand-font-heading)",
19204
+ fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
19205
+ lineHeight: 1.1,
19206
+ letterSpacing: "-0.025em",
19207
+ color: "var(--brand-text)",
19208
+ marginBottom: "1rem"
19209
+ },
19210
+ ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
19211
+ children: title
19212
+ }
19213
+ ),
19214
+ /* @__PURE__ */ jsx34(
19215
+ "p",
19216
+ {
19217
+ style: {
19218
+ fontFamily: "var(--brand-font-body)",
19219
+ fontSize: "1rem",
19220
+ lineHeight: 1.7,
19221
+ fontWeight: 300,
19222
+ color: "var(--brand-text-muted)",
19223
+ maxWidth: "340px"
19224
+ },
19225
+ ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
19226
+ children: "This page doesn't have any content yet."
19227
+ }
19228
+ )
19229
+ ] });
19230
+ }
17772
19231
  export {
17773
19232
  AI_DEFAULT_BRAND,
17774
19233
  AI_TREE_SCHEMA_VERSIONS,
@@ -17785,6 +19244,7 @@ export {
17785
19244
  DropdownMenuItem,
17786
19245
  DropdownMenuSeparator,
17787
19246
  DropdownMenuTrigger,
19247
+ EmptySection,
17788
19248
  ItemActionToolbar,
17789
19249
  ItemInteractionLayer,
17790
19250
  LinkEditorPanel,