bearminion 1.4.0 → 1.6.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 +22 -0
- package/README.md +2 -2
- package/dist/astro.d.ts +2 -2
- package/dist/astro.js +234 -13
- package/dist/content.d.ts +1 -1
- package/dist/content.js +3 -3
- package/dist/index.d.ts +35 -23
- package/dist/index.js +9 -7
- package/dist/{migrate-B56wxCiC.js → migrate-b408EaLp.js} +3 -1
- package/dist/{options-qFsKXOuE.d.ts → options-DdCyRbPs.d.ts} +35 -2
- package/dist/{page-C87bm-bf.d.ts → page-BFuPEq6Q.d.ts} +56 -3
- 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/rich-text.d.ts +17 -8
- package/dist/rich-text.js +45 -24
- package/dist/{theme-DMpt3hcT.js → theme-DFjHjgFO.js} +1 -1
- package/dist/{theme-colors-zICpze42.js → theme-colors-CHROGZkI.js} +24 -49
- package/dist/ui/mount.js +902 -245
- package/package.json +5 -5
package/dist/ui/mount.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
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 { _ as consentCopy, b as
|
|
4
|
+
import { _ as consentCopy, b as parseMediaSource, 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-CHROGZkI.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";
|
|
8
8
|
import { useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "preact/hooks";
|
|
9
9
|
import { Fragment as Fragment$1, jsx, jsxs } from "preact/jsx-runtime";
|
|
10
|
+
//#endregion
|
|
10
11
|
//#region ../../node_modules/.pnpm/@preact+signals-core@1.14.4/node_modules/@preact/signals-core/dist/signals-core.mjs
|
|
11
12
|
const i$1 = Symbol.for("preact-signals");
|
|
12
13
|
function t() {
|
|
@@ -5051,7 +5052,7 @@ function wrap(tr, range, wrappers) {
|
|
|
5051
5052
|
let start = range.start, end = range.end;
|
|
5052
5053
|
tr.step(new ReplaceAroundStep(start, end, start, end, new Slice(content, 0, 0), wrappers.length, true));
|
|
5053
5054
|
}
|
|
5054
|
-
function setBlockType(tr, from, to, type, attrs) {
|
|
5055
|
+
function setBlockType$1(tr, from, to, type, attrs) {
|
|
5055
5056
|
if (!type.isTextblock) throw new RangeError("Type given to setBlockType should be a textblock");
|
|
5056
5057
|
let mapFrom = tr.steps.length;
|
|
5057
5058
|
tr.doc.nodesBetween(from, to, (node, pos) => {
|
|
@@ -5861,7 +5862,7 @@ var Transform = class {
|
|
|
5861
5862
|
the given node type with the given attributes.
|
|
5862
5863
|
*/
|
|
5863
5864
|
setBlockType(from, to = from, type, attrs = null) {
|
|
5864
|
-
setBlockType(this, from, to, type, attrs);
|
|
5865
|
+
setBlockType$1(this, from, to, type, attrs);
|
|
5865
5866
|
return this;
|
|
5866
5867
|
}
|
|
5867
5868
|
/**
|
|
@@ -7269,6 +7270,37 @@ const selectTextblockStart = selectTextblockSide(-1);
|
|
|
7269
7270
|
Moves the cursor to the end of current text block.
|
|
7270
7271
|
*/
|
|
7271
7272
|
const selectTextblockEnd = selectTextblockSide(1);
|
|
7273
|
+
/**
|
|
7274
|
+
Returns a command that tries to set the selected textblocks to the
|
|
7275
|
+
given node type with the given attributes.
|
|
7276
|
+
*/
|
|
7277
|
+
function setBlockType(nodeType, attrs = null) {
|
|
7278
|
+
return function(state, dispatch) {
|
|
7279
|
+
let applicable = false;
|
|
7280
|
+
for (let i = 0; i < state.selection.ranges.length && !applicable; i++) {
|
|
7281
|
+
let { $from: { pos: from }, $to: { pos: to } } = state.selection.ranges[i];
|
|
7282
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
7283
|
+
if (applicable) return false;
|
|
7284
|
+
if (!node.isTextblock || node.hasMarkup(nodeType, attrs)) return;
|
|
7285
|
+
if (node.type == nodeType) applicable = true;
|
|
7286
|
+
else {
|
|
7287
|
+
let $pos = state.doc.resolve(pos), index = $pos.index();
|
|
7288
|
+
applicable = $pos.parent.canReplaceWith(index, index + 1, nodeType);
|
|
7289
|
+
}
|
|
7290
|
+
});
|
|
7291
|
+
}
|
|
7292
|
+
if (!applicable) return false;
|
|
7293
|
+
if (dispatch) {
|
|
7294
|
+
let tr = state.tr;
|
|
7295
|
+
for (let i = 0; i < state.selection.ranges.length; i++) {
|
|
7296
|
+
let { $from: { pos: from }, $to: { pos: to } } = state.selection.ranges[i];
|
|
7297
|
+
tr.setBlockType(from, to, nodeType, attrs);
|
|
7298
|
+
}
|
|
7299
|
+
dispatch(tr.scrollIntoView());
|
|
7300
|
+
}
|
|
7301
|
+
return true;
|
|
7302
|
+
};
|
|
7303
|
+
}
|
|
7272
7304
|
function markApplies(doc, ranges, type, enterAtoms) {
|
|
7273
7305
|
for (let i = 0; i < ranges.length; i++) {
|
|
7274
7306
|
let { $from, $to } = ranges[i];
|
|
@@ -7941,7 +7973,7 @@ for (var i = 65; i <= 90; i++) {
|
|
|
7941
7973
|
base[i] = String.fromCharCode(i + 32);
|
|
7942
7974
|
shift[i] = String.fromCharCode(i);
|
|
7943
7975
|
}
|
|
7944
|
-
for (var code
|
|
7976
|
+
for (var code in base) if (!shift.hasOwnProperty(code)) shift[code] = base[code];
|
|
7945
7977
|
function keyName(event) {
|
|
7946
7978
|
var name = !(mac$2 && event.metaKey && event.shiftKey && !event.ctrlKey && !event.altKey || ie$1 && event.shiftKey && event.key && event.key.length == 1 || event.key == "Unidentified") && event.key || (event.shiftKey ? shift : base)[event.keyCode] || event.key || "Unidentified";
|
|
7947
7979
|
if (name == "Esc") name = "Escape";
|
|
@@ -12825,115 +12857,173 @@ function checkStateComponent(plugin) {
|
|
|
12825
12857
|
}
|
|
12826
12858
|
//#endregion
|
|
12827
12859
|
//#region src/ui/rich-editor.ts
|
|
12828
|
-
/**
|
|
12829
|
-
|
|
12830
|
-
|
|
12831
|
-
|
|
12832
|
-
|
|
12833
|
-
|
|
12834
|
-
|
|
12835
|
-
|
|
12836
|
-
|
|
12837
|
-
|
|
12838
|
-
|
|
12839
|
-
|
|
12840
|
-
|
|
12841
|
-
|
|
12842
|
-
|
|
12843
|
-
content: "list_item+",
|
|
12844
|
-
group: "block",
|
|
12845
|
-
parseDOM: [{ tag: "ul" }],
|
|
12846
|
-
toDOM: () => ["ul", 0]
|
|
12847
|
-
},
|
|
12848
|
-
ordered_list: {
|
|
12849
|
-
content: "list_item+",
|
|
12850
|
-
group: "block",
|
|
12851
|
-
parseDOM: [{ tag: "ol" }],
|
|
12852
|
-
toDOM: () => ["ol", 0]
|
|
12860
|
+
/** A schema type that every rich schema defines, narrowed from `| undefined`. */
|
|
12861
|
+
function need(value) {
|
|
12862
|
+
if (value === void 0) throw new Error("Missing rich-text schema type");
|
|
12863
|
+
return value;
|
|
12864
|
+
}
|
|
12865
|
+
/** A heading the field allows: h2 or h3. The page title is the h1, and the markdown subset stops at three. */
|
|
12866
|
+
const headingNode = {
|
|
12867
|
+
attrs: { level: { default: 2 } },
|
|
12868
|
+
content: "inline*",
|
|
12869
|
+
group: "block",
|
|
12870
|
+
defining: true,
|
|
12871
|
+
parseDOM: [
|
|
12872
|
+
{
|
|
12873
|
+
tag: "h1",
|
|
12874
|
+
attrs: { level: 2 }
|
|
12853
12875
|
},
|
|
12854
|
-
|
|
12855
|
-
|
|
12856
|
-
|
|
12857
|
-
parseDOM: [{ tag: "li" }],
|
|
12858
|
-
toDOM: () => ["li", 0]
|
|
12876
|
+
{
|
|
12877
|
+
tag: "h2",
|
|
12878
|
+
attrs: { level: 2 }
|
|
12859
12879
|
},
|
|
12860
|
-
|
|
12861
|
-
|
|
12862
|
-
|
|
12863
|
-
selectable: false,
|
|
12864
|
-
parseDOM: [{ tag: "br" }],
|
|
12865
|
-
toDOM: () => ["br"]
|
|
12880
|
+
{
|
|
12881
|
+
tag: "h3",
|
|
12882
|
+
attrs: { level: 3 }
|
|
12866
12883
|
},
|
|
12867
|
-
|
|
12868
|
-
|
|
12869
|
-
|
|
12870
|
-
strong: {
|
|
12871
|
-
parseDOM: [{ tag: "strong" }, { tag: "b" }],
|
|
12872
|
-
toDOM: () => ["strong", 0]
|
|
12884
|
+
{
|
|
12885
|
+
tag: "h4",
|
|
12886
|
+
attrs: { level: 3 }
|
|
12873
12887
|
},
|
|
12874
|
-
|
|
12875
|
-
|
|
12876
|
-
|
|
12888
|
+
{
|
|
12889
|
+
tag: "h5",
|
|
12890
|
+
attrs: { level: 3 }
|
|
12877
12891
|
},
|
|
12878
|
-
|
|
12879
|
-
|
|
12880
|
-
|
|
12881
|
-
|
|
12882
|
-
|
|
12892
|
+
{
|
|
12893
|
+
tag: "h6",
|
|
12894
|
+
attrs: { level: 3 }
|
|
12895
|
+
}
|
|
12896
|
+
],
|
|
12897
|
+
toDOM: (node) => [`h${node.attrs.level}`, 0]
|
|
12898
|
+
};
|
|
12899
|
+
function createSchema(headings) {
|
|
12900
|
+
return new Schema({
|
|
12901
|
+
nodes: {
|
|
12902
|
+
doc: { content: headings ? "(paragraph | heading | bullet_list | ordered_list)+" : "(paragraph | bullet_list | ordered_list)+" },
|
|
12903
|
+
paragraph: {
|
|
12904
|
+
content: "inline*",
|
|
12905
|
+
group: "block",
|
|
12906
|
+
parseDOM: [{ tag: "p" }],
|
|
12907
|
+
toDOM: () => ["p", 0]
|
|
12908
|
+
},
|
|
12909
|
+
...headings ? { heading: headingNode } : {},
|
|
12910
|
+
bullet_list: {
|
|
12911
|
+
content: "list_item+",
|
|
12912
|
+
group: "block",
|
|
12913
|
+
parseDOM: [{ tag: "ul" }],
|
|
12914
|
+
toDOM: () => ["ul", 0]
|
|
12915
|
+
},
|
|
12916
|
+
ordered_list: {
|
|
12917
|
+
content: "list_item+",
|
|
12918
|
+
group: "block",
|
|
12919
|
+
parseDOM: [{ tag: "ol" }],
|
|
12920
|
+
toDOM: () => ["ol", 0]
|
|
12921
|
+
},
|
|
12922
|
+
list_item: {
|
|
12923
|
+
content: "paragraph",
|
|
12924
|
+
defining: true,
|
|
12925
|
+
parseDOM: [{ tag: "li" }],
|
|
12926
|
+
toDOM: () => ["li", 0]
|
|
12927
|
+
},
|
|
12928
|
+
hard_break: {
|
|
12929
|
+
inline: true,
|
|
12930
|
+
group: "inline",
|
|
12931
|
+
selectable: false,
|
|
12932
|
+
parseDOM: [{ tag: "br" }],
|
|
12933
|
+
toDOM: () => ["br"]
|
|
12934
|
+
},
|
|
12935
|
+
text: { group: "inline" }
|
|
12883
12936
|
},
|
|
12884
|
-
|
|
12885
|
-
|
|
12886
|
-
|
|
12887
|
-
|
|
12937
|
+
marks: {
|
|
12938
|
+
strong: {
|
|
12939
|
+
parseDOM: [{ tag: "strong" }, { tag: "b" }],
|
|
12940
|
+
toDOM: () => ["strong", 0]
|
|
12888
12941
|
},
|
|
12889
|
-
|
|
12890
|
-
|
|
12891
|
-
|
|
12892
|
-
|
|
12893
|
-
|
|
12894
|
-
|
|
12895
|
-
|
|
12896
|
-
|
|
12897
|
-
|
|
12898
|
-
|
|
12899
|
-
|
|
12942
|
+
em: {
|
|
12943
|
+
parseDOM: [{ tag: "em" }, { tag: "i" }],
|
|
12944
|
+
toDOM: () => ["em", 0]
|
|
12945
|
+
},
|
|
12946
|
+
code: {
|
|
12947
|
+
excludes: "_",
|
|
12948
|
+
inclusive: false,
|
|
12949
|
+
parseDOM: [{ tag: "code" }],
|
|
12950
|
+
toDOM: () => ["code", 0]
|
|
12951
|
+
},
|
|
12952
|
+
link: {
|
|
12953
|
+
attrs: {
|
|
12954
|
+
href: {},
|
|
12955
|
+
newTab: { default: null }
|
|
12956
|
+
},
|
|
12957
|
+
inclusive: false,
|
|
12958
|
+
parseDOM: [{
|
|
12959
|
+
tag: "a[href]",
|
|
12960
|
+
getAttrs: (dom) => {
|
|
12961
|
+
const href = dom.getAttribute("href") ?? "";
|
|
12962
|
+
if (!isSafeHref(href)) return false;
|
|
12963
|
+
const tab = dom.getAttribute("data-new-tab");
|
|
12964
|
+
return {
|
|
12965
|
+
href,
|
|
12966
|
+
newTab: tab === "true" ? "new" : tab === "false" ? "same" : null
|
|
12967
|
+
};
|
|
12968
|
+
}
|
|
12969
|
+
}],
|
|
12970
|
+
toDOM: (mark) => {
|
|
12971
|
+
const tab = mark.attrs.newTab;
|
|
12972
|
+
const attrs = { href: String(mark.attrs.href) };
|
|
12973
|
+
if (tab === "new") attrs["data-new-tab"] = "true";
|
|
12974
|
+
else if (tab === "same") attrs["data-new-tab"] = "false";
|
|
12975
|
+
return [
|
|
12976
|
+
"a",
|
|
12977
|
+
attrs,
|
|
12978
|
+
0
|
|
12979
|
+
];
|
|
12900
12980
|
}
|
|
12901
|
-
}],
|
|
12902
|
-
toDOM: (mark) => {
|
|
12903
|
-
const tab = mark.attrs.newTab;
|
|
12904
|
-
const attrs = { href: String(mark.attrs.href) };
|
|
12905
|
-
if (tab === "new") attrs["data-new-tab"] = "true";
|
|
12906
|
-
else if (tab === "same") attrs["data-new-tab"] = "false";
|
|
12907
|
-
return [
|
|
12908
|
-
"a",
|
|
12909
|
-
attrs,
|
|
12910
|
-
0
|
|
12911
|
-
];
|
|
12912
12981
|
}
|
|
12913
12982
|
}
|
|
12914
|
-
}
|
|
12915
|
-
}
|
|
12916
|
-
|
|
12917
|
-
|
|
12918
|
-
|
|
12919
|
-
|
|
12983
|
+
});
|
|
12984
|
+
}
|
|
12985
|
+
/**
|
|
12986
|
+
* The rich-text subset as a ProseMirror schema: paragraphs, line breaks, flat bulleted and numbered lists, bold,
|
|
12987
|
+
* italic, inline code and links. Nothing else can exist in the document, so nothing else can be pasted or typed into it.
|
|
12988
|
+
* Storage stays the markdown subset of `rich-text.ts`; this module only converts at the edges.
|
|
12989
|
+
*/
|
|
12990
|
+
const richSchema = createSchema(false);
|
|
12991
|
+
/** The same, plus h2 and h3, for the fields that ask for headings (`data-rich-headings` on the field). */
|
|
12992
|
+
const headingRichSchema = createSchema(true);
|
|
12993
|
+
function kitOf(headings) {
|
|
12994
|
+
const schema = headings ? headingRichSchema : richSchema;
|
|
12995
|
+
return {
|
|
12996
|
+
schema,
|
|
12997
|
+
parser: DOMParser.fromSchema(schema),
|
|
12998
|
+
serializer: DOMSerializer.fromSchema(schema),
|
|
12999
|
+
options: { headings }
|
|
13000
|
+
};
|
|
13001
|
+
}
|
|
13002
|
+
const plainKit = kitOf(false);
|
|
13003
|
+
const headingKit = kitOf(true);
|
|
13004
|
+
/** The kit a document or a state belongs to. */
|
|
13005
|
+
function kitFor(schema) {
|
|
13006
|
+
return schema === headingRichSchema ? headingKit : plainKit;
|
|
13007
|
+
}
|
|
12920
13008
|
function container(html) {
|
|
12921
13009
|
const node = document.createElement("div");
|
|
12922
13010
|
node.innerHTML = html;
|
|
12923
13011
|
return node;
|
|
12924
13012
|
}
|
|
12925
13013
|
/** Markdown → document, through the same renderer the page uses, so what you edit is what the page shows. */
|
|
12926
|
-
function docFromMarkdown(markdown) {
|
|
12927
|
-
|
|
13014
|
+
function docFromMarkdown(markdown, headings = false) {
|
|
13015
|
+
const kit = headings ? headingKit : plainKit;
|
|
13016
|
+
return kit.parser.parse(container(renderRichText(markdown, kit.options)));
|
|
12928
13017
|
}
|
|
12929
13018
|
/** Document → markdown, through the same serializer the draft has always used. */
|
|
12930
13019
|
function markdownFromDoc(doc) {
|
|
13020
|
+
const kit = kitFor(doc.type.schema);
|
|
12931
13021
|
const holder = document.createElement("div");
|
|
12932
|
-
holder.append(serializer.serializeFragment(doc.content));
|
|
12933
|
-
return markdownFromHtml(holder.innerHTML);
|
|
13022
|
+
holder.append(kit.serializer.serializeFragment(doc.content));
|
|
13023
|
+
return markdownFromHtml(holder.innerHTML, kit.options);
|
|
12934
13024
|
}
|
|
12935
|
-
function sliceFromMarkdown(markdown) {
|
|
12936
|
-
return parser.parseSlice(container(renderRichText(markdown)));
|
|
13025
|
+
function sliceFromMarkdown(markdown, kit) {
|
|
13026
|
+
return kit.parser.parseSlice(container(renderRichText(markdown, kit.options)));
|
|
12937
13027
|
}
|
|
12938
13028
|
/** Document position of the `offset`-th character of the text; breaks and block edges count for nothing. */
|
|
12939
13029
|
function posAtTextOffset(doc, offset) {
|
|
@@ -12954,8 +13044,10 @@ function posAtTextOffset(doc, offset) {
|
|
|
12954
13044
|
});
|
|
12955
13045
|
return found >= 0 ? found : end;
|
|
12956
13046
|
}
|
|
13047
|
+
/** A line break. A heading is one line of markdown, so Shift-Enter does nothing there. */
|
|
12957
13048
|
const insertLineBreak = (state, dispatch) => {
|
|
12958
|
-
|
|
13049
|
+
if (state.selection.$from.parent.type.name === "heading") return true;
|
|
13050
|
+
dispatch?.(state.tr.replaceSelectionWith(need(state.schema.nodes.hard_break).create()).scrollIntoView());
|
|
12959
13051
|
return true;
|
|
12960
13052
|
};
|
|
12961
13053
|
/**
|
|
@@ -12964,6 +13056,8 @@ const insertLineBreak = (state, dispatch) => {
|
|
|
12964
13056
|
*/
|
|
12965
13057
|
function wrapEachInList(kind) {
|
|
12966
13058
|
return (state, dispatch) => {
|
|
13059
|
+
const { paragraph, list_item } = state.schema.nodes;
|
|
13060
|
+
if (!paragraph || !list_item) return false;
|
|
12967
13061
|
const range = state.selection.$from.blockRange(state.selection.$to);
|
|
12968
13062
|
if (!range) return false;
|
|
12969
13063
|
const paragraphs = [];
|
|
@@ -12977,6 +13071,7 @@ function wrapEachInList(kind) {
|
|
|
12977
13071
|
}
|
|
12978
13072
|
/** The lists the selection touches (lists never nest). Works for a caret, a range and select-all alike. */
|
|
12979
13073
|
function listsIn(state) {
|
|
13074
|
+
const { bullet_list, ordered_list } = state.schema.nodes;
|
|
12980
13075
|
const { from, to } = state.selection;
|
|
12981
13076
|
const found = [];
|
|
12982
13077
|
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
@@ -13022,8 +13117,10 @@ function unwrapSelected(lists, from, to) {
|
|
|
13022
13117
|
};
|
|
13023
13118
|
}
|
|
13024
13119
|
/** Toggle a list: unwrap the same kind, convert the other kind, otherwise wrap. */
|
|
13025
|
-
function toggleList(
|
|
13120
|
+
function toggleList(name) {
|
|
13026
13121
|
return (state, dispatch) => {
|
|
13122
|
+
const kind = state.schema.nodes[name];
|
|
13123
|
+
if (!kind) return false;
|
|
13027
13124
|
const lists = listsIn(state);
|
|
13028
13125
|
if (lists.length === 0) return wrapInList(kind)(state, dispatch) || wrapEachInList(kind)(state, dispatch);
|
|
13029
13126
|
if (lists.every((list) => list.node.type === kind)) return unwrapSelected(lists, state.selection.from, state.selection.to)(state, dispatch);
|
|
@@ -13039,39 +13136,62 @@ function toggleList(kind) {
|
|
|
13039
13136
|
function setLink(href) {
|
|
13040
13137
|
return (state, dispatch) => {
|
|
13041
13138
|
const { from, to, empty } = state.selection;
|
|
13042
|
-
|
|
13139
|
+
const link = state.schema.marks.link;
|
|
13140
|
+
if (empty || !link || !isSafeHref(href)) return false;
|
|
13043
13141
|
dispatch?.(state.tr.addMark(from, to, link.create({ href })));
|
|
13044
13142
|
return true;
|
|
13045
13143
|
};
|
|
13046
13144
|
}
|
|
13047
|
-
/**
|
|
13145
|
+
/** Make the selected block a heading of this level, or a paragraph again when it already is one. */
|
|
13146
|
+
function toggleHeading(level) {
|
|
13147
|
+
return (state, dispatch) => {
|
|
13148
|
+
const { heading, paragraph } = state.schema.nodes;
|
|
13149
|
+
if (!heading || !paragraph) return false;
|
|
13150
|
+
const parent = state.selection.$from.parent;
|
|
13151
|
+
return (parent.type === heading && parent.attrs.level === level ? setBlockType(paragraph) : setBlockType(heading, { level }))(state, dispatch);
|
|
13152
|
+
};
|
|
13153
|
+
}
|
|
13154
|
+
/** The editing command behind a toolbar button. `url` is for `link`. It reads the node and mark types from the state it runs on. */
|
|
13048
13155
|
function commandFor(command, url = "") {
|
|
13049
13156
|
switch (command) {
|
|
13050
|
-
case "bold": return toggleMark(strong);
|
|
13051
|
-
case "italic": return toggleMark(em);
|
|
13052
|
-
case "code": return toggleMark(code);
|
|
13053
|
-
case "bulletList": return toggleList(bullet_list);
|
|
13054
|
-
case "orderedList": return toggleList(ordered_list);
|
|
13157
|
+
case "bold": return (state, dispatch) => toggleMark(need(state.schema.marks.strong))(state, dispatch);
|
|
13158
|
+
case "italic": return (state, dispatch) => toggleMark(need(state.schema.marks.em))(state, dispatch);
|
|
13159
|
+
case "code": return (state, dispatch) => toggleMark(need(state.schema.marks.code))(state, dispatch);
|
|
13160
|
+
case "bulletList": return toggleList("bullet_list");
|
|
13161
|
+
case "orderedList": return toggleList("ordered_list");
|
|
13055
13162
|
case "link": return setLink(url);
|
|
13163
|
+
case "heading2": return toggleHeading(2);
|
|
13164
|
+
case "heading3": return toggleHeading(3);
|
|
13056
13165
|
}
|
|
13057
13166
|
}
|
|
13167
|
+
/** Enter at the end of a heading starts a paragraph, not another heading. In the middle it splits the heading. */
|
|
13168
|
+
const enterInHeading = (state, dispatch) => {
|
|
13169
|
+
const { heading, paragraph } = state.schema.nodes;
|
|
13170
|
+
if (!heading || !paragraph || state.selection.$from.parent.type !== heading) return false;
|
|
13171
|
+
return splitBlockAs((_node, atEnd) => atEnd ? { type: paragraph } : null)(state, dispatch);
|
|
13172
|
+
};
|
|
13058
13173
|
/** Enter is a line break, a new item inside a list, and leaves the list from an empty item. */
|
|
13059
|
-
const enterCommand = chainCommands(splitListItem(list_item), liftEmptyBlock, insertLineBreak);
|
|
13174
|
+
const enterCommand = (state, dispatch) => chainCommands(enterInHeading, splitListItem(need(state.schema.nodes.list_item)), liftEmptyBlock, insertLineBreak)(state, dispatch);
|
|
13060
13175
|
function activeOf(state) {
|
|
13061
13176
|
const { $from, from, to, empty } = state.selection;
|
|
13062
|
-
const
|
|
13177
|
+
const { strong, em, code, link } = state.schema.marks;
|
|
13178
|
+
const { bullet_list, ordered_list, heading } = state.schema.nodes;
|
|
13179
|
+
const marked = (type) => type === void 0 ? false : empty ? type.isInSet(state.storedMarks ?? $from.marks()) !== void 0 : state.doc.rangeHasMark(from, to, type);
|
|
13063
13180
|
const lists = listsIn(state);
|
|
13181
|
+
const level = heading !== void 0 && $from.parent.type === heading ? $from.parent.attrs.level : 0;
|
|
13064
13182
|
return {
|
|
13065
13183
|
bold: marked(strong),
|
|
13066
13184
|
italic: marked(em),
|
|
13067
13185
|
code: marked(code),
|
|
13068
13186
|
link: marked(link),
|
|
13069
13187
|
bulletList: lists.length > 0 && lists.every((item) => item.node.type === bullet_list),
|
|
13070
|
-
orderedList: lists.length > 0 && lists.every((item) => item.node.type === ordered_list)
|
|
13188
|
+
orderedList: lists.length > 0 && lists.every((item) => item.node.type === ordered_list),
|
|
13189
|
+
heading: level === 2 || level === 3 ? level : 0,
|
|
13190
|
+
headings: heading !== void 0
|
|
13071
13191
|
};
|
|
13072
13192
|
}
|
|
13073
13193
|
function sameActive(a, b) {
|
|
13074
|
-
return a.bold === b.bold && a.italic === b.italic && a.code === b.code && a.link === b.link && a.bulletList === b.bulletList && a.orderedList === b.orderedList;
|
|
13194
|
+
return a.bold === b.bold && a.italic === b.italic && a.code === b.code && a.link === b.link && a.bulletList === b.bulletList && a.orderedList === b.orderedList && a.heading === b.heading && a.headings === b.headings;
|
|
13075
13195
|
}
|
|
13076
13196
|
const mounted = /* @__PURE__ */ new WeakMap();
|
|
13077
13197
|
/** The editor mounted on `field`, if any. */
|
|
@@ -13082,16 +13202,18 @@ function richEditorOf(field) {
|
|
|
13082
13202
|
* Make `field` a ProseMirror editor over `markdown`. The field element itself becomes the editable surface, so the
|
|
13083
13203
|
* page's own typography applies. Focus leaving the field (to anywhere but the editor's panels) ends the edit.
|
|
13084
13204
|
*/
|
|
13085
|
-
function mountRichEditor(field, { markdown, selection, onChange, onActive, keepsEdit, onDestroy }) {
|
|
13205
|
+
function mountRichEditor(field, { markdown, headings = false, selection, onChange, onActive, keepsEdit, onDestroy }) {
|
|
13086
13206
|
const existing = mounted.get(field);
|
|
13087
13207
|
if (existing) return existing;
|
|
13208
|
+
const kit = headings ? headingKit : plainKit;
|
|
13209
|
+
const { strong, em } = kit.schema.marks;
|
|
13088
13210
|
const stateFor = (source) => EditorState.create({
|
|
13089
|
-
doc: docFromMarkdown(source),
|
|
13211
|
+
doc: docFromMarkdown(source, headings),
|
|
13090
13212
|
plugins: [
|
|
13091
13213
|
history(),
|
|
13092
13214
|
keymap({
|
|
13093
|
-
"Mod-b": toggleMark(strong),
|
|
13094
|
-
"Mod-i": toggleMark(em),
|
|
13215
|
+
"Mod-b": toggleMark(need(strong)),
|
|
13216
|
+
"Mod-i": toggleMark(need(em)),
|
|
13095
13217
|
"Mod-z": undo,
|
|
13096
13218
|
"Mod-y": redo,
|
|
13097
13219
|
"Shift-Mod-z": redo,
|
|
@@ -13120,8 +13242,8 @@ function mountRichEditor(field, { markdown, selection, onChange, onActive, keeps
|
|
|
13120
13242
|
lastMarkdown = markdownFromDoc(next.doc);
|
|
13121
13243
|
onChange(lastMarkdown);
|
|
13122
13244
|
},
|
|
13123
|
-
transformPastedHTML: (html) => sanitizeRichHtml(html),
|
|
13124
|
-
clipboardTextParser: (text) => sliceFromMarkdown(stripPlainRich(text))
|
|
13245
|
+
transformPastedHTML: (html) => sanitizeRichHtml(html, kit.options),
|
|
13246
|
+
clipboardTextParser: (text) => sliceFromMarkdown(stripPlainRich(text, kit.options), kit)
|
|
13125
13247
|
});
|
|
13126
13248
|
if (selection) {
|
|
13127
13249
|
const { doc } = view.state;
|
|
@@ -13156,7 +13278,7 @@ function mountRichEditor(field, { markdown, selection, onChange, onActive, keeps
|
|
|
13156
13278
|
field.removeAttribute("contenteditable");
|
|
13157
13279
|
field.removeAttribute("translate");
|
|
13158
13280
|
if (!field.getAttribute("class")) field.removeAttribute("class");
|
|
13159
|
-
field.innerHTML = renderRichText(lastMarkdown);
|
|
13281
|
+
field.innerHTML = renderRichText(lastMarkdown, kit.options);
|
|
13160
13282
|
onDestroy?.();
|
|
13161
13283
|
}
|
|
13162
13284
|
};
|
|
@@ -13321,7 +13443,7 @@ function paintPage(draft) {
|
|
|
13321
13443
|
if (isRichField(raw, node.getAttribute("data-field-kind"))) {
|
|
13322
13444
|
const editing = richEditorOf(node);
|
|
13323
13445
|
if (editing) editing.setMarkdown(value);
|
|
13324
|
-
else node.innerHTML = renderRichText(value);
|
|
13446
|
+
else node.innerHTML = renderRichText(value, { headings: node.getAttribute("data-rich-headings") === "true" });
|
|
13325
13447
|
} else if (isHeadingField(path)) node.innerHTML = renderMarkedHtml(value, marksAt(draft, path));
|
|
13326
13448
|
else node.textContent = value;
|
|
13327
13449
|
}
|
|
@@ -13447,10 +13569,18 @@ function isOptionalPath(content, path) {
|
|
|
13447
13569
|
if (parts.length === 3 && arrayKey !== void 0 && key !== void 0 && /^\d+$/.test(index ?? "")) return content.items?.[arrayKey]?.includes(key) ?? false;
|
|
13448
13570
|
return false;
|
|
13449
13571
|
}
|
|
13450
|
-
/**
|
|
13572
|
+
/**
|
|
13573
|
+
* Section content keys in the order the form lists them: defaults first. An optional prop the defaults leave out sits
|
|
13574
|
+
* right after the optional prop listed before it (`body` after `heading`), or at the end when none of those exist.
|
|
13575
|
+
*/
|
|
13451
13576
|
function orderContentKeys(present, optional, defaultKeys) {
|
|
13452
13577
|
const keys = [.../* @__PURE__ */ new Set([...present, ...optional])];
|
|
13453
|
-
const rank = [...defaultKeys
|
|
13578
|
+
const rank = [...defaultKeys];
|
|
13579
|
+
optional.forEach((key, position) => {
|
|
13580
|
+
if (rank.includes(key)) return;
|
|
13581
|
+
const before = optional.slice(0, position).reverse().find((name) => rank.includes(name));
|
|
13582
|
+
rank.splice(before === void 0 ? rank.length : rank.indexOf(before) + 1, 0, key);
|
|
13583
|
+
});
|
|
13454
13584
|
const at = (key) => {
|
|
13455
13585
|
const found = rank.indexOf(key);
|
|
13456
13586
|
return found === -1 ? Number.MAX_SAFE_INTEGER : found;
|
|
@@ -13593,6 +13723,7 @@ function attachPageEditing(editor) {
|
|
|
13593
13723
|
let stop = () => {};
|
|
13594
13724
|
const handle = mountRichEditor(field, {
|
|
13595
13725
|
markdown: read(),
|
|
13726
|
+
headings: field.getAttribute("data-rich-headings") === "true",
|
|
13596
13727
|
selection,
|
|
13597
13728
|
onChange: (markdown) => edits.writeField(pathOf(field), markdown),
|
|
13598
13729
|
keepsEdit: isInEditor,
|
|
@@ -13867,6 +13998,24 @@ function hasAnalyticsSnippet(site) {
|
|
|
13867
13998
|
function consentReopenOf(site) {
|
|
13868
13999
|
return getAt(site, "consent.reopen") === "icon" ? "icon" : "footer";
|
|
13869
14000
|
}
|
|
14001
|
+
/** The JSON-LD blocks every page carries, as the draft holds them. */
|
|
14002
|
+
function siteJsonLdOf(site) {
|
|
14003
|
+
const stored = getAt(site, "seo.jsonLd");
|
|
14004
|
+
return Array.isArray(stored) ? stored.filter(isRecord$2) : [];
|
|
14005
|
+
}
|
|
14006
|
+
/** What Generate reads from the draft. The logo counts only when it is a full https address. */
|
|
14007
|
+
function siteJsonLdSourceOf(site) {
|
|
14008
|
+
const name = getAt(site, "name");
|
|
14009
|
+
const url = getAt(site, "url");
|
|
14010
|
+
const lang = getAt(site, "lang");
|
|
14011
|
+
const logo = getAt(site, "brandLogo.src");
|
|
14012
|
+
return {
|
|
14013
|
+
name: typeof name === "string" ? name : "",
|
|
14014
|
+
url: typeof url === "string" ? url : "",
|
|
14015
|
+
lang: typeof lang === "string" && lang !== "" ? lang : void 0,
|
|
14016
|
+
logo: typeof logo === "string" && /^https:\/\//i.test(logo) ? logo : void 0
|
|
14017
|
+
};
|
|
14018
|
+
}
|
|
13870
14019
|
//#endregion
|
|
13871
14020
|
//#region src/ui/controllers/reactions.ts
|
|
13872
14021
|
/** Signal effects that keep the page, storage and history in step with the editor state. */
|
|
@@ -14168,7 +14317,7 @@ function attachShortcuts(editor) {
|
|
|
14168
14317
|
return;
|
|
14169
14318
|
}
|
|
14170
14319
|
if (ui.previewing.peek()) {
|
|
14171
|
-
if (event.key === "Escape") {
|
|
14320
|
+
if (event.key === "Escape" && !document.querySelector("dialog[open]")) {
|
|
14172
14321
|
event.preventDefault();
|
|
14173
14322
|
chrome.exitPreview();
|
|
14174
14323
|
}
|
|
@@ -14432,8 +14581,10 @@ function createChrome(ui) {
|
|
|
14432
14581
|
for (const node of document.querySelectorAll("[data-field][contenteditable]")) node.removeAttribute("contenteditable");
|
|
14433
14582
|
if (document.activeElement instanceof HTMLElement) document.activeElement.blur();
|
|
14434
14583
|
document.documentElement.style.cursor = "";
|
|
14584
|
+
document.documentElement.setAttribute("data-bearminion-preview", "");
|
|
14435
14585
|
}
|
|
14436
14586
|
function exitPreview() {
|
|
14587
|
+
document.documentElement.removeAttribute("data-bearminion-preview");
|
|
14437
14588
|
writeStorage(session, PREVIEW_KEY, null);
|
|
14438
14589
|
n(() => {
|
|
14439
14590
|
ui.previewing.value = false;
|
|
@@ -14550,6 +14701,15 @@ function uploadImage(file) {
|
|
|
14550
14701
|
body
|
|
14551
14702
|
});
|
|
14552
14703
|
}
|
|
14704
|
+
const getVideos = () => request("/videos");
|
|
14705
|
+
function uploadVideo(file) {
|
|
14706
|
+
const body = new FormData();
|
|
14707
|
+
body.set("file", file);
|
|
14708
|
+
return request("/videos", {
|
|
14709
|
+
method: "POST",
|
|
14710
|
+
body
|
|
14711
|
+
});
|
|
14712
|
+
}
|
|
14553
14713
|
const getGit = () => request("/git");
|
|
14554
14714
|
const commit = (body) => request("/git/commit", sendJson("POST", body));
|
|
14555
14715
|
/** Saved versions of the site (`scope` site) or of one page. */
|
|
@@ -16596,7 +16756,7 @@ const FOCUSABLE = "button:not(:disabled), input:not(:disabled), [tabindex=\"0\"]
|
|
|
16596
16756
|
* Filled: thumbnail, file name, alt text, Change and Remove. The picker is a modal
|
|
16597
16757
|
* with an Upload tab (drop or browse) and a Library tab (pick or delete).
|
|
16598
16758
|
*/
|
|
16599
|
-
function ImageField({ label, path, value, images, withAlt = true, layout = true, hideable = false, onChange, onUploaded, onDeleted }) {
|
|
16759
|
+
function ImageField({ label, path, value, images, withAlt = true, layout = true, hideable = false, captionable = false, onChange, onUploaded, onDeleted }) {
|
|
16600
16760
|
const [open, setOpen] = useState(false);
|
|
16601
16761
|
const [error, setError] = useState("");
|
|
16602
16762
|
const [over, setOver] = useState(false);
|
|
@@ -16608,18 +16768,24 @@ function ImageField({ label, path, value, images, withAlt = true, layout = true,
|
|
|
16608
16768
|
const position = isImagePosition(value?.position) ? value.position : DEFAULT_IMAGE_POSITION;
|
|
16609
16769
|
const showLayout = layout && withAlt;
|
|
16610
16770
|
const hidden = hideable && value?.hidden === true;
|
|
16771
|
+
const caption = typeof value?.caption === "string" ? value.caption : "";
|
|
16611
16772
|
const current = {
|
|
16612
16773
|
src,
|
|
16613
16774
|
alt,
|
|
16775
|
+
...caption ? { caption } : {},
|
|
16614
16776
|
fit,
|
|
16615
16777
|
position,
|
|
16616
16778
|
...hidden ? { hidden } : {}
|
|
16617
16779
|
};
|
|
16618
16780
|
/** Emit the whole image object. A new file or alt keeps the fit and position already chosen. */
|
|
16619
|
-
const emit = (patch) =>
|
|
16620
|
-
|
|
16621
|
-
|
|
16622
|
-
|
|
16781
|
+
const emit = (patch) => {
|
|
16782
|
+
const next = {
|
|
16783
|
+
...current,
|
|
16784
|
+
...patch
|
|
16785
|
+
};
|
|
16786
|
+
if (next.caption === "") delete next.caption;
|
|
16787
|
+
onChange(compact(next));
|
|
16788
|
+
};
|
|
16623
16789
|
const empty = src === "" || src === "placeholder";
|
|
16624
16790
|
const shown = previewSrc(src);
|
|
16625
16791
|
const missing = useMissingFile(empty ? "" : src);
|
|
@@ -16686,15 +16852,9 @@ function ImageField({ label, path, value, images, withAlt = true, layout = true,
|
|
|
16686
16852
|
children: [
|
|
16687
16853
|
/* @__PURE__ */ jsx("span", {
|
|
16688
16854
|
class: "bm-label",
|
|
16689
|
-
children: /* @__PURE__ */
|
|
16690
|
-
class: "bm-label-
|
|
16691
|
-
children:
|
|
16692
|
-
class: "bm-label-text",
|
|
16693
|
-
children: label
|
|
16694
|
-
}), withAlt && !empty && /* @__PURE__ */ jsx("span", {
|
|
16695
|
-
class: "bm-meta-tag",
|
|
16696
|
-
children: "alt required"
|
|
16697
|
-
})]
|
|
16855
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
16856
|
+
class: "bm-label-text",
|
|
16857
|
+
children: label
|
|
16698
16858
|
})
|
|
16699
16859
|
}),
|
|
16700
16860
|
hideable && /* @__PURE__ */ jsxs("label", {
|
|
@@ -16822,12 +16982,46 @@ function ImageField({ label, path, value, images, withAlt = true, layout = true,
|
|
|
16822
16982
|
})]
|
|
16823
16983
|
})]
|
|
16824
16984
|
})]
|
|
16825
|
-
}), withAlt && /* @__PURE__ */
|
|
16826
|
-
class: "bm-
|
|
16827
|
-
|
|
16828
|
-
|
|
16829
|
-
|
|
16830
|
-
|
|
16985
|
+
}), withAlt && /* @__PURE__ */ jsxs("div", {
|
|
16986
|
+
class: "bm-stack bm-stack-tight",
|
|
16987
|
+
children: [
|
|
16988
|
+
/* @__PURE__ */ jsxs("span", {
|
|
16989
|
+
class: "bm-label bm-label-row",
|
|
16990
|
+
"aria-hidden": "true",
|
|
16991
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
16992
|
+
class: "bm-label-text",
|
|
16993
|
+
children: "Alt text"
|
|
16994
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
16995
|
+
class: "bm-meta-tag",
|
|
16996
|
+
children: "required"
|
|
16997
|
+
})]
|
|
16998
|
+
}),
|
|
16999
|
+
/* @__PURE__ */ jsx("input", {
|
|
17000
|
+
class: "bm-alt",
|
|
17001
|
+
"aria-label": "Alt",
|
|
17002
|
+
placeholder: "Describe the image",
|
|
17003
|
+
value: alt,
|
|
17004
|
+
onInput: (event) => emit({ alt: event.currentTarget.value })
|
|
17005
|
+
}),
|
|
17006
|
+
captionable && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("span", {
|
|
17007
|
+
class: "bm-label bm-label-row",
|
|
17008
|
+
"aria-hidden": "true",
|
|
17009
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
17010
|
+
class: "bm-label-text",
|
|
17011
|
+
children: "Caption"
|
|
17012
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
17013
|
+
class: "bm-meta-tag",
|
|
17014
|
+
children: "optional, shown when opened large"
|
|
17015
|
+
})]
|
|
17016
|
+
}), /* @__PURE__ */ jsx("textarea", {
|
|
17017
|
+
class: "bm-alt",
|
|
17018
|
+
rows: 2,
|
|
17019
|
+
"aria-label": "Caption",
|
|
17020
|
+
placeholder: "Shown under the picture. Empty uses the alt text.",
|
|
17021
|
+
value: caption,
|
|
17022
|
+
onInput: (event) => emit({ caption: event.currentTarget.value })
|
|
17023
|
+
})] })
|
|
17024
|
+
]
|
|
16831
17025
|
})] }),
|
|
16832
17026
|
missing && /* @__PURE__ */ jsxs("p", {
|
|
16833
17027
|
class: "bm-error",
|
|
@@ -17261,6 +17455,126 @@ function ChoiceField({ label, value, choices, icons, path, segmented = true, onC
|
|
|
17261
17455
|
});
|
|
17262
17456
|
}
|
|
17263
17457
|
//#endregion
|
|
17458
|
+
//#region src/ui/forms/video-source.tsx
|
|
17459
|
+
/** What a pasted video link is, or why it will not play. Empty for no link. */
|
|
17460
|
+
function describeVideoLink(value) {
|
|
17461
|
+
const link = value.trim();
|
|
17462
|
+
if (link === "") return null;
|
|
17463
|
+
const source = parseMediaSource(link);
|
|
17464
|
+
if (source?.kind === "youtube") return {
|
|
17465
|
+
ok: true,
|
|
17466
|
+
text: "YouTube video. It loads nothing until the visitor presses play."
|
|
17467
|
+
};
|
|
17468
|
+
if (source?.kind === "vimeo") return {
|
|
17469
|
+
ok: true,
|
|
17470
|
+
text: "Vimeo video. It loads nothing until the visitor presses play."
|
|
17471
|
+
};
|
|
17472
|
+
if (source?.kind === "file") return {
|
|
17473
|
+
ok: true,
|
|
17474
|
+
text: "Video file."
|
|
17475
|
+
};
|
|
17476
|
+
if (/^https?:\/\//i.test(link)) return {
|
|
17477
|
+
ok: false,
|
|
17478
|
+
text: "That is a page link, not a video. Use a YouTube or Vimeo link, or a link to a file ending in .mp4 or .webm."
|
|
17479
|
+
};
|
|
17480
|
+
return {
|
|
17481
|
+
ok: false,
|
|
17482
|
+
text: "Use a YouTube or Vimeo link, a link to a video file ending in .mp4 or .webm, or a path in public/ such as /videos/demo.webm."
|
|
17483
|
+
};
|
|
17484
|
+
}
|
|
17485
|
+
const megabytes = (bytes) => `${(bytes / 1048576).toFixed(bytes < 10485760 ? 1 : 0)} MB`;
|
|
17486
|
+
/** Under a video link field: says what the link is, uploads a video file, and picks one already uploaded. */
|
|
17487
|
+
function VideoSourceTools({ value, onPick }) {
|
|
17488
|
+
const input = useRef(null);
|
|
17489
|
+
const [library, setLibrary] = useState([]);
|
|
17490
|
+
const [busy, setBusy] = useState(false);
|
|
17491
|
+
const [note, setNote] = useState("");
|
|
17492
|
+
const [error, setError] = useState("");
|
|
17493
|
+
useEffect(() => {
|
|
17494
|
+
let live = true;
|
|
17495
|
+
getVideos().then((result) => {
|
|
17496
|
+
if (live && result.ok) setLibrary(result.value.videos);
|
|
17497
|
+
});
|
|
17498
|
+
return () => {
|
|
17499
|
+
live = false;
|
|
17500
|
+
};
|
|
17501
|
+
}, []);
|
|
17502
|
+
async function upload(file) {
|
|
17503
|
+
if (!file) return;
|
|
17504
|
+
setBusy(true);
|
|
17505
|
+
setError("");
|
|
17506
|
+
setNote("");
|
|
17507
|
+
const result = await uploadVideo(file);
|
|
17508
|
+
setBusy(false);
|
|
17509
|
+
if (!result.ok) {
|
|
17510
|
+
setError(result.status === 0 ? "Upload failed." : result.message);
|
|
17511
|
+
return;
|
|
17512
|
+
}
|
|
17513
|
+
setLibrary((current) => [...current.filter((item) => item.url !== result.value.url), result.value]);
|
|
17514
|
+
onPick(result.value.url);
|
|
17515
|
+
if ((result.value.bytes ?? 0) > 10485760) setNote(`${megabytes(result.value.bytes ?? 0)}: it is slow to load on every visit. YouTube or Vimeo is kinder to visitors.`);
|
|
17516
|
+
}
|
|
17517
|
+
const detected = describeVideoLink(value);
|
|
17518
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
17519
|
+
class: "bm-stack bm-stack-tight",
|
|
17520
|
+
children: [
|
|
17521
|
+
detected ? /* @__PURE__ */ jsx("span", {
|
|
17522
|
+
class: detected.ok ? "bm-hint" : "bm-error",
|
|
17523
|
+
role: detected.ok ? void 0 : "alert",
|
|
17524
|
+
children: detected.text
|
|
17525
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
17526
|
+
class: "bm-hint",
|
|
17527
|
+
children: "Paste a YouTube or Vimeo link as it is, or upload a video file."
|
|
17528
|
+
}),
|
|
17529
|
+
/* @__PURE__ */ jsx("input", {
|
|
17530
|
+
ref: input,
|
|
17531
|
+
type: "file",
|
|
17532
|
+
accept: "video/mp4,video/webm,video/quicktime,video/ogg",
|
|
17533
|
+
hidden: true,
|
|
17534
|
+
onChange: (event) => {
|
|
17535
|
+
const file = event.currentTarget.files?.[0];
|
|
17536
|
+
event.currentTarget.value = "";
|
|
17537
|
+
upload(file);
|
|
17538
|
+
}
|
|
17539
|
+
}),
|
|
17540
|
+
/* @__PURE__ */ jsx("button", {
|
|
17541
|
+
type: "button",
|
|
17542
|
+
class: "bm-add bm-add-row",
|
|
17543
|
+
disabled: busy,
|
|
17544
|
+
onClick: () => input.current?.click(),
|
|
17545
|
+
children: busy ? "Uploading…" : "Upload a video file"
|
|
17546
|
+
}),
|
|
17547
|
+
library.length > 0 && /* @__PURE__ */ jsxs("label", {
|
|
17548
|
+
class: "bm-field",
|
|
17549
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
17550
|
+
class: "bm-label",
|
|
17551
|
+
children: "Uploaded videos"
|
|
17552
|
+
}), /* @__PURE__ */ jsxs("select", {
|
|
17553
|
+
value: "",
|
|
17554
|
+
"aria-label": "Uploaded videos",
|
|
17555
|
+
onChange: (event) => event.currentTarget.value && onPick(event.currentTarget.value),
|
|
17556
|
+
children: [/* @__PURE__ */ jsx("option", {
|
|
17557
|
+
value: "",
|
|
17558
|
+
children: "Choose one…"
|
|
17559
|
+
}), library.map((video) => /* @__PURE__ */ jsxs("option", {
|
|
17560
|
+
value: video.url,
|
|
17561
|
+
children: [video.filename, video.bytes ? ` (${megabytes(video.bytes)})` : ""]
|
|
17562
|
+
}, video.url))]
|
|
17563
|
+
})]
|
|
17564
|
+
}),
|
|
17565
|
+
note && /* @__PURE__ */ jsx("span", {
|
|
17566
|
+
class: "bm-hint",
|
|
17567
|
+
children: note
|
|
17568
|
+
}),
|
|
17569
|
+
error && /* @__PURE__ */ jsx("span", {
|
|
17570
|
+
class: "bm-error",
|
|
17571
|
+
role: "alert",
|
|
17572
|
+
children: error
|
|
17573
|
+
})
|
|
17574
|
+
]
|
|
17575
|
+
});
|
|
17576
|
+
}
|
|
17577
|
+
//#endregion
|
|
17264
17578
|
//#region src/ui/forms/fields.tsx
|
|
17265
17579
|
/** `yearlyDiscount` and `cta_label` read as "Yearly discount" and "Cta label". Array item numbers stay numbers. */
|
|
17266
17580
|
function humanize(key) {
|
|
@@ -17328,7 +17642,7 @@ function ValueEditor({ label: rawLabel, value, path, rule, variant, sample, imag
|
|
|
17328
17642
|
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === void 0) {
|
|
17329
17643
|
const rich = typeof value === "string" && isRichField(path, null);
|
|
17330
17644
|
const heading = isHeadingField(path);
|
|
17331
|
-
|
|
17645
|
+
const control = /* @__PURE__ */ jsx(OptionControl, {
|
|
17332
17646
|
option: {
|
|
17333
17647
|
name: path,
|
|
17334
17648
|
label,
|
|
@@ -17342,122 +17656,39 @@ function ValueEditor({ label: rawLabel, value, path, rule, variant, sample, imag
|
|
|
17342
17656
|
value,
|
|
17343
17657
|
onChange: (next) => onField(path, next)
|
|
17344
17658
|
});
|
|
17659
|
+
if (!/(^|\.)props\.video$/.test(path)) return control;
|
|
17660
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
17661
|
+
class: "bm-stack bm-stack-tight",
|
|
17662
|
+
children: [control, /* @__PURE__ */ jsx(VideoSourceTools, {
|
|
17663
|
+
value: typeof value === "string" ? value : "",
|
|
17664
|
+
onPick: (url) => onField(path, url)
|
|
17665
|
+
})]
|
|
17666
|
+
});
|
|
17345
17667
|
}
|
|
17346
17668
|
if (isImageRecord(value)) return /* @__PURE__ */ jsx(ImageField, {
|
|
17347
17669
|
label,
|
|
17348
17670
|
path,
|
|
17349
17671
|
layout: !/(^|\.)logos\./.test(path),
|
|
17350
17672
|
hideable: path.startsWith("sections.") && !/(^|\.)logos\./.test(path),
|
|
17673
|
+
captionable: isOptionalPath(optionalSpec, `${path}.caption`),
|
|
17351
17674
|
value,
|
|
17352
17675
|
images,
|
|
17353
17676
|
onChange: (next) => onField(path, next),
|
|
17354
17677
|
onUploaded,
|
|
17355
17678
|
onDeleted
|
|
17356
17679
|
});
|
|
17357
|
-
if (Array.isArray(value)) {
|
|
17358
|
-
|
|
17359
|
-
|
|
17360
|
-
|
|
17361
|
-
|
|
17362
|
-
|
|
17363
|
-
|
|
17364
|
-
|
|
17365
|
-
|
|
17366
|
-
|
|
17367
|
-
|
|
17368
|
-
|
|
17369
|
-
class: "bm-array",
|
|
17370
|
-
children: [value.map((item, index) => /* @__PURE__ */ jsxs("details", {
|
|
17371
|
-
class: "bm-array-item",
|
|
17372
|
-
children: [/* @__PURE__ */ jsxs("summary", { children: [
|
|
17373
|
-
/* @__PURE__ */ jsx("span", {
|
|
17374
|
-
class: "bm-grip",
|
|
17375
|
-
"aria-hidden": "true",
|
|
17376
|
-
children: /* @__PURE__ */ jsx(Icon, {
|
|
17377
|
-
name: "grip",
|
|
17378
|
-
size: 12
|
|
17379
|
-
})
|
|
17380
|
-
}),
|
|
17381
|
-
/* @__PURE__ */ jsx("span", {
|
|
17382
|
-
class: "bm-array-summary",
|
|
17383
|
-
children: itemSummary(item) || `${label} ${index + 1}`
|
|
17384
|
-
}),
|
|
17385
|
-
/* @__PURE__ */ jsx("span", {
|
|
17386
|
-
class: "bm-chevron",
|
|
17387
|
-
"aria-hidden": "true",
|
|
17388
|
-
children: /* @__PURE__ */ jsx(Icon, {
|
|
17389
|
-
name: "chevron-right",
|
|
17390
|
-
size: 14
|
|
17391
|
-
})
|
|
17392
|
-
})
|
|
17393
|
-
] }), /* @__PURE__ */ jsxs("div", {
|
|
17394
|
-
class: "bm-array-body",
|
|
17395
|
-
children: [/* @__PURE__ */ jsx(ValueEditor, {
|
|
17396
|
-
label: `${index + 1}`,
|
|
17397
|
-
value: item,
|
|
17398
|
-
path: `${path}.${index}`,
|
|
17399
|
-
rule: void 0,
|
|
17400
|
-
variant,
|
|
17401
|
-
sample,
|
|
17402
|
-
images,
|
|
17403
|
-
onField,
|
|
17404
|
-
onUploaded,
|
|
17405
|
-
onDeleted
|
|
17406
|
-
}), /* @__PURE__ */ jsxs("div", {
|
|
17407
|
-
class: "bm-row bm-row-end",
|
|
17408
|
-
children: [
|
|
17409
|
-
/* @__PURE__ */ jsx("button", {
|
|
17410
|
-
type: "button",
|
|
17411
|
-
class: "bm-icon-btn",
|
|
17412
|
-
"aria-label": "Move item up",
|
|
17413
|
-
title: "Move up",
|
|
17414
|
-
disabled: index === 0,
|
|
17415
|
-
onClick: () => onField(path, reorderList(value, index, index - 1)),
|
|
17416
|
-
children: /* @__PURE__ */ jsx(Icon, {
|
|
17417
|
-
name: "arrow-up",
|
|
17418
|
-
size: 14
|
|
17419
|
-
})
|
|
17420
|
-
}),
|
|
17421
|
-
/* @__PURE__ */ jsx("button", {
|
|
17422
|
-
type: "button",
|
|
17423
|
-
class: "bm-icon-btn",
|
|
17424
|
-
"aria-label": "Move item down",
|
|
17425
|
-
title: "Move down",
|
|
17426
|
-
disabled: index === value.length - 1,
|
|
17427
|
-
onClick: () => onField(path, reorderList(value, index, index + 1)),
|
|
17428
|
-
children: /* @__PURE__ */ jsx(Icon, {
|
|
17429
|
-
name: "arrow-down",
|
|
17430
|
-
size: 14
|
|
17431
|
-
})
|
|
17432
|
-
}),
|
|
17433
|
-
/* @__PURE__ */ jsx("button", {
|
|
17434
|
-
type: "button",
|
|
17435
|
-
class: "bm-icon-btn bm-danger",
|
|
17436
|
-
"aria-label": "Remove item",
|
|
17437
|
-
title: "Remove",
|
|
17438
|
-
disabled: !allowRemove,
|
|
17439
|
-
onClick: () => onField(path, value.filter((_, itemIndex) => itemIndex !== index)),
|
|
17440
|
-
children: /* @__PURE__ */ jsx(Icon, {
|
|
17441
|
-
name: "trash",
|
|
17442
|
-
size: 14
|
|
17443
|
-
})
|
|
17444
|
-
})
|
|
17445
|
-
]
|
|
17446
|
-
})]
|
|
17447
|
-
})]
|
|
17448
|
-
}, `${path}.${index}`)), /* @__PURE__ */ jsx("button", {
|
|
17449
|
-
type: "button",
|
|
17450
|
-
class: "bm-add bm-add-row",
|
|
17451
|
-
disabled: !allowAdd,
|
|
17452
|
-
onClick: () => {
|
|
17453
|
-
const next = sample === void 0 ? "New" : structuredClone(sample);
|
|
17454
|
-
onField(path, [...value, next]);
|
|
17455
|
-
},
|
|
17456
|
-
children: "+ Add item"
|
|
17457
|
-
})]
|
|
17458
|
-
})]
|
|
17459
|
-
});
|
|
17460
|
-
}
|
|
17680
|
+
if (Array.isArray(value)) return /* @__PURE__ */ jsx(ArrayEditor, {
|
|
17681
|
+
label,
|
|
17682
|
+
value,
|
|
17683
|
+
path,
|
|
17684
|
+
rule,
|
|
17685
|
+
variant,
|
|
17686
|
+
sample,
|
|
17687
|
+
images,
|
|
17688
|
+
onField,
|
|
17689
|
+
onUploaded,
|
|
17690
|
+
onDeleted
|
|
17691
|
+
});
|
|
17461
17692
|
if (isLinkRecord(value)) {
|
|
17462
17693
|
const record = value;
|
|
17463
17694
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -17690,6 +17921,216 @@ function orderItemKeys(item, path, content) {
|
|
|
17690
17921
|
const dropped = parts.length === 2 && /^\d+$/.test(parts[1] ?? "") ? content?.items?.[parts[0] ?? ""] ?? [] : [];
|
|
17691
17922
|
return orderContentKeys(Object.keys(item), dropped, Object.keys(item));
|
|
17692
17923
|
}
|
|
17924
|
+
let itemSeed = 0;
|
|
17925
|
+
/** Ids that follow each list item when it moves, so its open card, focus and inputs move with it. */
|
|
17926
|
+
function useItemIds(length) {
|
|
17927
|
+
const ids = useRef([]);
|
|
17928
|
+
while (ids.current.length < length) ids.current.push(`item-${++itemSeed}`);
|
|
17929
|
+
if (ids.current.length > length) ids.current.length = length;
|
|
17930
|
+
return ids;
|
|
17931
|
+
}
|
|
17932
|
+
/** Where a drop lands: the index the dragged item ends up at, or null when it would not move. */
|
|
17933
|
+
function dropTarget(drag) {
|
|
17934
|
+
const slot = drag.mode === "before" ? drag.over : drag.over + 1;
|
|
17935
|
+
const to = slot > drag.from ? slot - 1 : slot;
|
|
17936
|
+
return to === drag.from ? null : to;
|
|
17937
|
+
}
|
|
17938
|
+
/** A list of items. Each card keeps its identity when it moves, so a card you moved stays open and you can move it again. */
|
|
17939
|
+
function ArrayEditor({ label, value, path, rule, variant, sample, images, onField, onUploaded, onDeleted }) {
|
|
17940
|
+
const allowAdd = canAddItems(value.length, rule, variant);
|
|
17941
|
+
const allowRemove = canRemoveItems(value.length, rule, variant);
|
|
17942
|
+
const ids = useItemIds(value.length);
|
|
17943
|
+
const list = useRef(null);
|
|
17944
|
+
const [drag, setDrag] = useState(null);
|
|
17945
|
+
function move(from, to, refocus) {
|
|
17946
|
+
if (from === to || to < 0 || to >= value.length) return;
|
|
17947
|
+
const [id] = ids.current.splice(from, 1);
|
|
17948
|
+
if (id === void 0) return;
|
|
17949
|
+
ids.current.splice(to, 0, id);
|
|
17950
|
+
onField(path, reorderList(value, from, to));
|
|
17951
|
+
if (!refocus) return;
|
|
17952
|
+
const wanted = id;
|
|
17953
|
+
requestAnimationFrame(() => requestAnimationFrame(() => {
|
|
17954
|
+
const card = list.current?.querySelector(`[data-item-id="${wanted}"]`);
|
|
17955
|
+
const first = card?.querySelector(`[data-move="${refocus}"]`);
|
|
17956
|
+
const other = card?.querySelector(`[data-move="${refocus === "up" ? "down" : "up"}"]`);
|
|
17957
|
+
(first && !first.disabled ? first : other)?.focus();
|
|
17958
|
+
}));
|
|
17959
|
+
}
|
|
17960
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
17961
|
+
class: "bm-stack bm-stack-tight",
|
|
17962
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
17963
|
+
class: "bm-label-row bm-label",
|
|
17964
|
+
children: [/* @__PURE__ */ jsx("span", { children: label }), /* @__PURE__ */ jsxs("span", {
|
|
17965
|
+
class: "bm-meta-tag",
|
|
17966
|
+
children: [value.length, rule?.recommended !== void 0 ? `, recommended ${rule.recommended}` : ""]
|
|
17967
|
+
})]
|
|
17968
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
17969
|
+
class: "bm-array",
|
|
17970
|
+
ref: list,
|
|
17971
|
+
children: [value.map((item, index) => {
|
|
17972
|
+
const id = ids.current[index] ?? `${path}.${index}`;
|
|
17973
|
+
const mode = (drag !== null && drag.over === index ? dropTarget(drag) : null) === null || drag === null ? null : drag.mode;
|
|
17974
|
+
const rowClass = [
|
|
17975
|
+
"bm-array-row",
|
|
17976
|
+
drag?.from === index ? "bm-dragging" : "",
|
|
17977
|
+
mode === "before" ? "bm-drop-before" : "",
|
|
17978
|
+
mode === "after" ? "bm-drop-after" : ""
|
|
17979
|
+
].filter(Boolean).join(" ");
|
|
17980
|
+
return /* @__PURE__ */ jsx("div", {
|
|
17981
|
+
class: rowClass,
|
|
17982
|
+
"data-item-id": id,
|
|
17983
|
+
"data-drop-hint": mode ? "Move here" : void 0,
|
|
17984
|
+
onDragOver: (event) => {
|
|
17985
|
+
if (!drag) return;
|
|
17986
|
+
event.preventDefault();
|
|
17987
|
+
event.stopPropagation();
|
|
17988
|
+
if (event.dataTransfer) event.dataTransfer.dropEffect = "move";
|
|
17989
|
+
const box = event.currentTarget.getBoundingClientRect();
|
|
17990
|
+
const next = event.clientY < box.top + box.height / 2 ? "before" : "after";
|
|
17991
|
+
if (drag.over !== index || drag.mode !== next) setDrag({
|
|
17992
|
+
...drag,
|
|
17993
|
+
over: index,
|
|
17994
|
+
mode: next
|
|
17995
|
+
});
|
|
17996
|
+
},
|
|
17997
|
+
onDragLeave: (event) => {
|
|
17998
|
+
const next = event.relatedTarget;
|
|
17999
|
+
if (!drag || next instanceof Node && event.currentTarget.contains(next)) return;
|
|
18000
|
+
if (drag.over === index) setDrag({
|
|
18001
|
+
...drag,
|
|
18002
|
+
over: drag.from
|
|
18003
|
+
});
|
|
18004
|
+
},
|
|
18005
|
+
onDrop: (event) => {
|
|
18006
|
+
if (!drag) return;
|
|
18007
|
+
event.preventDefault();
|
|
18008
|
+
event.stopPropagation();
|
|
18009
|
+
const to = dropTarget({
|
|
18010
|
+
...drag,
|
|
18011
|
+
over: index,
|
|
18012
|
+
mode: drag.over === index ? drag.mode : "before"
|
|
18013
|
+
});
|
|
18014
|
+
const from = drag.from;
|
|
18015
|
+
setDrag(null);
|
|
18016
|
+
if (to !== null) move(from, to);
|
|
18017
|
+
},
|
|
18018
|
+
children: /* @__PURE__ */ jsxs("details", {
|
|
18019
|
+
class: "bm-array-item",
|
|
18020
|
+
children: [/* @__PURE__ */ jsxs("summary", { children: [
|
|
18021
|
+
/* @__PURE__ */ jsx("span", {
|
|
18022
|
+
class: "bm-grip bm-array-grip",
|
|
18023
|
+
draggable: true,
|
|
18024
|
+
title: "Drag to reorder",
|
|
18025
|
+
"aria-hidden": "true",
|
|
18026
|
+
onClick: (event) => event.preventDefault(),
|
|
18027
|
+
onDragStart: (event) => {
|
|
18028
|
+
event.stopPropagation();
|
|
18029
|
+
const row = event.currentTarget.closest(".bm-array-row");
|
|
18030
|
+
if (event.dataTransfer) {
|
|
18031
|
+
event.dataTransfer.effectAllowed = "move";
|
|
18032
|
+
event.dataTransfer.setData("text/plain", id);
|
|
18033
|
+
if (row) event.dataTransfer.setDragImage(row, 16, 16);
|
|
18034
|
+
}
|
|
18035
|
+
setDrag({
|
|
18036
|
+
from: index,
|
|
18037
|
+
over: index,
|
|
18038
|
+
mode: "before"
|
|
18039
|
+
});
|
|
18040
|
+
},
|
|
18041
|
+
onDragEnd: () => setDrag(null),
|
|
18042
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
18043
|
+
name: "grip",
|
|
18044
|
+
size: 12
|
|
18045
|
+
})
|
|
18046
|
+
}),
|
|
18047
|
+
/* @__PURE__ */ jsx("span", {
|
|
18048
|
+
class: "bm-array-summary",
|
|
18049
|
+
children: itemSummary(item) || `${label} ${index + 1}`
|
|
18050
|
+
}),
|
|
18051
|
+
/* @__PURE__ */ jsx("span", {
|
|
18052
|
+
class: "bm-chevron",
|
|
18053
|
+
"aria-hidden": "true",
|
|
18054
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
18055
|
+
name: "chevron-right",
|
|
18056
|
+
size: 14
|
|
18057
|
+
})
|
|
18058
|
+
})
|
|
18059
|
+
] }), /* @__PURE__ */ jsxs("div", {
|
|
18060
|
+
class: "bm-array-body",
|
|
18061
|
+
children: [/* @__PURE__ */ jsx(ValueEditor, {
|
|
18062
|
+
label: `${index + 1}`,
|
|
18063
|
+
value: item,
|
|
18064
|
+
path: `${path}.${index}`,
|
|
18065
|
+
rule: void 0,
|
|
18066
|
+
variant,
|
|
18067
|
+
sample,
|
|
18068
|
+
images,
|
|
18069
|
+
onField,
|
|
18070
|
+
onUploaded,
|
|
18071
|
+
onDeleted
|
|
18072
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
18073
|
+
class: "bm-row bm-row-end",
|
|
18074
|
+
children: [
|
|
18075
|
+
/* @__PURE__ */ jsx("button", {
|
|
18076
|
+
type: "button",
|
|
18077
|
+
class: "bm-icon-btn",
|
|
18078
|
+
"aria-label": "Move item up",
|
|
18079
|
+
title: "Move up",
|
|
18080
|
+
"data-move": "up",
|
|
18081
|
+
disabled: index === 0,
|
|
18082
|
+
onClick: () => move(index, index - 1, "up"),
|
|
18083
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
18084
|
+
name: "arrow-up",
|
|
18085
|
+
size: 14
|
|
18086
|
+
})
|
|
18087
|
+
}),
|
|
18088
|
+
/* @__PURE__ */ jsx("button", {
|
|
18089
|
+
type: "button",
|
|
18090
|
+
class: "bm-icon-btn",
|
|
18091
|
+
"aria-label": "Move item down",
|
|
18092
|
+
title: "Move down",
|
|
18093
|
+
"data-move": "down",
|
|
18094
|
+
disabled: index === value.length - 1,
|
|
18095
|
+
onClick: () => move(index, index + 1, "down"),
|
|
18096
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
18097
|
+
name: "arrow-down",
|
|
18098
|
+
size: 14
|
|
18099
|
+
})
|
|
18100
|
+
}),
|
|
18101
|
+
/* @__PURE__ */ jsx("button", {
|
|
18102
|
+
type: "button",
|
|
18103
|
+
class: "bm-icon-btn bm-danger",
|
|
18104
|
+
"aria-label": "Remove item",
|
|
18105
|
+
title: "Remove",
|
|
18106
|
+
disabled: !allowRemove,
|
|
18107
|
+
onClick: () => {
|
|
18108
|
+
ids.current.splice(index, 1);
|
|
18109
|
+
onField(path, value.filter((_, itemIndex) => itemIndex !== index));
|
|
18110
|
+
},
|
|
18111
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
18112
|
+
name: "trash",
|
|
18113
|
+
size: 14
|
|
18114
|
+
})
|
|
18115
|
+
})
|
|
18116
|
+
]
|
|
18117
|
+
})]
|
|
18118
|
+
})]
|
|
18119
|
+
})
|
|
18120
|
+
}, id);
|
|
18121
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
18122
|
+
type: "button",
|
|
18123
|
+
class: "bm-add bm-add-row",
|
|
18124
|
+
disabled: !allowAdd,
|
|
18125
|
+
onClick: () => {
|
|
18126
|
+
const next = sample === void 0 ? "New" : structuredClone(sample);
|
|
18127
|
+
onField(path, [...value, next]);
|
|
18128
|
+
},
|
|
18129
|
+
children: "+ Add item"
|
|
18130
|
+
})]
|
|
18131
|
+
})]
|
|
18132
|
+
});
|
|
18133
|
+
}
|
|
17693
18134
|
//#endregion
|
|
17694
18135
|
//#region src/ui/dialogs/consent-dialog.tsx
|
|
17695
18136
|
const KEYS = [
|
|
@@ -18468,6 +18909,151 @@ function SeoDialog({ page, siteName, siteUrl, system, images, issues, onUploaded
|
|
|
18468
18909
|
});
|
|
18469
18910
|
}
|
|
18470
18911
|
//#endregion
|
|
18912
|
+
//#region src/ui/dialogs/site-jsonld-dialog.tsx
|
|
18913
|
+
/**
|
|
18914
|
+
* The structured data every page carries: an `Organization` and a `WebSite`, or any other markup. It edits a
|
|
18915
|
+
* copy: Apply writes it to the site draft in one step, Cancel and Escape drop it. Nothing reaches disk until save.
|
|
18916
|
+
*/
|
|
18917
|
+
function SiteJsonLdDialog({ blocks, source, onApply, onClose }) {
|
|
18918
|
+
const root = useRef(null);
|
|
18919
|
+
const [jsonText, setJsonText] = useState(() => formatJsonLd(blocks));
|
|
18920
|
+
const parsed = parseJsonLdInput(jsonText);
|
|
18921
|
+
const warnings = parsed.ok ? jsonLdWarnings(parsed.value) : [];
|
|
18922
|
+
const count = parsed.ok ? parsed.value.length : 0;
|
|
18923
|
+
const placeholder = isPlaceholderSiteUrl(source.url);
|
|
18924
|
+
useEffect(() => {
|
|
18925
|
+
root.current?.querySelector("textarea")?.focus();
|
|
18926
|
+
}, []);
|
|
18927
|
+
function generate() {
|
|
18928
|
+
if (!parsed.ok) return;
|
|
18929
|
+
setJsonText(formatJsonLd(upsertJsonLd(parsed.value, generateSiteJsonLd(source))));
|
|
18930
|
+
}
|
|
18931
|
+
return /* @__PURE__ */ jsx("div", {
|
|
18932
|
+
class: "bm-scrim",
|
|
18933
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
18934
|
+
class: "bm-dialog bm-seo",
|
|
18935
|
+
role: "dialog",
|
|
18936
|
+
"aria-modal": "true",
|
|
18937
|
+
"aria-label": "Edit site JSON-LD",
|
|
18938
|
+
ref: root,
|
|
18939
|
+
children: [
|
|
18940
|
+
/* @__PURE__ */ jsxs("div", {
|
|
18941
|
+
class: "bm-dialog-head",
|
|
18942
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
18943
|
+
class: "bm-dialog-heading",
|
|
18944
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
18945
|
+
class: "bm-dialog-title",
|
|
18946
|
+
children: "Structured data · every page"
|
|
18947
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
18948
|
+
class: "bm-dialog-sub",
|
|
18949
|
+
children: "Applied to the site draft. Nothing is written until you save."
|
|
18950
|
+
})]
|
|
18951
|
+
}), /* @__PURE__ */ jsx(DialogClose, { onClose })]
|
|
18952
|
+
}),
|
|
18953
|
+
/* @__PURE__ */ jsxs("div", {
|
|
18954
|
+
class: "bm-seo-body",
|
|
18955
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
18956
|
+
class: "bm-stack",
|
|
18957
|
+
children: [
|
|
18958
|
+
/* @__PURE__ */ jsx("p", {
|
|
18959
|
+
class: "bm-group",
|
|
18960
|
+
children: "Generate"
|
|
18961
|
+
}),
|
|
18962
|
+
/* @__PURE__ */ jsx("button", {
|
|
18963
|
+
type: "button",
|
|
18964
|
+
class: "bm-tertiary bm-seo-format",
|
|
18965
|
+
disabled: !parsed.ok,
|
|
18966
|
+
onClick: generate,
|
|
18967
|
+
children: "Generate Organization and WebSite"
|
|
18968
|
+
}),
|
|
18969
|
+
/* @__PURE__ */ jsxs("p", {
|
|
18970
|
+
class: "bm-hint",
|
|
18971
|
+
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."]
|
|
18972
|
+
}),
|
|
18973
|
+
placeholder && /* @__PURE__ */ jsxs("span", {
|
|
18974
|
+
class: "bm-error",
|
|
18975
|
+
role: "alert",
|
|
18976
|
+
children: [
|
|
18977
|
+
"The site address is still ",
|
|
18978
|
+
source.url,
|
|
18979
|
+
". Set the real one under Identity before you generate, or the markup names the wrong site."
|
|
18980
|
+
]
|
|
18981
|
+
})
|
|
18982
|
+
]
|
|
18983
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
18984
|
+
class: "bm-stack",
|
|
18985
|
+
children: [
|
|
18986
|
+
/* @__PURE__ */ jsx("p", {
|
|
18987
|
+
class: "bm-group",
|
|
18988
|
+
children: "JSON-LD"
|
|
18989
|
+
}),
|
|
18990
|
+
/* @__PURE__ */ jsxs("label", {
|
|
18991
|
+
class: "bm-label",
|
|
18992
|
+
children: [
|
|
18993
|
+
/* @__PURE__ */ jsxs("span", {
|
|
18994
|
+
class: "bm-label-row",
|
|
18995
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
18996
|
+
class: "bm-label-text",
|
|
18997
|
+
children: ["Blocks", /* @__PURE__ */ jsx(Info, {
|
|
18998
|
+
label: "About site JSON-LD",
|
|
18999
|
+
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."
|
|
19000
|
+
})]
|
|
19001
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
19002
|
+
class: "bm-meta-tag",
|
|
19003
|
+
children: parsed.ok ? `${count} ${count === 1 ? "block" : "blocks"}` : "invalid"
|
|
19004
|
+
})]
|
|
19005
|
+
}),
|
|
19006
|
+
/* @__PURE__ */ jsx("textarea", {
|
|
19007
|
+
class: "bm-mono bm-seo-json",
|
|
19008
|
+
rows: 14,
|
|
19009
|
+
spellcheck: false,
|
|
19010
|
+
autocomplete: "off",
|
|
19011
|
+
placeholder: "{\n \"@context\": \"https://schema.org\",\n \"@type\": \"Organization\",\n \"name\": \"Example\"\n}",
|
|
19012
|
+
"aria-invalid": !parsed.ok,
|
|
19013
|
+
value: jsonText,
|
|
19014
|
+
onInput: (event) => setJsonText(event.currentTarget.value)
|
|
19015
|
+
}),
|
|
19016
|
+
!parsed.ok && /* @__PURE__ */ jsx("span", {
|
|
19017
|
+
class: "bm-error",
|
|
19018
|
+
role: "alert",
|
|
19019
|
+
children: parsed.error
|
|
19020
|
+
}),
|
|
19021
|
+
warnings.map((warning) => /* @__PURE__ */ jsx("span", {
|
|
19022
|
+
class: "bm-hint",
|
|
19023
|
+
children: warning
|
|
19024
|
+
}, warning))
|
|
19025
|
+
]
|
|
19026
|
+
}),
|
|
19027
|
+
/* @__PURE__ */ jsx("button", {
|
|
19028
|
+
type: "button",
|
|
19029
|
+
class: "bm-tertiary bm-seo-format",
|
|
19030
|
+
disabled: !parsed.ok || parsed.value.length === 0,
|
|
19031
|
+
onClick: () => parsed.ok && setJsonText(formatJsonLd(parsed.value)),
|
|
19032
|
+
children: "Format JSON"
|
|
19033
|
+
})
|
|
19034
|
+
]
|
|
19035
|
+
})]
|
|
19036
|
+
}),
|
|
19037
|
+
/* @__PURE__ */ jsxs("div", {
|
|
19038
|
+
class: "bm-actions",
|
|
19039
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
19040
|
+
type: "button",
|
|
19041
|
+
class: "bm-primary",
|
|
19042
|
+
disabled: !parsed.ok,
|
|
19043
|
+
onClick: () => parsed.ok && onApply(parsed.value),
|
|
19044
|
+
children: "Apply"
|
|
19045
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
19046
|
+
type: "button",
|
|
19047
|
+
class: "bm-tertiary",
|
|
19048
|
+
onClick: onClose,
|
|
19049
|
+
children: "Cancel"
|
|
19050
|
+
})]
|
|
19051
|
+
})
|
|
19052
|
+
]
|
|
19053
|
+
})
|
|
19054
|
+
});
|
|
19055
|
+
}
|
|
19056
|
+
//#endregion
|
|
18471
19057
|
//#region src/ui/dialogs/editor-dialogs.tsx
|
|
18472
19058
|
function parseJson(text) {
|
|
18473
19059
|
if (!text) return null;
|
|
@@ -18598,6 +19184,19 @@ function SessionDialog({ editor, dialog }) {
|
|
|
18598
19184
|
onClose: close
|
|
18599
19185
|
});
|
|
18600
19186
|
}
|
|
19187
|
+
case "siteJsonLd": {
|
|
19188
|
+
const site = doc.site.value;
|
|
19189
|
+
if (!site) return null;
|
|
19190
|
+
return /* @__PURE__ */ jsx(SiteJsonLdDialog, {
|
|
19191
|
+
blocks: siteJsonLdOf(site),
|
|
19192
|
+
source: siteJsonLdSourceOf(site),
|
|
19193
|
+
onApply: (blocks) => {
|
|
19194
|
+
edits.writeField("seo.jsonLd", blocks.length > 0 ? blocks : void 0);
|
|
19195
|
+
close();
|
|
19196
|
+
},
|
|
19197
|
+
onClose: close
|
|
19198
|
+
});
|
|
19199
|
+
}
|
|
18601
19200
|
case "discard": return /* @__PURE__ */ jsx(ConfirmDialog, {
|
|
18602
19201
|
label: "Discard changes",
|
|
18603
19202
|
body: "Throw away every unsaved change to this page and the site, and reload both from the saved files?",
|
|
@@ -18780,6 +19379,30 @@ function RichTextBar() {
|
|
|
18780
19379
|
class: "bm-rich",
|
|
18781
19380
|
style: `top:${position.top}px;left:${position.left}px;`,
|
|
18782
19381
|
children: [
|
|
19382
|
+
active?.headings && /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
19383
|
+
/* @__PURE__ */ jsx("button", {
|
|
19384
|
+
type: "button",
|
|
19385
|
+
class: "bm-fmt-heading",
|
|
19386
|
+
"aria-label": "Heading",
|
|
19387
|
+
title: "Heading",
|
|
19388
|
+
"aria-pressed": active.heading === 2,
|
|
19389
|
+
onMouseDown: run("heading2"),
|
|
19390
|
+
children: "H2"
|
|
19391
|
+
}),
|
|
19392
|
+
/* @__PURE__ */ jsx("button", {
|
|
19393
|
+
type: "button",
|
|
19394
|
+
class: "bm-fmt-heading",
|
|
19395
|
+
"aria-label": "Subheading",
|
|
19396
|
+
title: "Subheading",
|
|
19397
|
+
"aria-pressed": active.heading === 3,
|
|
19398
|
+
onMouseDown: run("heading3"),
|
|
19399
|
+
children: "H3"
|
|
19400
|
+
}),
|
|
19401
|
+
/* @__PURE__ */ jsx("span", {
|
|
19402
|
+
class: "bm-sep",
|
|
19403
|
+
"aria-hidden": "true"
|
|
19404
|
+
})
|
|
19405
|
+
] }),
|
|
18783
19406
|
/* @__PURE__ */ jsx("button", {
|
|
18784
19407
|
type: "button",
|
|
18785
19408
|
class: "bm-fmt-bold",
|
|
@@ -19965,7 +20588,7 @@ function AdvancedLink({ edit }) {
|
|
|
19965
20588
|
}
|
|
19966
20589
|
//#endregion
|
|
19967
20590
|
//#region src/ui/editor.css?raw
|
|
19968
|
-
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";
|
|
20591
|
+
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-row {\n position: relative;\n}\n.bm-array-grip {\n cursor: grab;\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-heading {\n font-weight: 700;\n font-size: 12px;\n letter-spacing: -0.02em;\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";
|
|
19969
20592
|
//#endregion
|
|
19970
20593
|
//#region src/ui/styles.ts
|
|
19971
20594
|
function boxStyle(rect, offset = 0) {
|
|
@@ -21312,7 +21935,7 @@ function siteOptionControl(site, option, onField) {
|
|
|
21312
21935
|
onChange: (value) => onField(option.name, value)
|
|
21313
21936
|
}, option.name);
|
|
21314
21937
|
}
|
|
21315
|
-
function SiteForm({ site, options, images, onField, onOptional, onConsentEnabled, onEditConsent, onMarks, onUploaded, onDeleted }) {
|
|
21938
|
+
function SiteForm({ site, options, images, onField, onOptional, onConsentEnabled, onEditConsent, onEditJsonLd, onMarks, onUploaded, onDeleted }) {
|
|
21316
21939
|
const logo = getAt(site, "favicon") ?? getAt(site, "logo");
|
|
21317
21940
|
const groupOf = (option) => SITE_GROUPS.find((group) => option.name.startsWith(group.prefix));
|
|
21318
21941
|
const shown = options.filter((option) => !option.name.startsWith("footer.") && !option.name.startsWith("header.") && option.name !== "theme.mode");
|
|
@@ -21392,6 +22015,10 @@ function SiteForm({ site, options, images, onField, onOptional, onConsentEnabled
|
|
|
21392
22015
|
})
|
|
21393
22016
|
]
|
|
21394
22017
|
}),
|
|
22018
|
+
/* @__PURE__ */ jsx(JsonLdGroup, {
|
|
22019
|
+
site,
|
|
22020
|
+
onEdit: onEditJsonLd
|
|
22021
|
+
}),
|
|
21395
22022
|
/* @__PURE__ */ jsxs("div", {
|
|
21396
22023
|
class: "bm-stack",
|
|
21397
22024
|
children: [
|
|
@@ -21512,6 +22139,33 @@ function ConsentGroup({ enabled, snippets, onEnabled, onEdit }) {
|
|
|
21512
22139
|
]
|
|
21513
22140
|
});
|
|
21514
22141
|
}
|
|
22142
|
+
/** Structured data on every page: a summary here, the blocks in a dialog. */
|
|
22143
|
+
function JsonLdGroup({ site, onEdit }) {
|
|
22144
|
+
const blocks = siteJsonLdOf(site);
|
|
22145
|
+
const types = blocks.map((block) => typeof block["@type"] === "string" ? block["@type"] : "block");
|
|
22146
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
22147
|
+
class: "bm-stack",
|
|
22148
|
+
children: [
|
|
22149
|
+
/* @__PURE__ */ jsx("p", {
|
|
22150
|
+
class: "bm-group",
|
|
22151
|
+
children: "Structured data"
|
|
22152
|
+
}),
|
|
22153
|
+
/* @__PURE__ */ jsx("p", {
|
|
22154
|
+
class: "bm-hint",
|
|
22155
|
+
children: blocks.length === 0 ? "None. Add an Organization and a WebSite so search engines know who runs the site." : `On every page: ${types.join(", ")}.`
|
|
22156
|
+
}),
|
|
22157
|
+
/* @__PURE__ */ jsxs("button", {
|
|
22158
|
+
type: "button",
|
|
22159
|
+
class: "bm-tertiary bm-consent-edit",
|
|
22160
|
+
onClick: onEdit,
|
|
22161
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
22162
|
+
name: "settings",
|
|
22163
|
+
size: 14
|
|
22164
|
+
}), "Edit JSON-LD"]
|
|
22165
|
+
})
|
|
22166
|
+
]
|
|
22167
|
+
});
|
|
22168
|
+
}
|
|
21515
22169
|
//#endregion
|
|
21516
22170
|
//#region src/ui/forms/footer-form.tsx
|
|
21517
22171
|
/** Footer tab: layout, text, and the columns with their links. */
|
|
@@ -22430,6 +23084,9 @@ function SitePanel({ site, catalog }) {
|
|
|
22430
23084
|
onEditConsent: () => {
|
|
22431
23085
|
ui.dialog.value = { kind: "consent" };
|
|
22432
23086
|
},
|
|
23087
|
+
onEditJsonLd: () => {
|
|
23088
|
+
ui.dialog.value = { kind: "siteJsonLd" };
|
|
23089
|
+
},
|
|
22433
23090
|
onMarks: edits.applyMarksAt,
|
|
22434
23091
|
onUploaded: doc.addImage,
|
|
22435
23092
|
onDeleted: doc.dropImage
|