bearminion 1.3.0 → 1.5.0
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/CHANGELOG.md +12 -0
- package/dist/astro.d.ts +2 -2
- package/dist/astro.js +4 -4
- package/dist/content.d.ts +1 -1
- package/dist/content.js +2 -2
- package/dist/index.d.ts +49 -21
- package/dist/index.js +28 -6
- package/dist/{options-BdPRHcqA.d.ts → options-DBfoi9D1.d.ts} +1 -1
- package/dist/{page-DJ6L8RmH.d.ts → page-B5NoN74Q.d.ts} +118 -10
- package/dist/{page-ON4Jkcm-.js → page-NOFVEqn1.js} +125 -3
- package/dist/{preview-CTVEBdEY.js → preview--S1QUrMM.js} +1 -1
- package/dist/{reload-Cq434rtI.js → reload-D0vwI0OT.js} +1 -1
- package/dist/{theme-DMpt3hcT.js → theme-DFjHjgFO.js} +1 -1
- package/dist/{theme-colors-B2kAO2n6.js → theme-colors-CbCyVANH.js} +52 -51
- package/dist/ui/mount.js +1094 -570
- package/package.json +4 -4
package/dist/ui/mount.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as sectionFieldKey, i as isPropShown, t as defaultTheme } from "../theme-
|
|
2
|
-
import {
|
|
1
|
+
import { a as sectionFieldKey, i as isPropShown, t as defaultTheme } from "../theme-DFjHjgFO.js";
|
|
2
|
+
import { B as withImageLayout, F as DEFAULT_IMAGE_POSITION, I as IMAGE_FITS, L as IMAGE_POSITIONS, P as DEFAULT_IMAGE_FIT, R as isImageFit, S as upsertJsonLd, b as parseJsonLdInput, g as isPlaceholderSiteUrl, i as isSystemPage, m as generateSiteJsonLd, p as formatJsonLd, v as jsonLdWarnings, z as isImagePosition } from "../page-NOFVEqn1.js";
|
|
3
3
|
import { applyTextMark, fontMarks, marksFromEmphasis, reconcileMarks, renderMarkedHtml } from "../marks.js";
|
|
4
|
-
import { c as hasThemeColors, d as tokenToHex, f as
|
|
4
|
+
import { _ as consentCopy, c as hasThemeColors, d as tokenToHex, f as CONSENT_COPY, h as CONSENT_LIMITS, i as contrastChecks, n as HEX_COLOR, o as derivePalette, r as OVERRIDE_TOKENS, s as hasOverrides, t as DEFAULT_BRAND, u as themeCss, v as consentTextIssues } from "../theme-colors-CbCyVANH.js";
|
|
5
5
|
import { _ as writeSaveNotice, a as localDraftStorage, d as removeDraft, f as savedNoticeFor, g as writeDraftRecord, h as sweepExpiredDrafts, i as isForeignSave, l as readDraftRecord, m as siteSavedKey, n as draftBannerText, o as pageDraftKey, p as siteDraftKey, r as hashText, s as pageSavedKey, t as assessStoredDraft, u as reloadDecision, x as draftImageUsages } from "../draft-store-1Y4-sn45.js";
|
|
6
6
|
import { isSafeHref, markdownFromHtml, renderRichText, richLinkAt, sanitizeRichHtml, stripPlainRich, updateRichLink } from "../rich-text.js";
|
|
7
7
|
import { Component, Fragment, createContext, isValidElement, options, render } from "preact";
|
|
@@ -13832,6 +13832,59 @@ function imageSrcAt(doc, path) {
|
|
|
13832
13832
|
if (typeof value === "string") return value;
|
|
13833
13833
|
return isRecord$2(value) && typeof value.src === "string" ? value.src : "";
|
|
13834
13834
|
}
|
|
13835
|
+
const CONSENT_KEYS = [
|
|
13836
|
+
"title",
|
|
13837
|
+
"body",
|
|
13838
|
+
"accept",
|
|
13839
|
+
"reject",
|
|
13840
|
+
"settings",
|
|
13841
|
+
"policyLabel",
|
|
13842
|
+
"policyHref"
|
|
13843
|
+
];
|
|
13844
|
+
/** The banner texts stored in a site draft. Anything else in `consent`, such as `enabled`, is left out. */
|
|
13845
|
+
function consentTextOf(site) {
|
|
13846
|
+
const stored = getAt(site, "consent");
|
|
13847
|
+
const text = {};
|
|
13848
|
+
if (!isRecord$2(stored)) return text;
|
|
13849
|
+
for (const key of CONSENT_KEYS) {
|
|
13850
|
+
const value = stored[key];
|
|
13851
|
+
if (typeof value === "string") text[key] = value;
|
|
13852
|
+
}
|
|
13853
|
+
return text;
|
|
13854
|
+
}
|
|
13855
|
+
/** Whether the draft asks visitors before analytics runs. Only an explicit `false` turns it off. */
|
|
13856
|
+
function consentEnabled(site) {
|
|
13857
|
+
return getAt(site, "consent.enabled") !== false;
|
|
13858
|
+
}
|
|
13859
|
+
/** Whether the draft holds an analytics snippet that the banner would guard. */
|
|
13860
|
+
function hasAnalyticsSnippet(site) {
|
|
13861
|
+
return ["analytics.head", "analytics.bodyEnd"].some((path) => {
|
|
13862
|
+
const value = getAt(site, path);
|
|
13863
|
+
return typeof value === "string" && value.trim() !== "";
|
|
13864
|
+
});
|
|
13865
|
+
}
|
|
13866
|
+
/** Where the draft reopens the banner. Anything but `icon` is the footer link. */
|
|
13867
|
+
function consentReopenOf(site) {
|
|
13868
|
+
return getAt(site, "consent.reopen") === "icon" ? "icon" : "footer";
|
|
13869
|
+
}
|
|
13870
|
+
/** The JSON-LD blocks every page carries, as the draft holds them. */
|
|
13871
|
+
function siteJsonLdOf(site) {
|
|
13872
|
+
const stored = getAt(site, "seo.jsonLd");
|
|
13873
|
+
return Array.isArray(stored) ? stored.filter(isRecord$2) : [];
|
|
13874
|
+
}
|
|
13875
|
+
/** What Generate reads from the draft. The logo counts only when it is a full https address. */
|
|
13876
|
+
function siteJsonLdSourceOf(site) {
|
|
13877
|
+
const name = getAt(site, "name");
|
|
13878
|
+
const url = getAt(site, "url");
|
|
13879
|
+
const lang = getAt(site, "lang");
|
|
13880
|
+
const logo = getAt(site, "brandLogo.src");
|
|
13881
|
+
return {
|
|
13882
|
+
name: typeof name === "string" ? name : "",
|
|
13883
|
+
url: typeof url === "string" ? url : "",
|
|
13884
|
+
lang: typeof lang === "string" && lang !== "" ? lang : void 0,
|
|
13885
|
+
logo: typeof logo === "string" && /^https:\/\//i.test(logo) ? logo : void 0
|
|
13886
|
+
};
|
|
13887
|
+
}
|
|
13835
13888
|
//#endregion
|
|
13836
13889
|
//#region src/ui/controllers/reactions.ts
|
|
13837
13890
|
/** Signal effects that keep the page, storage and history in step with the editor state. */
|
|
@@ -16365,31 +16418,25 @@ function CommitDialog({ info, unsaved, onDone, onClose }) {
|
|
|
16365
16418
|
});
|
|
16366
16419
|
}
|
|
16367
16420
|
//#endregion
|
|
16368
|
-
//#region src/ui/
|
|
16369
|
-
|
|
16370
|
-
|
|
16371
|
-
|
|
16372
|
-
|
|
16373
|
-
|
|
16374
|
-
|
|
16375
|
-
|
|
16376
|
-
|
|
16377
|
-
|
|
16378
|
-
|
|
16379
|
-
|
|
16380
|
-
|
|
16381
|
-
|
|
16382
|
-
|
|
16383
|
-
|
|
16384
|
-
|
|
16385
|
-
|
|
16386
|
-
|
|
16387
|
-
onClick: () => onChange(option.value),
|
|
16388
|
-
children: option.label
|
|
16389
|
-
}, option.value))
|
|
16390
|
-
})]
|
|
16391
|
-
});
|
|
16392
|
-
}
|
|
16421
|
+
//#region src/ui/context.ts
|
|
16422
|
+
const LinksContext = createContext({
|
|
16423
|
+
pages: [],
|
|
16424
|
+
currentSlug: "index",
|
|
16425
|
+
anchors: [],
|
|
16426
|
+
editLink: () => {}
|
|
16427
|
+
});
|
|
16428
|
+
const IssueContext = createContext([]);
|
|
16429
|
+
/** Which props of the section being edited can be dropped. ValueEditor reads it to offer Remove, Add and "empty hides it". */
|
|
16430
|
+
const OptionalContext = createContext({ content: void 0 });
|
|
16431
|
+
/**
|
|
16432
|
+
* What the schema of the section being edited says about its fields, by field key: fixed choices and their icons
|
|
16433
|
+
* (ValueEditor shows a picker) and image fields (MissingField shows an image slot).
|
|
16434
|
+
*/
|
|
16435
|
+
const FieldsContext = createContext({
|
|
16436
|
+
choices: void 0,
|
|
16437
|
+
icons: void 0,
|
|
16438
|
+
images: void 0
|
|
16439
|
+
});
|
|
16393
16440
|
//#endregion
|
|
16394
16441
|
//#region src/ui/info.tsx
|
|
16395
16442
|
const GAP = 6;
|
|
@@ -16487,543 +16534,223 @@ function Info({ text, label = "More info", id }) {
|
|
|
16487
16534
|
});
|
|
16488
16535
|
}
|
|
16489
16536
|
//#endregion
|
|
16490
|
-
//#region src/ui/
|
|
16491
|
-
|
|
16492
|
-
|
|
16493
|
-
|
|
16494
|
-
|
|
16495
|
-
|
|
16496
|
-
|
|
16497
|
-
|
|
16498
|
-
|
|
16499
|
-
|
|
16500
|
-
}
|
|
16501
|
-
const
|
|
16502
|
-
|
|
16503
|
-
|
|
16537
|
+
//#region src/ui/image-field.tsx
|
|
16538
|
+
const FIT_LABEL = {
|
|
16539
|
+
cover: "Cover",
|
|
16540
|
+
contain: "Contain",
|
|
16541
|
+
auto: "Auto"
|
|
16542
|
+
};
|
|
16543
|
+
const FIT_HINT = {
|
|
16544
|
+
cover: "Fills the frame and crops the overflow.",
|
|
16545
|
+
contain: "Shows the whole image inside the frame.",
|
|
16546
|
+
auto: "Keeps the image’s own size and aspect ratio."
|
|
16547
|
+
};
|
|
16548
|
+
const POSITION_LABEL = {
|
|
16549
|
+
"top left": "Top left",
|
|
16550
|
+
top: "Top",
|
|
16551
|
+
"top right": "Top right",
|
|
16552
|
+
left: "Left",
|
|
16553
|
+
center: "Center",
|
|
16554
|
+
right: "Right",
|
|
16555
|
+
"bottom left": "Bottom left",
|
|
16556
|
+
bottom: "Bottom",
|
|
16557
|
+
"bottom right": "Bottom right"
|
|
16558
|
+
};
|
|
16559
|
+
/** Drop the keys that equal their default so saved JSON only holds real choices. */
|
|
16560
|
+
function compact(next) {
|
|
16561
|
+
const { fit, position, hidden, ...rest } = next;
|
|
16562
|
+
return {
|
|
16563
|
+
...rest,
|
|
16564
|
+
...hidden === true ? { hidden } : {},
|
|
16565
|
+
...fit && fit !== "cover" ? { fit } : {},
|
|
16566
|
+
...position && position !== "center" ? { position } : {}
|
|
16567
|
+
};
|
|
16504
16568
|
}
|
|
16505
|
-
/**
|
|
16506
|
-
|
|
16507
|
-
|
|
16508
|
-
|
|
16509
|
-
|
|
16510
|
-
|
|
16511
|
-
|
|
16512
|
-
|
|
16513
|
-
|
|
16514
|
-
|
|
16515
|
-
|
|
16516
|
-
|
|
16569
|
+
/**
|
|
16570
|
+
* True when `src` is a local image whose file is gone. The dev route answers HEAD with 404 for those; remote URLs and
|
|
16571
|
+
* placeholders are never "missing". Rechecks whenever the path changes.
|
|
16572
|
+
*/
|
|
16573
|
+
function useMissingFile(src) {
|
|
16574
|
+
const [missing, setMissing] = useState(false);
|
|
16575
|
+
useEffect(() => {
|
|
16576
|
+
const url = previewSrc(src);
|
|
16577
|
+
if (!url.startsWith("/__bearminion/assets?")) {
|
|
16578
|
+
setMissing(false);
|
|
16579
|
+
return;
|
|
16580
|
+
}
|
|
16581
|
+
let current = true;
|
|
16582
|
+
fetch(url, {
|
|
16583
|
+
method: "HEAD",
|
|
16584
|
+
cache: "no-store"
|
|
16585
|
+
}).then((response) => {
|
|
16586
|
+
if (current) setMissing(response.status === 404);
|
|
16587
|
+
}).catch(() => {
|
|
16588
|
+
if (current) setMissing(false);
|
|
16589
|
+
});
|
|
16590
|
+
return () => {
|
|
16591
|
+
current = false;
|
|
16592
|
+
};
|
|
16593
|
+
}, [src]);
|
|
16594
|
+
return missing;
|
|
16517
16595
|
}
|
|
16518
|
-
function
|
|
16519
|
-
|
|
16520
|
-
|
|
16521
|
-
|
|
16522
|
-
minute: "2-digit",
|
|
16523
|
-
hour12: false
|
|
16524
|
-
});
|
|
16596
|
+
function previewSrc(src) {
|
|
16597
|
+
if (src === "" || src === "placeholder") return "";
|
|
16598
|
+
if (/^https?:\/\//i.test(src) || src.startsWith("/")) return src;
|
|
16599
|
+
return `/__bearminion/assets?src=${encodeURIComponent(src.replace(/^\//, ""))}`;
|
|
16525
16600
|
}
|
|
16526
|
-
/**
|
|
16527
|
-
function
|
|
16528
|
-
|
|
16529
|
-
if (Number.isNaN(then)) return "";
|
|
16530
|
-
const minutes = Math.max(0, Math.round((now - then) / 6e4));
|
|
16531
|
-
if (minutes < 1) return "just now";
|
|
16532
|
-
if (minutes < 60) return `${minutes} min ago`;
|
|
16533
|
-
const hours = Math.round(minutes / 60);
|
|
16534
|
-
return hours < 24 ? `${hours} h ago` : versionClock(iso);
|
|
16601
|
+
/** Only react to files. Dragging text or a link over a drop target is not an upload. */
|
|
16602
|
+
function hasFiles(event) {
|
|
16603
|
+
return Array.from(event.dataTransfer?.types ?? []).includes("Files");
|
|
16535
16604
|
}
|
|
16536
|
-
|
|
16537
|
-
|
|
16538
|
-
if (!list || list.length === 0) return "";
|
|
16539
|
-
const names = [...new Set(list.map((item) => item.field))];
|
|
16540
|
-
return names.length <= 3 ? names.join(", ") : `${names.slice(0, 3).join(", ")} +${names.length - 3}`;
|
|
16605
|
+
function fileName(src) {
|
|
16606
|
+
return src.split("/").pop() || src;
|
|
16541
16607
|
}
|
|
16608
|
+
/** The tab last used in this page session, so a second pick starts where the first ended. */
|
|
16609
|
+
let lastTab = null;
|
|
16610
|
+
const ACCEPT = "image/png,image/jpeg,image/webp,image/gif,image/avif";
|
|
16611
|
+
const FOCUSABLE = "button:not(:disabled), input:not(:disabled), [tabindex=\"0\"]";
|
|
16542
16612
|
/**
|
|
16543
|
-
*
|
|
16544
|
-
*
|
|
16613
|
+
* One control for every image in the editor. Empty: a slot that opens the picker.
|
|
16614
|
+
* Filled: thumbnail, file name, alt text, Change and Remove. The picker is a modal
|
|
16615
|
+
* with an Upload tab (drop or browse) and a Library tab (pick or delete).
|
|
16545
16616
|
*/
|
|
16546
|
-
function
|
|
16617
|
+
function ImageField({ label, path, value, images, withAlt = true, layout = true, hideable = false, onChange, onUploaded, onDeleted }) {
|
|
16618
|
+
const [open, setOpen] = useState(false);
|
|
16619
|
+
const [error, setError] = useState("");
|
|
16620
|
+
const [over, setOver] = useState(false);
|
|
16547
16621
|
const root = useRef(null);
|
|
16548
|
-
|
|
16549
|
-
const
|
|
16550
|
-
const
|
|
16551
|
-
const
|
|
16552
|
-
const
|
|
16553
|
-
const
|
|
16554
|
-
const
|
|
16555
|
-
const
|
|
16556
|
-
|
|
16557
|
-
|
|
16558
|
-
|
|
16559
|
-
|
|
16560
|
-
|
|
16561
|
-
|
|
16562
|
-
|
|
16563
|
-
|
|
16564
|
-
|
|
16565
|
-
|
|
16566
|
-
|
|
16567
|
-
|
|
16568
|
-
|
|
16569
|
-
|
|
16570
|
-
|
|
16571
|
-
|
|
16572
|
-
|
|
16573
|
-
|
|
16574
|
-
|
|
16575
|
-
body.entries.forEach((entry, index) => {
|
|
16576
|
-
const after = data[index];
|
|
16577
|
-
const before = data[index + 1];
|
|
16578
|
-
if (after === null || before === null || before === void 0) return;
|
|
16579
|
-
next[entry.id] = scope === "page" ? describePageChanges(before, after, labels) : describeSiteChanges(before, after);
|
|
16580
|
-
});
|
|
16581
|
-
setDiffs(next);
|
|
16582
|
-
};
|
|
16583
|
-
load().catch(() => live && setFailed(true));
|
|
16584
|
-
return () => {
|
|
16585
|
-
live = false;
|
|
16586
|
-
};
|
|
16587
|
-
}, [base]);
|
|
16588
|
-
async function restore(entry) {
|
|
16589
|
-
const version = await getHistoryVersion(scope, slug, entry.id);
|
|
16590
|
-
if (!version.ok) {
|
|
16591
|
-
setFailed(true);
|
|
16592
|
-
return;
|
|
16593
|
-
}
|
|
16594
|
-
const when = formatVersionTime(entry.at);
|
|
16595
|
-
onRestore(scope, version.value, when);
|
|
16596
|
-
setRestored(when);
|
|
16622
|
+
const opener = useRef(null);
|
|
16623
|
+
const src = typeof value?.src === "string" ? value.src : "";
|
|
16624
|
+
const alt = typeof value?.alt === "string" ? value.alt : "";
|
|
16625
|
+
const fit = isImageFit(value?.fit) ? value.fit : DEFAULT_IMAGE_FIT;
|
|
16626
|
+
const position = isImagePosition(value?.position) ? value.position : DEFAULT_IMAGE_POSITION;
|
|
16627
|
+
const showLayout = layout && withAlt;
|
|
16628
|
+
const hidden = hideable && value?.hidden === true;
|
|
16629
|
+
const current = {
|
|
16630
|
+
src,
|
|
16631
|
+
alt,
|
|
16632
|
+
fit,
|
|
16633
|
+
position,
|
|
16634
|
+
...hidden ? { hidden } : {}
|
|
16635
|
+
};
|
|
16636
|
+
/** Emit the whole image object. A new file or alt keeps the fit and position already chosen. */
|
|
16637
|
+
const emit = (patch) => onChange(compact({
|
|
16638
|
+
...current,
|
|
16639
|
+
...patch
|
|
16640
|
+
}));
|
|
16641
|
+
const empty = src === "" || src === "placeholder";
|
|
16642
|
+
const shown = previewSrc(src);
|
|
16643
|
+
const missing = useMissingFile(empty ? "" : src);
|
|
16644
|
+
const fallbackAlt = withAlt ? "Image" : "";
|
|
16645
|
+
function openPicker(trigger) {
|
|
16646
|
+
opener.current = trigger;
|
|
16647
|
+
setError("");
|
|
16648
|
+
setOpen(true);
|
|
16597
16649
|
}
|
|
16598
|
-
|
|
16599
|
-
|
|
16600
|
-
|
|
16601
|
-
|
|
16602
|
-
if (last && last.day === day) last.items.push({
|
|
16603
|
-
entry,
|
|
16604
|
-
index
|
|
16650
|
+
function closePicker() {
|
|
16651
|
+
setOpen(false);
|
|
16652
|
+
requestAnimationFrame(() => {
|
|
16653
|
+
(opener.current?.isConnected ? opener.current : root.current)?.focus();
|
|
16605
16654
|
});
|
|
16606
|
-
|
|
16607
|
-
|
|
16608
|
-
|
|
16609
|
-
|
|
16610
|
-
|
|
16611
|
-
|
|
16655
|
+
}
|
|
16656
|
+
/** A file dropped on the card replaces the image without opening the picker. */
|
|
16657
|
+
async function upload(file) {
|
|
16658
|
+
if (!file) return;
|
|
16659
|
+
setError("");
|
|
16660
|
+
const result = await uploadFile(file);
|
|
16661
|
+
if ("error" in result) {
|
|
16662
|
+
setError(result.error);
|
|
16663
|
+
return;
|
|
16664
|
+
}
|
|
16665
|
+
onUploaded(result.image);
|
|
16666
|
+
emit({
|
|
16667
|
+
src: result.image.path,
|
|
16668
|
+
alt: alt || fallbackAlt
|
|
16612
16669
|
});
|
|
16670
|
+
}
|
|
16671
|
+
const trashed = () => onChange({
|
|
16672
|
+
src: withAlt ? "placeholder" : "",
|
|
16673
|
+
alt: "",
|
|
16674
|
+
...hidden ? { hidden } : {}
|
|
16675
|
+
});
|
|
16676
|
+
/** An empty image has no file yet; the placeholder keeps the saved object valid while it is hidden. */
|
|
16677
|
+
const setShown = (show) => emit({
|
|
16678
|
+
hidden: show ? void 0 : true,
|
|
16679
|
+
src: src === "" ? "placeholder" : src
|
|
16613
16680
|
});
|
|
16614
16681
|
return /* @__PURE__ */ jsxs("div", {
|
|
16615
|
-
class: "bm-
|
|
16616
|
-
|
|
16617
|
-
|
|
16618
|
-
"aria-label": "Version history",
|
|
16682
|
+
class: over ? "bm-imagefield bm-drop-files" : "bm-imagefield",
|
|
16683
|
+
"data-editor-field": path,
|
|
16684
|
+
tabIndex: -1,
|
|
16619
16685
|
ref: root,
|
|
16620
|
-
|
|
16686
|
+
"data-drop-hint": over ? "Drop to upload" : void 0,
|
|
16687
|
+
onDragOver: (event) => {
|
|
16688
|
+
if (open || !hasFiles(event)) return;
|
|
16689
|
+
event.preventDefault();
|
|
16690
|
+
if (event.dataTransfer) event.dataTransfer.dropEffect = "copy";
|
|
16691
|
+
if (!over) setOver(true);
|
|
16692
|
+
},
|
|
16693
|
+
onDragLeave: (event) => {
|
|
16694
|
+
const next = event.relatedTarget;
|
|
16695
|
+
if (next instanceof Node && event.currentTarget.contains(next)) return;
|
|
16696
|
+
setOver(false);
|
|
16697
|
+
},
|
|
16698
|
+
onDrop: (event) => {
|
|
16699
|
+
if (open || !hasFiles(event)) return;
|
|
16700
|
+
event.preventDefault();
|
|
16701
|
+
setOver(false);
|
|
16702
|
+
upload(event.dataTransfer?.files[0]);
|
|
16703
|
+
},
|
|
16621
16704
|
children: [
|
|
16622
|
-
/* @__PURE__ */
|
|
16623
|
-
class: "bm-
|
|
16624
|
-
children:
|
|
16625
|
-
class: "bm-
|
|
16626
|
-
children: [/* @__PURE__ */
|
|
16627
|
-
class: "bm-
|
|
16628
|
-
children:
|
|
16629
|
-
|
|
16630
|
-
|
|
16631
|
-
|
|
16632
|
-
/* @__PURE__ */ jsx("code", { children: ".editor/history" }),
|
|
16633
|
-
". They are not in git and not on the published site. The newest ",
|
|
16634
|
-
VERSION_CAP,
|
|
16635
|
-
" stay. To clear them, stop the dev server and delete the ",
|
|
16636
|
-
/* @__PURE__ */ jsx("code", { children: ".editor" }),
|
|
16637
|
-
" folder in your project root."
|
|
16638
|
-
] })
|
|
16639
|
-
})]
|
|
16640
|
-
}), /* @__PURE__ */ jsx("p", {
|
|
16641
|
-
class: "bm-dialog-sub",
|
|
16642
|
-
children: "Every save is kept. Restore loads a version into the draft; nothing is written until you save."
|
|
16705
|
+
/* @__PURE__ */ jsx("span", {
|
|
16706
|
+
class: "bm-label",
|
|
16707
|
+
children: /* @__PURE__ */ jsxs("span", {
|
|
16708
|
+
class: "bm-label-row",
|
|
16709
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
16710
|
+
class: "bm-label-text",
|
|
16711
|
+
children: label
|
|
16712
|
+
}), withAlt && !empty && /* @__PURE__ */ jsx("span", {
|
|
16713
|
+
class: "bm-meta-tag",
|
|
16714
|
+
children: "alt required"
|
|
16643
16715
|
})]
|
|
16644
|
-
})
|
|
16645
|
-
}),
|
|
16646
|
-
/* @__PURE__ */ jsx(Segmented, {
|
|
16647
|
-
hideLabel: true,
|
|
16648
|
-
label: "Scope",
|
|
16649
|
-
value: scope,
|
|
16650
|
-
options: [{
|
|
16651
|
-
value: "page",
|
|
16652
|
-
label: `Page "${page.title}"`
|
|
16653
|
-
}, {
|
|
16654
|
-
value: "site",
|
|
16655
|
-
label: "Site"
|
|
16656
|
-
}],
|
|
16657
|
-
onChange: (value) => setScope(value === "site" ? "site" : "page")
|
|
16716
|
+
})
|
|
16658
16717
|
}),
|
|
16659
|
-
|
|
16660
|
-
class: "bm-
|
|
16661
|
-
|
|
16662
|
-
|
|
16663
|
-
|
|
16664
|
-
|
|
16665
|
-
|
|
16666
|
-
|
|
16667
|
-
|
|
16668
|
-
|
|
16669
|
-
|
|
16670
|
-
|
|
16671
|
-
|
|
16672
|
-
|
|
16673
|
-
|
|
16718
|
+
hideable && /* @__PURE__ */ jsxs("label", {
|
|
16719
|
+
class: "bm-switch-row",
|
|
16720
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
16721
|
+
class: "bm-switch-text",
|
|
16722
|
+
children: /* @__PURE__ */ jsxs("span", {
|
|
16723
|
+
class: "bm-switch-title",
|
|
16724
|
+
children: ["Show in section", /* @__PURE__ */ jsx(Info, {
|
|
16725
|
+
label: "About showing the image",
|
|
16726
|
+
text: "Turn off to remove the image and its frame from the section. The chosen file stays, so turning it back on restores it."
|
|
16727
|
+
})]
|
|
16728
|
+
})
|
|
16729
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
16730
|
+
type: "checkbox",
|
|
16731
|
+
role: "switch",
|
|
16732
|
+
"aria-label": `Show ${label.toLowerCase()} in section`,
|
|
16733
|
+
checked: !hidden,
|
|
16734
|
+
onChange: (event) => setShown(event.currentTarget.checked)
|
|
16674
16735
|
})]
|
|
16675
16736
|
}),
|
|
16676
|
-
/* @__PURE__ */ jsxs("
|
|
16677
|
-
|
|
16737
|
+
empty ? /* @__PURE__ */ jsxs("button", {
|
|
16738
|
+
type: "button",
|
|
16739
|
+
class: "bm-slot",
|
|
16740
|
+
onClick: (event) => openPicker(event.currentTarget),
|
|
16678
16741
|
children: [
|
|
16679
|
-
|
|
16680
|
-
|
|
16681
|
-
|
|
16682
|
-
}),
|
|
16683
|
-
!failed && entries === null && /* @__PURE__ */ jsx("p", {
|
|
16684
|
-
class: "bm-note",
|
|
16685
|
-
children: "Loading…"
|
|
16742
|
+
/* @__PURE__ */ jsx(Icon, {
|
|
16743
|
+
name: "upload",
|
|
16744
|
+
size: 16
|
|
16686
16745
|
}),
|
|
16687
|
-
|
|
16688
|
-
class: "bm-
|
|
16689
|
-
children: "
|
|
16746
|
+
/* @__PURE__ */ jsx("span", {
|
|
16747
|
+
class: "bm-slot-title",
|
|
16748
|
+
children: "Add image"
|
|
16690
16749
|
}),
|
|
16691
|
-
|
|
16692
|
-
class: "bm-
|
|
16693
|
-
"
|
|
16694
|
-
|
|
16695
|
-
class: "bm-changes-area",
|
|
16696
|
-
children: group.day
|
|
16697
|
-
}), /* @__PURE__ */ jsx("ul", {
|
|
16698
|
-
class: "bm-versions",
|
|
16699
|
-
children: group.items.map(({ entry, index }) => {
|
|
16700
|
-
const isOpen = openId === entry.id;
|
|
16701
|
-
const list = diffs[entry.id];
|
|
16702
|
-
const stamp = formatVersionTime(entry.at);
|
|
16703
|
-
const earliest = index === (entries?.length ?? 0) - 1;
|
|
16704
|
-
return /* @__PURE__ */ jsxs("li", {
|
|
16705
|
-
class: "bm-version",
|
|
16706
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
16707
|
-
class: "bm-version-row",
|
|
16708
|
-
children: [
|
|
16709
|
-
/* @__PURE__ */ jsx("button", {
|
|
16710
|
-
type: "button",
|
|
16711
|
-
class: "bm-icon-btn",
|
|
16712
|
-
"aria-expanded": isOpen,
|
|
16713
|
-
"aria-label": `${isOpen ? "Hide" : "Show"} what changed in the version from ${stamp}`,
|
|
16714
|
-
onClick: () => setOpenId(isOpen ? null : entry.id),
|
|
16715
|
-
children: /* @__PURE__ */ jsx(Icon, {
|
|
16716
|
-
name: isOpen ? "chevron-up" : "chevron-down",
|
|
16717
|
-
size: 16
|
|
16718
|
-
})
|
|
16719
|
-
}),
|
|
16720
|
-
/* @__PURE__ */ jsxs("button", {
|
|
16721
|
-
type: "button",
|
|
16722
|
-
class: "bm-tertiary bm-version-when",
|
|
16723
|
-
onClick: () => setOpenId(isOpen ? null : entry.id),
|
|
16724
|
-
children: [/* @__PURE__ */ jsxs("span", {
|
|
16725
|
-
class: "bm-version-line",
|
|
16726
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
16727
|
-
class: "bm-version-rel",
|
|
16728
|
-
children: versionAgo(entry.at) || stamp
|
|
16729
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
16730
|
-
class: "bm-version-abs",
|
|
16731
|
-
children: versionClock(entry.at)
|
|
16732
|
-
})]
|
|
16733
|
-
}), versionSummary(list) && /* @__PURE__ */ jsx("span", {
|
|
16734
|
-
class: "bm-version-sum",
|
|
16735
|
-
children: versionSummary(list)
|
|
16736
|
-
})]
|
|
16737
|
-
}),
|
|
16738
|
-
index === 0 ? /* @__PURE__ */ jsx("span", {
|
|
16739
|
-
class: "bm-badge bm-badge-on",
|
|
16740
|
-
children: "current"
|
|
16741
|
-
}) : /* @__PURE__ */ jsxs("button", {
|
|
16742
|
-
type: "button",
|
|
16743
|
-
class: "bm-restore",
|
|
16744
|
-
"aria-label": `Restore the version from ${stamp}`,
|
|
16745
|
-
onClick: () => void restore(entry),
|
|
16746
|
-
children: [/* @__PURE__ */ jsx(Icon, {
|
|
16747
|
-
name: "rotate-ccw",
|
|
16748
|
-
size: 14
|
|
16749
|
-
}), "Restore"]
|
|
16750
|
-
})
|
|
16751
|
-
]
|
|
16752
|
-
}), isOpen && /* @__PURE__ */ jsx("div", {
|
|
16753
|
-
class: "bm-version-diff",
|
|
16754
|
-
children: !list || list.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
16755
|
-
class: "bm-note",
|
|
16756
|
-
children: earliest ? "The earliest kept version, so there is nothing older to compare with." : list ? "No difference from the save before it." : "Loading…"
|
|
16757
|
-
}) : groupChanges(list).map((area) => /* @__PURE__ */ jsxs("section", {
|
|
16758
|
-
"aria-label": area.area,
|
|
16759
|
-
children: [/* @__PURE__ */ jsx("h3", {
|
|
16760
|
-
class: "bm-changes-area",
|
|
16761
|
-
children: area.area
|
|
16762
|
-
}), /* @__PURE__ */ jsx("ul", { children: area.entries.map((item) => /* @__PURE__ */ jsx(ChangeRow, {
|
|
16763
|
-
item,
|
|
16764
|
-
scope
|
|
16765
|
-
}, `${item.field}:${item.kind}`)) })]
|
|
16766
|
-
}, area.area))
|
|
16767
|
-
})]
|
|
16768
|
-
}, entry.id);
|
|
16769
|
-
})
|
|
16770
|
-
})]
|
|
16771
|
-
}, group.day))
|
|
16772
|
-
]
|
|
16773
|
-
}),
|
|
16774
|
-
entries !== null && entries.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
16775
|
-
class: "bm-dialog-foot",
|
|
16776
|
-
children: /* @__PURE__ */ jsxs("p", {
|
|
16777
|
-
class: "bm-hint",
|
|
16778
|
-
children: [
|
|
16779
|
-
entries.length,
|
|
16780
|
-
" of ",
|
|
16781
|
-
VERSION_CAP,
|
|
16782
|
-
" kept on this computer. Older versions drop off."
|
|
16783
|
-
]
|
|
16784
|
-
})
|
|
16785
|
-
})
|
|
16786
|
-
]
|
|
16787
|
-
});
|
|
16788
|
-
}
|
|
16789
|
-
//#endregion
|
|
16790
|
-
//#region src/ui/context.ts
|
|
16791
|
-
const LinksContext = createContext({
|
|
16792
|
-
pages: [],
|
|
16793
|
-
currentSlug: "index",
|
|
16794
|
-
anchors: [],
|
|
16795
|
-
editLink: () => {}
|
|
16796
|
-
});
|
|
16797
|
-
const IssueContext = createContext([]);
|
|
16798
|
-
/** Which props of the section being edited can be dropped. ValueEditor reads it to offer Remove, Add and "empty hides it". */
|
|
16799
|
-
const OptionalContext = createContext({ content: void 0 });
|
|
16800
|
-
/**
|
|
16801
|
-
* What the schema of the section being edited says about its fields, by field key: fixed choices and their icons
|
|
16802
|
-
* (ValueEditor shows a picker) and image fields (MissingField shows an image slot).
|
|
16803
|
-
*/
|
|
16804
|
-
const FieldsContext = createContext({
|
|
16805
|
-
choices: void 0,
|
|
16806
|
-
icons: void 0,
|
|
16807
|
-
images: void 0
|
|
16808
|
-
});
|
|
16809
|
-
//#endregion
|
|
16810
|
-
//#region src/ui/image-field.tsx
|
|
16811
|
-
const FIT_LABEL = {
|
|
16812
|
-
cover: "Cover",
|
|
16813
|
-
contain: "Contain",
|
|
16814
|
-
auto: "Auto"
|
|
16815
|
-
};
|
|
16816
|
-
const FIT_HINT = {
|
|
16817
|
-
cover: "Fills the frame and crops the overflow.",
|
|
16818
|
-
contain: "Shows the whole image inside the frame.",
|
|
16819
|
-
auto: "Keeps the image’s own size and aspect ratio."
|
|
16820
|
-
};
|
|
16821
|
-
const POSITION_LABEL = {
|
|
16822
|
-
"top left": "Top left",
|
|
16823
|
-
top: "Top",
|
|
16824
|
-
"top right": "Top right",
|
|
16825
|
-
left: "Left",
|
|
16826
|
-
center: "Center",
|
|
16827
|
-
right: "Right",
|
|
16828
|
-
"bottom left": "Bottom left",
|
|
16829
|
-
bottom: "Bottom",
|
|
16830
|
-
"bottom right": "Bottom right"
|
|
16831
|
-
};
|
|
16832
|
-
/** Drop the keys that equal their default so saved JSON only holds real choices. */
|
|
16833
|
-
function compact(next) {
|
|
16834
|
-
const { fit, position, hidden, ...rest } = next;
|
|
16835
|
-
return {
|
|
16836
|
-
...rest,
|
|
16837
|
-
...hidden === true ? { hidden } : {},
|
|
16838
|
-
...fit && fit !== "cover" ? { fit } : {},
|
|
16839
|
-
...position && position !== "center" ? { position } : {}
|
|
16840
|
-
};
|
|
16841
|
-
}
|
|
16842
|
-
/**
|
|
16843
|
-
* True when `src` is a local image whose file is gone. The dev route answers HEAD with 404 for those; remote URLs and
|
|
16844
|
-
* placeholders are never "missing". Rechecks whenever the path changes.
|
|
16845
|
-
*/
|
|
16846
|
-
function useMissingFile(src) {
|
|
16847
|
-
const [missing, setMissing] = useState(false);
|
|
16848
|
-
useEffect(() => {
|
|
16849
|
-
const url = previewSrc(src);
|
|
16850
|
-
if (!url.startsWith("/__bearminion/assets?")) {
|
|
16851
|
-
setMissing(false);
|
|
16852
|
-
return;
|
|
16853
|
-
}
|
|
16854
|
-
let current = true;
|
|
16855
|
-
fetch(url, {
|
|
16856
|
-
method: "HEAD",
|
|
16857
|
-
cache: "no-store"
|
|
16858
|
-
}).then((response) => {
|
|
16859
|
-
if (current) setMissing(response.status === 404);
|
|
16860
|
-
}).catch(() => {
|
|
16861
|
-
if (current) setMissing(false);
|
|
16862
|
-
});
|
|
16863
|
-
return () => {
|
|
16864
|
-
current = false;
|
|
16865
|
-
};
|
|
16866
|
-
}, [src]);
|
|
16867
|
-
return missing;
|
|
16868
|
-
}
|
|
16869
|
-
function previewSrc(src) {
|
|
16870
|
-
if (src === "" || src === "placeholder") return "";
|
|
16871
|
-
if (/^https?:\/\//i.test(src) || src.startsWith("/")) return src;
|
|
16872
|
-
return `/__bearminion/assets?src=${encodeURIComponent(src.replace(/^\//, ""))}`;
|
|
16873
|
-
}
|
|
16874
|
-
/** Only react to files. Dragging text or a link over a drop target is not an upload. */
|
|
16875
|
-
function hasFiles(event) {
|
|
16876
|
-
return Array.from(event.dataTransfer?.types ?? []).includes("Files");
|
|
16877
|
-
}
|
|
16878
|
-
function fileName(src) {
|
|
16879
|
-
return src.split("/").pop() || src;
|
|
16880
|
-
}
|
|
16881
|
-
/** The tab last used in this page session, so a second pick starts where the first ended. */
|
|
16882
|
-
let lastTab = null;
|
|
16883
|
-
const ACCEPT = "image/png,image/jpeg,image/webp,image/gif,image/avif";
|
|
16884
|
-
const FOCUSABLE = "button:not(:disabled), input:not(:disabled), [tabindex=\"0\"]";
|
|
16885
|
-
/**
|
|
16886
|
-
* One control for every image in the editor. Empty: a slot that opens the picker.
|
|
16887
|
-
* Filled: thumbnail, file name, alt text, Change and Remove. The picker is a modal
|
|
16888
|
-
* with an Upload tab (drop or browse) and a Library tab (pick or delete).
|
|
16889
|
-
*/
|
|
16890
|
-
function ImageField({ label, path, value, images, withAlt = true, layout = true, hideable = false, onChange, onUploaded, onDeleted }) {
|
|
16891
|
-
const [open, setOpen] = useState(false);
|
|
16892
|
-
const [error, setError] = useState("");
|
|
16893
|
-
const [over, setOver] = useState(false);
|
|
16894
|
-
const root = useRef(null);
|
|
16895
|
-
const opener = useRef(null);
|
|
16896
|
-
const src = typeof value?.src === "string" ? value.src : "";
|
|
16897
|
-
const alt = typeof value?.alt === "string" ? value.alt : "";
|
|
16898
|
-
const fit = isImageFit(value?.fit) ? value.fit : DEFAULT_IMAGE_FIT;
|
|
16899
|
-
const position = isImagePosition(value?.position) ? value.position : DEFAULT_IMAGE_POSITION;
|
|
16900
|
-
const showLayout = layout && withAlt;
|
|
16901
|
-
const hidden = hideable && value?.hidden === true;
|
|
16902
|
-
const current = {
|
|
16903
|
-
src,
|
|
16904
|
-
alt,
|
|
16905
|
-
fit,
|
|
16906
|
-
position,
|
|
16907
|
-
...hidden ? { hidden } : {}
|
|
16908
|
-
};
|
|
16909
|
-
/** Emit the whole image object. A new file or alt keeps the fit and position already chosen. */
|
|
16910
|
-
const emit = (patch) => onChange(compact({
|
|
16911
|
-
...current,
|
|
16912
|
-
...patch
|
|
16913
|
-
}));
|
|
16914
|
-
const empty = src === "" || src === "placeholder";
|
|
16915
|
-
const shown = previewSrc(src);
|
|
16916
|
-
const missing = useMissingFile(empty ? "" : src);
|
|
16917
|
-
const fallbackAlt = withAlt ? "Image" : "";
|
|
16918
|
-
function openPicker(trigger) {
|
|
16919
|
-
opener.current = trigger;
|
|
16920
|
-
setError("");
|
|
16921
|
-
setOpen(true);
|
|
16922
|
-
}
|
|
16923
|
-
function closePicker() {
|
|
16924
|
-
setOpen(false);
|
|
16925
|
-
requestAnimationFrame(() => {
|
|
16926
|
-
(opener.current?.isConnected ? opener.current : root.current)?.focus();
|
|
16927
|
-
});
|
|
16928
|
-
}
|
|
16929
|
-
/** A file dropped on the card replaces the image without opening the picker. */
|
|
16930
|
-
async function upload(file) {
|
|
16931
|
-
if (!file) return;
|
|
16932
|
-
setError("");
|
|
16933
|
-
const result = await uploadFile(file);
|
|
16934
|
-
if ("error" in result) {
|
|
16935
|
-
setError(result.error);
|
|
16936
|
-
return;
|
|
16937
|
-
}
|
|
16938
|
-
onUploaded(result.image);
|
|
16939
|
-
emit({
|
|
16940
|
-
src: result.image.path,
|
|
16941
|
-
alt: alt || fallbackAlt
|
|
16942
|
-
});
|
|
16943
|
-
}
|
|
16944
|
-
const trashed = () => onChange({
|
|
16945
|
-
src: withAlt ? "placeholder" : "",
|
|
16946
|
-
alt: "",
|
|
16947
|
-
...hidden ? { hidden } : {}
|
|
16948
|
-
});
|
|
16949
|
-
/** An empty image has no file yet; the placeholder keeps the saved object valid while it is hidden. */
|
|
16950
|
-
const setShown = (show) => emit({
|
|
16951
|
-
hidden: show ? void 0 : true,
|
|
16952
|
-
src: src === "" ? "placeholder" : src
|
|
16953
|
-
});
|
|
16954
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
16955
|
-
class: over ? "bm-imagefield bm-drop-files" : "bm-imagefield",
|
|
16956
|
-
"data-editor-field": path,
|
|
16957
|
-
tabIndex: -1,
|
|
16958
|
-
ref: root,
|
|
16959
|
-
"data-drop-hint": over ? "Drop to upload" : void 0,
|
|
16960
|
-
onDragOver: (event) => {
|
|
16961
|
-
if (open || !hasFiles(event)) return;
|
|
16962
|
-
event.preventDefault();
|
|
16963
|
-
if (event.dataTransfer) event.dataTransfer.dropEffect = "copy";
|
|
16964
|
-
if (!over) setOver(true);
|
|
16965
|
-
},
|
|
16966
|
-
onDragLeave: (event) => {
|
|
16967
|
-
const next = event.relatedTarget;
|
|
16968
|
-
if (next instanceof Node && event.currentTarget.contains(next)) return;
|
|
16969
|
-
setOver(false);
|
|
16970
|
-
},
|
|
16971
|
-
onDrop: (event) => {
|
|
16972
|
-
if (open || !hasFiles(event)) return;
|
|
16973
|
-
event.preventDefault();
|
|
16974
|
-
setOver(false);
|
|
16975
|
-
upload(event.dataTransfer?.files[0]);
|
|
16976
|
-
},
|
|
16977
|
-
children: [
|
|
16978
|
-
/* @__PURE__ */ jsx("span", {
|
|
16979
|
-
class: "bm-label",
|
|
16980
|
-
children: /* @__PURE__ */ jsxs("span", {
|
|
16981
|
-
class: "bm-label-row",
|
|
16982
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
16983
|
-
class: "bm-label-text",
|
|
16984
|
-
children: label
|
|
16985
|
-
}), withAlt && !empty && /* @__PURE__ */ jsx("span", {
|
|
16986
|
-
class: "bm-meta-tag",
|
|
16987
|
-
children: "alt required"
|
|
16988
|
-
})]
|
|
16989
|
-
})
|
|
16990
|
-
}),
|
|
16991
|
-
hideable && /* @__PURE__ */ jsxs("label", {
|
|
16992
|
-
class: "bm-switch-row",
|
|
16993
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
16994
|
-
class: "bm-switch-text",
|
|
16995
|
-
children: /* @__PURE__ */ jsxs("span", {
|
|
16996
|
-
class: "bm-switch-title",
|
|
16997
|
-
children: ["Show in section", /* @__PURE__ */ jsx(Info, {
|
|
16998
|
-
label: "About showing the image",
|
|
16999
|
-
text: "Turn off to remove the image and its frame from the section. The chosen file stays, so turning it back on restores it."
|
|
17000
|
-
})]
|
|
17001
|
-
})
|
|
17002
|
-
}), /* @__PURE__ */ jsx("input", {
|
|
17003
|
-
type: "checkbox",
|
|
17004
|
-
role: "switch",
|
|
17005
|
-
"aria-label": `Show ${label.toLowerCase()} in section`,
|
|
17006
|
-
checked: !hidden,
|
|
17007
|
-
onChange: (event) => setShown(event.currentTarget.checked)
|
|
17008
|
-
})]
|
|
17009
|
-
}),
|
|
17010
|
-
empty ? /* @__PURE__ */ jsxs("button", {
|
|
17011
|
-
type: "button",
|
|
17012
|
-
class: "bm-slot",
|
|
17013
|
-
onClick: (event) => openPicker(event.currentTarget),
|
|
17014
|
-
children: [
|
|
17015
|
-
/* @__PURE__ */ jsx(Icon, {
|
|
17016
|
-
name: "upload",
|
|
17017
|
-
size: 16
|
|
17018
|
-
}),
|
|
17019
|
-
/* @__PURE__ */ jsx("span", {
|
|
17020
|
-
class: "bm-slot-title",
|
|
17021
|
-
children: "Add image"
|
|
17022
|
-
}),
|
|
17023
|
-
/* @__PURE__ */ jsx("span", {
|
|
17024
|
-
class: "bm-slot-hint",
|
|
17025
|
-
children: "Upload a file or pick from the library"
|
|
17026
|
-
})
|
|
16750
|
+
/* @__PURE__ */ jsx("span", {
|
|
16751
|
+
class: "bm-slot-hint",
|
|
16752
|
+
children: "Upload a file or pick from the library"
|
|
16753
|
+
})
|
|
17027
16754
|
]
|
|
17028
16755
|
}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
|
|
17029
16756
|
class: "bm-card",
|
|
@@ -17438,14 +17165,40 @@ function issuesFor(issues, path) {
|
|
|
17438
17165
|
if (!path) return [];
|
|
17439
17166
|
return issues.filter((issue) => issue.path === path);
|
|
17440
17167
|
}
|
|
17441
|
-
|
|
17442
|
-
|
|
17443
|
-
|
|
17444
|
-
|
|
17445
|
-
|
|
17446
|
-
|
|
17447
|
-
|
|
17448
|
-
|
|
17168
|
+
//#endregion
|
|
17169
|
+
//#region src/ui/forms/segmented.tsx
|
|
17170
|
+
/** Segmented control for a short list of exclusive choices. */
|
|
17171
|
+
function Segmented({ label, value, options, onChange, hideLabel }) {
|
|
17172
|
+
const values = options.map((option) => option.value);
|
|
17173
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
17174
|
+
class: "bm-field",
|
|
17175
|
+
children: [!hideLabel && /* @__PURE__ */ jsx("span", {
|
|
17176
|
+
class: "bm-label",
|
|
17177
|
+
children: label
|
|
17178
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
17179
|
+
class: "bm-segmented",
|
|
17180
|
+
role: "radiogroup",
|
|
17181
|
+
"aria-label": label,
|
|
17182
|
+
onKeyDown: (event) => stepRadio(event, values, value, onChange),
|
|
17183
|
+
children: options.map((option) => /* @__PURE__ */ jsx("button", {
|
|
17184
|
+
type: "button",
|
|
17185
|
+
role: "radio",
|
|
17186
|
+
"aria-checked": value === option.value,
|
|
17187
|
+
tabIndex: value === option.value ? 0 : -1,
|
|
17188
|
+
onClick: () => onChange(option.value),
|
|
17189
|
+
children: option.label
|
|
17190
|
+
}, option.value))
|
|
17191
|
+
})]
|
|
17192
|
+
});
|
|
17193
|
+
}
|
|
17194
|
+
/** `split` → "Split", `top-left` → "Top left". Values such as `16:9` stay as they are. */
|
|
17195
|
+
function choiceLabel(value) {
|
|
17196
|
+
const words = value.replace(/[_-]+/g, " ").trim();
|
|
17197
|
+
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
17198
|
+
}
|
|
17199
|
+
/**
|
|
17200
|
+
* One of a fixed list of values. With icons it is a grid of icon buttons; with up to four choices a segmented
|
|
17201
|
+
* control; otherwise a select. A value outside the list (old content) is kept and shown until a choice is made.
|
|
17449
17202
|
*/
|
|
17450
17203
|
function ChoiceField({ label, value, choices, icons, path, segmented = true, onChange }) {
|
|
17451
17204
|
const issues = issuesFor(useContext(IssueContext), path);
|
|
@@ -17937,24 +17690,527 @@ function Field({ label, value, onChange, multiline = false, path, prefix, hint,
|
|
|
17937
17690
|
children: meta
|
|
17938
17691
|
})]
|
|
17939
17692
|
}),
|
|
17940
|
-
prefix ? /* @__PURE__ */ jsxs("span", {
|
|
17941
|
-
class: "bm-prefix-wrap",
|
|
17942
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
17943
|
-
class: "bm-prefix",
|
|
17944
|
-
"aria-hidden": "true",
|
|
17945
|
-
children: prefix
|
|
17946
|
-
}), control]
|
|
17947
|
-
}) : control,
|
|
17948
|
-
/* @__PURE__ */ jsx(FieldErrors, { issues })
|
|
17693
|
+
prefix ? /* @__PURE__ */ jsxs("span", {
|
|
17694
|
+
class: "bm-prefix-wrap",
|
|
17695
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
17696
|
+
class: "bm-prefix",
|
|
17697
|
+
"aria-hidden": "true",
|
|
17698
|
+
children: prefix
|
|
17699
|
+
}), control]
|
|
17700
|
+
}) : control,
|
|
17701
|
+
/* @__PURE__ */ jsx(FieldErrors, { issues })
|
|
17702
|
+
]
|
|
17703
|
+
});
|
|
17704
|
+
}
|
|
17705
|
+
/** An array item's keys, plus the optional ones it has dropped, in the order the sample item lists them. */
|
|
17706
|
+
function orderItemKeys(item, path, content) {
|
|
17707
|
+
const parts = path.replace(/^sections\.\d+\.props\./, "").split(".");
|
|
17708
|
+
const dropped = parts.length === 2 && /^\d+$/.test(parts[1] ?? "") ? content?.items?.[parts[0] ?? ""] ?? [] : [];
|
|
17709
|
+
return orderContentKeys(Object.keys(item), dropped, Object.keys(item));
|
|
17710
|
+
}
|
|
17711
|
+
//#endregion
|
|
17712
|
+
//#region src/ui/dialogs/consent-dialog.tsx
|
|
17713
|
+
const KEYS = [
|
|
17714
|
+
"title",
|
|
17715
|
+
"body",
|
|
17716
|
+
"accept",
|
|
17717
|
+
"reject",
|
|
17718
|
+
"settings",
|
|
17719
|
+
"policyLabel",
|
|
17720
|
+
"policyHref"
|
|
17721
|
+
];
|
|
17722
|
+
function startDraft(consent) {
|
|
17723
|
+
const copy = consentCopy(consent);
|
|
17724
|
+
return {
|
|
17725
|
+
title: copy.title,
|
|
17726
|
+
body: copy.body,
|
|
17727
|
+
accept: copy.accept,
|
|
17728
|
+
reject: copy.reject,
|
|
17729
|
+
settings: copy.settings,
|
|
17730
|
+
policyLabel: copy.policyLabel,
|
|
17731
|
+
policyHref: copy.policyHref ?? ""
|
|
17732
|
+
};
|
|
17733
|
+
}
|
|
17734
|
+
/**
|
|
17735
|
+
* What the draft saves: only the texts that differ from the theme's own, so a site that never changed a
|
|
17736
|
+
* text keeps following the default. An emptied field goes back to the default too.
|
|
17737
|
+
*/
|
|
17738
|
+
function savedConsentText(draft) {
|
|
17739
|
+
const saved = {};
|
|
17740
|
+
for (const key of KEYS) {
|
|
17741
|
+
const value = draft[key].trim();
|
|
17742
|
+
const fallback = key === "policyHref" ? "" : CONSENT_COPY[key];
|
|
17743
|
+
if (value !== "" && value !== fallback) saved[key] = value;
|
|
17744
|
+
}
|
|
17745
|
+
return saved;
|
|
17746
|
+
}
|
|
17747
|
+
/**
|
|
17748
|
+
* The words of the cookie banner. It edits a copy: Apply writes it to the site draft in one step, Cancel and
|
|
17749
|
+
* Escape drop it. On and off lives in the sidebar, so this dialog never changes it.
|
|
17750
|
+
*/
|
|
17751
|
+
function ConsentDialog({ consent, reopen: startReopen, onApply, onClose }) {
|
|
17752
|
+
const root = useRef(null);
|
|
17753
|
+
const [draft, setDraft] = useState(() => startDraft(consent));
|
|
17754
|
+
const [reopen, setReopen] = useState(startReopen);
|
|
17755
|
+
const saved = savedConsentText(draft);
|
|
17756
|
+
const issues = consentTextIssues(saved);
|
|
17757
|
+
const valid = Object.keys(issues).length === 0;
|
|
17758
|
+
const preview = consentCopy(saved);
|
|
17759
|
+
useEffect(() => {
|
|
17760
|
+
root.current?.querySelector("input, textarea")?.focus();
|
|
17761
|
+
}, []);
|
|
17762
|
+
const set = (key) => (value) => setDraft((current) => ({
|
|
17763
|
+
...current,
|
|
17764
|
+
[key]: value
|
|
17765
|
+
}));
|
|
17766
|
+
const field = (key, label, extra = {}) => /* @__PURE__ */ jsx(Field, {
|
|
17767
|
+
label,
|
|
17768
|
+
value: draft[key],
|
|
17769
|
+
onChange: set(key),
|
|
17770
|
+
hint: issues[key] ? void 0 : extra.hint,
|
|
17771
|
+
multiline: extra.multiline,
|
|
17772
|
+
mono: extra.mono,
|
|
17773
|
+
meta: extra.limit ? `${draft[key].length} / ${extra.limit}` : void 0
|
|
17774
|
+
});
|
|
17775
|
+
return /* @__PURE__ */ jsx("div", {
|
|
17776
|
+
class: "bm-scrim",
|
|
17777
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
17778
|
+
class: "bm-dialog bm-consent",
|
|
17779
|
+
role: "dialog",
|
|
17780
|
+
"aria-modal": "true",
|
|
17781
|
+
"aria-label": "Edit cookie banner",
|
|
17782
|
+
ref: root,
|
|
17783
|
+
children: [
|
|
17784
|
+
/* @__PURE__ */ jsxs("div", {
|
|
17785
|
+
class: "bm-dialog-head",
|
|
17786
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
17787
|
+
class: "bm-dialog-heading",
|
|
17788
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
17789
|
+
class: "bm-dialog-title",
|
|
17790
|
+
children: "Cookie banner"
|
|
17791
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
17792
|
+
class: "bm-dialog-sub",
|
|
17793
|
+
children: "Applied to the site draft. Nothing is written until you save."
|
|
17794
|
+
})]
|
|
17795
|
+
}), /* @__PURE__ */ jsx(DialogClose, { onClose })]
|
|
17796
|
+
}),
|
|
17797
|
+
/* @__PURE__ */ jsxs("div", {
|
|
17798
|
+
class: "bm-consent-body",
|
|
17799
|
+
children: [
|
|
17800
|
+
/* @__PURE__ */ jsxs("div", {
|
|
17801
|
+
class: "bm-consent-preview",
|
|
17802
|
+
"aria-hidden": "true",
|
|
17803
|
+
children: [
|
|
17804
|
+
/* @__PURE__ */ jsx("p", {
|
|
17805
|
+
class: "bm-consent-preview-title",
|
|
17806
|
+
children: preview.title
|
|
17807
|
+
}),
|
|
17808
|
+
/* @__PURE__ */ jsxs("p", {
|
|
17809
|
+
class: "bm-consent-preview-text",
|
|
17810
|
+
children: [preview.body, preview.policyHref && /* @__PURE__ */ jsxs("span", {
|
|
17811
|
+
class: "bm-consent-preview-link",
|
|
17812
|
+
children: [" ", preview.policyLabel]
|
|
17813
|
+
})]
|
|
17814
|
+
}),
|
|
17815
|
+
/* @__PURE__ */ jsxs("div", {
|
|
17816
|
+
class: "bm-consent-preview-buttons",
|
|
17817
|
+
children: [/* @__PURE__ */ jsx("span", { children: preview.accept }), /* @__PURE__ */ jsx("span", { children: preview.reject })]
|
|
17818
|
+
})
|
|
17819
|
+
]
|
|
17820
|
+
}),
|
|
17821
|
+
/* @__PURE__ */ jsxs("div", {
|
|
17822
|
+
class: "bm-stack",
|
|
17823
|
+
children: [
|
|
17824
|
+
/* @__PURE__ */ jsx("p", {
|
|
17825
|
+
class: "bm-group",
|
|
17826
|
+
children: "Text"
|
|
17827
|
+
}),
|
|
17828
|
+
field("title", "Title", { limit: CONSENT_LIMITS.title }),
|
|
17829
|
+
field("body", "Message", {
|
|
17830
|
+
multiline: true,
|
|
17831
|
+
limit: CONSENT_LIMITS.body
|
|
17832
|
+
}),
|
|
17833
|
+
/* @__PURE__ */ jsxs("div", {
|
|
17834
|
+
class: "bm-row-split",
|
|
17835
|
+
children: [field("accept", "Accept button"), field("reject", "Reject button")]
|
|
17836
|
+
})
|
|
17837
|
+
]
|
|
17838
|
+
}),
|
|
17839
|
+
/* @__PURE__ */ jsxs("div", {
|
|
17840
|
+
class: "bm-stack",
|
|
17841
|
+
children: [
|
|
17842
|
+
/* @__PURE__ */ jsx("p", {
|
|
17843
|
+
class: "bm-group",
|
|
17844
|
+
children: "Privacy policy"
|
|
17845
|
+
}),
|
|
17846
|
+
field("policyHref", "Policy page", {
|
|
17847
|
+
hint: "Optional. A page on this site, such as /privacy, or a full address. Leave empty for no link.",
|
|
17848
|
+
mono: true
|
|
17849
|
+
}),
|
|
17850
|
+
field("policyLabel", "Policy link text")
|
|
17851
|
+
]
|
|
17852
|
+
}),
|
|
17853
|
+
/* @__PURE__ */ jsxs("div", {
|
|
17854
|
+
class: "bm-stack",
|
|
17855
|
+
children: [
|
|
17856
|
+
/* @__PURE__ */ jsx("p", {
|
|
17857
|
+
class: "bm-group",
|
|
17858
|
+
children: "Changing the choice"
|
|
17859
|
+
}),
|
|
17860
|
+
/* @__PURE__ */ jsx(Segmented, {
|
|
17861
|
+
label: "Visitors reopen it with",
|
|
17862
|
+
value: reopen,
|
|
17863
|
+
options: [{
|
|
17864
|
+
value: "footer",
|
|
17865
|
+
label: "Footer link"
|
|
17866
|
+
}, {
|
|
17867
|
+
value: "icon",
|
|
17868
|
+
label: "Corner icon"
|
|
17869
|
+
}],
|
|
17870
|
+
onChange: (value) => setReopen(value === "icon" ? "icon" : "footer")
|
|
17871
|
+
}),
|
|
17872
|
+
field("settings", reopen === "icon" ? "Icon label" : "Footer link text", { hint: reopen === "icon" ? "A small cookie icon in the bottom-left corner, shown once the banner is closed. The label is read out by screen readers." : "The link stays in the footer after visitors choose, so they can change their mind." })
|
|
17873
|
+
]
|
|
17874
|
+
}),
|
|
17875
|
+
Object.entries(issues).map(([key, message]) => /* @__PURE__ */ jsxs("span", {
|
|
17876
|
+
class: "bm-error",
|
|
17877
|
+
role: "alert",
|
|
17878
|
+
children: [
|
|
17879
|
+
KEY_LABEL[key],
|
|
17880
|
+
": ",
|
|
17881
|
+
message
|
|
17882
|
+
]
|
|
17883
|
+
}, key))
|
|
17884
|
+
]
|
|
17885
|
+
}),
|
|
17886
|
+
/* @__PURE__ */ jsxs("div", {
|
|
17887
|
+
class: "bm-actions",
|
|
17888
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
17889
|
+
type: "button",
|
|
17890
|
+
class: "bm-primary",
|
|
17891
|
+
disabled: !valid,
|
|
17892
|
+
onClick: () => onApply(saved, reopen),
|
|
17893
|
+
children: "Apply"
|
|
17894
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
17895
|
+
type: "button",
|
|
17896
|
+
class: "bm-tertiary",
|
|
17897
|
+
onClick: onClose,
|
|
17898
|
+
children: "Cancel"
|
|
17899
|
+
})]
|
|
17900
|
+
})
|
|
17901
|
+
]
|
|
17902
|
+
})
|
|
17903
|
+
});
|
|
17904
|
+
}
|
|
17905
|
+
const KEY_LABEL = {
|
|
17906
|
+
title: "Title",
|
|
17907
|
+
body: "Message",
|
|
17908
|
+
accept: "Accept button",
|
|
17909
|
+
reject: "Reject button",
|
|
17910
|
+
settings: "Footer link text or icon label",
|
|
17911
|
+
policyLabel: "Policy link text",
|
|
17912
|
+
policyHref: "Policy page"
|
|
17913
|
+
};
|
|
17914
|
+
//#endregion
|
|
17915
|
+
//#region src/ui/dialogs/history-dialog.tsx
|
|
17916
|
+
function formatVersionTime(iso) {
|
|
17917
|
+
const date = new Date(iso);
|
|
17918
|
+
if (Number.isNaN(date.getTime())) return iso;
|
|
17919
|
+
return date.toLocaleString(void 0, {
|
|
17920
|
+
month: "short",
|
|
17921
|
+
day: "numeric",
|
|
17922
|
+
hour: "2-digit",
|
|
17923
|
+
minute: "2-digit"
|
|
17924
|
+
});
|
|
17925
|
+
}
|
|
17926
|
+
const VERSION_CAP = 50;
|
|
17927
|
+
function startOfDay(date) {
|
|
17928
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
|
|
17929
|
+
}
|
|
17930
|
+
/** Today / Yesterday / "12 Mar" (with the year when it is not this one). */
|
|
17931
|
+
function versionDay(iso, now = /* @__PURE__ */ new Date()) {
|
|
17932
|
+
const date = new Date(iso);
|
|
17933
|
+
if (Number.isNaN(date.getTime())) return iso;
|
|
17934
|
+
const days = Math.round((startOfDay(now) - startOfDay(date)) / 864e5);
|
|
17935
|
+
if (days === 0) return "Today";
|
|
17936
|
+
if (days === 1) return "Yesterday";
|
|
17937
|
+
return date.toLocaleDateString(void 0, {
|
|
17938
|
+
day: "numeric",
|
|
17939
|
+
month: "short",
|
|
17940
|
+
...date.getFullYear() === now.getFullYear() ? {} : { year: "numeric" }
|
|
17941
|
+
});
|
|
17942
|
+
}
|
|
17943
|
+
function versionClock(iso) {
|
|
17944
|
+
const date = new Date(iso);
|
|
17945
|
+
return Number.isNaN(date.getTime()) ? "" : date.toLocaleTimeString(void 0, {
|
|
17946
|
+
hour: "2-digit",
|
|
17947
|
+
minute: "2-digit",
|
|
17948
|
+
hour12: false
|
|
17949
|
+
});
|
|
17950
|
+
}
|
|
17951
|
+
/** "just now", "2 min ago", "3 h ago"; older than a day shows only the clock time. */
|
|
17952
|
+
function versionAgo(iso, now = Date.now()) {
|
|
17953
|
+
const then = new Date(iso).getTime();
|
|
17954
|
+
if (Number.isNaN(then)) return "";
|
|
17955
|
+
const minutes = Math.max(0, Math.round((now - then) / 6e4));
|
|
17956
|
+
if (minutes < 1) return "just now";
|
|
17957
|
+
if (minutes < 60) return `${minutes} min ago`;
|
|
17958
|
+
const hours = Math.round(minutes / 60);
|
|
17959
|
+
return hours < 24 ? `${hours} h ago` : versionClock(iso);
|
|
17960
|
+
}
|
|
17961
|
+
/** "title, hero › heading +2": the first changed fields of one save, for the collapsed row. */
|
|
17962
|
+
function versionSummary(list) {
|
|
17963
|
+
if (!list || list.length === 0) return "";
|
|
17964
|
+
const names = [...new Set(list.map((item) => item.field))];
|
|
17965
|
+
return names.length <= 3 ? names.join(", ") : `${names.slice(0, 3).join(", ")} +${names.length - 3}`;
|
|
17966
|
+
}
|
|
17967
|
+
/**
|
|
17968
|
+
* Saved versions of this page and of the site, newest first and grouped by day. A version shows what that save changed,
|
|
17969
|
+
* and Restore loads it into the draft. Nothing is written until the next save, and the restore can be undone from here.
|
|
17970
|
+
*/
|
|
17971
|
+
function HistoryDialog({ slug, labels, page, onRestore, onUndo, onClose }) {
|
|
17972
|
+
const root = useRef(null);
|
|
17973
|
+
useDialogFocus(root);
|
|
17974
|
+
const [scope, setScope] = useState("page");
|
|
17975
|
+
const [entries, setEntries] = useState(null);
|
|
17976
|
+
const [diffs, setDiffs] = useState({});
|
|
17977
|
+
const [failed, setFailed] = useState(false);
|
|
17978
|
+
const [openId, setOpenId] = useState(null);
|
|
17979
|
+
const [restored, setRestored] = useState(null);
|
|
17980
|
+
const base = `${scope}:${slug}`;
|
|
17981
|
+
useEffect(() => {
|
|
17982
|
+
let live = true;
|
|
17983
|
+
setEntries(null);
|
|
17984
|
+
setDiffs({});
|
|
17985
|
+
setOpenId(null);
|
|
17986
|
+
setRestored(null);
|
|
17987
|
+
setFailed(false);
|
|
17988
|
+
const load = async () => {
|
|
17989
|
+
const listed = await listHistory(scope, slug);
|
|
17990
|
+
if (!listed.ok) throw new Error(listed.message);
|
|
17991
|
+
const body = listed.value;
|
|
17992
|
+
if (!live) return;
|
|
17993
|
+
setEntries(body.entries);
|
|
17994
|
+
const data = await Promise.all(body.entries.map(async (entry) => {
|
|
17995
|
+
const version = await getHistoryVersion(scope, slug, entry.id);
|
|
17996
|
+
return version.ok ? version.value : null;
|
|
17997
|
+
}));
|
|
17998
|
+
if (!live) return;
|
|
17999
|
+
const next = {};
|
|
18000
|
+
body.entries.forEach((entry, index) => {
|
|
18001
|
+
const after = data[index];
|
|
18002
|
+
const before = data[index + 1];
|
|
18003
|
+
if (after === null || before === null || before === void 0) return;
|
|
18004
|
+
next[entry.id] = scope === "page" ? describePageChanges(before, after, labels) : describeSiteChanges(before, after);
|
|
18005
|
+
});
|
|
18006
|
+
setDiffs(next);
|
|
18007
|
+
};
|
|
18008
|
+
load().catch(() => live && setFailed(true));
|
|
18009
|
+
return () => {
|
|
18010
|
+
live = false;
|
|
18011
|
+
};
|
|
18012
|
+
}, [base]);
|
|
18013
|
+
async function restore(entry) {
|
|
18014
|
+
const version = await getHistoryVersion(scope, slug, entry.id);
|
|
18015
|
+
if (!version.ok) {
|
|
18016
|
+
setFailed(true);
|
|
18017
|
+
return;
|
|
18018
|
+
}
|
|
18019
|
+
const when = formatVersionTime(entry.at);
|
|
18020
|
+
onRestore(scope, version.value, when);
|
|
18021
|
+
setRestored(when);
|
|
18022
|
+
}
|
|
18023
|
+
const days = [];
|
|
18024
|
+
entries?.forEach((entry, index) => {
|
|
18025
|
+
const day = versionDay(entry.at);
|
|
18026
|
+
const last = days[days.length - 1];
|
|
18027
|
+
if (last && last.day === day) last.items.push({
|
|
18028
|
+
entry,
|
|
18029
|
+
index
|
|
18030
|
+
});
|
|
18031
|
+
else days.push({
|
|
18032
|
+
day,
|
|
18033
|
+
items: [{
|
|
18034
|
+
entry,
|
|
18035
|
+
index
|
|
18036
|
+
}]
|
|
18037
|
+
});
|
|
18038
|
+
});
|
|
18039
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
18040
|
+
class: "bm-dialog bm-changes",
|
|
18041
|
+
role: "dialog",
|
|
18042
|
+
"aria-modal": "true",
|
|
18043
|
+
"aria-label": "Version history",
|
|
18044
|
+
ref: root,
|
|
18045
|
+
style: "top:12%;left:50%;transform:translateX(-50%);",
|
|
18046
|
+
children: [
|
|
18047
|
+
/* @__PURE__ */ jsxs("div", {
|
|
18048
|
+
class: "bm-dialog-head",
|
|
18049
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
18050
|
+
class: "bm-dialog-heading",
|
|
18051
|
+
children: [/* @__PURE__ */ jsxs("h2", {
|
|
18052
|
+
class: "bm-dialog-title",
|
|
18053
|
+
children: ["Version history", /* @__PURE__ */ jsx(Info, {
|
|
18054
|
+
label: "Where versions are kept",
|
|
18055
|
+
text: /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
18056
|
+
"Versions are kept only on this computer, in ",
|
|
18057
|
+
/* @__PURE__ */ jsx("code", { children: ".editor/history" }),
|
|
18058
|
+
". They are not in git and not on the published site. The newest ",
|
|
18059
|
+
VERSION_CAP,
|
|
18060
|
+
" stay. To clear them, stop the dev server and delete the ",
|
|
18061
|
+
/* @__PURE__ */ jsx("code", { children: ".editor" }),
|
|
18062
|
+
" folder in your project root."
|
|
18063
|
+
] })
|
|
18064
|
+
})]
|
|
18065
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
18066
|
+
class: "bm-dialog-sub",
|
|
18067
|
+
children: "Every save is kept. Restore loads a version into the draft; nothing is written until you save."
|
|
18068
|
+
})]
|
|
18069
|
+
}), /* @__PURE__ */ jsx(DialogClose, { onClose })]
|
|
18070
|
+
}),
|
|
18071
|
+
/* @__PURE__ */ jsx(Segmented, {
|
|
18072
|
+
hideLabel: true,
|
|
18073
|
+
label: "Scope",
|
|
18074
|
+
value: scope,
|
|
18075
|
+
options: [{
|
|
18076
|
+
value: "page",
|
|
18077
|
+
label: `Page "${page.title}"`
|
|
18078
|
+
}, {
|
|
18079
|
+
value: "site",
|
|
18080
|
+
label: "Site"
|
|
18081
|
+
}],
|
|
18082
|
+
onChange: (value) => setScope(value === "site" ? "site" : "page")
|
|
18083
|
+
}),
|
|
18084
|
+
restored !== null && /* @__PURE__ */ jsxs("div", {
|
|
18085
|
+
class: "bm-restored",
|
|
18086
|
+
role: "status",
|
|
18087
|
+
children: [/* @__PURE__ */ jsxs("span", { children: [
|
|
18088
|
+
"Restored the version from ",
|
|
18089
|
+
restored,
|
|
18090
|
+
" into the draft."
|
|
18091
|
+
] }), /* @__PURE__ */ jsx("button", {
|
|
18092
|
+
type: "button",
|
|
18093
|
+
class: "bm-tertiary",
|
|
18094
|
+
onClick: () => {
|
|
18095
|
+
onUndo();
|
|
18096
|
+
setRestored(null);
|
|
18097
|
+
},
|
|
18098
|
+
children: "Undo"
|
|
18099
|
+
})]
|
|
18100
|
+
}),
|
|
18101
|
+
/* @__PURE__ */ jsxs("div", {
|
|
18102
|
+
class: "bm-changes-list",
|
|
18103
|
+
children: [
|
|
18104
|
+
failed && /* @__PURE__ */ jsx("p", {
|
|
18105
|
+
class: "bm-error",
|
|
18106
|
+
children: "Could not load the saved versions."
|
|
18107
|
+
}),
|
|
18108
|
+
!failed && entries === null && /* @__PURE__ */ jsx("p", {
|
|
18109
|
+
class: "bm-note",
|
|
18110
|
+
children: "Loading…"
|
|
18111
|
+
}),
|
|
18112
|
+
entries !== null && entries.length === 0 && /* @__PURE__ */ jsx("p", {
|
|
18113
|
+
class: "bm-note",
|
|
18114
|
+
children: "Nothing saved yet. Every save keeps a copy here, newest first."
|
|
18115
|
+
}),
|
|
18116
|
+
days.map((group) => /* @__PURE__ */ jsxs("section", {
|
|
18117
|
+
class: "bm-day",
|
|
18118
|
+
"aria-label": group.day,
|
|
18119
|
+
children: [/* @__PURE__ */ jsx("h3", {
|
|
18120
|
+
class: "bm-changes-area",
|
|
18121
|
+
children: group.day
|
|
18122
|
+
}), /* @__PURE__ */ jsx("ul", {
|
|
18123
|
+
class: "bm-versions",
|
|
18124
|
+
children: group.items.map(({ entry, index }) => {
|
|
18125
|
+
const isOpen = openId === entry.id;
|
|
18126
|
+
const list = diffs[entry.id];
|
|
18127
|
+
const stamp = formatVersionTime(entry.at);
|
|
18128
|
+
const earliest = index === (entries?.length ?? 0) - 1;
|
|
18129
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
18130
|
+
class: "bm-version",
|
|
18131
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
18132
|
+
class: "bm-version-row",
|
|
18133
|
+
children: [
|
|
18134
|
+
/* @__PURE__ */ jsx("button", {
|
|
18135
|
+
type: "button",
|
|
18136
|
+
class: "bm-icon-btn",
|
|
18137
|
+
"aria-expanded": isOpen,
|
|
18138
|
+
"aria-label": `${isOpen ? "Hide" : "Show"} what changed in the version from ${stamp}`,
|
|
18139
|
+
onClick: () => setOpenId(isOpen ? null : entry.id),
|
|
18140
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
18141
|
+
name: isOpen ? "chevron-up" : "chevron-down",
|
|
18142
|
+
size: 16
|
|
18143
|
+
})
|
|
18144
|
+
}),
|
|
18145
|
+
/* @__PURE__ */ jsxs("button", {
|
|
18146
|
+
type: "button",
|
|
18147
|
+
class: "bm-tertiary bm-version-when",
|
|
18148
|
+
onClick: () => setOpenId(isOpen ? null : entry.id),
|
|
18149
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
18150
|
+
class: "bm-version-line",
|
|
18151
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
18152
|
+
class: "bm-version-rel",
|
|
18153
|
+
children: versionAgo(entry.at) || stamp
|
|
18154
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
18155
|
+
class: "bm-version-abs",
|
|
18156
|
+
children: versionClock(entry.at)
|
|
18157
|
+
})]
|
|
18158
|
+
}), versionSummary(list) && /* @__PURE__ */ jsx("span", {
|
|
18159
|
+
class: "bm-version-sum",
|
|
18160
|
+
children: versionSummary(list)
|
|
18161
|
+
})]
|
|
18162
|
+
}),
|
|
18163
|
+
index === 0 ? /* @__PURE__ */ jsx("span", {
|
|
18164
|
+
class: "bm-badge bm-badge-on",
|
|
18165
|
+
children: "current"
|
|
18166
|
+
}) : /* @__PURE__ */ jsxs("button", {
|
|
18167
|
+
type: "button",
|
|
18168
|
+
class: "bm-restore",
|
|
18169
|
+
"aria-label": `Restore the version from ${stamp}`,
|
|
18170
|
+
onClick: () => void restore(entry),
|
|
18171
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
18172
|
+
name: "rotate-ccw",
|
|
18173
|
+
size: 14
|
|
18174
|
+
}), "Restore"]
|
|
18175
|
+
})
|
|
18176
|
+
]
|
|
18177
|
+
}), isOpen && /* @__PURE__ */ jsx("div", {
|
|
18178
|
+
class: "bm-version-diff",
|
|
18179
|
+
children: !list || list.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
18180
|
+
class: "bm-note",
|
|
18181
|
+
children: earliest ? "The earliest kept version, so there is nothing older to compare with." : list ? "No difference from the save before it." : "Loading…"
|
|
18182
|
+
}) : groupChanges(list).map((area) => /* @__PURE__ */ jsxs("section", {
|
|
18183
|
+
"aria-label": area.area,
|
|
18184
|
+
children: [/* @__PURE__ */ jsx("h3", {
|
|
18185
|
+
class: "bm-changes-area",
|
|
18186
|
+
children: area.area
|
|
18187
|
+
}), /* @__PURE__ */ jsx("ul", { children: area.entries.map((item) => /* @__PURE__ */ jsx(ChangeRow, {
|
|
18188
|
+
item,
|
|
18189
|
+
scope
|
|
18190
|
+
}, `${item.field}:${item.kind}`)) })]
|
|
18191
|
+
}, area.area))
|
|
18192
|
+
})]
|
|
18193
|
+
}, entry.id);
|
|
18194
|
+
})
|
|
18195
|
+
})]
|
|
18196
|
+
}, group.day))
|
|
18197
|
+
]
|
|
18198
|
+
}),
|
|
18199
|
+
entries !== null && entries.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
18200
|
+
class: "bm-dialog-foot",
|
|
18201
|
+
children: /* @__PURE__ */ jsxs("p", {
|
|
18202
|
+
class: "bm-hint",
|
|
18203
|
+
children: [
|
|
18204
|
+
entries.length,
|
|
18205
|
+
" of ",
|
|
18206
|
+
VERSION_CAP,
|
|
18207
|
+
" kept on this computer. Older versions drop off."
|
|
18208
|
+
]
|
|
18209
|
+
})
|
|
18210
|
+
})
|
|
17949
18211
|
]
|
|
17950
18212
|
});
|
|
17951
18213
|
}
|
|
17952
|
-
/** An array item's keys, plus the optional ones it has dropped, in the order the sample item lists them. */
|
|
17953
|
-
function orderItemKeys(item, path, content) {
|
|
17954
|
-
const parts = path.replace(/^sections\.\d+\.props\./, "").split(".");
|
|
17955
|
-
const dropped = parts.length === 2 && /^\d+$/.test(parts[1] ?? "") ? content?.items?.[parts[0] ?? ""] ?? [] : [];
|
|
17956
|
-
return orderContentKeys(Object.keys(item), dropped, Object.keys(item));
|
|
17957
|
-
}
|
|
17958
18214
|
//#endregion
|
|
17959
18215
|
//#region src/ui/forms/serp-preview.tsx
|
|
17960
18216
|
/** Address as a search result shows it: no scheme, no `www.`, no trailing slash. Without a site URL, the path alone. */
|
|
@@ -18230,6 +18486,151 @@ function SeoDialog({ page, siteName, siteUrl, system, images, issues, onUploaded
|
|
|
18230
18486
|
});
|
|
18231
18487
|
}
|
|
18232
18488
|
//#endregion
|
|
18489
|
+
//#region src/ui/dialogs/site-jsonld-dialog.tsx
|
|
18490
|
+
/**
|
|
18491
|
+
* The structured data every page carries: an `Organization` and a `WebSite`, or any other markup. It edits a
|
|
18492
|
+
* copy: Apply writes it to the site draft in one step, Cancel and Escape drop it. Nothing reaches disk until save.
|
|
18493
|
+
*/
|
|
18494
|
+
function SiteJsonLdDialog({ blocks, source, onApply, onClose }) {
|
|
18495
|
+
const root = useRef(null);
|
|
18496
|
+
const [jsonText, setJsonText] = useState(() => formatJsonLd(blocks));
|
|
18497
|
+
const parsed = parseJsonLdInput(jsonText);
|
|
18498
|
+
const warnings = parsed.ok ? jsonLdWarnings(parsed.value) : [];
|
|
18499
|
+
const count = parsed.ok ? parsed.value.length : 0;
|
|
18500
|
+
const placeholder = isPlaceholderSiteUrl(source.url);
|
|
18501
|
+
useEffect(() => {
|
|
18502
|
+
root.current?.querySelector("textarea")?.focus();
|
|
18503
|
+
}, []);
|
|
18504
|
+
function generate() {
|
|
18505
|
+
if (!parsed.ok) return;
|
|
18506
|
+
setJsonText(formatJsonLd(upsertJsonLd(parsed.value, generateSiteJsonLd(source))));
|
|
18507
|
+
}
|
|
18508
|
+
return /* @__PURE__ */ jsx("div", {
|
|
18509
|
+
class: "bm-scrim",
|
|
18510
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
18511
|
+
class: "bm-dialog bm-seo",
|
|
18512
|
+
role: "dialog",
|
|
18513
|
+
"aria-modal": "true",
|
|
18514
|
+
"aria-label": "Edit site JSON-LD",
|
|
18515
|
+
ref: root,
|
|
18516
|
+
children: [
|
|
18517
|
+
/* @__PURE__ */ jsxs("div", {
|
|
18518
|
+
class: "bm-dialog-head",
|
|
18519
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
18520
|
+
class: "bm-dialog-heading",
|
|
18521
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
18522
|
+
class: "bm-dialog-title",
|
|
18523
|
+
children: "Structured data · every page"
|
|
18524
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
18525
|
+
class: "bm-dialog-sub",
|
|
18526
|
+
children: "Applied to the site draft. Nothing is written until you save."
|
|
18527
|
+
})]
|
|
18528
|
+
}), /* @__PURE__ */ jsx(DialogClose, { onClose })]
|
|
18529
|
+
}),
|
|
18530
|
+
/* @__PURE__ */ jsxs("div", {
|
|
18531
|
+
class: "bm-seo-body",
|
|
18532
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
18533
|
+
class: "bm-stack",
|
|
18534
|
+
children: [
|
|
18535
|
+
/* @__PURE__ */ jsx("p", {
|
|
18536
|
+
class: "bm-group",
|
|
18537
|
+
children: "Generate"
|
|
18538
|
+
}),
|
|
18539
|
+
/* @__PURE__ */ jsx("button", {
|
|
18540
|
+
type: "button",
|
|
18541
|
+
class: "bm-tertiary bm-seo-format",
|
|
18542
|
+
disabled: !parsed.ok,
|
|
18543
|
+
onClick: generate,
|
|
18544
|
+
children: "Generate Organization and WebSite"
|
|
18545
|
+
}),
|
|
18546
|
+
/* @__PURE__ */ jsxs("p", {
|
|
18547
|
+
class: "bm-hint",
|
|
18548
|
+
children: ["Filled from the site name, address and language. Blocks you already have with the same @id are replaced, the rest are kept.", source.logo ? "" : " No logo is added: paste a full https address as \"logo\" by hand, because a local image only gets its address when the site is built."]
|
|
18549
|
+
}),
|
|
18550
|
+
placeholder && /* @__PURE__ */ jsxs("span", {
|
|
18551
|
+
class: "bm-error",
|
|
18552
|
+
role: "alert",
|
|
18553
|
+
children: [
|
|
18554
|
+
"The site address is still ",
|
|
18555
|
+
source.url,
|
|
18556
|
+
". Set the real one under Identity before you generate, or the markup names the wrong site."
|
|
18557
|
+
]
|
|
18558
|
+
})
|
|
18559
|
+
]
|
|
18560
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
18561
|
+
class: "bm-stack",
|
|
18562
|
+
children: [
|
|
18563
|
+
/* @__PURE__ */ jsx("p", {
|
|
18564
|
+
class: "bm-group",
|
|
18565
|
+
children: "JSON-LD"
|
|
18566
|
+
}),
|
|
18567
|
+
/* @__PURE__ */ jsxs("label", {
|
|
18568
|
+
class: "bm-label",
|
|
18569
|
+
children: [
|
|
18570
|
+
/* @__PURE__ */ jsxs("span", {
|
|
18571
|
+
class: "bm-label-row",
|
|
18572
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
18573
|
+
class: "bm-label-text",
|
|
18574
|
+
children: ["Blocks", /* @__PURE__ */ jsx(Info, {
|
|
18575
|
+
label: "About site JSON-LD",
|
|
18576
|
+
text: "Added to every page. A page's own JSON-LD replaces only the generated page markup, and a page block with the same @id wins over the site's. Paste an object, an array of objects, or a whole script tag."
|
|
18577
|
+
})]
|
|
18578
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
18579
|
+
class: "bm-meta-tag",
|
|
18580
|
+
children: parsed.ok ? `${count} ${count === 1 ? "block" : "blocks"}` : "invalid"
|
|
18581
|
+
})]
|
|
18582
|
+
}),
|
|
18583
|
+
/* @__PURE__ */ jsx("textarea", {
|
|
18584
|
+
class: "bm-mono bm-seo-json",
|
|
18585
|
+
rows: 14,
|
|
18586
|
+
spellcheck: false,
|
|
18587
|
+
autocomplete: "off",
|
|
18588
|
+
placeholder: "{\n \"@context\": \"https://schema.org\",\n \"@type\": \"Organization\",\n \"name\": \"Example\"\n}",
|
|
18589
|
+
"aria-invalid": !parsed.ok,
|
|
18590
|
+
value: jsonText,
|
|
18591
|
+
onInput: (event) => setJsonText(event.currentTarget.value)
|
|
18592
|
+
}),
|
|
18593
|
+
!parsed.ok && /* @__PURE__ */ jsx("span", {
|
|
18594
|
+
class: "bm-error",
|
|
18595
|
+
role: "alert",
|
|
18596
|
+
children: parsed.error
|
|
18597
|
+
}),
|
|
18598
|
+
warnings.map((warning) => /* @__PURE__ */ jsx("span", {
|
|
18599
|
+
class: "bm-hint",
|
|
18600
|
+
children: warning
|
|
18601
|
+
}, warning))
|
|
18602
|
+
]
|
|
18603
|
+
}),
|
|
18604
|
+
/* @__PURE__ */ jsx("button", {
|
|
18605
|
+
type: "button",
|
|
18606
|
+
class: "bm-tertiary bm-seo-format",
|
|
18607
|
+
disabled: !parsed.ok || parsed.value.length === 0,
|
|
18608
|
+
onClick: () => parsed.ok && setJsonText(formatJsonLd(parsed.value)),
|
|
18609
|
+
children: "Format JSON"
|
|
18610
|
+
})
|
|
18611
|
+
]
|
|
18612
|
+
})]
|
|
18613
|
+
}),
|
|
18614
|
+
/* @__PURE__ */ jsxs("div", {
|
|
18615
|
+
class: "bm-actions",
|
|
18616
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
18617
|
+
type: "button",
|
|
18618
|
+
class: "bm-primary",
|
|
18619
|
+
disabled: !parsed.ok,
|
|
18620
|
+
onClick: () => parsed.ok && onApply(parsed.value),
|
|
18621
|
+
children: "Apply"
|
|
18622
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
18623
|
+
type: "button",
|
|
18624
|
+
class: "bm-tertiary",
|
|
18625
|
+
onClick: onClose,
|
|
18626
|
+
children: "Cancel"
|
|
18627
|
+
})]
|
|
18628
|
+
})
|
|
18629
|
+
]
|
|
18630
|
+
})
|
|
18631
|
+
});
|
|
18632
|
+
}
|
|
18633
|
+
//#endregion
|
|
18233
18634
|
//#region src/ui/dialogs/editor-dialogs.tsx
|
|
18234
18635
|
function parseJson(text) {
|
|
18235
18636
|
if (!text) return null;
|
|
@@ -18341,6 +18742,38 @@ function SessionDialog({ editor, dialog }) {
|
|
|
18341
18742
|
onClose: close
|
|
18342
18743
|
});
|
|
18343
18744
|
}
|
|
18745
|
+
case "consent": {
|
|
18746
|
+
const site = doc.site.value;
|
|
18747
|
+
if (!site) return null;
|
|
18748
|
+
return /* @__PURE__ */ jsx(ConsentDialog, {
|
|
18749
|
+
consent: consentTextOf(site),
|
|
18750
|
+
reopen: consentReopenOf(site),
|
|
18751
|
+
onApply: (consent, reopen) => {
|
|
18752
|
+
const enabled = getAt(site, "consent.enabled");
|
|
18753
|
+
const next = {
|
|
18754
|
+
...enabled === void 0 ? {} : { enabled },
|
|
18755
|
+
...reopen === "icon" ? { reopen } : {},
|
|
18756
|
+
...consent
|
|
18757
|
+
};
|
|
18758
|
+
edits.writeField("consent", Object.keys(next).length > 0 ? next : void 0);
|
|
18759
|
+
close();
|
|
18760
|
+
},
|
|
18761
|
+
onClose: close
|
|
18762
|
+
});
|
|
18763
|
+
}
|
|
18764
|
+
case "siteJsonLd": {
|
|
18765
|
+
const site = doc.site.value;
|
|
18766
|
+
if (!site) return null;
|
|
18767
|
+
return /* @__PURE__ */ jsx(SiteJsonLdDialog, {
|
|
18768
|
+
blocks: siteJsonLdOf(site),
|
|
18769
|
+
source: siteJsonLdSourceOf(site),
|
|
18770
|
+
onApply: (blocks) => {
|
|
18771
|
+
edits.writeField("seo.jsonLd", blocks.length > 0 ? blocks : void 0);
|
|
18772
|
+
close();
|
|
18773
|
+
},
|
|
18774
|
+
onClose: close
|
|
18775
|
+
});
|
|
18776
|
+
}
|
|
18344
18777
|
case "discard": return /* @__PURE__ */ jsx(ConfirmDialog, {
|
|
18345
18778
|
label: "Discard changes",
|
|
18346
18779
|
body: "Throw away every unsaved change to this page and the site, and reload both from the saved files?",
|
|
@@ -19708,7 +20141,7 @@ function AdvancedLink({ edit }) {
|
|
|
19708
20141
|
}
|
|
19709
20142
|
//#endregion
|
|
19710
20143
|
//#region src/ui/editor.css?raw
|
|
19711
|
-
var editor_default = "/*\n * Editor styles. They live in a shadow root, so they never meet the site's Tailwind layer.\n * The editor is always light. Tokens and values follow the v4 editor design handoff.\n * The short names (--ink, --line, --accent ...) alias the --bm-* tokens so older rules read the same.\n */\n:host {\n all: initial;\n\n /* ---------- v4 tokens (editor-tokens.css) ---------- */\n --bm-font-ui: \"Bricolage Grotesque Variable\", \"Bricolage Grotesque\", ui-sans-serif, system-ui, sans-serif;\n --bm-font-mono: \"JetBrains Mono\", ui-monospace, SFMono-Regular, Menlo, monospace;\n --bm-font-serif: \"Instrument Serif\", Georgia, serif;\n\n --bm-ink: oklch(0.21 0.012 255);\n --bm-label: oklch(0.3 0.01 255);\n --bm-section: oklch(0.42 0.01 255);\n --bm-help: oklch(0.47 0.01 255);\n --bm-muted: oklch(0.5 0.01 255);\n --bm-icon: oklch(0.55 0.01 255);\n --bm-info: oklch(0.6 0.01 255);\n\n --bm-fill: oklch(0.955 0.003 250);\n --bm-fill-hover: oklch(0.93 0.004 250);\n --bm-fill-soft: oklch(0.965 0.003 250);\n --bm-fill-nested: oklch(0.97 0.003 250);\n --bm-border: oklch(0.88 0.005 250);\n --bm-divider: oklch(0.93 0.004 250);\n --bm-tab-rule: oklch(0.92 0.004 250);\n\n --bm-primary: oklch(0.46 0.13 145);\n --bm-primary-hover: oklch(0.4 0.12 145);\n --bm-primary-ring: oklch(0.46 0.13 145 / 0.15);\n --bm-link: oklch(0.4 0.12 145);\n\n --bm-saved-bg: oklch(0.94 0.045 145);\n --bm-saved-fg: oklch(0.36 0.1 145);\n --bm-unsaved-bg: oklch(0.95 0.05 80);\n --bm-unsaved-fg: oklch(0.48 0.1 60);\n --bm-unsaved-dot: oklch(0.75 0.15 70);\n --bm-badge-green-bg: oklch(0.93 0.06 145);\n --bm-badge-green-fg: oklch(0.38 0.1 145);\n --bm-badge-amber-bg: oklch(0.95 0.04 75);\n --bm-badge-amber-fg: oklch(0.45 0.09 60);\n\n --bm-danger: oklch(0.55 0.19 25);\n --bm-danger-text: oklch(0.52 0.17 25);\n --bm-danger-icon: oklch(0.58 0.18 25);\n --bm-danger-soft: oklch(0.96 0.025 20);\n\n --bm-diff-del-bg: oklch(0.97 0.02 20);\n --bm-diff-del-fg: oklch(0.45 0.13 25);\n --bm-diff-add-bg: oklch(0.96 0.04 145);\n --bm-diff-add-fg: oklch(0.34 0.1 145);\n\n --bm-switch-off: oklch(0.82 0.005 250);\n\n --bm-shadow-pop: 0 0 0 1px oklch(0.9 0.005 250), 0 16px 36px -16px oklch(0.2 0.02 255 / 0.35);\n --bm-shadow-dialog: 0 0 0 1px oklch(0.9 0.005 250), 0 24px 48px -24px oklch(0.2 0.02 255 / 0.35);\n --bm-shadow-segment-on: 0 1px 2px oklch(0 0 0 / 0.08), 0 0 0 1px oklch(0.9 0.005 250);\n\n /* ---------- aliases used by the rules below ---------- */\n --ink: var(--bm-ink);\n --ink-2: var(--bm-label);\n --muted: var(--bm-muted);\n --line: var(--bm-border);\n --line-soft: var(--bm-divider);\n --fill: var(--bm-fill);\n --fill-hover: var(--bm-fill-hover);\n --fill-soft: var(--bm-fill-soft);\n --accent: var(--bm-primary);\n --accent-hover: var(--bm-primary-hover);\n --accent-tint: oklch(0.96 0.03 145);\n --accent-ring: var(--bm-primary-ring);\n --warn-tint: var(--bm-unsaved-bg);\n --warn-ink: var(--bm-unsaved-fg);\n --warn-dot: var(--bm-unsaved-dot);\n --danger-hover: oklch(0.48 0.18 25);\n --danger-tint: var(--bm-danger-soft);\n --danger-ink: var(--bm-danger-text);\n --info-dot: oklch(0.6 0.15 255);\n --sans: var(--bm-font-ui);\n --mono: var(--bm-font-mono);\n --serif: var(--bm-font-serif);\n --shadow-pop: var(--bm-shadow-pop);\n}\n* {\n box-sizing: border-box;\n}\nbutton,\ninput,\nselect,\ntextarea {\n font: inherit;\n color: inherit;\n}\nbutton {\n cursor: pointer;\n}\n:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 2px;\n}\n\n/* On-page outlines */\n.bm-outline {\n position: fixed;\n pointer-events: none;\n border-radius: 6px;\n z-index: 1;\n}\n.bm-outline-section {\n border: 2px solid var(--accent);\n}\n.bm-outline-field {\n border: 2px solid var(--accent);\n background: oklch(0.46 0.13 145 / 0.08);\n}\n.bm-image-bar {\n flex-wrap: nowrap;\n left: auto;\n white-space: nowrap;\n}\n.bm-image-bar-group {\n display: flex;\n gap: 2px;\n}\n.bm-image-bar button[aria-pressed=\"true\"] {\n background: var(--bm-saved-bg);\n color: var(--bm-saved-fg);\n}\n.bm-image-bar-grid {\n display: grid;\n grid-template-columns: repeat(3, 9px);\n grid-template-rows: repeat(3, 9px);\n gap: 2px;\n align-self: center;\n padding: 0 4px;\n}\n.bm-image-bar .bm-pos-cell {\n width: 9px;\n height: 9px;\n /* `.bm-rich button` sets a 32px minimum for the bar's text buttons; the cells stay 9px. */\n min-width: 0;\n min-height: 0;\n padding: 0;\n border-radius: 2px;\n background: var(--line);\n}\n.bm-image-bar .bm-pos-cell:hover:not(:disabled) {\n background: var(--muted);\n}\n.bm-image-bar .bm-pos-cell[aria-pressed=\"true\"] {\n background: var(--accent);\n}\n.bm-image-bar .bm-pos-cell:disabled {\n opacity: 0.3;\n cursor: default;\n}\n.bm-outline-image {\n background: oklch(0.46 0.13 145 / 0.12);\n}\n.bm-outline-image::after {\n content: \"Change image\";\n position: absolute;\n top: 8px;\n left: 8px;\n padding: 4px 9px;\n border-radius: 6px;\n background: var(--accent);\n color: #fff;\n font: 500 11px / 1.3 var(--sans, system-ui, sans-serif);\n}\n\n.bm-toolbar,\n.bm-dock,\n.bm-exit-preview,\n.bm-sidebar,\n.bm-popover,\n.bm-dialog,\n.bm-rich,\n.bm-banner,\n.bm-scrim {\n pointer-events: auto;\n font-family: var(--sans);\n font-size: 13px;\n color: var(--ink);\n -webkit-font-smoothing: antialiased;\n}\n\n/*\n * Insert between sections. Three states:\n * idle / hover a solid line and a 28px circle; hover changes nothing.\n * open the picker is open: the line stays and a \"between A and B\" chip replaces the circle.\n */\n.bm-gap {\n position: fixed;\n z-index: 2;\n height: 0;\n pointer-events: none;\n}\n.bm-gap::before {\n content: \"\";\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n height: 0;\n border-top: 2px solid var(--bm-primary);\n transform: translateY(-50%);\n}\n.bm-gap-add {\n position: absolute;\n left: 50%;\n top: 0;\n transform: translate(-50%, -50%);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n width: 28px;\n height: 28px;\n padding: 0;\n border: 0;\n border-radius: 999px;\n pointer-events: auto;\n cursor: pointer;\n white-space: nowrap;\n background: var(--bm-primary);\n color: #fff;\n box-shadow: 0 0 0 2px #fff;\n}\n.bm-gap-add .bm-icon {\n width: 14px;\n height: 14px;\n stroke-width: 2.4;\n}\n.bm-gap-add:focus-visible {\n outline: 2px solid var(--ink);\n outline-offset: 3px;\n}\n.bm-gap-chip {\n position: absolute;\n left: 50%;\n top: 0;\n transform: translate(-50%, -50%);\n padding: 4px 10px;\n border-radius: 999px;\n background: #fff;\n color: var(--bm-ink);\n box-shadow: 0 0 0 1.5px var(--bm-primary);\n white-space: nowrap;\n pointer-events: none;\n font-size: 12px;\n font-weight: 500;\n}\n\n/* Light floating bars: section toolbar, rich text bar, heading format bar */\n.bm-toolbar,\n.bm-rich {\n position: fixed;\n z-index: 3;\n display: flex;\n gap: 2px;\n padding: 4px;\n border-radius: 12px;\n background: #fff;\n color: var(--ink);\n box-shadow: var(--bm-shadow-pop);\n}\n.bm-toolbar button,\n.bm-rich button {\n border: 0;\n border-radius: 7px;\n background: transparent;\n color: inherit;\n min-height: 32px;\n padding: 0 10px;\n font-size: 12px;\n font-weight: 500;\n}\n.bm-toolbar button:hover:not(:disabled),\n.bm-rich button:hover {\n background: var(--fill);\n}\n.bm-rich button[aria-pressed=\"true\"] {\n background: var(--bm-saved-bg);\n box-shadow: inset 0 0 0 1px oklch(0.46 0.13 145 / 0.4);\n}\n.bm-toolbar :focus-visible,\n.bm-rich :focus-visible {\n outline-color: var(--accent);\n}\n.bm-toolbar {\n align-items: center;\n max-width: calc(100vw - 16px);\n padding: 5px;\n}\n.bm-toolbar button {\n width: 34px;\n min-height: 34px;\n height: 34px;\n padding: 0;\n border-radius: 8px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: oklch(0.4 0.01 255);\n}\n.bm-toolbar button:hover:not(:disabled) {\n background: var(--bm-fill);\n color: var(--bm-ink);\n}\n.bm-toolbar button:disabled {\n opacity: 0.35;\n cursor: default;\n}\n.bm-toolbar .bm-danger {\n color: var(--bm-danger-icon);\n}\n.bm-toolbar .bm-danger:hover:not(:disabled) {\n background: var(--bm-danger-soft);\n color: var(--bm-danger-icon);\n}\n.bm-toolbar-active {\n box-shadow:\n 0 0 0 2px var(--bm-primary),\n 0 10px 24px -12px oklch(0.2 0.02 255 / 0.35);\n}\n.bm-toolbar-name {\n display: block;\n max-width: 16rem;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n padding: 0 12px 0 10px;\n}\n.bm-toolbar-title {\n font-size: 14px;\n font-weight: 600;\n color: var(--bm-ink);\n}\n.bm-toolbar-anchor {\n font-family: var(--mono);\n font-size: 12px;\n font-weight: 400;\n color: var(--bm-muted);\n}\n.bm-sep {\n width: 1px;\n align-self: stretch;\n margin: 4px 2px;\n background: var(--line);\n}\n.bm-toolbar .bm-sep {\n align-self: center;\n height: 20px;\n margin: 0 4px;\n background: oklch(0.9 0.005 250);\n}\n.bm-icon {\n width: 16px;\n height: 16px;\n display: block;\n}\n.bm-rich {\n align-items: center;\n flex-wrap: wrap;\n max-width: calc(100vw - 16px);\n}\n.bm-rich button {\n min-height: 32px;\n min-width: 32px;\n}\n.bm-rich input {\n width: 168px;\n min-height: 30px;\n padding: 0 8px;\n border: 0;\n border-radius: 7px;\n background: var(--fill);\n color: var(--ink);\n font-size: 12px;\n}\n.bm-rich input:focus {\n outline: none;\n box-shadow: 0 0 0 2px var(--accent);\n}\n\n/* Heading format bar: each button shows \"Aa\" in its own weight or font over a mono caption. */\n.bm-rich.bm-format-bar {\n flex-wrap: nowrap;\n}\n.bm-rich.bm-format-bar button.bm-fmt {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 2px;\n min-width: 42px;\n height: 48px;\n min-height: 48px;\n padding: 0 8px;\n border-radius: 8px;\n}\n.bm-fmt-sample {\n font-size: 15px;\n line-height: 1;\n color: var(--bm-ink);\n}\n.bm-fmt-caption {\n font-family: var(--mono);\n font-size: 9px;\n letter-spacing: 0.02em;\n color: var(--bm-muted);\n}\n.bm-rich.bm-format-bar button.bm-fmt[aria-pressed=\"true\"] {\n background: var(--bm-saved-bg);\n box-shadow: inset 0 0 0 1px oklch(0.46 0.13 145 / 0.4);\n}\n.bm-format-bar .bm-sep {\n align-self: stretch;\n margin: 6px 4px;\n}\n\n/* Light popovers: add menu, section picker, edit link */\n.bm-popover {\n position: fixed;\n z-index: 3;\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 180px;\n max-height: min(420px, calc(100vh - 16px));\n overflow: auto;\n padding: 6px;\n border-radius: 12px;\n background: #fff;\n box-shadow: var(--bm-shadow-pop);\n}\n.bm-popover > button {\n display: flex;\n align-items: center;\n gap: 8px;\n min-height: 34px;\n padding: 0 10px;\n border: 0;\n border-radius: 7px;\n background: transparent;\n text-align: left;\n font-size: 13px;\n font-weight: 500;\n}\n.bm-popover > button:hover {\n background: var(--fill);\n}\n\n/* Add section picker: wireframe cards */\n.bm-popover[aria-label=\"Add section\"][role=\"listbox\"] {\n gap: 14px;\n width: 640px;\n max-width: calc(100vw - 16px);\n max-height: calc(100vh - 16px);\n padding: 16px;\n border-radius: 14px;\n overflow: visible;\n box-shadow: var(--bm-shadow-dialog);\n}\n.bm-picker-head {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.bm-picker-title {\n font-size: 17px;\n font-weight: 600;\n letter-spacing: -0.01em;\n}\n.bm-picker-where {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 13px;\n color: var(--bm-muted);\n}\n.bm-picker-grid {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 10px;\n max-height: 26rem;\n overflow-y: auto;\n padding: 2px 6px 2px 2px;\n margin-right: -6px;\n scrollbar-gutter: stable;\n}\n.bm-popover .bm-picker-grid > button {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n justify-content: flex-start;\n gap: 6px;\n min-height: 0;\n padding: 8px 8px 10px;\n border: 1px solid oklch(0.9 0.005 250);\n border-radius: 10px;\n background: #fff;\n text-align: left;\n font-weight: 400;\n}\n.bm-popover .bm-picker-grid > button:hover {\n background: #fff;\n border-color: var(--bm-primary);\n box-shadow: 0 0 0 3px oklch(0.46 0.13 145 / 0.12);\n}\n.bm-wire {\n position: relative;\n display: block;\n width: 100%;\n height: 76px;\n border-radius: 7px;\n background: oklch(0.97 0 0);\n overflow: hidden;\n}\n.bm-wire > span {\n position: absolute;\n display: block;\n}\n.bm-type-text {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 0 2px;\n}\n.bm-type-name {\n font-size: 14px;\n font-weight: 600;\n line-height: 1.25;\n color: var(--bm-ink);\n}\n.bm-type-variants {\n font-family: var(--mono);\n font-size: 10px;\n font-weight: 400;\n line-height: 1.35;\n white-space: nowrap;\n color: var(--muted);\n}\n.bm-type-desc {\n padding: 0 2px;\n font-size: 12px;\n line-height: 1.4;\n color: var(--bm-help);\n}\n.bm-picker-note {\n margin: 0;\n font-size: 12px;\n color: var(--bm-muted);\n}\n\n.bm-popover[aria-label=\"Edit link\"] {\n gap: 14px;\n padding: 16px;\n max-height: calc(100vh - 16px);\n}\n\n/* Dialogs and banners */\n.bm-dialog {\n position: fixed;\n z-index: 6;\n display: flex;\n flex-direction: column;\n gap: 16px;\n width: min(400px, calc(100vw - 32px));\n padding: 20px;\n border-radius: 14px;\n background: #fff;\n box-shadow: var(--bm-shadow-dialog);\n}\n.bm-dialog-head {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 12px;\n}\n.bm-dialog-heading {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.bm-dialog-sub {\n margin: 0;\n font-size: 13px;\n line-height: 1.4;\n color: var(--bm-help);\n}\n.bm-dialog .bm-dialog-x,\n.bm-popover .bm-dialog-x,\n.bm-modal .bm-dialog-x {\n flex: none;\n width: 32px;\n height: 32px;\n margin: -6px -8px -6px 0;\n background: transparent;\n color: var(--bm-icon);\n}\n.bm-dialog-title {\n margin: 0;\n font-size: 17px;\n font-weight: 600;\n letter-spacing: -0.01em;\n color: var(--bm-ink);\n}\n.bm-popover .bm-dialog-title {\n font-size: 16px;\n}\n.bm-dialog .bm-note {\n color: var(--bm-label);\n font-size: 14px;\n line-height: 1.5;\n}\n.bm-dialog button,\n.bm-popover .bm-actions button,\n.bm-banner button {\n min-height: 38px;\n padding: 0 16px;\n border: 0;\n border-radius: 8px;\n background: var(--fill);\n font-size: 14px;\n font-weight: 400;\n}\n.bm-dialog button:hover:not(:disabled),\n.bm-popover .bm-actions button:hover:not(:disabled) {\n background: var(--fill-hover);\n}\n/* The primary action comes first in the DOM (focus lands on it); row-reverse shows it last, on the right. */\n.bm-actions {\n display: flex;\n flex-direction: row-reverse;\n justify-content: flex-start;\n align-items: center;\n gap: 8px;\n}\n.bm-dialog > .bm-actions,\n.bm-popover[aria-label=\"Edit link\"] > .bm-actions {\n padding-top: 12px;\n border-top: 1px solid var(--bm-divider);\n}\n.bm-dialog-foot {\n display: flex;\n align-items: center;\n gap: 8px;\n padding-top: 12px;\n border-top: 1px solid var(--bm-divider);\n}\n.bm-dialog-foot > .bm-hint {\n flex: 1;\n min-width: 0;\n margin: 0;\n}\n.bm-dialog-foot .bm-actions {\n flex: none;\n}\n\n/* Alert dialogs: [icon tile] [title / body / note] [x] */\n.bm-alert-main {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n}\n.bm-alert-copy {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.bm-alert-body {\n margin: 0;\n font-size: 14px;\n line-height: 1.5;\n color: var(--bm-label);\n overflow-wrap: anywhere;\n}\n.bm-alert-body strong {\n font-weight: 600;\n color: var(--bm-ink);\n}\n.bm-alert-note {\n margin: 0;\n font-size: 12px;\n line-height: 1.45;\n color: var(--bm-muted);\n}\n.bm-tile {\n flex: none;\n display: grid;\n place-items: center;\n width: 36px;\n height: 36px;\n border-radius: 9px;\n}\n.bm-tile .bm-icon {\n width: 18px;\n height: 18px;\n}\n.bm-tile-warn {\n background: var(--bm-unsaved-bg);\n color: var(--bm-unsaved-fg);\n}\n.bm-tile-danger {\n background: var(--bm-danger-soft);\n color: var(--bm-danger-icon);\n}\n.bm-tile-neutral {\n background: var(--bm-fill);\n color: var(--bm-label);\n}\n\n.bm-banner {\n position: fixed;\n z-index: 6;\n display: flex;\n align-items: center;\n gap: 12px;\n width: min(440px, calc(100vw - 32px));\n padding: 12px 14px;\n border-radius: 12px;\n box-shadow: 0 14px 34px -10px oklch(0.2 0.02 255 / 0.4);\n}\n.bm-banner button {\n min-height: 34px;\n padding: 0 14px;\n font-size: 13px;\n font-weight: 500;\n}\n.bm-banner-ink {\n background: var(--ink);\n color: #fff;\n}\n.bm-banner-light {\n background: #fff;\n box-shadow: var(--shadow-pop);\n}\n.bm-banner-text {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n.bm-banner-title {\n margin: 0;\n font-size: 13px;\n font-weight: 500;\n line-height: 1.35;\n}\n.bm-banner-sub {\n margin: 0;\n font-size: 12px;\n line-height: 1.4;\n opacity: 0.7;\n}\n.bm-banner .bm-actions {\n flex: none;\n gap: 6px;\n}\n.bm-banner-ink button {\n min-height: 30px;\n padding: 0 12px;\n font-size: 12px;\n background: oklch(1 0 0 / 0.12);\n color: #fff;\n}\n.bm-banner-ink button:hover {\n background: oklch(1 0 0 / 0.2);\n}\n.bm-banner-ink .bm-primary {\n background: #fff;\n color: var(--ink);\n}\n.bm-banner-ink .bm-primary:hover {\n background: oklch(0.94 0.003 250);\n}\n.bm-banner-ink :focus-visible {\n outline-color: oklch(0.82 0.14 145);\n}\n.bm-dot-stale {\n background: oklch(0.7 0.18 25) !important;\n}\n.bm-dot-info {\n background: var(--info-dot) !important;\n}\n\n/* Closed editor. The dock sits where the sidebar header's controls sit: header padding (top 14, right 14) minus the dock's 7px padding. */\n.bm-dock {\n position: fixed;\n z-index: 5;\n top: 7px;\n right: 7px;\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 7px 7px 7px 10px;\n border-radius: 14px;\n background: #fff;\n box-shadow: var(--bm-shadow-pop);\n}\n.bm-dot {\n flex: none;\n width: 8px;\n height: 8px;\n border-radius: 99px;\n background: oklch(0.72 0.17 145);\n}\n.bm-dot-dirty {\n background: var(--warn-dot);\n}\n\n/* Sidebar shell */\n.bm-sidebar {\n position: fixed;\n z-index: 4;\n top: 0;\n right: 0;\n bottom: 0;\n width: min(440px, 100vw);\n display: flex;\n flex-direction: column;\n background: #fff;\n box-shadow:\n 0 0 0 1px var(--line),\n -24px 0 48px -24px oklch(0.2 0.02 255 / 0.25);\n}\n.bm-resize {\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n width: 6px;\n cursor: col-resize;\n touch-action: none;\n z-index: 2;\n}\n.bm-resize:hover,\n.bm-resize:focus-visible {\n background: oklch(0.46 0.13 145 / 0.25);\n}\n.bm-resize:focus-visible {\n outline-offset: -2px;\n}\n\n.bm-head {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 14px 14px 14px 16px;\n border-bottom: 1px solid var(--line-soft);\n}\n.bm-mark {\n flex: none;\n display: block;\n width: 28px;\n height: auto;\n aspect-ratio: 21 / 19;\n image-rendering: pixelated;\n user-select: none;\n}\n/* Bear, name and slogan. Takes the space the controls leave; the text fades out when it no longer fits. */\n.bm-brand {\n position: relative;\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.bm-head-text {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n transition: opacity 160ms ease;\n}\n.bm-brand-compact .bm-head-text {\n opacity: 0;\n}\n/* The same two lines, in a card left of the sidebar (over the page), level with the header, while the pointer is on\n the brand. 16px is the header's left padding: the card clears the sidebar edge by 10px. */\n.bm-brand-tip {\n position: absolute;\n z-index: 5;\n top: 50%;\n right: calc(100% + 16px + 10px);\n display: flex;\n flex-direction: column;\n padding: 8px 12px;\n border-radius: 10px;\n background: #fff;\n box-shadow: var(--bm-shadow-pop);\n white-space: nowrap;\n opacity: 0;\n transform: translate(4px, -50%);\n pointer-events: none;\n transition:\n opacity 120ms ease,\n transform 120ms ease;\n}\n.bm-brand-compact:hover .bm-brand-tip {\n opacity: 1;\n transform: translate(0, -50%);\n transition-delay: 120ms;\n}\n@media (prefers-reduced-motion: reduce) {\n .bm-head-text,\n .bm-brand-tip {\n transition: none;\n }\n}\n/* A ring with one open quarter that turns while a save is written and the page reloads. */\n.bm-spin {\n flex: none;\n width: 14px;\n height: 14px;\n border: 2px solid currentColor;\n border-right-color: transparent;\n border-radius: 50%;\n animation: bm-spin 0.8s linear infinite;\n}\n@keyframes bm-spin {\n to {\n transform: rotate(360deg);\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .bm-spin {\n animation: none;\n }\n}\n.bm-faces {\n display: grid;\n}\n.bm-face {\n grid-area: 1 / 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n white-space: nowrap;\n}\n.bm-face[hidden-face] {\n visibility: hidden;\n}\n.bm-save:disabled {\n opacity: 0.85;\n cursor: progress;\n}\n.bm-head-title {\n font-size: 15px;\n font-weight: 600;\n letter-spacing: -0.015em;\n /* The controls beside it never shrink, so a narrow sidebar cuts the name, not the buttons. */\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-head-sub {\n font-family: var(--mono);\n font-size: 11px;\n color: var(--muted);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-pill {\n flex: none;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 24px;\n padding: 0 10px;\n border-radius: 999px;\n background: var(--bm-saved-bg);\n color: var(--bm-saved-fg);\n font-size: 12px;\n font-weight: 500;\n}\n.bm-pill .bm-dot {\n width: 6px;\n height: 6px;\n background: var(--accent);\n}\n.bm-pill-dirty {\n background: var(--warn-tint);\n color: var(--warn-ink);\n}\n.bm-pill-dirty .bm-dot {\n background: var(--warn-dot);\n}\nbutton.bm-pill-btn {\n min-height: 0;\n padding: 0 10px;\n border: 0;\n border-radius: 999px;\n background: var(--warn-tint);\n color: var(--warn-ink);\n font-family: inherit;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n}\n:is(.bm-sidebar, .bm-dock) button.bm-pill-btn:hover:not(:disabled) {\n background: oklch(0.93 0.06 75);\n color: var(--warn-ink);\n}\nbutton.bm-pill-btn:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 2px;\n}\n.bm-dialog-wide {\n width: min(400px, calc(100vw - 32px));\n}\n.bm-dialog .bm-actions {\n flex-wrap: nowrap;\n}\n.bm-dialog .bm-actions button {\n flex: none;\n white-space: nowrap;\n}\n.bm-changes {\n width: min(540px, calc(100vw - 32px));\n}\n.bm-commit-files {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n max-height: min(34vh, 280px);\n overflow-y: auto;\n}\n.bm-commit-files label {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n cursor: pointer;\n}\n.bm-commit-mark {\n font:\n 600 11px / 1 ui-monospace,\n monospace;\n width: 18px;\n text-align: center;\n opacity: 0.7;\n}\n.bm-commit-msg {\n width: 100%;\n box-sizing: border-box;\n resize: vertical;\n font: inherit;\n font-size: 14px;\n padding: 8px 12px;\n border: 1px solid var(--line);\n border-radius: 8px;\n background: transparent;\n color: inherit;\n}\n\n/* Unsaved changes review and version history: groups, rows, diff lines */\n.bm-changes-list {\n display: flex;\n flex-direction: column;\n gap: 16px;\n max-height: 22rem;\n overflow-y: auto;\n margin: 0 -4px;\n padding: 0 4px;\n}\n.bm-changes-group {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.bm-changes-area {\n margin: 0;\n display: flex;\n align-items: baseline;\n gap: 8px;\n font-family: var(--mono);\n font-size: 11px;\n font-weight: 500;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--bm-section);\n}\n.bm-changes-file {\n font-weight: 400;\n letter-spacing: 0;\n text-transform: none;\n color: oklch(0.55 0.01 255);\n}\n.bm-changes-count {\n margin-left: auto;\n font-weight: 400;\n letter-spacing: 0;\n color: var(--bm-muted);\n}\n.bm-changes ul {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n border: 1px solid var(--bm-border);\n border-radius: 10px;\n overflow: hidden;\n}\n.bm-change {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 10px;\n font-size: 14px;\n line-height: 1.4;\n}\n.bm-change + .bm-change {\n border-top: 1px solid var(--bm-divider);\n}\n.bm-change-mark {\n flex: none;\n display: grid;\n place-items: center;\n width: 22px;\n height: 22px;\n border-radius: 6px;\n font-family: var(--mono);\n font-size: 13px;\n font-weight: 600;\n background: var(--bm-unsaved-bg);\n color: var(--bm-unsaved-fg);\n}\n.bm-change-added .bm-change-mark {\n background: var(--bm-badge-green-bg);\n color: var(--bm-badge-green-fg);\n}\n.bm-change-removed .bm-change-mark {\n background: var(--bm-danger-soft);\n color: var(--bm-danger-text);\n}\n.bm-change-body {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.bm-change-field {\n font-weight: 500;\n color: var(--bm-ink);\n overflow-wrap: anywhere;\n}\n.bm-change-values {\n display: flex;\n flex-direction: column;\n gap: 3px;\n min-width: 0;\n}\n.bm-change-values del,\n.bm-change-values ins {\n display: flex;\n gap: 6px;\n min-width: 0;\n padding: 3px 7px;\n border-radius: 5px;\n text-decoration: none;\n font-size: 13px;\n}\n.bm-change-values del {\n background: var(--bm-diff-del-bg);\n color: var(--bm-diff-del-fg);\n}\n.bm-change-values ins {\n background: var(--bm-diff-add-bg);\n color: var(--bm-diff-add-fg);\n}\n.bm-change-values del::before {\n content: \"\\2212\";\n flex: none;\n font-family: var(--mono);\n}\n.bm-change-values ins::before {\n content: \"+\";\n flex: none;\n font-family: var(--mono);\n}\n.bm-change-values del > span,\n.bm-change-values ins > span {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-change-values del > span {\n text-decoration: line-through;\n text-decoration-color: oklch(0.45 0.13 25 / 0.5);\n}\n.bm-changes-empty {\n margin: 0;\n padding: 24px 0;\n text-align: center;\n font-size: 14px;\n color: var(--bm-muted);\n}\n\n.bm-changes ul.bm-versions {\n gap: 0;\n}\n.bm-version {\n padding: 0;\n}\n.bm-version + .bm-version {\n border-top: 1px solid var(--bm-divider);\n}\n.bm-version-when strong {\n font-weight: 600;\n}\n.bm-tipped {\n position: relative;\n}\n.bm-tipped::after {\n content: attr(data-tip);\n position: absolute;\n right: calc(100% + 6px);\n top: 50%;\n transform: translateY(-50%);\n padding: 4px 8px;\n border-radius: 6px;\n background: var(--ink);\n color: #fff;\n font-size: 12px;\n font-weight: 500;\n line-height: 1.3;\n white-space: nowrap;\n pointer-events: none;\n visibility: hidden;\n z-index: 5;\n}\n.bm-version-row > .bm-tipped:first-child::after {\n right: auto;\n left: calc(100% + 6px);\n}\n.bm-tipped:hover::after,\n.bm-tipped:focus-visible::after {\n visibility: visible;\n}\n.bm-version-row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 8px 8px 10px;\n}\n.bm-version-when {\n flex: 1;\n min-width: 0;\n margin-right: auto;\n padding: 0 !important;\n min-height: 32px !important;\n font-weight: 500;\n display: flex !important;\n flex-direction: column;\n align-items: flex-start !important;\n justify-content: center;\n gap: 2px;\n text-align: left;\n}\n.bm-version-line {\n display: flex;\n align-items: baseline;\n gap: 8px;\n}\n.bm-version-sum {\n max-width: 100%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n font-weight: 400;\n color: var(--bm-help);\n}\n.bm-version-row .bm-restore {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n flex: none;\n min-height: 32px;\n padding: 0 10px;\n border-radius: 7px;\n font-size: 13px;\n font-weight: 500;\n}\n.bm-version-rel {\n font-size: 14px;\n font-weight: 500;\n color: var(--bm-ink);\n}\n.bm-version-abs {\n font-family: var(--mono);\n font-size: 11px;\n color: var(--bm-muted);\n}\n.bm-version-diff {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 4px 10px 12px 10px;\n border-top: 1px solid var(--bm-divider);\n margin-top: 0;\n}\n.bm-version-diff ul {\n margin: 0;\n}\n.bm-day {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.bm-restored {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 8px 8px 12px;\n border-radius: 9px;\n background: var(--bm-diff-add-bg);\n color: var(--bm-saved-fg);\n font-size: 13px;\n}\n.bm-restored > span {\n flex: 1;\n min-width: 0;\n}\n\n.bm-tabs {\n display: flex;\n gap: 0;\n padding: 0 12px;\n overflow-x: auto;\n border-bottom: 1px solid var(--bm-tab-rule);\n}\n.bm-tabs button {\n flex: 1 1 auto;\n height: 46px;\n padding: 0 6px;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: oklch(0.48 0.01 255);\n font-size: 13px;\n font-weight: 400;\n}\n.bm-tabs button:hover {\n color: var(--ink);\n background: var(--fill-soft);\n}\n.bm-tabs button[aria-selected=\"true\"] {\n color: var(--ink);\n background: transparent;\n font-weight: 600;\n box-shadow: inset 0 -2px 0 var(--accent);\n}\n.bm-tabs button:focus-visible {\n outline-offset: -2px;\n}\n\n.bm-body {\n flex: 1;\n min-height: 0;\n overflow: auto;\n padding: 24px 24px 32px;\n display: flex;\n flex-direction: column;\n gap: 18px;\n}\n.bm-foot {\n display: flex;\n justify-content: space-between;\n gap: 12px;\n padding: 12px 16px;\n border-top: 1px solid var(--line-soft);\n font-family: var(--mono);\n font-size: 10px;\n color: var(--muted);\n}\n.bm-foot span:first-child {\n flex: none;\n white-space: nowrap;\n}\n.bm-foot span:last-child {\n text-align: right;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n}\n\n/* Group header: mono 11/500, then a 1px rule. With a right-hand item the rule sits on the flex wrapper. */\n.bm-group {\n margin: 18px 0 0;\n padding-bottom: 10px;\n border-bottom: 1px solid var(--bm-divider);\n font-family: var(--mono);\n font-size: 11px;\n font-weight: 500;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--bm-section);\n}\n.bm-group:first-child {\n margin-top: 0;\n}\n.bm-stack-tight > .bm-group {\n margin-bottom: 10px;\n}\n.bm-meta {\n margin: 0;\n font-family: var(--mono);\n font-size: 11px;\n color: var(--muted);\n}\n\n/* Fields */\n.bm-label {\n display: flex;\n flex-direction: column;\n gap: 8px;\n font-size: 13px;\n font-weight: 500;\n color: var(--bm-label);\n}\n.bm-field {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.bm-hint {\n font-size: 12px;\n line-height: 1.45;\n font-weight: 400;\n color: var(--muted);\n}\n.bm-label-text {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n}\n.bm-info {\n position: relative;\n display: inline-flex;\n align-items: center;\n vertical-align: middle;\n margin-left: 4px;\n}\n.bm-info-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n margin: -3px 0;\n border-radius: 999px;\n color: var(--bm-info);\n cursor: help;\n text-transform: none;\n letter-spacing: 0;\n}\n.bm-info-btn:hover,\n.bm-info-btn[aria-expanded=\"true\"] {\n color: var(--ink);\n}\n.bm-info-btn:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 1px;\n}\n.bm-tip {\n position: fixed;\n z-index: 2147483647;\n visibility: hidden;\n pointer-events: none;\n padding: 8px 10px;\n border-radius: 8px;\n box-sizing: border-box;\n background: var(--ink);\n color: oklch(0.98 0.003 250);\n font-family: var(--sans, inherit);\n font-size: 12px;\n line-height: 1.45;\n font-weight: 400;\n text-transform: none;\n letter-spacing: 0;\n text-align: left;\n white-space: normal;\n box-shadow: 0 6px 20px rgb(0 0 0 / 0.25);\n}\n.bm-tip-open {\n visibility: visible;\n}\n:is(.bm-sidebar, .bm-popover, .bm-seo) :where(input:not([type=\"checkbox\"]):not([type=\"file\"]), select, textarea) {\n width: 100%;\n min-height: 40px;\n padding: 0 12px;\n border: 1px solid var(--bm-border);\n border-radius: 8px;\n background: #fff;\n color: var(--ink);\n font-size: 14px;\n font-weight: 400;\n outline: none;\n}\n:is(.bm-sidebar, .bm-popover, .bm-seo) textarea {\n padding: 8px 12px;\n min-height: 72px;\n line-height: 1.5;\n resize: vertical;\n}\n:is(.bm-sidebar, .bm-popover) select:not([size]) {\n appearance: none;\n padding-right: 32px;\n /* A caret made of gradients, not a `data:` image: a site's Content-Security-Policy can block those. */\n background-image: linear-gradient(45deg, transparent 50%, #777 50%), linear-gradient(135deg, #777 50%, transparent 50%);\n background-size: 6px 6px;\n background-repeat: no-repeat;\n background-position:\n right 17px center,\n right 12px center;\n}\n.bm-sidebar select[size],\n.bm-popover select[size] {\n padding: 4px;\n font-size: 12px;\n}\n.bm-sidebar select[size] option,\n.bm-sidebar select[size] optgroup,\n.bm-popover select[size] option,\n.bm-popover select[size] optgroup {\n padding: 3px 6px;\n}\n:is(.bm-sidebar, .bm-popover, .bm-seo) :where(input:not([type=\"checkbox\"]):not([type=\"file\"]), select, textarea):focus {\n border-color: var(--bm-primary);\n box-shadow: 0 0 0 3px var(--bm-primary-ring);\n}\n:is(.bm-sidebar, .bm-popover, .bm-seo) :where(input[aria-label=\"Link URL\"], .bm-mono) {\n font-family: var(--mono);\n font-size: 13px;\n}\n.bm-prefix-wrap {\n display: flex;\n align-items: center;\n min-height: 40px;\n border: 1px solid var(--bm-border);\n border-radius: 8px;\n background: #fff;\n overflow: hidden;\n}\n.bm-prefix-wrap:focus-within {\n border-color: var(--bm-primary);\n box-shadow: 0 0 0 3px var(--bm-primary-ring);\n}\n.bm-prefix {\n padding: 0 2px 0 12px;\n font-family: var(--mono);\n font-size: 13px;\n color: var(--muted);\n}\n.bm-prefix-wrap input {\n border: 0;\n box-shadow: none !important;\n min-height: 38px;\n padding-left: 2px;\n font-family: var(--mono);\n font-size: 13px;\n}\n.bm-stack {\n display: flex;\n flex-direction: column;\n gap: 18px;\n}\n.bm-stack-tight {\n gap: 8px;\n}\n.bm-groups {\n gap: 36px;\n}\n.bm-list {\n display: flex;\n flex-direction: column;\n border-radius: 10px;\n box-shadow: 0 0 0 1px var(--bm-border);\n overflow: hidden;\n}\n.bm-list-row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px 8px 12px;\n border-bottom: 1px solid var(--line-soft);\n}\n.bm-list-row:last-child {\n border-bottom: 0;\n}\n.bm-list-row-on {\n background: var(--accent-tint);\n}\n.bm-list-row a {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n color: var(--ink);\n text-decoration: none;\n}\n.bm-list-row a:hover .bm-list-title {\n text-decoration: underline;\n}\n.bm-list-title {\n font-size: 14px;\n font-weight: 500;\n}\n.bm-list-path {\n font-family: var(--mono);\n font-size: 11px;\n color: var(--muted);\n}\n.bm-badge-home {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n background: var(--bm-badge-green-bg);\n color: var(--bm-badge-green-fg);\n}\n.bm-badge {\n font-family: var(--mono);\n font-size: 11px;\n padding: 3px 7px;\n border-radius: 5px;\n background: var(--bm-badge-amber-bg);\n color: var(--bm-badge-amber-fg);\n}\n.bm-badge-system {\n background: var(--fill);\n color: var(--ink-2);\n}\n.bm-badge-on {\n background: var(--bm-badge-green-bg);\n color: var(--bm-badge-green-fg);\n}\n.bm-row {\n display: flex;\n gap: 6px;\n align-items: center;\n flex-wrap: wrap;\n}\n.bm-row-split {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 10px;\n}\n.bm-error {\n color: var(--danger-ink);\n font-size: 12px;\n font-weight: 400;\n margin: 0;\n}\n.bm-warn {\n color: var(--warn-ink);\n font-size: 12px;\n margin: 0;\n}\n.bm-note {\n margin: 0;\n color: var(--bm-help);\n font-size: 13px;\n line-height: 1.55;\n font-weight: 400;\n}\n.bm-group + .bm-note,\n.bm-group-split + .bm-note {\n margin-top: -4px;\n}\n\n/* Switch: 36 x 20, knob 16 inset 2 */\n.bm-check {\n flex-direction: row;\n align-items: center;\n gap: 10px;\n font-weight: 500;\n}\n:is(.bm-check, .bm-switch-row) input[type=\"checkbox\"] {\n appearance: none;\n position: relative;\n flex: none;\n width: 36px;\n height: 20px;\n margin: 0;\n border-radius: 999px;\n background: var(--bm-switch-off);\n cursor: pointer;\n transition: background 0.15s;\n}\n:is(.bm-check, .bm-switch-row) input[type=\"checkbox\"]::before {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: #fff;\n box-shadow: 0 1px 2px oklch(0 0 0 / 0.2);\n transition: transform 0.15s;\n}\n:is(.bm-check, .bm-switch-row) input[type=\"checkbox\"]:checked {\n background: var(--bm-primary);\n}\n:is(.bm-check, .bm-switch-row) input[type=\"checkbox\"]:checked::before {\n transform: translateX(16px);\n}\n.bm-popover .bm-check {\n color: var(--bm-label);\n font-size: 14px;\n}\n\n/* Buttons inside the sidebar */\n.bm-sidebar :where(button:not([role=\"tab\"]):not([role=\"radio\"]):not(.bm-resize)) {\n min-height: 32px;\n padding: 0 12px;\n border: 0;\n border-radius: 8px;\n background: var(--fill);\n color: var(--ink);\n font-size: 13px;\n font-weight: 500;\n}\n.bm-sidebar :where(button:not([role=\"tab\"]):not([role=\"radio\"]):not(.bm-resize)):hover:not(:disabled) {\n background: var(--fill-hover);\n}\n.bm-sidebar button:disabled {\n opacity: 0.45;\n cursor: default;\n}\n/* Dashed \"Add ...\" buttons: Add link, Add button, Add column, Add item */\n.bm-sidebar .bm-add {\n min-height: 44px;\n background: oklch(0.985 0.002 250);\n color: var(--bm-link);\n border: 1px dashed oklch(0.85 0.005 250);\n border-radius: 8px;\n}\n.bm-sidebar .bm-add:hover:not(:disabled) {\n background: var(--accent-tint);\n}\n\n/* Cards, lists, images */\n.bm-card {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 12px;\n border: 1px solid var(--bm-border);\n border-radius: 10px;\n background: #fff;\n}\n.bm-card .bm-card {\n background: var(--bm-fill-nested);\n border-color: var(--line-soft);\n}\n.bm-card > .bm-label:first-child:not(:only-child) {\n font-family: var(--mono);\n font-size: 11px;\n letter-spacing: 0.04em;\n font-weight: 500;\n color: var(--muted);\n}\n.bm-card[draggable=\"true\"] {\n cursor: grab;\n}\n/* Drag and drop: the target shows what a drop would do. */\n.bm-dragging {\n opacity: 0.4;\n}\n.bm-drop-before,\n.bm-drop-after,\n.bm-drop-child {\n --drop: var(--accent);\n}\n.bm-drop-before::before,\n.bm-drop-after::before {\n content: \"\";\n position: absolute;\n left: 0;\n right: 0;\n height: 3px;\n border-radius: 3px;\n background: var(--drop);\n box-shadow: 0 0 0 2px var(--accent-ring);\n pointer-events: none;\n z-index: 2;\n}\n.bm-drop-before::before {\n top: -6px;\n}\n.bm-drop-after::before {\n bottom: -6px;\n}\n.bm-drop-child {\n box-shadow: inset 0 0 0 2px var(--drop);\n background: var(--accent-tint);\n}\n.bm-drop-before::after,\n.bm-drop-after::after,\n.bm-drop-child::after,\n.bm-drop-files::after {\n content: attr(data-drop-hint);\n position: absolute;\n right: 8px;\n padding: 3px 8px;\n border-radius: 6px;\n background: var(--ink);\n color: #fff;\n font-size: 11px;\n font-weight: 500;\n line-height: 1.3;\n white-space: nowrap;\n pointer-events: none;\n z-index: 3;\n}\n.bm-drop-before::after {\n top: -22px;\n}\n.bm-drop-after::after {\n bottom: -22px;\n}\n.bm-drop-child::after {\n top: -11px;\n right: 12px;\n}\n.bm-drop-files {\n box-shadow: inset 0 0 0 2px var(--accent);\n background: var(--accent-tint);\n}\n.bm-drop-files::after {\n top: -11px;\n right: 12px;\n}\n.bm-thumb {\n width: 100%;\n height: 96px;\n object-fit: cover;\n border-radius: 7px;\n background: var(--fill);\n}\n.bm-drop {\n display: flex;\n flex-direction: column;\n gap: 8px;\n border: 1px dashed var(--line);\n border-radius: 10px;\n padding: 10px;\n background: var(--fill-soft);\n}\n.bm-drop input[type=\"file\"] {\n width: 100%;\n font-size: 12px;\n color: var(--muted);\n}\n.bm-drop input[type=\"file\"]::file-selector-button {\n height: 32px;\n margin-right: 10px;\n padding: 0 12px;\n border: 0;\n border-radius: 7px;\n background: var(--ink);\n color: #fff;\n font: inherit;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n}\n.bm-alert {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 12px 14px;\n border-radius: 12px;\n background: var(--danger-tint);\n box-shadow: 0 0 0 1px oklch(0.55 0.18 25 / 0.35);\n}\n.bm-alert-title {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--danger-ink);\n}\n.bm-alert .bm-error {\n font-family: var(--mono);\n font-size: 11px;\n}\n.bm-alert-notice {\n background: var(--warn-tint);\n box-shadow: 0 0 0 1px oklch(0.45 0.09 65 / 0.2);\n}\n.bm-alert-notice .bm-note {\n color: var(--warn-ink);\n font-size: 12px;\n}\n\n/* Header actions */\n.bm-save {\n flex: none;\n display: inline-flex;\n align-items: center;\n gap: 8px;\n height: 34px;\n padding: 0 12px;\n border: 0;\n border-radius: 8px;\n background: var(--accent);\n color: #fff;\n font-size: 13px;\n font-weight: 600;\n}\n.bm-dialog .bm-primary kbd {\n font-family: var(--mono);\n font-size: 11px;\n font-weight: 400;\n margin-left: 8px;\n opacity: 0.8;\n}\n.bm-save kbd {\n font-family: var(--mono);\n font-size: 10px;\n font-weight: 400;\n opacity: 0.75;\n}\n/* Icon buttons: 32 x 32, transparent, grey icon, grey fill on hover. */\n.bm-icon-btn {\n flex: none;\n display: inline-grid;\n place-items: center;\n width: 32px;\n height: 32px;\n min-height: 0 !important;\n padding: 0 !important;\n border: 0;\n border-radius: 7px;\n background: transparent;\n color: var(--bm-icon);\n}\n.bm-icon-btn:hover:not(:disabled) {\n background: var(--bm-fill);\n color: var(--bm-ink);\n}\n.bm-icon-btn:disabled {\n opacity: 0.35;\n}\n.bm-close {\n width: 32px;\n height: 32px;\n background: transparent;\n color: var(--bm-icon);\n border-radius: 7px;\n}\n.bm-close:hover:not(:disabled) {\n background: var(--fill) !important;\n color: var(--ink);\n}\n.bm-dock .bm-save {\n font-family: var(--sans);\n}\n.bm-split {\n position: relative;\n display: inline-flex;\n flex: none;\n}\n.bm-split .bm-split-main {\n border-radius: 8px 0 0 8px;\n}\n.bm-split .bm-split-toggle {\n width: 28px;\n padding: 0;\n justify-content: center;\n border-radius: 0 8px 8px 0;\n box-shadow: inset 1px 0 0 oklch(1 0 0 / 0.35);\n}\n.bm-split .bm-split-toggle:focus-visible,\n.bm-split .bm-split-main:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 2px;\n}\n.bm-split-menu {\n position: absolute;\n z-index: 8;\n top: calc(100% + 6px);\n right: 0;\n min-width: 200px;\n display: flex;\n flex-direction: column;\n padding: 4px;\n border-radius: 10px;\n background: #fff;\n box-shadow: var(--bm-shadow-pop);\n}\n.bm-split-menu button.bm-split-item {\n display: flex;\n align-items: center;\n width: 100%;\n min-height: 34px;\n padding: 0 10px;\n border: 0;\n border-radius: 7px;\n background: transparent;\n color: var(--ink);\n font-family: var(--sans);\n font-size: 13px;\n font-weight: 500;\n text-align: left;\n}\n.bm-split-menu button.bm-split-item:hover:not(:disabled),\n.bm-split-menu button.bm-split-item:focus-visible {\n background: var(--fill);\n outline: none;\n}\n.bm-split-menu button.bm-split-danger {\n color: var(--danger-ink);\n}\n.bm-split-menu button.bm-split-item {\n justify-content: space-between;\n gap: 16px;\n}\n.bm-split-menu button.bm-split-item kbd {\n font-family: var(--mono);\n font-size: 11px;\n font-weight: 400;\n color: var(--muted);\n}\n.bm-split-menu button.bm-split-sep {\n margin-top: 5px;\n position: relative;\n}\n.bm-split-menu button.bm-split-sep::before {\n content: \"\";\n position: absolute;\n left: 6px;\n right: 6px;\n top: -3px;\n height: 1px;\n background: var(--line);\n}\n.bm-split-menu button.bm-split-danger:hover:not(:disabled),\n.bm-split-menu button.bm-split-danger:focus-visible {\n background: var(--danger-tint);\n}\n.bm-split-menu button.bm-split-item:disabled {\n opacity: 0.45;\n cursor: default;\n}\n.bm-sidebar .bm-icon-btn.bm-danger {\n color: var(--bm-danger-icon);\n}\n.bm-sidebar .bm-icon-btn.bm-danger:hover:not(:disabled) {\n background: var(--bm-danger-soft);\n color: var(--bm-danger-icon);\n}\n.bm-small {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n min-height: 28px !important;\n padding: 0 10px !important;\n font-family: var(--sans);\n font-size: 12px !important;\n letter-spacing: 0;\n text-transform: none;\n align-self: flex-start;\n}\n.bm-group-split {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n margin-top: 18px;\n margin-bottom: 0;\n padding-bottom: 10px;\n border-bottom: 1px solid var(--bm-divider);\n}\n.bm-group-split:first-child {\n margin-top: 0;\n}\n.bm-group-split > .bm-group {\n margin: 0;\n padding: 0;\n border: 0;\n}\n.bm-group-split .bm-small {\n text-transform: none;\n letter-spacing: 0;\n}\n.bm-group-split span.bm-small {\n font-family: var(--mono);\n font-size: 11px !important;\n font-weight: 400;\n color: var(--bm-muted);\n padding: 0 !important;\n min-height: 0 !important;\n}\n.bm-stack-tight > .bm-group-split {\n margin-bottom: 10px;\n}\n\n/* Labels with a right-hand tag */\n.bm-label.bm-label-row,\n.bm-label-row {\n flex-direction: row;\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 8px;\n}\n.bm-meta-tag {\n font-family: var(--mono);\n font-size: 11px;\n font-weight: 400;\n letter-spacing: 0;\n color: var(--bm-muted);\n}\n\n/* Switch rows */\n.bm-switch-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n cursor: pointer;\n}\n.bm-switch-text {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n.bm-switch-title {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 14px;\n font-weight: 500;\n color: var(--bm-label);\n}\n\n/* Tone cards and segmented controls */\n.bm-tones {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 6px;\n}\n:is(.bm-sidebar, .bm-popover) .bm-tone {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: 6px;\n min-height: 0;\n padding: 5px 5px 6px;\n border-radius: 9px;\n background: #fff;\n border: 0;\n box-shadow: inset 0 0 0 1px var(--bm-border);\n font-size: 12px;\n font-weight: 500;\n text-align: center;\n}\n:is(.bm-sidebar, .bm-popover) .bm-tone:hover {\n background: var(--fill-soft);\n}\n:is(.bm-sidebar, .bm-popover) .bm-tone[aria-checked=\"true\"] {\n box-shadow: inset 0 0 0 2px var(--accent);\n background: #fff;\n}\n.bm-tones-2 {\n grid-template-columns: repeat(2, 1fr);\n}\n.bm-tones-3 {\n grid-template-columns: repeat(3, 1fr);\n}\n/* A miniature of each button look, drawn with the editor's colours so it does not depend on the site theme. */\n.bm-btnsample {\n display: grid;\n place-items: center;\n height: 26px;\n border-radius: 6px;\n background: var(--fill-soft);\n}\n.bm-btnsample > span {\n display: block;\n font-size: 10px;\n font-weight: 600;\n line-height: 1;\n}\n.bm-btnsample-default > span {\n color: var(--ink-2);\n}\n.bm-btnsample-primary > span {\n padding: 5px 12px;\n border-radius: 999px;\n background: var(--accent);\n color: #fff;\n}\n.bm-btnsample-secondary > span {\n padding: 5px 12px;\n border-radius: 999px;\n color: var(--ink);\n box-shadow: inset 0 0 0 1px var(--ink-2);\n}\n.bm-btnsample-tertiary > span {\n color: var(--accent);\n text-decoration: underline;\n text-underline-offset: 3px;\n}\n.bm-swatch {\n display: inline-block;\n flex: none;\n width: 10px;\n height: 10px;\n border-radius: 3px;\n box-shadow: inset 0 0 0 1px oklch(0.2 0.02 255 / 0.15);\n}\n.bm-swatch-lg {\n display: block;\n width: auto;\n height: 22px;\n border-radius: 6px;\n}\n.bm-swatch-base {\n background: #fff;\n}\n.bm-swatch-subtle {\n background: oklch(0.96 0.005 250);\n}\n.bm-swatch-inverse {\n background: var(--ink);\n}\n.bm-swatch-brand {\n background: var(--accent);\n}\n.bm-segmented {\n display: flex;\n padding: 3px;\n border-radius: 9px;\n background: var(--bm-fill);\n}\n.bm-sidebar .bm-segmented button,\n.bm-dialog .bm-segmented button {\n flex: 1;\n min-height: 36px;\n height: 36px;\n padding: 0 8px;\n border: 0;\n border-radius: 7px;\n background: transparent;\n color: oklch(0.45 0.01 255);\n font-size: 13px;\n font-weight: 400;\n}\n.bm-sidebar .bm-segmented button:hover:not(:disabled),\n.bm-dialog .bm-segmented button:hover:not(:disabled) {\n background: oklch(1 0 0 / 0.5);\n}\n.bm-sidebar .bm-segmented button[aria-checked=\"true\"],\n.bm-dialog .bm-segmented button[aria-checked=\"true\"] {\n background: #fff;\n color: var(--ink);\n box-shadow: var(--bm-shadow-segment-on);\n}\n.bm-icon-picker {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(40px, 1fr));\n gap: 4px;\n padding: 3px;\n border-radius: 9px;\n background: var(--bm-fill);\n}\n.bm-sidebar .bm-icon-picker button {\n display: grid;\n place-items: center;\n min-height: 40px;\n height: 40px;\n padding: 0;\n border: 0;\n border-radius: 7px;\n background: transparent;\n color: oklch(0.45 0.01 255);\n}\n.bm-sidebar .bm-icon-picker button:hover {\n background: oklch(1 0 0 / 0.5);\n color: var(--ink);\n}\n.bm-sidebar .bm-icon-picker button[aria-checked=\"true\"] {\n background: #fff;\n color: var(--accent);\n box-shadow: var(--bm-shadow-segment-on);\n}\n.bm-icon-picker svg {\n width: 20px;\n height: 20px;\n}\n.bm-actions-split {\n display: flex;\n gap: 8px;\n padding-top: 4px;\n}\n.bm-actions-split button {\n flex: 1;\n min-height: 40px !important;\n font-size: 14px !important;\n}\n.bm-sidebar .bm-actions-split .bm-danger {\n background: var(--bm-danger-soft);\n}\n\n/* Arrays, links, images */\n.bm-array {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.bm-array-item {\n border: 1px solid var(--bm-border);\n border-radius: 10px;\n background: #fff;\n overflow: hidden;\n}\n.bm-array-item summary {\n display: flex;\n align-items: center;\n gap: 8px;\n min-height: 44px;\n padding: 0 8px 0 10px;\n font-size: 14px;\n font-weight: 500;\n color: var(--ink);\n cursor: pointer;\n list-style: none;\n}\n.bm-array-item summary::-webkit-details-marker {\n display: none;\n}\n.bm-array-item summary:hover {\n background: var(--fill-soft);\n}\n.bm-array-summary {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-grip,\n.bm-chevron {\n display: inline-grid;\n place-items: center;\n color: oklch(0.65 0.01 255);\n}\n.bm-chevron {\n transition: transform 0.12s;\n}\ndetails[open] > summary .bm-chevron {\n transform: rotate(90deg);\n}\n.bm-array-body {\n display: flex;\n flex-direction: column;\n gap: 18px;\n padding: 12px;\n background: var(--bm-fill-nested);\n border-top: 1px solid var(--line-soft);\n}\n.bm-row-end {\n justify-content: flex-end;\n}\n.bm-sidebar .bm-add-row {\n width: 100%;\n}\n.bm-link-btn {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n min-height: 40px;\n padding: 0 12px;\n border: 1px solid var(--bm-border);\n border-radius: 8px;\n background: #fff;\n text-align: left;\n color: var(--ink-2);\n}\n.bm-sidebar .bm-link-btn {\n background: #fff;\n border: 1px solid var(--bm-border);\n font-size: 14px;\n padding: 0 12px;\n}\n.bm-sidebar .bm-link-btn:hover:not(:disabled) {\n background: #fff;\n border-color: oklch(0.8 0.005 250);\n}\n.bm-link-label {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--ink);\n font-weight: 500;\n}\n.bm-link-href {\n font-family: var(--mono);\n font-size: 11px;\n color: var(--muted);\n max-width: 45%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-stripes {\n background: repeating-linear-gradient(135deg, oklch(0.93 0.004 250) 0 6px, oklch(0.965 0.003 250) 6px 12px);\n}\n\n/* Image field: label above, compact 56px row, alt text under it */\n.bm-imagefield {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.bm-image-row {\n display: flex;\n gap: 10px;\n align-items: center;\n}\n.bm-image-row .bm-thumb {\n flex: none;\n width: 56px;\n height: 56px;\n border-radius: 6px;\n}\n.bm-image-meta {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 6px;\n justify-content: center;\n}\n.bm-image-path {\n font-family: var(--mono);\n font-size: 12px;\n color: var(--bm-label);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-sidebar .bm-alt {\n min-height: 40px;\n font-size: 14px;\n}\n.bm-image-actions {\n flex-wrap: nowrap;\n gap: 6px;\n}\n.bm-image-actions > button:not(.bm-icon-btn) {\n flex: 1;\n min-height: 36px;\n font-size: 13px;\n}\n.bm-layout {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding-top: 2px;\n}\n.bm-layout-row {\n display: grid;\n grid-template-columns: 56px 1fr;\n align-items: center;\n column-gap: 10px;\n row-gap: 4px;\n}\n.bm-layout-row .bm-label {\n margin: 0;\n}\n.bm-layout-row .bm-layout-hint {\n grid-column: 2;\n}\n.bm-position {\n display: grid;\n grid-template-columns: repeat(3, 20px);\n grid-template-rows: repeat(3, 20px);\n gap: 3px;\n padding: 4px;\n width: max-content;\n border-radius: 8px;\n background: var(--fill);\n}\n.bm-sidebar .bm-position-cell {\n width: 20px;\n height: 20px;\n min-height: 0;\n padding: 0;\n border: 0;\n border-radius: 5px;\n background: #fff;\n box-shadow: inset 0 0 0 1px var(--line);\n}\n.bm-sidebar .bm-position-cell:hover:not(:disabled) {\n background: var(--accent-tint);\n}\n.bm-sidebar .bm-position-cell[aria-checked=\"true\"],\n.bm-sidebar .bm-position-cell[aria-checked=\"true\"]:hover:not(:disabled) {\n background: var(--accent);\n box-shadow: none;\n}\n.bm-sidebar .bm-position-cell:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.bm-slot,\n.bm-dropzone {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 4px;\n width: 100%;\n min-height: 96px;\n padding: 14px;\n border: 1.5px dashed oklch(0.82 0.006 250);\n border-radius: 10px;\n background: var(--fill-soft);\n color: var(--ink-2);\n text-align: center;\n cursor: pointer;\n}\n.bm-sidebar .bm-slot:hover:not(:disabled),\n.bm-dropzone:hover {\n border-color: var(--accent);\n background: var(--accent-tint);\n}\n.bm-dropzone {\n min-height: 180px;\n position: relative;\n}\n.bm-dropzone:focus-within {\n outline: 2px solid var(--accent);\n outline-offset: 2px;\n}\n.bm-dropzone-over {\n border-color: var(--accent);\n background: var(--accent-tint);\n}\n.bm-slot-title {\n font-size: 13px;\n font-weight: 600;\n color: var(--ink);\n}\n.bm-slot-hint {\n font-size: 11px;\n color: var(--muted);\n}\n.bm-scrim {\n position: fixed;\n inset: 0;\n z-index: 7;\n display: flex;\n align-items: flex-start;\n justify-content: center;\n padding: 10vh 16px 16px;\n background: oklch(0.2 0.01 255 / 0.4);\n}\n.bm-modal {\n display: flex;\n flex-direction: column;\n width: min(480px, 100%);\n max-height: min(560px, 80vh);\n overflow: hidden;\n padding: 20px 0 0;\n border-radius: 14px;\n background: #fff;\n box-shadow: var(--bm-shadow-dialog);\n}\n.bm-modal-head {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 0 20px 12px;\n}\n.bm-modal-head .bm-dialog-title {\n flex: none;\n}\n.bm-modal-head .bm-meta-tag {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-modal-head .bm-icon-btn {\n margin: -6px -8px -6px 0;\n}\n.bm-modal .bm-tabs {\n margin: 0 20px;\n padding: 3px;\n gap: 0;\n border: 0;\n border-radius: 9px;\n background: var(--bm-fill);\n overflow: visible;\n}\n.bm-modal .bm-tabs button {\n flex: 1;\n height: 36px;\n min-height: 36px;\n padding: 0 8px;\n border-radius: 7px;\n font-size: 13px;\n font-weight: 400;\n}\n.bm-modal .bm-tabs button:hover {\n background: oklch(1 0 0 / 0.5);\n}\n.bm-modal .bm-tabs button[aria-selected=\"true\"] {\n background: #fff;\n color: var(--ink);\n font-weight: 500;\n box-shadow: var(--bm-shadow-segment-on);\n}\n.bm-modal-body {\n flex: 1;\n min-height: 0;\n overflow: auto;\n padding: 16px 20px 20px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.bm-library {\n display: grid;\n grid-template-columns: repeat(4, minmax(0, 1fr));\n gap: 10px;\n}\n.bm-library-item {\n position: relative;\n}\n.bm-library-pick {\n display: flex;\n flex-direction: column;\n gap: 4px;\n width: 100%;\n min-height: 0;\n padding: 4px;\n border: 0;\n border-radius: 9px;\n background: var(--fill-soft);\n text-align: left;\n font-family: var(--mono);\n font-size: 9px;\n font-weight: 400;\n color: var(--muted);\n word-break: break-all;\n cursor: pointer;\n}\n.bm-library-pick:hover {\n background: var(--fill-hover);\n}\n.bm-library-pick img {\n width: 100%;\n aspect-ratio: 1 / 1;\n object-fit: cover;\n border-radius: 6px;\n background: var(--fill);\n}\n.bm-library-pick[aria-pressed=\"true\"] {\n box-shadow: 0 0 0 2px var(--accent);\n background: var(--accent-tint);\n}\n.bm-library-delete {\n position: absolute;\n top: 8px;\n right: 8px;\n width: 24px;\n height: 24px;\n min-height: 0;\n background: #fff;\n box-shadow: 0 1px 3px oklch(0.2 0.01 255 / 0.25);\n opacity: 0;\n}\n.bm-library-item:hover .bm-library-delete,\n.bm-library-delete:focus-visible {\n opacity: 1;\n}\n.bm-library-empty {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 8px;\n}\n.bm-modal-confirm {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 14px 20px 16px;\n border-top: 1px solid var(--line-soft);\n background: var(--danger-tint);\n}\n.bm-modal-confirm .bm-note {\n color: var(--danger-ink);\n font-size: 13px;\n line-height: 1.5;\n}\n.bm-visually-hidden {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0 0 0 0);\n border: 0;\n white-space: nowrap;\n}\n.bm-popover-title {\n grid-column: 1 / -1;\n padding: 0;\n font-size: 16px;\n font-weight: 600;\n letter-spacing: -0.01em;\n color: var(--bm-ink);\n}\n.bm-popover-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0;\n}\n.bm-popover-head-wide {\n grid-column: 1 / -1;\n}\n.bm-popover[aria-label=\"Edit link\"],\n.bm-popover[aria-label=\"Add section\"][role=\"listbox\"] {\n z-index: 5;\n}\n.bm-popover :is(.bm-fmt-bold) {\n font-weight: 700;\n}\n.bm-fmt-bold {\n font-weight: 700;\n font-size: 13px;\n}\n.bm-fmt-italic {\n font-family: var(--serif);\n font-style: italic;\n font-size: 15px;\n}\n\n/* Edit link: Advanced row */\n.bm-advanced {\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n.bm-popover button.bm-advanced-toggle {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n min-height: 32px;\n padding: 0 2px;\n border: 0;\n border-radius: 6px;\n background: transparent;\n color: var(--bm-label);\n font-size: 13px;\n font-weight: 500;\n text-align: left;\n}\n.bm-popover button.bm-advanced-toggle:hover {\n background: transparent;\n color: var(--bm-ink);\n}\n.bm-advanced-toggle .bm-chevron {\n transition: transform 0.12s;\n}\n.bm-advanced-toggle[aria-expanded=\"true\"] .bm-chevron {\n transform: rotate(90deg);\n}\n.bm-advanced-summary {\n margin-left: auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: var(--mono);\n font-size: 11px;\n font-weight: 400;\n color: var(--bm-muted);\n}\n\n/* SEO card and dialog */\n.bm-seo-card {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.bm-seo-badges {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.bm-seo-badge {\n padding: 2px 8px;\n border-radius: 999px;\n background: var(--fill-soft);\n color: var(--bm-label);\n font-family: var(--mono);\n font-size: 11px;\n}\n.bm-seo-edit {\n align-self: flex-start;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n.bm-dialog.bm-seo {\n position: relative;\n width: min(560px, 100%);\n max-height: calc(100vh - 10vh - 32px);\n min-height: 0;\n}\n.bm-seo-body {\n display: flex;\n flex-direction: column;\n gap: 20px;\n min-height: 0;\n overflow-y: auto;\n padding: 2px 4px 2px 2px;\n}\n.bm-seo-json {\n min-height: 220px;\n white-space: pre;\n overflow: auto;\n tab-size: 2;\n}\n.bm-seo-json[aria-invalid=\"true\"] {\n border-color: var(--bm-danger);\n}\n.bm-seo-format {\n align-self: flex-start;\n}\n\n/* SEO preview, OG row, outline */\n.bm-serp {\n display: flex;\n flex-direction: column;\n gap: 3px;\n padding: 12px 14px;\n border-radius: 10px;\n background: var(--fill-soft);\n}\n.bm-serp-image {\n flex-direction: row;\n align-items: flex-start;\n gap: 12px;\n}\n.bm-serp-text {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: 3px;\n min-width: 0;\n}\n.bm-serp-thumb {\n flex: none;\n width: 64px;\n height: 64px;\n border-radius: 8px;\n object-fit: cover;\n background: var(--fill);\n}\n.bm-serp-title {\n font-size: 15px;\n font-weight: 500;\n color: oklch(0.42 0.12 260);\n}\n.bm-serp-url {\n font-family: var(--mono);\n font-size: 11px;\n color: oklch(0.45 0.08 145);\n}\n.bm-serp-desc {\n font-size: 12px;\n line-height: 1.45;\n color: oklch(0.45 0.01 255);\n}\n.bm-outline-list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.bm-sidebar .bm-outline-row {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n min-height: 40px;\n padding: 0 10px;\n border-radius: 8px;\n background: transparent;\n text-align: left;\n font-size: 14px;\n}\n.bm-sidebar .bm-outline-row:hover:not(:disabled) {\n background: var(--fill-soft);\n}\n\n/* Section rows (Pages tab): grip, tone swatch, name over #anchor and variant, then actions. */\n.bm-srow {\n position: relative;\n display: flex;\n align-items: center;\n gap: 4px;\n min-height: 52px;\n padding: 6px 6px 6px 8px;\n border: 1px solid var(--bm-border);\n border-radius: 10px;\n background: #fff;\n}\n.bm-srow:hover {\n border-color: oklch(0.8 0.008 250);\n}\n.bm-srow-grip {\n flex: none;\n width: 16px;\n height: 32px;\n display: inline-grid;\n place-items: center;\n cursor: grab;\n touch-action: none;\n}\n.bm-srow-grip:active {\n cursor: grabbing;\n}\n.bm-srow .bm-outline-row {\n flex: 1;\n min-width: 0;\n width: auto;\n padding: 0 8px 0 4px;\n gap: 10px;\n}\n.bm-srow .bm-swatch {\n width: 10px;\n height: 10px;\n border-radius: 2px;\n}\n.bm-srow-text {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 1px;\n}\n.bm-srow-name {\n font-size: 14px;\n font-weight: 500;\n color: var(--bm-ink);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-srow-meta {\n font-family: var(--mono);\n font-size: 11px;\n font-weight: 400;\n color: var(--bm-muted);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-srow .bm-outline-meta {\n flex: none;\n max-width: 40%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-srow-btn {\n width: 32px;\n height: 32px;\n background: transparent;\n color: var(--bm-icon);\n border-radius: 7px;\n}\n.bm-srow-btn:disabled {\n opacity: 0.35;\n}\n.bm-sidebar .bm-srow-btn:hover:not(:disabled) {\n background: var(--bm-fill);\n color: var(--bm-ink);\n}\n.bm-sidebar .bm-srow-btn.bm-danger {\n color: var(--bm-danger-icon);\n}\n.bm-sidebar .bm-srow-btn.bm-danger:hover:not(:disabled) {\n background: var(--bm-danger-soft);\n}\n.bm-srow-sep {\n flex: none;\n width: 1px;\n height: 20px;\n margin: 0 2px;\n background: var(--bm-divider);\n}\n.bm-outline-n {\n width: 18px;\n font-family: var(--mono);\n font-size: 11px;\n color: oklch(0.55 0.01 255);\n}\n.bm-outline-label {\n flex: 1;\n min-width: 0;\n font-weight: 500;\n}\n.bm-outline-meta {\n font-family: var(--mono);\n font-size: 11px;\n font-weight: 400;\n color: var(--muted);\n}\n\n/* Navigation and footer link cards */\n.bm-nav-card {\n padding: 12px 12px 14px;\n gap: 10px;\n}\n.bm-nested {\n margin: 6px 0 0 16px;\n}\n.bm-nav-children {\n margin-top: -4px;\n}\n.bm-nav-head {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n.bm-nav-head .bm-icon-btn,\n.bm-column-head .bm-icon-btn {\n background: var(--bm-fill);\n color: oklch(0.45 0.01 255);\n}\n.bm-nav-head .bm-icon-btn:hover:not(:disabled),\n.bm-column-head .bm-icon-btn:hover:not(:disabled) {\n background: var(--bm-fill-hover);\n color: var(--bm-ink);\n}\n.bm-sidebar .bm-nav-head .bm-icon-btn.bm-danger,\n.bm-sidebar .bm-column-head .bm-icon-btn.bm-danger {\n background: var(--bm-danger-soft);\n color: var(--bm-danger-icon);\n}\n.bm-sidebar .bm-nav-label {\n flex: 1;\n min-width: 0;\n min-height: 32px;\n padding: 0 8px;\n border-color: transparent;\n font-weight: 500;\n}\n.bm-sidebar .bm-nav-label:hover {\n border-color: var(--line);\n}\n.bm-column {\n padding: 0;\n gap: 0;\n}\n.bm-column-head {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px;\n cursor: grab;\n}\n.bm-column-links > summary {\n display: flex;\n align-items: center;\n gap: 6px;\n min-height: 40px;\n padding: 0 12px;\n border-top: 1px solid var(--line-soft);\n font-size: 13px;\n font-weight: 500;\n color: var(--bm-label);\n cursor: pointer;\n list-style: none;\n}\n.bm-column-links > summary::-webkit-details-marker {\n display: none;\n}\n.bm-column-links > :not(summary) {\n margin: 10px 12px 0;\n}\n.bm-column-links > :last-child {\n margin-bottom: 12px;\n}\n\n/* Wordmark preview */\n.bm-card-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n.bm-card-head .bm-label {\n flex: none;\n}\n.bm-combo {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n:is(.bm-sidebar, .bm-popover) .bm-combo-trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 6px;\n width: auto;\n max-width: 140px;\n min-height: 38px;\n padding: 0 10px 0 12px;\n border: 0;\n border-left: 1px solid var(--bm-border);\n border-radius: 0;\n background: var(--fill-soft);\n color: var(--ink-2);\n font-family: var(--mono);\n font-size: 12px;\n font-weight: 400;\n text-align: left;\n}\n:is(.bm-sidebar, .bm-popover) .bm-combo-trigger:hover:not(:disabled) {\n background: var(--fill-hover);\n}\n:is(.bm-sidebar, .bm-popover) .bm-combo-trigger[aria-expanded=\"true\"] {\n background: var(--accent-tint);\n color: var(--accent-hover);\n}\n/* One URL field: the text input and the page picker share a single border. */\n.bm-linkfield {\n display: flex;\n align-items: stretch;\n min-height: 40px;\n border: 1px solid var(--bm-border);\n border-radius: 8px;\n background: #fff;\n overflow: hidden;\n}\n.bm-linkfield:focus-within {\n border-color: var(--bm-primary);\n box-shadow: 0 0 0 3px var(--bm-primary-ring);\n}\n.bm-linkfield > input {\n flex: 1;\n min-width: 0;\n border: 0 !important;\n box-shadow: none !important;\n border-radius: 0;\n min-height: 38px;\n background: transparent;\n}\n.bm-linkfield .bm-combo {\n flex: none;\n max-width: 45%;\n}\n.bm-linkfield .bm-combo-trigger {\n height: 100%;\n}\n.bm-linkfield .bm-combo-trigger:focus-visible {\n outline-offset: -2px;\n}\n.bm-combo-trigger > span {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-combo-trigger .bm-combo-custom {\n color: var(--muted);\n}\n.bm-combo-panel {\n position: fixed;\n z-index: 6;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 6px;\n border: 1px solid var(--bm-border);\n border-radius: 10px;\n background: #fff;\n box-shadow: 0 6px 18px oklch(0.2 0.02 255 / 0.1);\n}\n.bm-combo-list {\n max-height: 200px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n}\n.bm-combo-group {\n margin: 6px 6px 2px;\n font-family: var(--mono);\n font-size: 10px;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--muted);\n}\n.bm-combo-option {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 10px;\n padding: 6px 8px;\n border-radius: 6px;\n font-size: 13px;\n color: var(--ink);\n cursor: pointer;\n}\n.bm-combo-option[aria-selected=\"true\"] {\n font-weight: 600;\n color: var(--accent-hover);\n}\n.bm-combo-active {\n background: var(--fill);\n}\n.bm-combo-value {\n flex: none;\n max-width: 50%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: var(--mono);\n font-size: 11px;\n color: var(--muted);\n}\n.bm-combo-empty {\n margin: 0;\n padding: 8px;\n font-size: 12px;\n color: var(--muted);\n}\n.bm-format {\n margin-top: -4px;\n}\n.bm-sidebar .bm-format button[aria-pressed=\"true\"] {\n background: #fff;\n color: var(--ink);\n box-shadow: var(--bm-shadow-segment-on);\n}\n.bm-wordmark {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 12px;\n padding: 12px 14px;\n border-radius: 10px;\n background: var(--bm-fill-soft);\n}\n.bm-wordmark-name {\n font-size: 24px;\n font-weight: 600;\n letter-spacing: -0.03em;\n}\n.bm-alert-notice {\n flex-direction: row;\n align-items: flex-start;\n gap: 8px;\n color: var(--warn-ink);\n}\n.bm-alert-notice .bm-icon {\n margin-top: 2px;\n flex: none;\n}\n\n/*\n * Button system. Three variants, one danger modifier, one rule set.\n * secondary (default): neutral fill, ink text.\n * .bm-primary: the one main action of a surface. Solid accent, white text (AA).\n * .bm-tertiary: low emphasis text button (Cancel, Reset). No fill, no underline, grey fill on hover.\n * .bm-danger: modifier. primary = solid red, secondary = red text, tertiary = red text.\n * The scope list gives every rule enough specificity to beat the per-surface defaults above,\n * so the hover state of one variant can never fall back to the secondary fill.\n */\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button:where(.bm-primary, .bm-tertiary, .bm-danger):not(.bm-icon-btn, .bm-save) {\n transition:\n background-color 0.12s,\n color 0.12s;\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button.bm-primary {\n background: var(--bm-primary);\n color: #fff;\n box-shadow: none;\n font-weight: 600;\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button.bm-primary:hover:not(:disabled) {\n background: var(--bm-primary-hover);\n color: #fff;\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button.bm-primary.bm-danger {\n background: var(--bm-danger);\n color: #fff;\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button.bm-primary.bm-danger:hover:not(:disabled) {\n background: var(--danger-hover);\n color: #fff;\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button.bm-danger:not(.bm-primary, .bm-tertiary, .bm-icon-btn) {\n background: var(--bm-danger-soft);\n color: var(--bm-danger-text);\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button.bm-danger:not(.bm-primary, .bm-tertiary, .bm-icon-btn):hover:not(:disabled) {\n background: oklch(0.93 0.04 25);\n color: var(--bm-danger-text);\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button.bm-tertiary {\n min-height: 32px;\n padding: 0 10px;\n border-radius: 8px;\n background: transparent;\n color: var(--bm-label);\n font-weight: 400;\n box-shadow: none;\n text-decoration: none;\n}\n:is(.bm-dialog, .bm-popover, .bm-modal) button.bm-tertiary {\n min-height: 38px;\n font-size: 14px;\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button.bm-tertiary:hover:not(:disabled) {\n background: var(--bm-fill);\n color: var(--bm-ink);\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button.bm-tertiary.bm-danger {\n background: transparent;\n color: var(--bm-danger-text);\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button.bm-tertiary.bm-danger:hover:not(:disabled) {\n background: var(--bm-danger-soft);\n color: var(--bm-danger-text);\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button:disabled {\n opacity: 0.5;\n cursor: default;\n}\n.bm-save:hover:not(:disabled) {\n background: var(--accent-hover);\n color: #fff;\n}\n.bm-sidebar .bm-save:hover:not(:disabled) {\n background: var(--accent-hover);\n color: #fff;\n}\n.bm-popover .bm-actions-remove,\n.bm-dialog .bm-actions-remove {\n margin-right: auto;\n}\n.bm-modal :where(button:not([role=\"tab\"], .bm-library-pick, .bm-icon-btn, .bm-tertiary)) {\n min-height: 38px;\n padding: 0 16px;\n border: 0;\n border-radius: 8px;\n background: var(--fill);\n color: var(--ink);\n font-size: 14px;\n font-weight: 400;\n}\n.bm-modal :where(button:not([role=\"tab\"], .bm-library-pick, .bm-icon-btn, .bm-tertiary)):hover:not(:disabled) {\n background: var(--fill-hover);\n}\n.bm-modal .bm-dropzone {\n font-family: var(--sans);\n}\n\n/* Design tab */\n.bm-colorrow {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.bm-colorpick {\n flex: none;\n width: 40px !important;\n height: 40px;\n min-height: 40px !important;\n padding: 2px !important;\n border: 1px solid var(--bm-border);\n border-radius: 8px;\n background: #fff;\n cursor: pointer;\n}\n.bm-colorhex {\n flex: 1;\n min-width: 0;\n font-family: var(--mono);\n font-size: 13px;\n text-transform: lowercase;\n}\n.bm-colorhex[aria-invalid=\"true\"] {\n border-color: var(--bm-danger);\n}\n.bm-overrides {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--bm-border);\n border-radius: 10px;\n overflow: hidden;\n}\n.bm-override-row {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 10px 8px 12px;\n}\n.bm-override-row + .bm-override-row {\n border-top: 1px solid var(--bm-divider);\n}\n.bm-override-label {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: baseline;\n gap: 6px;\n font-size: 13px;\n font-weight: 500;\n color: var(--bm-label);\n}\n.bm-override-label .bm-meta-tag {\n font-size: 10px;\n}\n.bm-override-row .bm-colorpick {\n width: 32px !important;\n height: 32px;\n min-height: 32px !important;\n padding: 1px !important;\n border-radius: 7px;\n}\n.bm-override-row .bm-colorhex {\n flex: none;\n width: 104px;\n min-height: 36px;\n padding: 0 10px;\n}\n.bm-tone-matrix {\n display: grid;\n grid-template-columns: 60px minmax(0, 1fr) minmax(0, 1fr);\n gap: 8px;\n align-items: stretch;\n}\n.bm-tone-pick {\n border: 0;\n text-align: left;\n font: inherit;\n cursor: pointer;\n}\n.bm-tone-cell[aria-pressed=\"true\"] {\n outline: 2px solid var(--bm-primary);\n outline-offset: 0;\n}\n.bm-tone-head {\n font-family: var(--mono);\n font-size: 10px;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--muted);\n}\n.bm-tone-name {\n align-self: center;\n font-size: 12px;\n font-weight: 500;\n color: var(--ink-2);\n}\n.bm-tone-cell {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 6px;\n min-width: 0;\n min-height: 46px;\n padding: 0 10px;\n border-radius: 8px;\n overflow: hidden;\n white-space: nowrap;\n}\n.bm-tone-aa {\n flex: none;\n font-size: 16px;\n font-weight: 600;\n}\n.bm-tone-muted {\n flex: 0 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: 11px;\n}\n.bm-tone-chip {\n flex: none;\n padding: 2px 6px;\n border-radius: 5px;\n font-size: 10px;\n font-weight: 600;\n}\n.bm-tone-link {\n flex: none;\n font-size: 11px;\n text-decoration: underline;\n text-underline-offset: 2px;\n}\n.bm-contrast-list {\n margin: 6px 0 0;\n padding-left: 16px;\n font-family: var(--mono);\n font-size: 11px;\n color: var(--warn-ink);\n}\n.bm-range {\n flex: 1;\n min-width: 0;\n min-height: 28px;\n accent-color: var(--accent);\n cursor: pointer;\n}\n/* Preview: the only editor UI left on screen. */\n.bm-exit-preview {\n position: fixed;\n z-index: 7;\n right: 16px;\n bottom: 16px;\n display: inline-flex;\n align-items: center;\n gap: 8px;\n height: 40px;\n padding: 0 16px;\n border: 0;\n border-radius: 999px;\n background: var(--ink);\n color: #fff;\n font-size: 13px;\n font-weight: 500;\n box-shadow: 0 10px 30px oklch(0.2 0.02 255 / 0.3);\n}\n.bm-exit-preview:hover {\n background: oklch(0.28 0.012 255);\n}\n.bm-exit-preview:focus-visible {\n outline-color: oklch(0.82 0.14 145);\n outline-offset: 3px;\n}\n.bm-srow-add {\n display: flex;\n justify-content: flex-end;\n padding-top: 6px;\n}\n.bm-sidebar .bm-add-section {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n min-height: 32px;\n padding: 0 12px;\n border: 0;\n border-radius: 8px;\n background: var(--bm-primary);\n color: #fff;\n font-size: 13px;\n font-weight: 500;\n}\n.bm-sidebar .bm-add-section:hover {\n background: var(--bm-primary-hover);\n}\n";
|
|
20144
|
+
var editor_default = "/*\n * Editor styles. They live in a shadow root, so they never meet the site's Tailwind layer.\n * The editor is always light. Tokens and values follow the v4 editor design handoff.\n * The short names (--ink, --line, --accent ...) alias the --bm-* tokens so older rules read the same.\n */\n:host {\n all: initial;\n\n /* ---------- v4 tokens (editor-tokens.css) ---------- */\n --bm-font-ui: \"Bricolage Grotesque Variable\", \"Bricolage Grotesque\", ui-sans-serif, system-ui, sans-serif;\n --bm-font-mono: \"JetBrains Mono\", ui-monospace, SFMono-Regular, Menlo, monospace;\n --bm-font-serif: \"Instrument Serif\", Georgia, serif;\n\n --bm-ink: oklch(0.21 0.012 255);\n --bm-label: oklch(0.3 0.01 255);\n --bm-section: oklch(0.42 0.01 255);\n --bm-help: oklch(0.47 0.01 255);\n --bm-muted: oklch(0.5 0.01 255);\n --bm-icon: oklch(0.55 0.01 255);\n --bm-info: oklch(0.6 0.01 255);\n\n --bm-fill: oklch(0.955 0.003 250);\n --bm-fill-hover: oklch(0.93 0.004 250);\n --bm-fill-soft: oklch(0.965 0.003 250);\n --bm-fill-nested: oklch(0.97 0.003 250);\n --bm-border: oklch(0.88 0.005 250);\n --bm-divider: oklch(0.93 0.004 250);\n --bm-tab-rule: oklch(0.92 0.004 250);\n\n --bm-primary: oklch(0.46 0.13 145);\n --bm-primary-hover: oklch(0.4 0.12 145);\n --bm-primary-ring: oklch(0.46 0.13 145 / 0.15);\n --bm-link: oklch(0.4 0.12 145);\n\n --bm-saved-bg: oklch(0.94 0.045 145);\n --bm-saved-fg: oklch(0.36 0.1 145);\n --bm-unsaved-bg: oklch(0.95 0.05 80);\n --bm-unsaved-fg: oklch(0.48 0.1 60);\n --bm-unsaved-dot: oklch(0.75 0.15 70);\n --bm-badge-green-bg: oklch(0.93 0.06 145);\n --bm-badge-green-fg: oklch(0.38 0.1 145);\n --bm-badge-amber-bg: oklch(0.95 0.04 75);\n --bm-badge-amber-fg: oklch(0.45 0.09 60);\n\n --bm-danger: oklch(0.55 0.19 25);\n --bm-danger-text: oklch(0.52 0.17 25);\n --bm-danger-icon: oklch(0.58 0.18 25);\n --bm-danger-soft: oklch(0.96 0.025 20);\n\n --bm-diff-del-bg: oklch(0.97 0.02 20);\n --bm-diff-del-fg: oklch(0.45 0.13 25);\n --bm-diff-add-bg: oklch(0.96 0.04 145);\n --bm-diff-add-fg: oklch(0.34 0.1 145);\n\n --bm-switch-off: oklch(0.82 0.005 250);\n\n --bm-shadow-pop: 0 0 0 1px oklch(0.9 0.005 250), 0 16px 36px -16px oklch(0.2 0.02 255 / 0.35);\n --bm-shadow-dialog: 0 0 0 1px oklch(0.9 0.005 250), 0 24px 48px -24px oklch(0.2 0.02 255 / 0.35);\n --bm-shadow-segment-on: 0 1px 2px oklch(0 0 0 / 0.08), 0 0 0 1px oklch(0.9 0.005 250);\n\n /* ---------- aliases used by the rules below ---------- */\n --ink: var(--bm-ink);\n --ink-2: var(--bm-label);\n --muted: var(--bm-muted);\n --line: var(--bm-border);\n --line-soft: var(--bm-divider);\n --fill: var(--bm-fill);\n --fill-hover: var(--bm-fill-hover);\n --fill-soft: var(--bm-fill-soft);\n --accent: var(--bm-primary);\n --accent-hover: var(--bm-primary-hover);\n --accent-tint: oklch(0.96 0.03 145);\n --accent-ring: var(--bm-primary-ring);\n --warn-tint: var(--bm-unsaved-bg);\n --warn-ink: var(--bm-unsaved-fg);\n --warn-dot: var(--bm-unsaved-dot);\n --danger-hover: oklch(0.48 0.18 25);\n --danger-tint: var(--bm-danger-soft);\n --danger-ink: var(--bm-danger-text);\n --info-dot: oklch(0.6 0.15 255);\n --sans: var(--bm-font-ui);\n --mono: var(--bm-font-mono);\n --serif: var(--bm-font-serif);\n --shadow-pop: var(--bm-shadow-pop);\n}\n* {\n box-sizing: border-box;\n}\nbutton,\ninput,\nselect,\ntextarea {\n font: inherit;\n color: inherit;\n}\nbutton {\n cursor: pointer;\n}\n:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 2px;\n}\n\n/* On-page outlines */\n.bm-outline {\n position: fixed;\n pointer-events: none;\n border-radius: 6px;\n z-index: 1;\n}\n.bm-outline-section {\n border: 2px solid var(--accent);\n}\n.bm-outline-field {\n border: 2px solid var(--accent);\n background: oklch(0.46 0.13 145 / 0.08);\n}\n.bm-image-bar {\n flex-wrap: nowrap;\n left: auto;\n white-space: nowrap;\n}\n.bm-image-bar-group {\n display: flex;\n gap: 2px;\n}\n.bm-image-bar button[aria-pressed=\"true\"] {\n background: var(--bm-saved-bg);\n color: var(--bm-saved-fg);\n}\n.bm-image-bar-grid {\n display: grid;\n grid-template-columns: repeat(3, 9px);\n grid-template-rows: repeat(3, 9px);\n gap: 2px;\n align-self: center;\n padding: 0 4px;\n}\n.bm-image-bar .bm-pos-cell {\n width: 9px;\n height: 9px;\n /* `.bm-rich button` sets a 32px minimum for the bar's text buttons; the cells stay 9px. */\n min-width: 0;\n min-height: 0;\n padding: 0;\n border-radius: 2px;\n background: var(--line);\n}\n.bm-image-bar .bm-pos-cell:hover:not(:disabled) {\n background: var(--muted);\n}\n.bm-image-bar .bm-pos-cell[aria-pressed=\"true\"] {\n background: var(--accent);\n}\n.bm-image-bar .bm-pos-cell:disabled {\n opacity: 0.3;\n cursor: default;\n}\n.bm-outline-image {\n background: oklch(0.46 0.13 145 / 0.12);\n}\n.bm-outline-image::after {\n content: \"Change image\";\n position: absolute;\n top: 8px;\n left: 8px;\n padding: 4px 9px;\n border-radius: 6px;\n background: var(--accent);\n color: #fff;\n font: 500 11px / 1.3 var(--sans, system-ui, sans-serif);\n}\n\n.bm-toolbar,\n.bm-dock,\n.bm-exit-preview,\n.bm-sidebar,\n.bm-popover,\n.bm-dialog,\n.bm-rich,\n.bm-banner,\n.bm-scrim {\n pointer-events: auto;\n font-family: var(--sans);\n font-size: 13px;\n color: var(--ink);\n -webkit-font-smoothing: antialiased;\n}\n\n/*\n * Insert between sections. Three states:\n * idle / hover a solid line and a 28px circle; hover changes nothing.\n * open the picker is open: the line stays and a \"between A and B\" chip replaces the circle.\n */\n.bm-gap {\n position: fixed;\n z-index: 2;\n height: 0;\n pointer-events: none;\n}\n.bm-gap::before {\n content: \"\";\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n height: 0;\n border-top: 2px solid var(--bm-primary);\n transform: translateY(-50%);\n}\n.bm-gap-add {\n position: absolute;\n left: 50%;\n top: 0;\n transform: translate(-50%, -50%);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n width: 28px;\n height: 28px;\n padding: 0;\n border: 0;\n border-radius: 999px;\n pointer-events: auto;\n cursor: pointer;\n white-space: nowrap;\n background: var(--bm-primary);\n color: #fff;\n box-shadow: 0 0 0 2px #fff;\n}\n.bm-gap-add .bm-icon {\n width: 14px;\n height: 14px;\n stroke-width: 2.4;\n}\n.bm-gap-add:focus-visible {\n outline: 2px solid var(--ink);\n outline-offset: 3px;\n}\n.bm-gap-chip {\n position: absolute;\n left: 50%;\n top: 0;\n transform: translate(-50%, -50%);\n padding: 4px 10px;\n border-radius: 999px;\n background: #fff;\n color: var(--bm-ink);\n box-shadow: 0 0 0 1.5px var(--bm-primary);\n white-space: nowrap;\n pointer-events: none;\n font-size: 12px;\n font-weight: 500;\n}\n\n/* Light floating bars: section toolbar, rich text bar, heading format bar */\n.bm-toolbar,\n.bm-rich {\n position: fixed;\n z-index: 3;\n display: flex;\n gap: 2px;\n padding: 4px;\n border-radius: 12px;\n background: #fff;\n color: var(--ink);\n box-shadow: var(--bm-shadow-pop);\n}\n.bm-toolbar button,\n.bm-rich button {\n border: 0;\n border-radius: 7px;\n background: transparent;\n color: inherit;\n min-height: 32px;\n padding: 0 10px;\n font-size: 12px;\n font-weight: 500;\n}\n.bm-toolbar button:hover:not(:disabled),\n.bm-rich button:hover {\n background: var(--fill);\n}\n.bm-rich button[aria-pressed=\"true\"] {\n background: var(--bm-saved-bg);\n box-shadow: inset 0 0 0 1px oklch(0.46 0.13 145 / 0.4);\n}\n.bm-toolbar :focus-visible,\n.bm-rich :focus-visible {\n outline-color: var(--accent);\n}\n.bm-toolbar {\n align-items: center;\n max-width: calc(100vw - 16px);\n padding: 5px;\n}\n.bm-toolbar button {\n width: 34px;\n min-height: 34px;\n height: 34px;\n padding: 0;\n border-radius: 8px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: oklch(0.4 0.01 255);\n}\n.bm-toolbar button:hover:not(:disabled) {\n background: var(--bm-fill);\n color: var(--bm-ink);\n}\n.bm-toolbar button:disabled {\n opacity: 0.35;\n cursor: default;\n}\n.bm-toolbar .bm-danger {\n color: var(--bm-danger-icon);\n}\n.bm-toolbar .bm-danger:hover:not(:disabled) {\n background: var(--bm-danger-soft);\n color: var(--bm-danger-icon);\n}\n.bm-toolbar-active {\n box-shadow:\n 0 0 0 2px var(--bm-primary),\n 0 10px 24px -12px oklch(0.2 0.02 255 / 0.35);\n}\n.bm-toolbar-name {\n display: block;\n max-width: 16rem;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n padding: 0 12px 0 10px;\n}\n.bm-toolbar-title {\n font-size: 14px;\n font-weight: 600;\n color: var(--bm-ink);\n}\n.bm-toolbar-anchor {\n font-family: var(--mono);\n font-size: 12px;\n font-weight: 400;\n color: var(--bm-muted);\n}\n.bm-sep {\n width: 1px;\n align-self: stretch;\n margin: 4px 2px;\n background: var(--line);\n}\n.bm-toolbar .bm-sep {\n align-self: center;\n height: 20px;\n margin: 0 4px;\n background: oklch(0.9 0.005 250);\n}\n.bm-icon {\n width: 16px;\n height: 16px;\n display: block;\n}\n.bm-rich {\n align-items: center;\n flex-wrap: wrap;\n max-width: calc(100vw - 16px);\n}\n.bm-rich button {\n min-height: 32px;\n min-width: 32px;\n}\n.bm-rich input {\n width: 168px;\n min-height: 30px;\n padding: 0 8px;\n border: 0;\n border-radius: 7px;\n background: var(--fill);\n color: var(--ink);\n font-size: 12px;\n}\n.bm-rich input:focus {\n outline: none;\n box-shadow: 0 0 0 2px var(--accent);\n}\n\n/* Heading format bar: each button shows \"Aa\" in its own weight or font over a mono caption. */\n.bm-rich.bm-format-bar {\n flex-wrap: nowrap;\n}\n.bm-rich.bm-format-bar button.bm-fmt {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 2px;\n min-width: 42px;\n height: 48px;\n min-height: 48px;\n padding: 0 8px;\n border-radius: 8px;\n}\n.bm-fmt-sample {\n font-size: 15px;\n line-height: 1;\n color: var(--bm-ink);\n}\n.bm-fmt-caption {\n font-family: var(--mono);\n font-size: 9px;\n letter-spacing: 0.02em;\n color: var(--bm-muted);\n}\n.bm-rich.bm-format-bar button.bm-fmt[aria-pressed=\"true\"] {\n background: var(--bm-saved-bg);\n box-shadow: inset 0 0 0 1px oklch(0.46 0.13 145 / 0.4);\n}\n.bm-format-bar .bm-sep {\n align-self: stretch;\n margin: 6px 4px;\n}\n\n/* Light popovers: add menu, section picker, edit link */\n.bm-popover {\n position: fixed;\n z-index: 3;\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 180px;\n max-height: min(420px, calc(100vh - 16px));\n overflow: auto;\n padding: 6px;\n border-radius: 12px;\n background: #fff;\n box-shadow: var(--bm-shadow-pop);\n}\n.bm-popover > button {\n display: flex;\n align-items: center;\n gap: 8px;\n min-height: 34px;\n padding: 0 10px;\n border: 0;\n border-radius: 7px;\n background: transparent;\n text-align: left;\n font-size: 13px;\n font-weight: 500;\n}\n.bm-popover > button:hover {\n background: var(--fill);\n}\n\n/* Add section picker: wireframe cards */\n.bm-popover[aria-label=\"Add section\"][role=\"listbox\"] {\n gap: 14px;\n width: 640px;\n max-width: calc(100vw - 16px);\n max-height: calc(100vh - 16px);\n padding: 16px;\n border-radius: 14px;\n overflow: visible;\n box-shadow: var(--bm-shadow-dialog);\n}\n.bm-picker-head {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.bm-picker-title {\n font-size: 17px;\n font-weight: 600;\n letter-spacing: -0.01em;\n}\n.bm-picker-where {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 13px;\n color: var(--bm-muted);\n}\n.bm-picker-grid {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 10px;\n max-height: 26rem;\n overflow-y: auto;\n padding: 2px 6px 2px 2px;\n margin-right: -6px;\n scrollbar-gutter: stable;\n}\n.bm-popover .bm-picker-grid > button {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n justify-content: flex-start;\n gap: 6px;\n min-height: 0;\n padding: 8px 8px 10px;\n border: 1px solid oklch(0.9 0.005 250);\n border-radius: 10px;\n background: #fff;\n text-align: left;\n font-weight: 400;\n}\n.bm-popover .bm-picker-grid > button:hover {\n background: #fff;\n border-color: var(--bm-primary);\n box-shadow: 0 0 0 3px oklch(0.46 0.13 145 / 0.12);\n}\n.bm-wire {\n position: relative;\n display: block;\n width: 100%;\n height: 76px;\n border-radius: 7px;\n background: oklch(0.97 0 0);\n overflow: hidden;\n}\n.bm-wire > span {\n position: absolute;\n display: block;\n}\n.bm-type-text {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 0 2px;\n}\n.bm-type-name {\n font-size: 14px;\n font-weight: 600;\n line-height: 1.25;\n color: var(--bm-ink);\n}\n.bm-type-variants {\n font-family: var(--mono);\n font-size: 10px;\n font-weight: 400;\n line-height: 1.35;\n white-space: nowrap;\n color: var(--muted);\n}\n.bm-type-desc {\n padding: 0 2px;\n font-size: 12px;\n line-height: 1.4;\n color: var(--bm-help);\n}\n.bm-picker-note {\n margin: 0;\n font-size: 12px;\n color: var(--bm-muted);\n}\n\n.bm-popover[aria-label=\"Edit link\"] {\n gap: 14px;\n padding: 16px;\n max-height: calc(100vh - 16px);\n}\n\n/* Dialogs and banners */\n.bm-dialog {\n position: fixed;\n z-index: 6;\n display: flex;\n flex-direction: column;\n gap: 16px;\n width: min(400px, calc(100vw - 32px));\n padding: 20px;\n border-radius: 14px;\n background: #fff;\n box-shadow: var(--bm-shadow-dialog);\n}\n.bm-dialog-head {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 12px;\n}\n.bm-dialog-heading {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.bm-dialog-sub {\n margin: 0;\n font-size: 13px;\n line-height: 1.4;\n color: var(--bm-help);\n}\n.bm-dialog .bm-dialog-x,\n.bm-popover .bm-dialog-x,\n.bm-modal .bm-dialog-x {\n flex: none;\n width: 32px;\n height: 32px;\n margin: -6px -8px -6px 0;\n background: transparent;\n color: var(--bm-icon);\n}\n.bm-dialog-title {\n margin: 0;\n font-size: 17px;\n font-weight: 600;\n letter-spacing: -0.01em;\n color: var(--bm-ink);\n}\n.bm-popover .bm-dialog-title {\n font-size: 16px;\n}\n.bm-dialog .bm-note {\n color: var(--bm-label);\n font-size: 14px;\n line-height: 1.5;\n}\n.bm-dialog button,\n.bm-popover .bm-actions button,\n.bm-banner button {\n min-height: 38px;\n padding: 0 16px;\n border: 0;\n border-radius: 8px;\n background: var(--fill);\n font-size: 14px;\n font-weight: 400;\n}\n.bm-dialog button:hover:not(:disabled),\n.bm-popover .bm-actions button:hover:not(:disabled) {\n background: var(--fill-hover);\n}\n/* The primary action comes first in the DOM (focus lands on it); row-reverse shows it last, on the right. */\n.bm-actions {\n display: flex;\n flex-direction: row-reverse;\n justify-content: flex-start;\n align-items: center;\n gap: 8px;\n}\n.bm-dialog > .bm-actions,\n.bm-popover[aria-label=\"Edit link\"] > .bm-actions {\n padding-top: 12px;\n border-top: 1px solid var(--bm-divider);\n}\n.bm-dialog-foot {\n display: flex;\n align-items: center;\n gap: 8px;\n padding-top: 12px;\n border-top: 1px solid var(--bm-divider);\n}\n.bm-dialog-foot > .bm-hint {\n flex: 1;\n min-width: 0;\n margin: 0;\n}\n.bm-dialog-foot .bm-actions {\n flex: none;\n}\n\n/* Alert dialogs: [icon tile] [title / body / note] [x] */\n.bm-alert-main {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n}\n.bm-alert-copy {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.bm-alert-body {\n margin: 0;\n font-size: 14px;\n line-height: 1.5;\n color: var(--bm-label);\n overflow-wrap: anywhere;\n}\n.bm-alert-body strong {\n font-weight: 600;\n color: var(--bm-ink);\n}\n.bm-alert-note {\n margin: 0;\n font-size: 12px;\n line-height: 1.45;\n color: var(--bm-muted);\n}\n.bm-tile {\n flex: none;\n display: grid;\n place-items: center;\n width: 36px;\n height: 36px;\n border-radius: 9px;\n}\n.bm-tile .bm-icon {\n width: 18px;\n height: 18px;\n}\n.bm-tile-warn {\n background: var(--bm-unsaved-bg);\n color: var(--bm-unsaved-fg);\n}\n.bm-tile-danger {\n background: var(--bm-danger-soft);\n color: var(--bm-danger-icon);\n}\n.bm-tile-neutral {\n background: var(--bm-fill);\n color: var(--bm-label);\n}\n\n.bm-banner {\n position: fixed;\n z-index: 6;\n display: flex;\n align-items: center;\n gap: 12px;\n width: min(440px, calc(100vw - 32px));\n padding: 12px 14px;\n border-radius: 12px;\n box-shadow: 0 14px 34px -10px oklch(0.2 0.02 255 / 0.4);\n}\n.bm-banner button {\n min-height: 34px;\n padding: 0 14px;\n font-size: 13px;\n font-weight: 500;\n}\n.bm-banner-ink {\n background: var(--ink);\n color: #fff;\n}\n.bm-banner-light {\n background: #fff;\n box-shadow: var(--shadow-pop);\n}\n.bm-banner-text {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n.bm-banner-title {\n margin: 0;\n font-size: 13px;\n font-weight: 500;\n line-height: 1.35;\n}\n.bm-banner-sub {\n margin: 0;\n font-size: 12px;\n line-height: 1.4;\n opacity: 0.7;\n}\n.bm-banner .bm-actions {\n flex: none;\n gap: 6px;\n}\n.bm-banner-ink button {\n min-height: 30px;\n padding: 0 12px;\n font-size: 12px;\n background: oklch(1 0 0 / 0.12);\n color: #fff;\n}\n.bm-banner-ink button:hover {\n background: oklch(1 0 0 / 0.2);\n}\n.bm-banner-ink .bm-primary {\n background: #fff;\n color: var(--ink);\n}\n.bm-banner-ink .bm-primary:hover {\n background: oklch(0.94 0.003 250);\n}\n.bm-banner-ink :focus-visible {\n outline-color: oklch(0.82 0.14 145);\n}\n.bm-dot-stale {\n background: oklch(0.7 0.18 25) !important;\n}\n.bm-dot-info {\n background: var(--info-dot) !important;\n}\n\n/* Closed editor. The dock sits where the sidebar header's controls sit: header padding (top 14, right 14) minus the dock's 7px padding. */\n.bm-dock {\n position: fixed;\n z-index: 5;\n top: 7px;\n right: 7px;\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 7px 7px 7px 10px;\n border-radius: 14px;\n background: #fff;\n box-shadow: var(--bm-shadow-pop);\n}\n.bm-dot {\n flex: none;\n width: 8px;\n height: 8px;\n border-radius: 99px;\n background: oklch(0.72 0.17 145);\n}\n.bm-dot-dirty {\n background: var(--warn-dot);\n}\n\n/* Sidebar shell */\n.bm-sidebar {\n position: fixed;\n z-index: 4;\n top: 0;\n right: 0;\n bottom: 0;\n width: min(440px, 100vw);\n display: flex;\n flex-direction: column;\n background: #fff;\n box-shadow:\n 0 0 0 1px var(--line),\n -24px 0 48px -24px oklch(0.2 0.02 255 / 0.25);\n}\n.bm-resize {\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n width: 6px;\n cursor: col-resize;\n touch-action: none;\n z-index: 2;\n}\n.bm-resize:hover,\n.bm-resize:focus-visible {\n background: oklch(0.46 0.13 145 / 0.25);\n}\n.bm-resize:focus-visible {\n outline-offset: -2px;\n}\n\n.bm-head {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 14px 14px 14px 16px;\n border-bottom: 1px solid var(--line-soft);\n}\n.bm-mark {\n flex: none;\n display: block;\n width: 28px;\n height: auto;\n aspect-ratio: 21 / 19;\n image-rendering: pixelated;\n user-select: none;\n}\n/* Bear, name and slogan. Takes the space the controls leave; the text fades out when it no longer fits. */\n.bm-brand {\n position: relative;\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.bm-head-text {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n transition: opacity 160ms ease;\n}\n.bm-brand-compact .bm-head-text {\n opacity: 0;\n}\n/* The same two lines, in a card left of the sidebar (over the page), level with the header, while the pointer is on\n the brand. 16px is the header's left padding: the card clears the sidebar edge by 10px. */\n.bm-brand-tip {\n position: absolute;\n z-index: 5;\n top: 50%;\n right: calc(100% + 16px + 10px);\n display: flex;\n flex-direction: column;\n padding: 8px 12px;\n border-radius: 10px;\n background: #fff;\n box-shadow: var(--bm-shadow-pop);\n white-space: nowrap;\n opacity: 0;\n transform: translate(4px, -50%);\n pointer-events: none;\n transition:\n opacity 120ms ease,\n transform 120ms ease;\n}\n.bm-brand-compact:hover .bm-brand-tip {\n opacity: 1;\n transform: translate(0, -50%);\n transition-delay: 120ms;\n}\n@media (prefers-reduced-motion: reduce) {\n .bm-head-text,\n .bm-brand-tip {\n transition: none;\n }\n}\n/* A ring with one open quarter that turns while a save is written and the page reloads. */\n.bm-spin {\n flex: none;\n width: 14px;\n height: 14px;\n border: 2px solid currentColor;\n border-right-color: transparent;\n border-radius: 50%;\n animation: bm-spin 0.8s linear infinite;\n}\n@keyframes bm-spin {\n to {\n transform: rotate(360deg);\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .bm-spin {\n animation: none;\n }\n}\n.bm-faces {\n display: grid;\n}\n.bm-face {\n grid-area: 1 / 1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n white-space: nowrap;\n}\n.bm-face[hidden-face] {\n visibility: hidden;\n}\n.bm-save:disabled {\n opacity: 0.85;\n cursor: progress;\n}\n.bm-head-title {\n font-size: 15px;\n font-weight: 600;\n letter-spacing: -0.015em;\n /* The controls beside it never shrink, so a narrow sidebar cuts the name, not the buttons. */\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-head-sub {\n font-family: var(--mono);\n font-size: 11px;\n color: var(--muted);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-pill {\n flex: none;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 24px;\n padding: 0 10px;\n border-radius: 999px;\n background: var(--bm-saved-bg);\n color: var(--bm-saved-fg);\n font-size: 12px;\n font-weight: 500;\n}\n.bm-pill .bm-dot {\n width: 6px;\n height: 6px;\n background: var(--accent);\n}\n.bm-pill-dirty {\n background: var(--warn-tint);\n color: var(--warn-ink);\n}\n.bm-pill-dirty .bm-dot {\n background: var(--warn-dot);\n}\nbutton.bm-pill-btn {\n min-height: 0;\n padding: 0 10px;\n border: 0;\n border-radius: 999px;\n background: var(--warn-tint);\n color: var(--warn-ink);\n font-family: inherit;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n}\n:is(.bm-sidebar, .bm-dock) button.bm-pill-btn:hover:not(:disabled) {\n background: oklch(0.93 0.06 75);\n color: var(--warn-ink);\n}\nbutton.bm-pill-btn:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 2px;\n}\n.bm-dialog-wide {\n width: min(400px, calc(100vw - 32px));\n}\n.bm-dialog .bm-actions {\n flex-wrap: nowrap;\n}\n.bm-dialog .bm-actions button {\n flex: none;\n white-space: nowrap;\n}\n.bm-changes {\n width: min(540px, calc(100vw - 32px));\n}\n.bm-commit-files {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n max-height: min(34vh, 280px);\n overflow-y: auto;\n}\n.bm-commit-files label {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n cursor: pointer;\n}\n.bm-commit-mark {\n font:\n 600 11px / 1 ui-monospace,\n monospace;\n width: 18px;\n text-align: center;\n opacity: 0.7;\n}\n.bm-commit-msg {\n width: 100%;\n box-sizing: border-box;\n resize: vertical;\n font: inherit;\n font-size: 14px;\n padding: 8px 12px;\n border: 1px solid var(--line);\n border-radius: 8px;\n background: transparent;\n color: inherit;\n}\n\n/* Unsaved changes review and version history: groups, rows, diff lines */\n.bm-changes-list {\n display: flex;\n flex-direction: column;\n gap: 16px;\n max-height: 22rem;\n overflow-y: auto;\n margin: 0 -4px;\n padding: 0 4px;\n}\n.bm-changes-group {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.bm-changes-area {\n margin: 0;\n display: flex;\n align-items: baseline;\n gap: 8px;\n font-family: var(--mono);\n font-size: 11px;\n font-weight: 500;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--bm-section);\n}\n.bm-changes-file {\n font-weight: 400;\n letter-spacing: 0;\n text-transform: none;\n color: oklch(0.55 0.01 255);\n}\n.bm-changes-count {\n margin-left: auto;\n font-weight: 400;\n letter-spacing: 0;\n color: var(--bm-muted);\n}\n.bm-changes ul {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n border: 1px solid var(--bm-border);\n border-radius: 10px;\n overflow: hidden;\n}\n.bm-change {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 10px;\n font-size: 14px;\n line-height: 1.4;\n}\n.bm-change + .bm-change {\n border-top: 1px solid var(--bm-divider);\n}\n.bm-change-mark {\n flex: none;\n display: grid;\n place-items: center;\n width: 22px;\n height: 22px;\n border-radius: 6px;\n font-family: var(--mono);\n font-size: 13px;\n font-weight: 600;\n background: var(--bm-unsaved-bg);\n color: var(--bm-unsaved-fg);\n}\n.bm-change-added .bm-change-mark {\n background: var(--bm-badge-green-bg);\n color: var(--bm-badge-green-fg);\n}\n.bm-change-removed .bm-change-mark {\n background: var(--bm-danger-soft);\n color: var(--bm-danger-text);\n}\n.bm-change-body {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.bm-change-field {\n font-weight: 500;\n color: var(--bm-ink);\n overflow-wrap: anywhere;\n}\n.bm-change-values {\n display: flex;\n flex-direction: column;\n gap: 3px;\n min-width: 0;\n}\n.bm-change-values del,\n.bm-change-values ins {\n display: flex;\n gap: 6px;\n min-width: 0;\n padding: 3px 7px;\n border-radius: 5px;\n text-decoration: none;\n font-size: 13px;\n}\n.bm-change-values del {\n background: var(--bm-diff-del-bg);\n color: var(--bm-diff-del-fg);\n}\n.bm-change-values ins {\n background: var(--bm-diff-add-bg);\n color: var(--bm-diff-add-fg);\n}\n.bm-change-values del::before {\n content: \"\\2212\";\n flex: none;\n font-family: var(--mono);\n}\n.bm-change-values ins::before {\n content: \"+\";\n flex: none;\n font-family: var(--mono);\n}\n.bm-change-values del > span,\n.bm-change-values ins > span {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-change-values del > span {\n text-decoration: line-through;\n text-decoration-color: oklch(0.45 0.13 25 / 0.5);\n}\n.bm-changes-empty {\n margin: 0;\n padding: 24px 0;\n text-align: center;\n font-size: 14px;\n color: var(--bm-muted);\n}\n\n.bm-changes ul.bm-versions {\n gap: 0;\n}\n.bm-version {\n padding: 0;\n}\n.bm-version + .bm-version {\n border-top: 1px solid var(--bm-divider);\n}\n.bm-version-when strong {\n font-weight: 600;\n}\n.bm-tipped {\n position: relative;\n}\n.bm-tipped::after {\n content: attr(data-tip);\n position: absolute;\n right: calc(100% + 6px);\n top: 50%;\n transform: translateY(-50%);\n padding: 4px 8px;\n border-radius: 6px;\n background: var(--ink);\n color: #fff;\n font-size: 12px;\n font-weight: 500;\n line-height: 1.3;\n white-space: nowrap;\n pointer-events: none;\n visibility: hidden;\n z-index: 5;\n}\n.bm-version-row > .bm-tipped:first-child::after {\n right: auto;\n left: calc(100% + 6px);\n}\n.bm-tipped:hover::after,\n.bm-tipped:focus-visible::after {\n visibility: visible;\n}\n.bm-version-row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 8px 8px 10px;\n}\n.bm-version-when {\n flex: 1;\n min-width: 0;\n margin-right: auto;\n padding: 0 !important;\n min-height: 32px !important;\n font-weight: 500;\n display: flex !important;\n flex-direction: column;\n align-items: flex-start !important;\n justify-content: center;\n gap: 2px;\n text-align: left;\n}\n.bm-version-line {\n display: flex;\n align-items: baseline;\n gap: 8px;\n}\n.bm-version-sum {\n max-width: 100%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n font-weight: 400;\n color: var(--bm-help);\n}\n.bm-version-row .bm-restore {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n flex: none;\n min-height: 32px;\n padding: 0 10px;\n border-radius: 7px;\n font-size: 13px;\n font-weight: 500;\n}\n.bm-version-rel {\n font-size: 14px;\n font-weight: 500;\n color: var(--bm-ink);\n}\n.bm-version-abs {\n font-family: var(--mono);\n font-size: 11px;\n color: var(--bm-muted);\n}\n.bm-version-diff {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 4px 10px 12px 10px;\n border-top: 1px solid var(--bm-divider);\n margin-top: 0;\n}\n.bm-version-diff ul {\n margin: 0;\n}\n.bm-day {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.bm-restored {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 8px 8px 12px;\n border-radius: 9px;\n background: var(--bm-diff-add-bg);\n color: var(--bm-saved-fg);\n font-size: 13px;\n}\n.bm-restored > span {\n flex: 1;\n min-width: 0;\n}\n\n.bm-tabs {\n display: flex;\n gap: 0;\n padding: 0 12px;\n overflow-x: auto;\n border-bottom: 1px solid var(--bm-tab-rule);\n}\n.bm-tabs button {\n flex: 1 1 auto;\n height: 46px;\n padding: 0 6px;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: oklch(0.48 0.01 255);\n font-size: 13px;\n font-weight: 400;\n}\n.bm-tabs button:hover {\n color: var(--ink);\n background: var(--fill-soft);\n}\n.bm-tabs button[aria-selected=\"true\"] {\n color: var(--ink);\n background: transparent;\n font-weight: 600;\n box-shadow: inset 0 -2px 0 var(--accent);\n}\n.bm-tabs button:focus-visible {\n outline-offset: -2px;\n}\n\n.bm-body {\n flex: 1;\n min-height: 0;\n overflow: auto;\n padding: 24px 24px 32px;\n display: flex;\n flex-direction: column;\n gap: 18px;\n}\n.bm-foot {\n display: flex;\n justify-content: space-between;\n gap: 12px;\n padding: 12px 16px;\n border-top: 1px solid var(--line-soft);\n font-family: var(--mono);\n font-size: 10px;\n color: var(--muted);\n}\n.bm-foot span:first-child {\n flex: none;\n white-space: nowrap;\n}\n.bm-foot span:last-child {\n text-align: right;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n min-width: 0;\n}\n\n/* Group header: mono 11/500, then a 1px rule. With a right-hand item the rule sits on the flex wrapper. */\n.bm-group {\n margin: 18px 0 0;\n padding-bottom: 10px;\n border-bottom: 1px solid var(--bm-divider);\n font-family: var(--mono);\n font-size: 11px;\n font-weight: 500;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--bm-section);\n}\n.bm-group:first-child {\n margin-top: 0;\n}\n.bm-stack-tight > .bm-group {\n margin-bottom: 10px;\n}\n.bm-meta {\n margin: 0;\n font-family: var(--mono);\n font-size: 11px;\n color: var(--muted);\n}\n\n/* Fields */\n.bm-label {\n display: flex;\n flex-direction: column;\n gap: 8px;\n font-size: 13px;\n font-weight: 500;\n color: var(--bm-label);\n}\n.bm-field {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.bm-hint {\n font-size: 12px;\n line-height: 1.45;\n font-weight: 400;\n color: var(--muted);\n}\n.bm-label-text {\n display: inline-flex;\n align-items: center;\n min-width: 0;\n}\n.bm-info {\n position: relative;\n display: inline-flex;\n align-items: center;\n vertical-align: middle;\n margin-left: 4px;\n}\n.bm-info-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n margin: -3px 0;\n border-radius: 999px;\n color: var(--bm-info);\n cursor: help;\n text-transform: none;\n letter-spacing: 0;\n}\n.bm-info-btn:hover,\n.bm-info-btn[aria-expanded=\"true\"] {\n color: var(--ink);\n}\n.bm-info-btn:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 1px;\n}\n.bm-tip {\n position: fixed;\n z-index: 2147483647;\n visibility: hidden;\n pointer-events: none;\n padding: 8px 10px;\n border-radius: 8px;\n box-sizing: border-box;\n background: var(--ink);\n color: oklch(0.98 0.003 250);\n font-family: var(--sans, inherit);\n font-size: 12px;\n line-height: 1.45;\n font-weight: 400;\n text-transform: none;\n letter-spacing: 0;\n text-align: left;\n white-space: normal;\n box-shadow: 0 6px 20px rgb(0 0 0 / 0.25);\n}\n.bm-tip-open {\n visibility: visible;\n}\n:is(.bm-sidebar, .bm-popover, .bm-seo, .bm-consent) :where(input:not([type=\"checkbox\"]):not([type=\"file\"]), select, textarea) {\n width: 100%;\n min-height: 40px;\n padding: 0 12px;\n border: 1px solid var(--bm-border);\n border-radius: 8px;\n background: #fff;\n color: var(--ink);\n font-size: 14px;\n font-weight: 400;\n outline: none;\n}\n:is(.bm-sidebar, .bm-popover, .bm-seo, .bm-consent) textarea {\n padding: 8px 12px;\n min-height: 72px;\n line-height: 1.5;\n resize: vertical;\n}\n:is(.bm-sidebar, .bm-popover) select:not([size]) {\n appearance: none;\n padding-right: 32px;\n /* A caret made of gradients, not a `data:` image: a site's Content-Security-Policy can block those. */\n background-image: linear-gradient(45deg, transparent 50%, #777 50%), linear-gradient(135deg, #777 50%, transparent 50%);\n background-size: 6px 6px;\n background-repeat: no-repeat;\n background-position:\n right 17px center,\n right 12px center;\n}\n.bm-sidebar select[size],\n.bm-popover select[size] {\n padding: 4px;\n font-size: 12px;\n}\n.bm-sidebar select[size] option,\n.bm-sidebar select[size] optgroup,\n.bm-popover select[size] option,\n.bm-popover select[size] optgroup {\n padding: 3px 6px;\n}\n:is(.bm-sidebar, .bm-popover, .bm-seo, .bm-consent) :where(input:not([type=\"checkbox\"]):not([type=\"file\"]), select, textarea):focus {\n border-color: var(--bm-primary);\n box-shadow: 0 0 0 3px var(--bm-primary-ring);\n}\n:is(.bm-sidebar, .bm-popover, .bm-seo, .bm-consent) :where(input[aria-label=\"Link URL\"], .bm-mono) {\n font-family: var(--mono);\n font-size: 13px;\n}\n.bm-prefix-wrap {\n display: flex;\n align-items: center;\n min-height: 40px;\n border: 1px solid var(--bm-border);\n border-radius: 8px;\n background: #fff;\n overflow: hidden;\n}\n.bm-prefix-wrap:focus-within {\n border-color: var(--bm-primary);\n box-shadow: 0 0 0 3px var(--bm-primary-ring);\n}\n.bm-prefix {\n padding: 0 2px 0 12px;\n font-family: var(--mono);\n font-size: 13px;\n color: var(--muted);\n}\n.bm-prefix-wrap input {\n border: 0;\n box-shadow: none !important;\n min-height: 38px;\n padding-left: 2px;\n font-family: var(--mono);\n font-size: 13px;\n}\n.bm-stack {\n display: flex;\n flex-direction: column;\n gap: 18px;\n}\n.bm-stack-tight {\n gap: 8px;\n}\n.bm-groups {\n gap: 36px;\n}\n.bm-list {\n display: flex;\n flex-direction: column;\n border-radius: 10px;\n box-shadow: 0 0 0 1px var(--bm-border);\n overflow: hidden;\n}\n.bm-list-row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px 8px 12px;\n border-bottom: 1px solid var(--line-soft);\n}\n.bm-list-row:last-child {\n border-bottom: 0;\n}\n.bm-list-row-on {\n background: var(--accent-tint);\n}\n.bm-list-row a {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n color: var(--ink);\n text-decoration: none;\n}\n.bm-list-row a:hover .bm-list-title {\n text-decoration: underline;\n}\n.bm-list-title {\n font-size: 14px;\n font-weight: 500;\n}\n.bm-list-path {\n font-family: var(--mono);\n font-size: 11px;\n color: var(--muted);\n}\n.bm-badge-home {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n background: var(--bm-badge-green-bg);\n color: var(--bm-badge-green-fg);\n}\n.bm-badge {\n font-family: var(--mono);\n font-size: 11px;\n padding: 3px 7px;\n border-radius: 5px;\n background: var(--bm-badge-amber-bg);\n color: var(--bm-badge-amber-fg);\n}\n.bm-badge-system {\n background: var(--fill);\n color: var(--ink-2);\n}\n.bm-badge-on {\n background: var(--bm-badge-green-bg);\n color: var(--bm-badge-green-fg);\n}\n.bm-row {\n display: flex;\n gap: 6px;\n align-items: center;\n flex-wrap: wrap;\n}\n.bm-row-split {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 10px;\n}\n.bm-error {\n color: var(--danger-ink);\n font-size: 12px;\n font-weight: 400;\n margin: 0;\n}\n.bm-warn {\n color: var(--warn-ink);\n font-size: 12px;\n margin: 0;\n}\n.bm-note {\n margin: 0;\n color: var(--bm-help);\n font-size: 13px;\n line-height: 1.55;\n font-weight: 400;\n}\n.bm-group + .bm-note,\n.bm-group-split + .bm-note {\n margin-top: -4px;\n}\n\n/* Switch: 36 x 20, knob 16 inset 2 */\n.bm-check {\n flex-direction: row;\n align-items: center;\n gap: 10px;\n font-weight: 500;\n}\n:is(.bm-check, .bm-switch-row) input[type=\"checkbox\"] {\n appearance: none;\n position: relative;\n flex: none;\n width: 36px;\n height: 20px;\n margin: 0;\n border-radius: 999px;\n background: var(--bm-switch-off);\n cursor: pointer;\n transition: background 0.15s;\n}\n:is(.bm-check, .bm-switch-row) input[type=\"checkbox\"]::before {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: #fff;\n box-shadow: 0 1px 2px oklch(0 0 0 / 0.2);\n transition: transform 0.15s;\n}\n:is(.bm-check, .bm-switch-row) input[type=\"checkbox\"]:checked {\n background: var(--bm-primary);\n}\n:is(.bm-check, .bm-switch-row) input[type=\"checkbox\"]:checked::before {\n transform: translateX(16px);\n}\n.bm-popover .bm-check {\n color: var(--bm-label);\n font-size: 14px;\n}\n\n/* Buttons inside the sidebar */\n.bm-sidebar :where(button:not([role=\"tab\"]):not([role=\"radio\"]):not(.bm-resize)) {\n min-height: 32px;\n padding: 0 12px;\n border: 0;\n border-radius: 8px;\n background: var(--fill);\n color: var(--ink);\n font-size: 13px;\n font-weight: 500;\n}\n.bm-sidebar :where(button:not([role=\"tab\"]):not([role=\"radio\"]):not(.bm-resize)):hover:not(:disabled) {\n background: var(--fill-hover);\n}\n.bm-sidebar button:disabled {\n opacity: 0.45;\n cursor: default;\n}\n/* Dashed \"Add ...\" buttons: Add link, Add button, Add column, Add item */\n.bm-sidebar .bm-add {\n min-height: 44px;\n background: oklch(0.985 0.002 250);\n color: var(--bm-link);\n border: 1px dashed oklch(0.85 0.005 250);\n border-radius: 8px;\n}\n.bm-sidebar .bm-add:hover:not(:disabled) {\n background: var(--accent-tint);\n}\n\n/* Cards, lists, images */\n.bm-card {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 12px;\n border: 1px solid var(--bm-border);\n border-radius: 10px;\n background: #fff;\n}\n.bm-card .bm-card {\n background: var(--bm-fill-nested);\n border-color: var(--line-soft);\n}\n.bm-card > .bm-label:first-child:not(:only-child) {\n font-family: var(--mono);\n font-size: 11px;\n letter-spacing: 0.04em;\n font-weight: 500;\n color: var(--muted);\n}\n.bm-card[draggable=\"true\"] {\n cursor: grab;\n}\n/* Drag and drop: the target shows what a drop would do. */\n.bm-dragging {\n opacity: 0.4;\n}\n.bm-drop-before,\n.bm-drop-after,\n.bm-drop-child {\n --drop: var(--accent);\n}\n.bm-drop-before::before,\n.bm-drop-after::before {\n content: \"\";\n position: absolute;\n left: 0;\n right: 0;\n height: 3px;\n border-radius: 3px;\n background: var(--drop);\n box-shadow: 0 0 0 2px var(--accent-ring);\n pointer-events: none;\n z-index: 2;\n}\n.bm-drop-before::before {\n top: -6px;\n}\n.bm-drop-after::before {\n bottom: -6px;\n}\n.bm-drop-child {\n box-shadow: inset 0 0 0 2px var(--drop);\n background: var(--accent-tint);\n}\n.bm-drop-before::after,\n.bm-drop-after::after,\n.bm-drop-child::after,\n.bm-drop-files::after {\n content: attr(data-drop-hint);\n position: absolute;\n right: 8px;\n padding: 3px 8px;\n border-radius: 6px;\n background: var(--ink);\n color: #fff;\n font-size: 11px;\n font-weight: 500;\n line-height: 1.3;\n white-space: nowrap;\n pointer-events: none;\n z-index: 3;\n}\n.bm-drop-before::after {\n top: -22px;\n}\n.bm-drop-after::after {\n bottom: -22px;\n}\n.bm-drop-child::after {\n top: -11px;\n right: 12px;\n}\n.bm-drop-files {\n box-shadow: inset 0 0 0 2px var(--accent);\n background: var(--accent-tint);\n}\n.bm-drop-files::after {\n top: -11px;\n right: 12px;\n}\n.bm-thumb {\n width: 100%;\n height: 96px;\n object-fit: cover;\n border-radius: 7px;\n background: var(--fill);\n}\n.bm-drop {\n display: flex;\n flex-direction: column;\n gap: 8px;\n border: 1px dashed var(--line);\n border-radius: 10px;\n padding: 10px;\n background: var(--fill-soft);\n}\n.bm-drop input[type=\"file\"] {\n width: 100%;\n font-size: 12px;\n color: var(--muted);\n}\n.bm-drop input[type=\"file\"]::file-selector-button {\n height: 32px;\n margin-right: 10px;\n padding: 0 12px;\n border: 0;\n border-radius: 7px;\n background: var(--ink);\n color: #fff;\n font: inherit;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n}\n.bm-alert {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 12px 14px;\n border-radius: 12px;\n background: var(--danger-tint);\n box-shadow: 0 0 0 1px oklch(0.55 0.18 25 / 0.35);\n}\n.bm-alert-title {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--danger-ink);\n}\n.bm-alert .bm-error {\n font-family: var(--mono);\n font-size: 11px;\n}\n.bm-alert-notice {\n background: var(--warn-tint);\n box-shadow: 0 0 0 1px oklch(0.45 0.09 65 / 0.2);\n}\n.bm-alert-notice .bm-note {\n color: var(--warn-ink);\n font-size: 12px;\n}\n\n/* Header actions */\n.bm-save {\n flex: none;\n display: inline-flex;\n align-items: center;\n gap: 8px;\n height: 34px;\n padding: 0 12px;\n border: 0;\n border-radius: 8px;\n background: var(--accent);\n color: #fff;\n font-size: 13px;\n font-weight: 600;\n}\n.bm-dialog .bm-primary kbd {\n font-family: var(--mono);\n font-size: 11px;\n font-weight: 400;\n margin-left: 8px;\n opacity: 0.8;\n}\n.bm-save kbd {\n font-family: var(--mono);\n font-size: 10px;\n font-weight: 400;\n opacity: 0.75;\n}\n/* Icon buttons: 32 x 32, transparent, grey icon, grey fill on hover. */\n.bm-icon-btn {\n flex: none;\n display: inline-grid;\n place-items: center;\n width: 32px;\n height: 32px;\n min-height: 0 !important;\n padding: 0 !important;\n border: 0;\n border-radius: 7px;\n background: transparent;\n color: var(--bm-icon);\n}\n.bm-icon-btn:hover:not(:disabled) {\n background: var(--bm-fill);\n color: var(--bm-ink);\n}\n.bm-icon-btn:disabled {\n opacity: 0.35;\n}\n.bm-close {\n width: 32px;\n height: 32px;\n background: transparent;\n color: var(--bm-icon);\n border-radius: 7px;\n}\n.bm-close:hover:not(:disabled) {\n background: var(--fill) !important;\n color: var(--ink);\n}\n.bm-dock .bm-save {\n font-family: var(--sans);\n}\n.bm-split {\n position: relative;\n display: inline-flex;\n flex: none;\n}\n.bm-split .bm-split-main {\n border-radius: 8px 0 0 8px;\n}\n.bm-split .bm-split-toggle {\n width: 28px;\n padding: 0;\n justify-content: center;\n border-radius: 0 8px 8px 0;\n box-shadow: inset 1px 0 0 oklch(1 0 0 / 0.35);\n}\n.bm-split .bm-split-toggle:focus-visible,\n.bm-split .bm-split-main:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 2px;\n}\n.bm-split-menu {\n position: absolute;\n z-index: 8;\n top: calc(100% + 6px);\n right: 0;\n min-width: 200px;\n display: flex;\n flex-direction: column;\n padding: 4px;\n border-radius: 10px;\n background: #fff;\n box-shadow: var(--bm-shadow-pop);\n}\n.bm-split-menu button.bm-split-item {\n display: flex;\n align-items: center;\n width: 100%;\n min-height: 34px;\n padding: 0 10px;\n border: 0;\n border-radius: 7px;\n background: transparent;\n color: var(--ink);\n font-family: var(--sans);\n font-size: 13px;\n font-weight: 500;\n text-align: left;\n}\n.bm-split-menu button.bm-split-item:hover:not(:disabled),\n.bm-split-menu button.bm-split-item:focus-visible {\n background: var(--fill);\n outline: none;\n}\n.bm-split-menu button.bm-split-danger {\n color: var(--danger-ink);\n}\n.bm-split-menu button.bm-split-item {\n justify-content: space-between;\n gap: 16px;\n}\n.bm-split-menu button.bm-split-item kbd {\n font-family: var(--mono);\n font-size: 11px;\n font-weight: 400;\n color: var(--muted);\n}\n.bm-split-menu button.bm-split-sep {\n margin-top: 5px;\n position: relative;\n}\n.bm-split-menu button.bm-split-sep::before {\n content: \"\";\n position: absolute;\n left: 6px;\n right: 6px;\n top: -3px;\n height: 1px;\n background: var(--line);\n}\n.bm-split-menu button.bm-split-danger:hover:not(:disabled),\n.bm-split-menu button.bm-split-danger:focus-visible {\n background: var(--danger-tint);\n}\n.bm-split-menu button.bm-split-item:disabled {\n opacity: 0.45;\n cursor: default;\n}\n.bm-sidebar .bm-icon-btn.bm-danger {\n color: var(--bm-danger-icon);\n}\n.bm-sidebar .bm-icon-btn.bm-danger:hover:not(:disabled) {\n background: var(--bm-danger-soft);\n color: var(--bm-danger-icon);\n}\n.bm-small {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n min-height: 28px !important;\n padding: 0 10px !important;\n font-family: var(--sans);\n font-size: 12px !important;\n letter-spacing: 0;\n text-transform: none;\n align-self: flex-start;\n}\n.bm-group-split {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n margin-top: 18px;\n margin-bottom: 0;\n padding-bottom: 10px;\n border-bottom: 1px solid var(--bm-divider);\n}\n.bm-group-split:first-child {\n margin-top: 0;\n}\n.bm-group-split > .bm-group {\n margin: 0;\n padding: 0;\n border: 0;\n}\n.bm-group-split .bm-small {\n text-transform: none;\n letter-spacing: 0;\n}\n.bm-group-split span.bm-small {\n font-family: var(--mono);\n font-size: 11px !important;\n font-weight: 400;\n color: var(--bm-muted);\n padding: 0 !important;\n min-height: 0 !important;\n}\n.bm-stack-tight > .bm-group-split {\n margin-bottom: 10px;\n}\n\n/* Labels with a right-hand tag */\n.bm-label.bm-label-row,\n.bm-label-row {\n flex-direction: row;\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 8px;\n}\n.bm-meta-tag {\n font-family: var(--mono);\n font-size: 11px;\n font-weight: 400;\n letter-spacing: 0;\n color: var(--bm-muted);\n}\n\n/* Switch rows */\n.bm-switch-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n cursor: pointer;\n}\n.bm-switch-text {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n.bm-switch-title {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 14px;\n font-weight: 500;\n color: var(--bm-label);\n}\n\n/* Tone cards and segmented controls */\n.bm-tones {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 6px;\n}\n:is(.bm-sidebar, .bm-popover) .bm-tone {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: 6px;\n min-height: 0;\n padding: 5px 5px 6px;\n border-radius: 9px;\n background: #fff;\n border: 0;\n box-shadow: inset 0 0 0 1px var(--bm-border);\n font-size: 12px;\n font-weight: 500;\n text-align: center;\n}\n:is(.bm-sidebar, .bm-popover) .bm-tone:hover {\n background: var(--fill-soft);\n}\n:is(.bm-sidebar, .bm-popover) .bm-tone[aria-checked=\"true\"] {\n box-shadow: inset 0 0 0 2px var(--accent);\n background: #fff;\n}\n.bm-tones-2 {\n grid-template-columns: repeat(2, 1fr);\n}\n.bm-tones-3 {\n grid-template-columns: repeat(3, 1fr);\n}\n/* A miniature of each button look, drawn with the editor's colours so it does not depend on the site theme. */\n.bm-btnsample {\n display: grid;\n place-items: center;\n height: 26px;\n border-radius: 6px;\n background: var(--fill-soft);\n}\n.bm-btnsample > span {\n display: block;\n font-size: 10px;\n font-weight: 600;\n line-height: 1;\n}\n.bm-btnsample-default > span {\n color: var(--ink-2);\n}\n.bm-btnsample-primary > span {\n padding: 5px 12px;\n border-radius: 999px;\n background: var(--accent);\n color: #fff;\n}\n.bm-btnsample-secondary > span {\n padding: 5px 12px;\n border-radius: 999px;\n color: var(--ink);\n box-shadow: inset 0 0 0 1px var(--ink-2);\n}\n.bm-btnsample-tertiary > span {\n color: var(--accent);\n text-decoration: underline;\n text-underline-offset: 3px;\n}\n.bm-swatch {\n display: inline-block;\n flex: none;\n width: 10px;\n height: 10px;\n border-radius: 3px;\n box-shadow: inset 0 0 0 1px oklch(0.2 0.02 255 / 0.15);\n}\n.bm-swatch-lg {\n display: block;\n width: auto;\n height: 22px;\n border-radius: 6px;\n}\n.bm-swatch-base {\n background: #fff;\n}\n.bm-swatch-subtle {\n background: oklch(0.96 0.005 250);\n}\n.bm-swatch-inverse {\n background: var(--ink);\n}\n.bm-swatch-brand {\n background: var(--accent);\n}\n.bm-segmented {\n display: flex;\n padding: 3px;\n border-radius: 9px;\n background: var(--bm-fill);\n}\n.bm-sidebar .bm-segmented button,\n.bm-dialog .bm-segmented button {\n flex: 1;\n min-height: 36px;\n height: 36px;\n padding: 0 8px;\n border: 0;\n border-radius: 7px;\n background: transparent;\n color: oklch(0.45 0.01 255);\n font-size: 13px;\n font-weight: 400;\n}\n.bm-sidebar .bm-segmented button:hover:not(:disabled),\n.bm-dialog .bm-segmented button:hover:not(:disabled) {\n background: oklch(1 0 0 / 0.5);\n}\n.bm-sidebar .bm-segmented button[aria-checked=\"true\"],\n.bm-dialog .bm-segmented button[aria-checked=\"true\"] {\n background: #fff;\n color: var(--ink);\n box-shadow: var(--bm-shadow-segment-on);\n}\n.bm-icon-picker {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(40px, 1fr));\n gap: 4px;\n padding: 3px;\n border-radius: 9px;\n background: var(--bm-fill);\n}\n.bm-sidebar .bm-icon-picker button {\n display: grid;\n place-items: center;\n min-height: 40px;\n height: 40px;\n padding: 0;\n border: 0;\n border-radius: 7px;\n background: transparent;\n color: oklch(0.45 0.01 255);\n}\n.bm-sidebar .bm-icon-picker button:hover {\n background: oklch(1 0 0 / 0.5);\n color: var(--ink);\n}\n.bm-sidebar .bm-icon-picker button[aria-checked=\"true\"] {\n background: #fff;\n color: var(--accent);\n box-shadow: var(--bm-shadow-segment-on);\n}\n.bm-icon-picker svg {\n width: 20px;\n height: 20px;\n}\n.bm-actions-split {\n display: flex;\n gap: 8px;\n padding-top: 4px;\n}\n.bm-actions-split button {\n flex: 1;\n min-height: 40px !important;\n font-size: 14px !important;\n}\n.bm-sidebar .bm-actions-split .bm-danger {\n background: var(--bm-danger-soft);\n}\n\n/* Arrays, links, images */\n.bm-array {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.bm-array-item {\n border: 1px solid var(--bm-border);\n border-radius: 10px;\n background: #fff;\n overflow: hidden;\n}\n.bm-array-item summary {\n display: flex;\n align-items: center;\n gap: 8px;\n min-height: 44px;\n padding: 0 8px 0 10px;\n font-size: 14px;\n font-weight: 500;\n color: var(--ink);\n cursor: pointer;\n list-style: none;\n}\n.bm-array-item summary::-webkit-details-marker {\n display: none;\n}\n.bm-array-item summary:hover {\n background: var(--fill-soft);\n}\n.bm-array-summary {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-grip,\n.bm-chevron {\n display: inline-grid;\n place-items: center;\n color: oklch(0.65 0.01 255);\n}\n.bm-chevron {\n transition: transform 0.12s;\n}\ndetails[open] > summary .bm-chevron {\n transform: rotate(90deg);\n}\n.bm-array-body {\n display: flex;\n flex-direction: column;\n gap: 18px;\n padding: 12px;\n background: var(--bm-fill-nested);\n border-top: 1px solid var(--line-soft);\n}\n.bm-row-end {\n justify-content: flex-end;\n}\n.bm-sidebar .bm-add-row {\n width: 100%;\n}\n.bm-link-btn {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n min-height: 40px;\n padding: 0 12px;\n border: 1px solid var(--bm-border);\n border-radius: 8px;\n background: #fff;\n text-align: left;\n color: var(--ink-2);\n}\n.bm-sidebar .bm-link-btn {\n background: #fff;\n border: 1px solid var(--bm-border);\n font-size: 14px;\n padding: 0 12px;\n}\n.bm-sidebar .bm-link-btn:hover:not(:disabled) {\n background: #fff;\n border-color: oklch(0.8 0.005 250);\n}\n.bm-link-label {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--ink);\n font-weight: 500;\n}\n.bm-link-href {\n font-family: var(--mono);\n font-size: 11px;\n color: var(--muted);\n max-width: 45%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-stripes {\n background: repeating-linear-gradient(135deg, oklch(0.93 0.004 250) 0 6px, oklch(0.965 0.003 250) 6px 12px);\n}\n\n/* Image field: label above, compact 56px row, alt text under it */\n.bm-imagefield {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.bm-image-row {\n display: flex;\n gap: 10px;\n align-items: center;\n}\n.bm-image-row .bm-thumb {\n flex: none;\n width: 56px;\n height: 56px;\n border-radius: 6px;\n}\n.bm-image-meta {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 6px;\n justify-content: center;\n}\n.bm-image-path {\n font-family: var(--mono);\n font-size: 12px;\n color: var(--bm-label);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-sidebar .bm-alt {\n min-height: 40px;\n font-size: 14px;\n}\n.bm-image-actions {\n flex-wrap: nowrap;\n gap: 6px;\n}\n.bm-image-actions > button:not(.bm-icon-btn) {\n flex: 1;\n min-height: 36px;\n font-size: 13px;\n}\n.bm-layout {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding-top: 2px;\n}\n.bm-layout-row {\n display: grid;\n grid-template-columns: 56px 1fr;\n align-items: center;\n column-gap: 10px;\n row-gap: 4px;\n}\n.bm-layout-row .bm-label {\n margin: 0;\n}\n.bm-layout-row .bm-layout-hint {\n grid-column: 2;\n}\n.bm-position {\n display: grid;\n grid-template-columns: repeat(3, 20px);\n grid-template-rows: repeat(3, 20px);\n gap: 3px;\n padding: 4px;\n width: max-content;\n border-radius: 8px;\n background: var(--fill);\n}\n.bm-sidebar .bm-position-cell {\n width: 20px;\n height: 20px;\n min-height: 0;\n padding: 0;\n border: 0;\n border-radius: 5px;\n background: #fff;\n box-shadow: inset 0 0 0 1px var(--line);\n}\n.bm-sidebar .bm-position-cell:hover:not(:disabled) {\n background: var(--accent-tint);\n}\n.bm-sidebar .bm-position-cell[aria-checked=\"true\"],\n.bm-sidebar .bm-position-cell[aria-checked=\"true\"]:hover:not(:disabled) {\n background: var(--accent);\n box-shadow: none;\n}\n.bm-sidebar .bm-position-cell:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.bm-slot,\n.bm-dropzone {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 4px;\n width: 100%;\n min-height: 96px;\n padding: 14px;\n border: 1.5px dashed oklch(0.82 0.006 250);\n border-radius: 10px;\n background: var(--fill-soft);\n color: var(--ink-2);\n text-align: center;\n cursor: pointer;\n}\n.bm-sidebar .bm-slot:hover:not(:disabled),\n.bm-dropzone:hover {\n border-color: var(--accent);\n background: var(--accent-tint);\n}\n.bm-dropzone {\n min-height: 180px;\n position: relative;\n}\n.bm-dropzone:focus-within {\n outline: 2px solid var(--accent);\n outline-offset: 2px;\n}\n.bm-dropzone-over {\n border-color: var(--accent);\n background: var(--accent-tint);\n}\n.bm-slot-title {\n font-size: 13px;\n font-weight: 600;\n color: var(--ink);\n}\n.bm-slot-hint {\n font-size: 11px;\n color: var(--muted);\n}\n.bm-scrim {\n position: fixed;\n inset: 0;\n z-index: 7;\n display: flex;\n align-items: flex-start;\n justify-content: center;\n padding: 10vh 16px 16px;\n background: oklch(0.2 0.01 255 / 0.4);\n}\n.bm-modal {\n display: flex;\n flex-direction: column;\n width: min(480px, 100%);\n max-height: min(560px, 80vh);\n overflow: hidden;\n padding: 20px 0 0;\n border-radius: 14px;\n background: #fff;\n box-shadow: var(--bm-shadow-dialog);\n}\n.bm-modal-head {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 0 20px 12px;\n}\n.bm-modal-head .bm-dialog-title {\n flex: none;\n}\n.bm-modal-head .bm-meta-tag {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-modal-head .bm-icon-btn {\n margin: -6px -8px -6px 0;\n}\n.bm-modal .bm-tabs {\n margin: 0 20px;\n padding: 3px;\n gap: 0;\n border: 0;\n border-radius: 9px;\n background: var(--bm-fill);\n overflow: visible;\n}\n.bm-modal .bm-tabs button {\n flex: 1;\n height: 36px;\n min-height: 36px;\n padding: 0 8px;\n border-radius: 7px;\n font-size: 13px;\n font-weight: 400;\n}\n.bm-modal .bm-tabs button:hover {\n background: oklch(1 0 0 / 0.5);\n}\n.bm-modal .bm-tabs button[aria-selected=\"true\"] {\n background: #fff;\n color: var(--ink);\n font-weight: 500;\n box-shadow: var(--bm-shadow-segment-on);\n}\n.bm-modal-body {\n flex: 1;\n min-height: 0;\n overflow: auto;\n padding: 16px 20px 20px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.bm-library {\n display: grid;\n grid-template-columns: repeat(4, minmax(0, 1fr));\n gap: 10px;\n}\n.bm-library-item {\n position: relative;\n}\n.bm-library-pick {\n display: flex;\n flex-direction: column;\n gap: 4px;\n width: 100%;\n min-height: 0;\n padding: 4px;\n border: 0;\n border-radius: 9px;\n background: var(--fill-soft);\n text-align: left;\n font-family: var(--mono);\n font-size: 9px;\n font-weight: 400;\n color: var(--muted);\n word-break: break-all;\n cursor: pointer;\n}\n.bm-library-pick:hover {\n background: var(--fill-hover);\n}\n.bm-library-pick img {\n width: 100%;\n aspect-ratio: 1 / 1;\n object-fit: cover;\n border-radius: 6px;\n background: var(--fill);\n}\n.bm-library-pick[aria-pressed=\"true\"] {\n box-shadow: 0 0 0 2px var(--accent);\n background: var(--accent-tint);\n}\n.bm-library-delete {\n position: absolute;\n top: 8px;\n right: 8px;\n width: 24px;\n height: 24px;\n min-height: 0;\n background: #fff;\n box-shadow: 0 1px 3px oklch(0.2 0.01 255 / 0.25);\n opacity: 0;\n}\n.bm-library-item:hover .bm-library-delete,\n.bm-library-delete:focus-visible {\n opacity: 1;\n}\n.bm-library-empty {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 8px;\n}\n.bm-modal-confirm {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 14px 20px 16px;\n border-top: 1px solid var(--line-soft);\n background: var(--danger-tint);\n}\n.bm-modal-confirm .bm-note {\n color: var(--danger-ink);\n font-size: 13px;\n line-height: 1.5;\n}\n.bm-visually-hidden {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0 0 0 0);\n border: 0;\n white-space: nowrap;\n}\n.bm-popover-title {\n grid-column: 1 / -1;\n padding: 0;\n font-size: 16px;\n font-weight: 600;\n letter-spacing: -0.01em;\n color: var(--bm-ink);\n}\n.bm-popover-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0;\n}\n.bm-popover-head-wide {\n grid-column: 1 / -1;\n}\n.bm-popover[aria-label=\"Edit link\"],\n.bm-popover[aria-label=\"Add section\"][role=\"listbox\"] {\n z-index: 5;\n}\n.bm-popover :is(.bm-fmt-bold) {\n font-weight: 700;\n}\n.bm-fmt-bold {\n font-weight: 700;\n font-size: 13px;\n}\n.bm-fmt-italic {\n font-family: var(--serif);\n font-style: italic;\n font-size: 15px;\n}\n\n/* Edit link: Advanced row */\n.bm-advanced {\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n.bm-popover button.bm-advanced-toggle {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n min-height: 32px;\n padding: 0 2px;\n border: 0;\n border-radius: 6px;\n background: transparent;\n color: var(--bm-label);\n font-size: 13px;\n font-weight: 500;\n text-align: left;\n}\n.bm-popover button.bm-advanced-toggle:hover {\n background: transparent;\n color: var(--bm-ink);\n}\n.bm-advanced-toggle .bm-chevron {\n transition: transform 0.12s;\n}\n.bm-advanced-toggle[aria-expanded=\"true\"] .bm-chevron {\n transform: rotate(90deg);\n}\n.bm-advanced-summary {\n margin-left: auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: var(--mono);\n font-size: 11px;\n font-weight: 400;\n color: var(--bm-muted);\n}\n\n/* SEO card and dialog */\n.bm-seo-card {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.bm-seo-badges {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.bm-seo-badge {\n padding: 2px 8px;\n border-radius: 999px;\n background: var(--fill-soft);\n color: var(--bm-label);\n font-family: var(--mono);\n font-size: 11px;\n}\n.bm-seo-edit,\n.bm-consent-edit {\n align-self: flex-start;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n.bm-dialog.bm-seo {\n position: relative;\n width: min(560px, 100%);\n max-height: calc(100vh - 10vh - 32px);\n min-height: 0;\n}\n.bm-seo-body {\n display: flex;\n flex-direction: column;\n gap: 20px;\n min-height: 0;\n overflow-y: auto;\n padding: 2px 4px 2px 2px;\n}\n.bm-seo-json {\n min-height: 220px;\n white-space: pre;\n overflow: auto;\n tab-size: 2;\n}\n.bm-seo-json[aria-invalid=\"true\"] {\n border-color: var(--bm-danger);\n}\n.bm-seo-format {\n align-self: flex-start;\n}\n\n/* Cookie banner dialog */\n.bm-dialog.bm-consent {\n position: relative;\n width: min(520px, 100%);\n max-height: calc(100vh - 10vh - 32px);\n min-height: 0;\n}\n.bm-consent-body {\n display: flex;\n flex-direction: column;\n gap: 20px;\n min-height: 0;\n overflow-y: auto;\n padding: 2px 4px 2px 2px;\n}\n.bm-consent-preview {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 14px 16px;\n border-radius: 12px;\n background: var(--fill-soft);\n}\n.bm-consent-preview-title {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n color: var(--bm-label);\n}\n.bm-consent-preview-text {\n margin: 0;\n font-size: 13px;\n line-height: 1.45;\n color: var(--bm-muted);\n overflow-wrap: anywhere;\n}\n.bm-consent-preview-link {\n text-decoration: underline;\n text-underline-offset: 3px;\n}\n.bm-consent-preview-buttons {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n.bm-consent-preview-buttons > span {\n padding: 5px 14px;\n border-radius: 999px;\n box-shadow: inset 0 0 0 1px var(--bm-border);\n font-size: 13px;\n font-weight: 500;\n color: var(--bm-label);\n}\n\n/* SEO preview, OG row, outline */\n.bm-serp {\n display: flex;\n flex-direction: column;\n gap: 3px;\n padding: 12px 14px;\n border-radius: 10px;\n background: var(--fill-soft);\n}\n.bm-serp-image {\n flex-direction: row;\n align-items: flex-start;\n gap: 12px;\n}\n.bm-serp-text {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: 3px;\n min-width: 0;\n}\n.bm-serp-thumb {\n flex: none;\n width: 64px;\n height: 64px;\n border-radius: 8px;\n object-fit: cover;\n background: var(--fill);\n}\n.bm-serp-title {\n font-size: 15px;\n font-weight: 500;\n color: oklch(0.42 0.12 260);\n}\n.bm-serp-url {\n font-family: var(--mono);\n font-size: 11px;\n color: oklch(0.45 0.08 145);\n}\n.bm-serp-desc {\n font-size: 12px;\n line-height: 1.45;\n color: oklch(0.45 0.01 255);\n}\n.bm-outline-list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n.bm-sidebar .bm-outline-row {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n min-height: 40px;\n padding: 0 10px;\n border-radius: 8px;\n background: transparent;\n text-align: left;\n font-size: 14px;\n}\n.bm-sidebar .bm-outline-row:hover:not(:disabled) {\n background: var(--fill-soft);\n}\n\n/* Section rows (Pages tab): grip, tone swatch, name over #anchor and variant, then actions. */\n.bm-srow {\n position: relative;\n display: flex;\n align-items: center;\n gap: 4px;\n min-height: 52px;\n padding: 6px 6px 6px 8px;\n border: 1px solid var(--bm-border);\n border-radius: 10px;\n background: #fff;\n}\n.bm-srow:hover {\n border-color: oklch(0.8 0.008 250);\n}\n.bm-srow-grip {\n flex: none;\n width: 16px;\n height: 32px;\n display: inline-grid;\n place-items: center;\n cursor: grab;\n touch-action: none;\n}\n.bm-srow-grip:active {\n cursor: grabbing;\n}\n.bm-srow .bm-outline-row {\n flex: 1;\n min-width: 0;\n width: auto;\n padding: 0 8px 0 4px;\n gap: 10px;\n}\n.bm-srow .bm-swatch {\n width: 10px;\n height: 10px;\n border-radius: 2px;\n}\n.bm-srow-text {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 1px;\n}\n.bm-srow-name {\n font-size: 14px;\n font-weight: 500;\n color: var(--bm-ink);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-srow-meta {\n font-family: var(--mono);\n font-size: 11px;\n font-weight: 400;\n color: var(--bm-muted);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-srow .bm-outline-meta {\n flex: none;\n max-width: 40%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-srow-btn {\n width: 32px;\n height: 32px;\n background: transparent;\n color: var(--bm-icon);\n border-radius: 7px;\n}\n.bm-srow-btn:disabled {\n opacity: 0.35;\n}\n.bm-sidebar .bm-srow-btn:hover:not(:disabled) {\n background: var(--bm-fill);\n color: var(--bm-ink);\n}\n.bm-sidebar .bm-srow-btn.bm-danger {\n color: var(--bm-danger-icon);\n}\n.bm-sidebar .bm-srow-btn.bm-danger:hover:not(:disabled) {\n background: var(--bm-danger-soft);\n}\n.bm-srow-sep {\n flex: none;\n width: 1px;\n height: 20px;\n margin: 0 2px;\n background: var(--bm-divider);\n}\n.bm-outline-n {\n width: 18px;\n font-family: var(--mono);\n font-size: 11px;\n color: oklch(0.55 0.01 255);\n}\n.bm-outline-label {\n flex: 1;\n min-width: 0;\n font-weight: 500;\n}\n.bm-outline-meta {\n font-family: var(--mono);\n font-size: 11px;\n font-weight: 400;\n color: var(--muted);\n}\n\n/* Navigation and footer link cards */\n.bm-nav-card {\n padding: 12px 12px 14px;\n gap: 10px;\n}\n.bm-nested {\n margin: 6px 0 0 16px;\n}\n.bm-nav-children {\n margin-top: -4px;\n}\n.bm-nav-head {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n.bm-nav-head .bm-icon-btn,\n.bm-column-head .bm-icon-btn {\n background: var(--bm-fill);\n color: oklch(0.45 0.01 255);\n}\n.bm-nav-head .bm-icon-btn:hover:not(:disabled),\n.bm-column-head .bm-icon-btn:hover:not(:disabled) {\n background: var(--bm-fill-hover);\n color: var(--bm-ink);\n}\n.bm-sidebar .bm-nav-head .bm-icon-btn.bm-danger,\n.bm-sidebar .bm-column-head .bm-icon-btn.bm-danger {\n background: var(--bm-danger-soft);\n color: var(--bm-danger-icon);\n}\n.bm-sidebar .bm-nav-label {\n flex: 1;\n min-width: 0;\n min-height: 32px;\n padding: 0 8px;\n border-color: transparent;\n font-weight: 500;\n}\n.bm-sidebar .bm-nav-label:hover {\n border-color: var(--line);\n}\n.bm-column {\n padding: 0;\n gap: 0;\n}\n.bm-column-head {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px;\n cursor: grab;\n}\n.bm-column-links > summary {\n display: flex;\n align-items: center;\n gap: 6px;\n min-height: 40px;\n padding: 0 12px;\n border-top: 1px solid var(--line-soft);\n font-size: 13px;\n font-weight: 500;\n color: var(--bm-label);\n cursor: pointer;\n list-style: none;\n}\n.bm-column-links > summary::-webkit-details-marker {\n display: none;\n}\n.bm-column-links > :not(summary) {\n margin: 10px 12px 0;\n}\n.bm-column-links > :last-child {\n margin-bottom: 12px;\n}\n\n/* Wordmark preview */\n.bm-card-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n.bm-card-head .bm-label {\n flex: none;\n}\n.bm-combo {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n:is(.bm-sidebar, .bm-popover) .bm-combo-trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 6px;\n width: auto;\n max-width: 140px;\n min-height: 38px;\n padding: 0 10px 0 12px;\n border: 0;\n border-left: 1px solid var(--bm-border);\n border-radius: 0;\n background: var(--fill-soft);\n color: var(--ink-2);\n font-family: var(--mono);\n font-size: 12px;\n font-weight: 400;\n text-align: left;\n}\n:is(.bm-sidebar, .bm-popover) .bm-combo-trigger:hover:not(:disabled) {\n background: var(--fill-hover);\n}\n:is(.bm-sidebar, .bm-popover) .bm-combo-trigger[aria-expanded=\"true\"] {\n background: var(--accent-tint);\n color: var(--accent-hover);\n}\n/* One URL field: the text input and the page picker share a single border. */\n.bm-linkfield {\n display: flex;\n align-items: stretch;\n min-height: 40px;\n border: 1px solid var(--bm-border);\n border-radius: 8px;\n background: #fff;\n overflow: hidden;\n}\n.bm-linkfield:focus-within {\n border-color: var(--bm-primary);\n box-shadow: 0 0 0 3px var(--bm-primary-ring);\n}\n.bm-linkfield > input {\n flex: 1;\n min-width: 0;\n border: 0 !important;\n box-shadow: none !important;\n border-radius: 0;\n min-height: 38px;\n background: transparent;\n}\n.bm-linkfield .bm-combo {\n flex: none;\n max-width: 45%;\n}\n.bm-linkfield .bm-combo-trigger {\n height: 100%;\n}\n.bm-linkfield .bm-combo-trigger:focus-visible {\n outline-offset: -2px;\n}\n.bm-combo-trigger > span {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bm-combo-trigger .bm-combo-custom {\n color: var(--muted);\n}\n.bm-combo-panel {\n position: fixed;\n z-index: 6;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 6px;\n border: 1px solid var(--bm-border);\n border-radius: 10px;\n background: #fff;\n box-shadow: 0 6px 18px oklch(0.2 0.02 255 / 0.1);\n}\n.bm-combo-list {\n max-height: 200px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n}\n.bm-combo-group {\n margin: 6px 6px 2px;\n font-family: var(--mono);\n font-size: 10px;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--muted);\n}\n.bm-combo-option {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 10px;\n padding: 6px 8px;\n border-radius: 6px;\n font-size: 13px;\n color: var(--ink);\n cursor: pointer;\n}\n.bm-combo-option[aria-selected=\"true\"] {\n font-weight: 600;\n color: var(--accent-hover);\n}\n.bm-combo-active {\n background: var(--fill);\n}\n.bm-combo-value {\n flex: none;\n max-width: 50%;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-family: var(--mono);\n font-size: 11px;\n color: var(--muted);\n}\n.bm-combo-empty {\n margin: 0;\n padding: 8px;\n font-size: 12px;\n color: var(--muted);\n}\n.bm-format {\n margin-top: -4px;\n}\n.bm-sidebar .bm-format button[aria-pressed=\"true\"] {\n background: #fff;\n color: var(--ink);\n box-shadow: var(--bm-shadow-segment-on);\n}\n.bm-wordmark {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 12px;\n padding: 12px 14px;\n border-radius: 10px;\n background: var(--bm-fill-soft);\n}\n.bm-wordmark-name {\n font-size: 24px;\n font-weight: 600;\n letter-spacing: -0.03em;\n}\n.bm-alert-notice {\n flex-direction: row;\n align-items: flex-start;\n gap: 8px;\n color: var(--warn-ink);\n}\n.bm-alert-notice .bm-icon {\n margin-top: 2px;\n flex: none;\n}\n\n/*\n * Button system. Three variants, one danger modifier, one rule set.\n * secondary (default): neutral fill, ink text.\n * .bm-primary: the one main action of a surface. Solid accent, white text (AA).\n * .bm-tertiary: low emphasis text button (Cancel, Reset). No fill, no underline, grey fill on hover.\n * .bm-danger: modifier. primary = solid red, secondary = red text, tertiary = red text.\n * The scope list gives every rule enough specificity to beat the per-surface defaults above,\n * so the hover state of one variant can never fall back to the secondary fill.\n */\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button:where(.bm-primary, .bm-tertiary, .bm-danger):not(.bm-icon-btn, .bm-save) {\n transition:\n background-color 0.12s,\n color 0.12s;\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button.bm-primary {\n background: var(--bm-primary);\n color: #fff;\n box-shadow: none;\n font-weight: 600;\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button.bm-primary:hover:not(:disabled) {\n background: var(--bm-primary-hover);\n color: #fff;\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button.bm-primary.bm-danger {\n background: var(--bm-danger);\n color: #fff;\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button.bm-primary.bm-danger:hover:not(:disabled) {\n background: var(--danger-hover);\n color: #fff;\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button.bm-danger:not(.bm-primary, .bm-tertiary, .bm-icon-btn) {\n background: var(--bm-danger-soft);\n color: var(--bm-danger-text);\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button.bm-danger:not(.bm-primary, .bm-tertiary, .bm-icon-btn):hover:not(:disabled) {\n background: oklch(0.93 0.04 25);\n color: var(--bm-danger-text);\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button.bm-tertiary {\n min-height: 32px;\n padding: 0 10px;\n border-radius: 8px;\n background: transparent;\n color: var(--bm-label);\n font-weight: 400;\n box-shadow: none;\n text-decoration: none;\n}\n:is(.bm-dialog, .bm-popover, .bm-modal) button.bm-tertiary {\n min-height: 38px;\n font-size: 14px;\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button.bm-tertiary:hover:not(:disabled) {\n background: var(--bm-fill);\n color: var(--bm-ink);\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button.bm-tertiary.bm-danger {\n background: transparent;\n color: var(--bm-danger-text);\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button.bm-tertiary.bm-danger:hover:not(:disabled) {\n background: var(--bm-danger-soft);\n color: var(--bm-danger-text);\n}\n:is(.bm-dialog, .bm-popover, .bm-modal, .bm-sidebar) button:disabled {\n opacity: 0.5;\n cursor: default;\n}\n.bm-save:hover:not(:disabled) {\n background: var(--accent-hover);\n color: #fff;\n}\n.bm-sidebar .bm-save:hover:not(:disabled) {\n background: var(--accent-hover);\n color: #fff;\n}\n.bm-popover .bm-actions-remove,\n.bm-dialog .bm-actions-remove {\n margin-right: auto;\n}\n.bm-modal :where(button:not([role=\"tab\"], .bm-library-pick, .bm-icon-btn, .bm-tertiary)) {\n min-height: 38px;\n padding: 0 16px;\n border: 0;\n border-radius: 8px;\n background: var(--fill);\n color: var(--ink);\n font-size: 14px;\n font-weight: 400;\n}\n.bm-modal :where(button:not([role=\"tab\"], .bm-library-pick, .bm-icon-btn, .bm-tertiary)):hover:not(:disabled) {\n background: var(--fill-hover);\n}\n.bm-modal .bm-dropzone {\n font-family: var(--sans);\n}\n\n/* Design tab */\n.bm-colorrow {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.bm-colorpick {\n flex: none;\n width: 40px !important;\n height: 40px;\n min-height: 40px !important;\n padding: 2px !important;\n border: 1px solid var(--bm-border);\n border-radius: 8px;\n background: #fff;\n cursor: pointer;\n}\n.bm-colorhex {\n flex: 1;\n min-width: 0;\n font-family: var(--mono);\n font-size: 13px;\n text-transform: lowercase;\n}\n.bm-colorhex[aria-invalid=\"true\"] {\n border-color: var(--bm-danger);\n}\n.bm-overrides {\n display: flex;\n flex-direction: column;\n border: 1px solid var(--bm-border);\n border-radius: 10px;\n overflow: hidden;\n}\n.bm-override-row {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 10px 8px 12px;\n}\n.bm-override-row + .bm-override-row {\n border-top: 1px solid var(--bm-divider);\n}\n.bm-override-label {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: baseline;\n gap: 6px;\n font-size: 13px;\n font-weight: 500;\n color: var(--bm-label);\n}\n.bm-override-label .bm-meta-tag {\n font-size: 10px;\n}\n.bm-override-row .bm-colorpick {\n width: 32px !important;\n height: 32px;\n min-height: 32px !important;\n padding: 1px !important;\n border-radius: 7px;\n}\n.bm-override-row .bm-colorhex {\n flex: none;\n width: 104px;\n min-height: 36px;\n padding: 0 10px;\n}\n.bm-tone-matrix {\n display: grid;\n grid-template-columns: 60px minmax(0, 1fr) minmax(0, 1fr);\n gap: 8px;\n align-items: stretch;\n}\n.bm-tone-pick {\n border: 0;\n text-align: left;\n font: inherit;\n cursor: pointer;\n}\n.bm-tone-cell[aria-pressed=\"true\"] {\n outline: 2px solid var(--bm-primary);\n outline-offset: 0;\n}\n.bm-tone-head {\n font-family: var(--mono);\n font-size: 10px;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--muted);\n}\n.bm-tone-name {\n align-self: center;\n font-size: 12px;\n font-weight: 500;\n color: var(--ink-2);\n}\n.bm-tone-cell {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 6px;\n min-width: 0;\n min-height: 46px;\n padding: 0 10px;\n border-radius: 8px;\n overflow: hidden;\n white-space: nowrap;\n}\n.bm-tone-aa {\n flex: none;\n font-size: 16px;\n font-weight: 600;\n}\n.bm-tone-muted {\n flex: 0 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: 11px;\n}\n.bm-tone-chip {\n flex: none;\n padding: 2px 6px;\n border-radius: 5px;\n font-size: 10px;\n font-weight: 600;\n}\n.bm-tone-link {\n flex: none;\n font-size: 11px;\n text-decoration: underline;\n text-underline-offset: 2px;\n}\n.bm-contrast-list {\n margin: 6px 0 0;\n padding-left: 16px;\n font-family: var(--mono);\n font-size: 11px;\n color: var(--warn-ink);\n}\n.bm-range {\n flex: 1;\n min-width: 0;\n min-height: 28px;\n accent-color: var(--accent);\n cursor: pointer;\n}\n/* Preview: the only editor UI left on screen. */\n.bm-exit-preview {\n position: fixed;\n z-index: 7;\n right: 16px;\n bottom: 16px;\n display: inline-flex;\n align-items: center;\n gap: 8px;\n height: 40px;\n padding: 0 16px;\n border: 0;\n border-radius: 999px;\n background: var(--ink);\n color: #fff;\n font-size: 13px;\n font-weight: 500;\n box-shadow: 0 10px 30px oklch(0.2 0.02 255 / 0.3);\n}\n.bm-exit-preview:hover {\n background: oklch(0.28 0.012 255);\n}\n.bm-exit-preview:focus-visible {\n outline-color: oklch(0.82 0.14 145);\n outline-offset: 3px;\n}\n.bm-srow-add {\n display: flex;\n justify-content: flex-end;\n padding-top: 6px;\n}\n.bm-sidebar .bm-add-section {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n min-height: 32px;\n padding: 0 12px;\n border: 0;\n border-radius: 8px;\n background: var(--bm-primary);\n color: #fff;\n font-size: 13px;\n font-weight: 500;\n}\n.bm-sidebar .bm-add-section:hover {\n background: var(--bm-primary-hover);\n}\n";
|
|
19712
20145
|
//#endregion
|
|
19713
20146
|
//#region src/ui/styles.ts
|
|
19714
20147
|
function boxStyle(rect, offset = 0) {
|
|
@@ -21055,7 +21488,7 @@ function siteOptionControl(site, option, onField) {
|
|
|
21055
21488
|
onChange: (value) => onField(option.name, value)
|
|
21056
21489
|
}, option.name);
|
|
21057
21490
|
}
|
|
21058
|
-
function SiteForm({ site, options, images, onField, onOptional, onMarks, onUploaded, onDeleted }) {
|
|
21491
|
+
function SiteForm({ site, options, images, onField, onOptional, onConsentEnabled, onEditConsent, onEditJsonLd, onMarks, onUploaded, onDeleted }) {
|
|
21059
21492
|
const logo = getAt(site, "favicon") ?? getAt(site, "logo");
|
|
21060
21493
|
const groupOf = (option) => SITE_GROUPS.find((group) => option.name.startsWith(group.prefix));
|
|
21061
21494
|
const shown = options.filter((option) => !option.name.startsWith("footer.") && !option.name.startsWith("header.") && option.name !== "theme.mode");
|
|
@@ -21135,6 +21568,10 @@ function SiteForm({ site, options, images, onField, onOptional, onMarks, onUploa
|
|
|
21135
21568
|
})
|
|
21136
21569
|
]
|
|
21137
21570
|
}),
|
|
21571
|
+
/* @__PURE__ */ jsx(JsonLdGroup, {
|
|
21572
|
+
site,
|
|
21573
|
+
onEdit: onEditJsonLd
|
|
21574
|
+
}),
|
|
21138
21575
|
/* @__PURE__ */ jsxs("div", {
|
|
21139
21576
|
class: "bm-stack",
|
|
21140
21577
|
children: [
|
|
@@ -21162,10 +21599,16 @@ function SiteForm({ site, options, images, onField, onOptional, onMarks, onUploa
|
|
|
21162
21599
|
}),
|
|
21163
21600
|
/* @__PURE__ */ jsx("p", {
|
|
21164
21601
|
class: "bm-hint",
|
|
21165
|
-
children: "Runs on your live site only: not while you edit, and not on Vercel or Netlify previews.
|
|
21602
|
+
children: "Runs on your live site only: not while you edit, and not on Vercel or Netlify previews."
|
|
21166
21603
|
})
|
|
21167
21604
|
]
|
|
21168
21605
|
}),
|
|
21606
|
+
/* @__PURE__ */ jsx(ConsentGroup, {
|
|
21607
|
+
enabled: consentEnabled(site),
|
|
21608
|
+
snippets: hasAnalyticsSnippet(site),
|
|
21609
|
+
onEnabled: onConsentEnabled,
|
|
21610
|
+
onEdit: onEditConsent
|
|
21611
|
+
}),
|
|
21169
21612
|
SITE_GROUPS.map((group) => {
|
|
21170
21613
|
const items = shown.filter((option) => groupOf(option) === group);
|
|
21171
21614
|
if (items.length === 0) return null;
|
|
@@ -21206,6 +21649,76 @@ function SiteGroup({ title, items, control, extra }) {
|
|
|
21206
21649
|
]
|
|
21207
21650
|
});
|
|
21208
21651
|
}
|
|
21652
|
+
/** The cookie banner: a switch here, its text in a dialog. */
|
|
21653
|
+
function ConsentGroup({ enabled, snippets, onEnabled, onEdit }) {
|
|
21654
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
21655
|
+
class: "bm-stack",
|
|
21656
|
+
children: [
|
|
21657
|
+
/* @__PURE__ */ jsx("p", {
|
|
21658
|
+
class: "bm-group",
|
|
21659
|
+
children: "Cookie banner"
|
|
21660
|
+
}),
|
|
21661
|
+
/* @__PURE__ */ jsxs("label", {
|
|
21662
|
+
class: "bm-switch-row",
|
|
21663
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
21664
|
+
class: "bm-switch-text",
|
|
21665
|
+
children: /* @__PURE__ */ jsxs("span", {
|
|
21666
|
+
class: "bm-switch-title",
|
|
21667
|
+
children: ["Ask before analytics runs", /* @__PURE__ */ jsx(Info, {
|
|
21668
|
+
label: "About the cookie banner",
|
|
21669
|
+
text: "Visitors in the EU and UK must agree before analytics cookies are set. With the banner on, nothing from the snippets loads until they accept. Google tags also get Consent Mode v2 signals."
|
|
21670
|
+
})]
|
|
21671
|
+
})
|
|
21672
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
21673
|
+
type: "checkbox",
|
|
21674
|
+
role: "switch",
|
|
21675
|
+
checked: enabled,
|
|
21676
|
+
onChange: (event) => onEnabled(event.currentTarget.checked)
|
|
21677
|
+
})]
|
|
21678
|
+
}),
|
|
21679
|
+
/* @__PURE__ */ jsx("p", {
|
|
21680
|
+
class: "bm-hint",
|
|
21681
|
+
children: !enabled ? "Off. Your snippets run for every visitor from the first page, EU visitors included." : snippets ? "On. Your snippets wait until the visitor accepts. The banner also shows while you edit, with analytics off." : "On. It appears once you add an analytics snippet above."
|
|
21682
|
+
}),
|
|
21683
|
+
/* @__PURE__ */ jsxs("button", {
|
|
21684
|
+
type: "button",
|
|
21685
|
+
class: "bm-tertiary bm-consent-edit",
|
|
21686
|
+
onClick: onEdit,
|
|
21687
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
21688
|
+
name: "settings",
|
|
21689
|
+
size: 14
|
|
21690
|
+
}), "Edit banner"]
|
|
21691
|
+
})
|
|
21692
|
+
]
|
|
21693
|
+
});
|
|
21694
|
+
}
|
|
21695
|
+
/** Structured data on every page: a summary here, the blocks in a dialog. */
|
|
21696
|
+
function JsonLdGroup({ site, onEdit }) {
|
|
21697
|
+
const blocks = siteJsonLdOf(site);
|
|
21698
|
+
const types = blocks.map((block) => typeof block["@type"] === "string" ? block["@type"] : "block");
|
|
21699
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
21700
|
+
class: "bm-stack",
|
|
21701
|
+
children: [
|
|
21702
|
+
/* @__PURE__ */ jsx("p", {
|
|
21703
|
+
class: "bm-group",
|
|
21704
|
+
children: "Structured data"
|
|
21705
|
+
}),
|
|
21706
|
+
/* @__PURE__ */ jsx("p", {
|
|
21707
|
+
class: "bm-hint",
|
|
21708
|
+
children: blocks.length === 0 ? "None. Add an Organization and a WebSite so search engines know who runs the site." : `On every page: ${types.join(", ")}.`
|
|
21709
|
+
}),
|
|
21710
|
+
/* @__PURE__ */ jsxs("button", {
|
|
21711
|
+
type: "button",
|
|
21712
|
+
class: "bm-tertiary bm-consent-edit",
|
|
21713
|
+
onClick: onEdit,
|
|
21714
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
21715
|
+
name: "settings",
|
|
21716
|
+
size: 14
|
|
21717
|
+
}), "Edit JSON-LD"]
|
|
21718
|
+
})
|
|
21719
|
+
]
|
|
21720
|
+
});
|
|
21721
|
+
}
|
|
21209
21722
|
//#endregion
|
|
21210
21723
|
//#region src/ui/forms/footer-form.tsx
|
|
21211
21724
|
/** Footer tab: layout, text, and the columns with their links. */
|
|
@@ -22104,7 +22617,7 @@ function DesignPanel({ site }) {
|
|
|
22104
22617
|
});
|
|
22105
22618
|
}
|
|
22106
22619
|
function SitePanel({ site, catalog }) {
|
|
22107
|
-
const { doc, edits } = useEditor();
|
|
22620
|
+
const { doc, edits, ui } = useEditor();
|
|
22108
22621
|
return /* @__PURE__ */ jsx(SiteForm, {
|
|
22109
22622
|
site,
|
|
22110
22623
|
options: catalog.siteOptions,
|
|
@@ -22116,6 +22629,17 @@ function SitePanel({ site, catalog }) {
|
|
|
22116
22629
|
const node = getAt(doc.site.peek(), "analytics");
|
|
22117
22630
|
if (typeof node === "object" && node !== null && Object.keys(node).length === 0) edits.writeOrClear("analytics", "");
|
|
22118
22631
|
},
|
|
22632
|
+
onConsentEnabled: (enabled) => {
|
|
22633
|
+
edits.writeField("consent.enabled", enabled ? void 0 : false);
|
|
22634
|
+
const node = getAt(doc.site.peek(), "consent");
|
|
22635
|
+
if (typeof node === "object" && node !== null && Object.keys(node).length === 0) edits.writeOrClear("consent", "");
|
|
22636
|
+
},
|
|
22637
|
+
onEditConsent: () => {
|
|
22638
|
+
ui.dialog.value = { kind: "consent" };
|
|
22639
|
+
},
|
|
22640
|
+
onEditJsonLd: () => {
|
|
22641
|
+
ui.dialog.value = { kind: "siteJsonLd" };
|
|
22642
|
+
},
|
|
22119
22643
|
onMarks: edits.applyMarksAt,
|
|
22120
22644
|
onUploaded: doc.addImage,
|
|
22121
22645
|
onDeleted: doc.dropImage
|