smoodly 0.0.12 → 0.0.13

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.
Files changed (132) hide show
  1. package/README.md +219 -25
  2. package/dist/admin/editor/CanvasOverlay.d.ts +6 -2
  3. package/dist/admin/editor/CanvasOverlay.js +9 -11
  4. package/dist/admin/editor/EditorView.js +130 -25
  5. package/dist/admin/editor/FloatingToolbar.d.ts +16 -0
  6. package/dist/admin/editor/FloatingToolbar.js +14 -0
  7. package/dist/admin/editor/InlineRichtext.d.ts +9 -0
  8. package/dist/admin/editor/InlineRichtext.js +50 -0
  9. package/dist/admin/editor/Outline.d.ts +16 -10
  10. package/dist/admin/editor/Outline.js +13 -10
  11. package/dist/admin/editor/inline-dom.d.ts +27 -0
  12. package/dist/admin/editor/inline-dom.js +43 -0
  13. package/dist/admin/editor/inline-session.d.ts +30 -0
  14. package/dist/admin/editor/inline-session.js +19 -0
  15. package/dist/admin/editor/protocol.d.ts +22 -1
  16. package/dist/admin/editor/useCanvasBridge.d.ts +8 -4
  17. package/dist/admin/editor/useCanvasBridge.js +12 -7
  18. package/dist/admin/editor/useInlineSession.d.ts +46 -0
  19. package/dist/admin/editor/useInlineSession.js +244 -0
  20. package/dist/admin/field-paths.d.ts +13 -0
  21. package/dist/admin/field-paths.js +49 -0
  22. package/dist/admin/forms/FieldWidget.d.ts +7 -2
  23. package/dist/admin/forms/FieldWidget.js +12 -40
  24. package/dist/admin/forms/ListField.d.ts +10 -0
  25. package/dist/admin/forms/ListField.js +42 -0
  26. package/dist/admin/forms/MediaWidget.js +10 -3
  27. package/dist/admin/forms/RichtextElementCard.d.ts +3 -0
  28. package/dist/admin/forms/RichtextElementCard.js +37 -0
  29. package/dist/admin/forms/RichtextImageCard.d.ts +3 -0
  30. package/dist/admin/forms/RichtextImageCard.js +42 -0
  31. package/dist/admin/forms/RichtextInsertMenu.d.ts +6 -0
  32. package/dist/admin/forms/RichtextInsertMenu.js +6 -0
  33. package/dist/admin/forms/RichtextPopovers.d.ts +7 -0
  34. package/dist/admin/forms/RichtextPopovers.js +25 -0
  35. package/dist/admin/forms/RichtextToolbar.d.ts +4 -2
  36. package/dist/admin/forms/RichtextToolbar.js +4 -2
  37. package/dist/admin/forms/RichtextWidget.d.ts +4 -1
  38. package/dist/admin/forms/RichtextWidget.js +23 -120
  39. package/dist/admin/forms/elements-context.d.ts +7 -0
  40. package/dist/admin/forms/elements-context.js +13 -0
  41. package/dist/admin/forms/reveal.d.ts +5 -0
  42. package/dist/admin/forms/reveal.js +47 -0
  43. package/dist/admin/forms/richtext-extensions.d.ts +8 -0
  44. package/dist/admin/forms/richtext-extensions.js +11 -0
  45. package/dist/admin/forms/richtext-form-context.d.ts +16 -0
  46. package/dist/admin/forms/richtext-form-context.js +18 -0
  47. package/dist/admin/forms/richtext-insert.d.ts +38 -0
  48. package/dist/admin/forms/richtext-insert.js +37 -0
  49. package/dist/admin/forms/richtext-nodes.d.ts +37 -0
  50. package/dist/admin/forms/richtext-nodes.js +85 -0
  51. package/dist/admin/forms/richtext-paste.js +5 -0
  52. package/dist/admin/forms/tabs.d.ts +3 -0
  53. package/dist/admin/forms/tabs.js +6 -0
  54. package/dist/admin/forms/useRichtextEditor.d.ts +51 -0
  55. package/dist/admin/forms/useRichtextEditor.js +281 -0
  56. package/dist/admin/next/bridge.js +51 -20
  57. package/dist/admin/ops-impl.js +13 -8
  58. package/dist/admin/richtext-doc.d.ts +3 -0
  59. package/dist/admin/richtext-doc.js +12 -0
  60. package/dist/admin/serialize.d.ts +7 -0
  61. package/dist/admin/serialize.js +16 -1
  62. package/dist/admin/shell/AdminApp.js +2 -1
  63. package/dist/admin/shell/EntriesList.js +34 -12
  64. package/dist/admin/shell/EntryForm.js +22 -15
  65. package/dist/admin/shell/IconRail.js +6 -5
  66. package/dist/admin/shell/ListView.d.ts +9 -2
  67. package/dist/admin/shell/ListView.js +7 -6
  68. package/dist/admin/shell/LoginView.js +2 -2
  69. package/dist/admin/shell/PagesList.js +53 -24
  70. package/dist/admin/shell/SharedForm.js +17 -11
  71. package/dist/admin/shell/SharedList.js +5 -3
  72. package/dist/admin/shell/entries-list.d.ts +4 -0
  73. package/dist/admin/shell/entries-list.js +6 -0
  74. package/dist/admin/shell/pages-tree.d.ts +24 -0
  75. package/dist/admin/shell/pages-tree.js +59 -1
  76. package/dist/admin/tree-ops.d.ts +9 -1
  77. package/dist/admin/tree-ops.js +27 -0
  78. package/dist/admin/ui/LocaleSwitcher.d.ts +10 -0
  79. package/dist/admin/ui/LocaleSwitcher.js +28 -8
  80. package/dist/admin/ui/Modal.js +14 -2
  81. package/dist/admin/ui/ResizeHandle.d.ts +1 -0
  82. package/dist/admin/ui/ResizeHandle.js +127 -0
  83. package/dist/admin/ui/primitives.d.ts +29 -4
  84. package/dist/admin/ui/primitives.js +29 -10
  85. package/dist/admin/ui/resize.d.ts +16 -0
  86. package/dist/admin/ui/resize.js +23 -0
  87. package/dist/admin/ui/sortable.d.ts +23 -0
  88. package/dist/admin/ui/sortable.js +57 -0
  89. package/dist/admin/ui/theme.d.ts +3 -1
  90. package/dist/admin/ui/theme.js +512 -165
  91. package/dist/admin/ui/useSortable.d.ts +35 -0
  92. package/dist/admin/ui/useSortable.js +220 -0
  93. package/dist/admin/validate.d.ts +6 -1
  94. package/dist/admin/validate.js +28 -9
  95. package/dist/config.d.ts +1 -0
  96. package/dist/config.js +36 -0
  97. package/dist/entry-store.d.ts +5 -2
  98. package/dist/entry-store.js +4 -3
  99. package/dist/fields.d.ts +4 -0
  100. package/dist/fields.js +2 -2
  101. package/dist/image/SmoodlyImage.d.ts +4 -1
  102. package/dist/image/SmoodlyImage.js +2 -2
  103. package/dist/index.d.ts +8 -1
  104. package/dist/index.js +5 -1
  105. package/dist/marks.d.ts +53 -0
  106. package/dist/marks.js +43 -0
  107. package/dist/media.d.ts +3 -2
  108. package/dist/media.js +48 -6
  109. package/dist/refs.d.ts +2 -2
  110. package/dist/refs.js +33 -12
  111. package/dist/render.js +2 -1
  112. package/dist/resolve.d.ts +8 -2
  113. package/dist/resolve.js +8 -7
  114. package/dist/richtext/index.d.ts +12 -0
  115. package/dist/richtext/index.js +9 -0
  116. package/dist/richtext-render.d.ts +22 -2
  117. package/dist/richtext-render.js +40 -13
  118. package/dist/richtext-walk.d.ts +44 -0
  119. package/dist/richtext-walk.js +46 -0
  120. package/dist/schema.d.ts +1 -1
  121. package/dist/schema.js +13 -1
  122. package/dist/section-wrapper.js +2 -1
  123. package/dist/site.d.ts +2 -0
  124. package/dist/site.js +7 -4
  125. package/dist/store.d.ts +3 -1
  126. package/dist/store.js +4 -4
  127. package/dist/supabase-entry-store.d.ts +1 -0
  128. package/dist/supabase-entry-store.js +2 -1
  129. package/dist/supabase-store.js +1 -1
  130. package/dist/toolset.d.ts +7 -1
  131. package/dist/toolset.js +13 -4
  132. package/package.json +6 -1
@@ -0,0 +1,23 @@
1
+ // The inspector's drag-to-resize maths (spec 2026-09-10 §1, §5): pure, so
2
+ // the handle and the pre-paint boot script agree on one clamp.
3
+ /** Where the dragged width is remembered, browser-side only (like the theme). */
4
+ export const INSPECTOR_W_KEY = "smoodly-inspector-w";
5
+ /** Below this the richtext toolbar and the outline rows no longer fit. */
6
+ export const MIN_INSPECTOR_W = 260;
7
+ /** The token's value in the warm look; what a reset returns to. */
8
+ export const DEFAULT_INSPECTOR_W = 320;
9
+ /** One arrow-key press. */
10
+ export const RESIZE_STEP = 16;
11
+ /** Half the editor row (canvas + handle + inspector), never below the minimum. */
12
+ export const maxInspectorW = (rowWidth) => Math.max(MIN_INSPECTOR_W, Math.floor(rowWidth / 2));
13
+ /** An integer inside [MIN, half the row]. */
14
+ export const clampInspectorW = (px, rowWidth) => Math.min(maxInspectorW(rowWidth), Math.max(MIN_INSPECTOR_W, Math.round(px)));
15
+ /** A stored value is trusted only as a plain integer ≥ MIN; anything else is
16
+ * ignored (never clamped or rewritten). The boot script in theme.tsx repeats
17
+ * this test inline — keep the two in step. */
18
+ export const parseStoredWidth = (raw) => {
19
+ if (raw === null || !/^\d+$/.test(raw))
20
+ return null;
21
+ const n = Number(raw);
22
+ return Number.isSafeInteger(n) && n >= MIN_INSPECTOR_W ? n : null;
23
+ };
@@ -0,0 +1,23 @@
1
+ /** A row's box in the container's coordinate space, measured once at drag start. */
2
+ export type RowRect = {
3
+ top: number;
4
+ height: number;
5
+ };
6
+ /** The drop index for a dragged row whose centre is at `centerY`: the
7
+ * number of OTHER rows whose midpoint that centre has passed. */
8
+ export declare function slotAt(rects: RowRect[], from: number, centerY: number): number;
9
+ /** The translateY of every row while the dragged row (`from`) hovers at
10
+ * slot `to`: the rows between them slide by the dragged row's height to
11
+ * open the gap; the dragged row's own entry is 0 (it follows the pointer). */
12
+ export declare function gapTransforms(rects: RowRect[], from: number, to: number): number[];
13
+ /** Where the dragged row settles once dropped at `to`, as a translateY
14
+ * from its measured position — used for the pending hold. */
15
+ export declare function restingOffset(rects: RowRect[], from: number, to: number): number;
16
+ /** The dragged row's translateY held inside the rows' extent: its top no
17
+ * higher than the first row's, its bottom no lower than the last row's.
18
+ * A transform past the extent grows the scroller's overflow, and the
19
+ * edge autoscroll would chase that growth forever (found in the
20
+ * 2026-09-09 walk). */
21
+ export declare function clampOffset(rects: RowRect[], from: number, dy: number): number;
22
+ /** The keyboard step: one row up or down, null at the ends. */
23
+ export declare function stepIndex(count: number, from: number, dir: -1 | 1): number | null;
@@ -0,0 +1,57 @@
1
+ // The drag gesture's maths as pure functions over row rectangles, so the
2
+ // hook (./useSortable.ts) is thin and this is tested without a DOM
3
+ // (spec 2026-09-09 §3). Every index is a position in the rendered row
4
+ // list; `to` is the index in the RESULTING order, moveItem's convention.
5
+ /** The drop index for a dragged row whose centre is at `centerY`: the
6
+ * number of OTHER rows whose midpoint that centre has passed. */
7
+ export function slotAt(rects, from, centerY) {
8
+ let slot = 0;
9
+ rects.forEach((r, i) => {
10
+ if (i !== from && centerY > r.top + r.height / 2)
11
+ slot++;
12
+ });
13
+ return slot;
14
+ }
15
+ /** The translateY of every row while the dragged row (`from`) hovers at
16
+ * slot `to`: the rows between them slide by the dragged row's height to
17
+ * open the gap; the dragged row's own entry is 0 (it follows the pointer). */
18
+ export function gapTransforms(rects, from, to) {
19
+ const h = rects[from]?.height ?? 0;
20
+ return rects.map((_, i) => {
21
+ if (i === from)
22
+ return 0;
23
+ if (to > from && i > from && i <= to)
24
+ return -h;
25
+ if (to < from && i >= to && i < from)
26
+ return h;
27
+ return 0;
28
+ });
29
+ }
30
+ /** Where the dragged row settles once dropped at `to`, as a translateY
31
+ * from its measured position — used for the pending hold. */
32
+ export function restingOffset(rects, from, to) {
33
+ if (to === from || !rects[from] || !rects[to])
34
+ return 0;
35
+ const own = rects[from];
36
+ if (to > from)
37
+ return rects[to].top + rects[to].height - own.height - own.top;
38
+ return rects[to].top - own.top;
39
+ }
40
+ /** The dragged row's translateY held inside the rows' extent: its top no
41
+ * higher than the first row's, its bottom no lower than the last row's.
42
+ * A transform past the extent grows the scroller's overflow, and the
43
+ * edge autoscroll would chase that growth forever (found in the
44
+ * 2026-09-09 walk). */
45
+ export function clampOffset(rects, from, dy) {
46
+ const own = rects[from];
47
+ if (!own || rects.length === 0)
48
+ return dy;
49
+ const top = Math.min(...rects.map((r) => r.top));
50
+ const bottom = Math.max(...rects.map((r) => r.top + r.height));
51
+ return Math.max(top - own.top, Math.min(bottom - own.height - own.top, dy));
52
+ }
53
+ /** The keyboard step: one row up or down, null at the ends. */
54
+ export function stepIndex(count, from, dir) {
55
+ const to = from + dir;
56
+ return to < 0 || to >= count ? null : to;
57
+ }
@@ -1,5 +1,7 @@
1
- export declare const ADMIN_CSS = "\n@import url(\"https://fonts.googleapis.com/css2?family=Public+Sans:wght@400;500;600;700&display=swap\");\n\n.sm-admin{\n --font-sans:'Public Sans',-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif;\n --font-mono:ui-monospace,Menlo,Consolas,monospace;\n --text-2xs:9px; --text-xs:10px; --text-sm:11px; --text-md:12px;\n --text-lg:13px; --text-xl:14px; --text-2xl:15px; --text-3xl:20px; --text-4xl:30px;\n --tracking-tight:-0.02em; --tracking-label:0.1em; --tracking-tag:0.08em;\n\n --radius-xs:1px; --radius-sm:3px; --radius-md:4px; --radius-round:999px;\n --control-h:28px; --control-h-lg:36px; --row-h:38px; --topbar-h:48px;\n --nav-w:72px; --inspector-w:380px;\n --border:1px solid var(--line);\n --outline-selection:1.5px solid var(--selection);\n\n --brand-600:#0038B1; --brand-500:#2554D6; --brand-300:#6E96FF;\n --brand-200:#A9C0FF; --brand-100:#DCE4F9; --brand-50:#E3EAFA;\n --gray-0:#FFFFFF; --gray-50:#F1F2F6; --gray-100:#E1E4EC; --gray-300:#C9D0E2;\n --gray-500:#8B93A7; --gray-600:#6B7386; --gray-700:#4A5165; --gray-900:#0F1420;\n --green-700:#1F7A46; --green-50:#E3F3EA; --green-300:#6FD39A; --green-500:#2E9E5B;\n --amber-700:#8A5A00; --amber-50:#FBF1DC; --amber-300:#E8B85A;\n --red-700:#B42318; --red-50:#FDECEC; --red-300:#F5A5A0;\n\n --bg:var(--gray-50); --surface:var(--gray-0); --surface-inset:var(--gray-50);\n --input-bg:var(--gray-0); --line:var(--gray-100); --line-dashed:var(--gray-300);\n --text:var(--gray-900); --text-muted:var(--gray-600); --text-subtle:var(--gray-500);\n --accent:var(--brand-600); --on-accent:#FFFFFF; --accent-soft:var(--brand-50);\n --selection:var(--brand-600); --nav-active-bg:var(--brand-100); --nav-active-fg:var(--brand-600);\n --hover:rgba(15,20,32,.045); --press:rgba(15,20,32,.08);\n --control-hover:var(--gray-100);\n\n --status-published-fg:var(--green-700); --status-published-bg:var(--green-50);\n --status-draft-fg:var(--amber-700); --status-draft-bg:var(--amber-50);\n --status-scheduled-fg:var(--accent); --status-scheduled-bg:var(--accent-soft);\n --status-saved-dot:var(--green-500);\n --warn:var(--amber-700); --warn-soft:var(--amber-50);\n --danger:var(--red-700); --danger-soft:var(--red-50);\n\n --page-bg:#FFFFFF; --page-text:var(--gray-900);\n --shadow-page:0 1px 2px rgba(0,0,0,.08), 0 8px 24px rgba(0,0,0,.06);\n --shadow-float:0 2px 8px rgba(0,0,0,.25);\n --shadow-seg:0 1px 2px rgba(0,0,0,.08);\n --focus-ring:0 0 0 3px var(--accent-soft);\n --shadow-pop:0 8px 32px rgba(0,0,0,.18);\n --logo-filter:none;\n\n /* Which half of the theme toggle is visible. A token, so the icon can never\n disagree with the colors \u2014 both follow the one cascade below. */\n --when-light:inline-flex; --when-dark:none;\n}\nhtml[data-theme=\"dark\"] .sm-admin,.sm-admin[data-theme=\"dark\"]{\n --bg:#0D0F14; --surface:#15181F; --surface-inset:#0D0F14; --input-bg:#1B1F28;\n --line:#2A2F3B; --line-dashed:#2A2F3B;\n --text:#EEF0F5; --text-muted:#8C94A8; --text-subtle:#6B7386;\n --accent:var(--brand-300); --on-accent:#06122F; --accent-soft:rgba(110,150,255,.16);\n --nav-active-bg:rgba(110,150,255,.14); --nav-active-fg:var(--brand-200);\n --hover:rgba(238,240,245,.05); --press:rgba(238,240,245,.09);\n --control-hover:var(--input-bg);\n\n --status-published-fg:var(--green-300); --status-published-bg:rgba(111,211,154,.14);\n --status-draft-fg:var(--amber-300); --status-draft-bg:rgba(232,184,90,.14);\n --warn:var(--amber-300); --warn-soft:rgba(232,184,90,.14);\n --danger:var(--red-300); --danger-soft:rgba(244,102,92,.14);\n\n --shadow-page:0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.35);\n --shadow-pop:0 8px 32px rgba(0,0,0,.5);\n --logo-filter:brightness(0) invert(1);\n --when-light:none; --when-dark:inline-flex;\n}\n@media (prefers-color-scheme:dark){\n html:not([data-theme=\"light\"]) .sm-admin:not([data-theme=\"light\"]){\n --bg:#0D0F14; --surface:#15181F; --surface-inset:#0D0F14; --input-bg:#1B1F28;\n --line:#2A2F3B; --line-dashed:#2A2F3B;\n --text:#EEF0F5; --text-muted:#8C94A8; --text-subtle:#6B7386;\n --accent:var(--brand-300); --on-accent:#06122F; --accent-soft:rgba(110,150,255,.16);\n --nav-active-bg:rgba(110,150,255,.14); --nav-active-fg:var(--brand-200);\n --hover:rgba(238,240,245,.05); --press:rgba(238,240,245,.09);\n --control-hover:var(--input-bg);\n\n --status-published-fg:var(--green-300); --status-published-bg:rgba(111,211,154,.14);\n --status-draft-fg:var(--amber-300); --status-draft-bg:rgba(232,184,90,.14);\n --warn:var(--amber-300); --warn-soft:rgba(232,184,90,.14);\n --danger:var(--red-300); --danger-soft:rgba(244,102,92,.14);\n\n --shadow-page:0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.35);\n --shadow-pop:0 8px 32px rgba(0,0,0,.5);\n --logo-filter:brightness(0) invert(1);\n --when-light:none; --when-dark:inline-flex;\n}\n}\n/* Last, and the same specificity as the host-attribute rule: an explicit\n choice from the theme toggle wins over a dark <html> and over the OS. */\n.sm-admin[data-theme=\"light\"]{\n --brand-600:#0038B1; --brand-500:#2554D6; --brand-300:#6E96FF;\n --brand-200:#A9C0FF; --brand-100:#DCE4F9; --brand-50:#E3EAFA;\n --gray-0:#FFFFFF; --gray-50:#F1F2F6; --gray-100:#E1E4EC; --gray-300:#C9D0E2;\n --gray-500:#8B93A7; --gray-600:#6B7386; --gray-700:#4A5165; --gray-900:#0F1420;\n --green-700:#1F7A46; --green-50:#E3F3EA; --green-300:#6FD39A; --green-500:#2E9E5B;\n --amber-700:#8A5A00; --amber-50:#FBF1DC; --amber-300:#E8B85A;\n --red-700:#B42318; --red-50:#FDECEC; --red-300:#F5A5A0;\n\n --bg:var(--gray-50); --surface:var(--gray-0); --surface-inset:var(--gray-50);\n --input-bg:var(--gray-0); --line:var(--gray-100); --line-dashed:var(--gray-300);\n --text:var(--gray-900); --text-muted:var(--gray-600); --text-subtle:var(--gray-500);\n --accent:var(--brand-600); --on-accent:#FFFFFF; --accent-soft:var(--brand-50);\n --selection:var(--brand-600); --nav-active-bg:var(--brand-100); --nav-active-fg:var(--brand-600);\n --hover:rgba(15,20,32,.045); --press:rgba(15,20,32,.08);\n --control-hover:var(--gray-100);\n\n --status-published-fg:var(--green-700); --status-published-bg:var(--green-50);\n --status-draft-fg:var(--amber-700); --status-draft-bg:var(--amber-50);\n --status-scheduled-fg:var(--accent); --status-scheduled-bg:var(--accent-soft);\n --status-saved-dot:var(--green-500);\n --warn:var(--amber-700); --warn-soft:var(--amber-50);\n --danger:var(--red-700); --danger-soft:var(--red-50);\n\n --page-bg:#FFFFFF; --page-text:var(--gray-900);\n --shadow-page:0 1px 2px rgba(0,0,0,.08), 0 8px 24px rgba(0,0,0,.06);\n --shadow-float:0 2px 8px rgba(0,0,0,.25);\n --shadow-seg:0 1px 2px rgba(0,0,0,.08);\n --focus-ring:0 0 0 3px var(--accent-soft);\n --shadow-pop:0 8px 32px rgba(0,0,0,.18);\n --logo-filter:none;\n\n /* Which half of the theme toggle is visible. A token, so the icon can never\n disagree with the colors \u2014 both follow the one cascade below. */\n --when-light:inline-flex; --when-dark:none;\n}\n\n/* The admin owns the viewport; a host <body> margin would push it into a\n page scrollbar. Scoped by :has, so only a page holding the admin is touched. */\nbody:has(.sm-admin){margin:0}\n.sm-admin,.sm-admin *,.sm-admin *::before,.sm-admin *::after{box-sizing:border-box}\n.sm-admin{\n font-family:var(--font-sans);font-size:var(--text-md);line-height:1.4;\n color:var(--text);background:var(--surface);\n -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;\n}\n.sm-admin h1,.sm-admin h2,.sm-admin h3,.sm-admin p,.sm-admin ul{margin:0}\n/* :where() keeps this at zero specificity \u2014 a bare <a> is accent, but any\n component class on it (.sm-nav-item, .sm-btn, .sm-back) decides its own\n colour. Without this the element selector silently beat every one of them. */\n:where(.sm-admin a){color:var(--accent);text-decoration:none}\n:where(.sm-admin a:hover){color:var(--brand-500)}\n.sm-admin button,.sm-admin input,.sm-admin select,.sm-admin textarea{font-family:inherit}\n.sm-admin ::placeholder{color:var(--text-subtle)}\n.sm-admin ::selection{background:var(--accent-soft);color:var(--text)}\n.sm-mono{font-family:var(--font-mono)}\n\n/* \u2500\u2500 shell \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.sm-shell{display:grid;grid-template-columns:var(--nav-w) 1fr;height:100vh;height:100dvh;overflow:hidden}\n.sm-shell>.sm-main{min-width:0;min-height:0;display:flex;flex-direction:column;overflow:hidden}\n.sm-wordmark{height:18px;width:auto;filter:var(--logo-filter);flex:none}\n/* The design system's IconRail (components/navigation/IconRail.html),\n ported as-is: a 72px rail, 18px Lucide icons over a 10px label, a\n 40\u00D732 active tile, the app icon on top and the avatar at the bottom. */\n.sm-rail{\n width:var(--nav-w);height:100%;display:flex;flex-direction:column;align-items:center;gap:14px;\n padding:14px 6px 12px;background:var(--surface);border-right:var(--border);color:var(--text);\n font:500 10px/1 var(--font-sans);position:relative;\n}\n.sm-rail__logo{width:28px;height:28px;display:block;flex:none}\n.sm-rail__list{display:flex;flex-direction:column;gap:6px;width:100%;margin:0;padding:0;list-style:none}\n.sm-rail__item{position:relative}\n.sm-rail__btn{\n appearance:none;background:none;border:0;margin:0;padding:2px 0;width:100%;display:flex;flex-direction:column;\n align-items:center;gap:5px;color:inherit;font:inherit;cursor:pointer;border-radius:var(--radius-md);text-decoration:none;\n}\n.sm-rail__btn:hover{color:inherit}\n.sm-rail__btn:focus-visible{outline:2px solid var(--accent);outline-offset:-2px;box-shadow:none}\n.sm-rail__tile{position:relative;width:40px;height:32px;border-radius:var(--radius-md);display:grid;place-items:center;transition:background .12s}\n.sm-rail__btn:hover .sm-rail__tile{background:var(--surface-inset)}\n.sm-rail__btn[aria-current=\"page\"],.sm-rail__btn[aria-expanded=\"true\"]{color:var(--nav-active-fg);font-weight:600}\n.sm-rail__btn[aria-current=\"page\"]:hover,.sm-rail__btn[aria-expanded=\"true\"]:hover{color:var(--nav-active-fg)}\n.sm-rail__btn[aria-current=\"page\"] .sm-rail__tile,.sm-rail__btn[aria-expanded=\"true\"] .sm-rail__tile{background:var(--nav-active-bg)}\n.sm-rail__tile svg{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round}\n.sm-rail__chev{position:absolute;right:3px;bottom:2px;width:8px;height:8px}\n.sm-rail__chev svg{width:8px;height:8px;stroke-width:3}\n.sm-rail__label{letter-spacing:.01em;white-space:nowrap}\n/* Not greyed: the reference renders every inactive item at --text and marks\n the unavailable ones only by being inert (no pointer, no hover). */\n.sm-rail__btn[aria-disabled=\"true\"]{cursor:default}\n.sm-rail__btn[aria-disabled=\"true\"]:hover .sm-rail__tile{background:transparent}\n.sm-rail__foot{margin-top:auto;display:flex;flex-direction:column;align-items:center}\n.sm-rail__user{\n width:26px;height:26px;border-radius:var(--radius-round);background:var(--accent-soft);color:var(--accent);\n display:grid;place-items:center;font-size:var(--text-sm);font-weight:600;border:0;cursor:pointer;padding:0;\n}\n.sm-rail__user:focus-visible{outline:2px solid var(--accent);outline-offset:1px;box-shadow:none}\n/* Flyout: 6px right of the rail, aligned with its item (the user menu aligns to the bottom). */\n.sm-rail__menu{\n position:absolute;left:calc(100% + 6px);top:0;z-index:20;width:168px;padding:6px;margin:0;list-style:none;\n background:var(--surface);border:var(--border);border-radius:var(--radius-md);box-shadow:0 4px 20px rgba(0,0,0,.14);\n display:none;flex-direction:column;gap:1px;font-size:var(--text-md);font-weight:500;color:var(--text);\n}\n.sm-rail__menu[data-open=\"true\"]{display:flex}\n.sm-rail__menu--up{top:auto;bottom:0}\n.sm-rail__menu-title{font-size:var(--text-2xs);font-weight:600;letter-spacing:var(--tracking-label);text-transform:uppercase;color:var(--text-muted);padding:6px 8px 4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.sm-rail__menu-item{\n appearance:none;background:none;border:0;width:100%;display:flex;align-items:center;justify-content:space-between;\n padding:6px 8px;border-radius:var(--radius-sm);color:inherit;font:inherit;cursor:pointer;text-align:left;text-decoration:none;\n}\n.sm-rail__menu-item:hover,.sm-rail__menu-item:focus-visible{background:var(--surface-inset);color:inherit;outline:0;box-shadow:none}\n.sm-rail__menu-item[aria-current=\"page\"],.sm-rail__menu-item[aria-current=\"page\"]:hover{background:var(--nav-active-bg);color:var(--nav-active-fg);font-weight:600}\n.sm-when-light{display:var(--when-light);align-items:center;gap:8px}\n.sm-when-dark{display:var(--when-dark);align-items:center;gap:8px}\n/* .sm-nav-item survives as the inspector's link style (EditorView). */\n.sm-nav-item{\n display:block;padding:6px 10px;border-radius:var(--radius-md);line-height:1.3;\n color:var(--text);font-weight:500;text-decoration:none;cursor:pointer;\n background:transparent;border:0;width:100%;text-align:left;font-size:var(--text-md);\n overflow:hidden;text-overflow:ellipsis;white-space:nowrap;\n}\n.sm-nav-item:hover{background:var(--surface-inset);color:var(--text)}\n\n/* \u2500\u2500 top bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.sm-topbar{\n height:var(--topbar-h);flex:none;display:flex;align-items:center;justify-content:space-between;\n gap:16px;padding:0 16px;border-bottom:var(--border);background:var(--surface);\n}\n.sm-topbar__left{display:flex;align-items:center;gap:10px;min-width:0}\n.sm-topbar__right{display:flex;align-items:center;gap:8px;flex:none}\n.sm-crumbs{display:flex;align-items:center;gap:8px;color:var(--text-muted);min-width:0}\n.sm-crumbs__sep{opacity:.5}\n.sm-crumbs__here{color:var(--text);font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.sm-back{border:0;background:transparent;padding:0;color:var(--text);font:inherit;cursor:pointer;line-height:1}\n.sm-back:hover{color:var(--accent)}\n.sm-title{font-size:var(--text-xl);font-weight:600}\n.sm-count{color:var(--text-muted)}\n\n/* \u2500\u2500 buttons \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.sm-btn{\n display:inline-flex;align-items:center;justify-content:center;gap:6px;\n height:var(--control-h);padding:0 12px;border-radius:var(--radius-md);border:0;\n font-size:var(--text-md);font-weight:500;line-height:1;white-space:nowrap;cursor:pointer;\n text-decoration:none;\n}\n.sm-btn--lg{height:var(--control-h-lg);padding:0 16px}\n.sm-btn--primary{background:var(--accent);color:var(--on-accent);font-weight:600;padding:0 14px}\n.sm-btn--primary:hover{background:var(--brand-500);color:var(--on-accent)}\n.sm-btn--secondary{background:var(--surface-inset);color:var(--text)}\n.sm-btn--secondary:hover{background:var(--control-hover);color:var(--text)}\n.sm-btn--ghost{background:transparent;color:var(--accent);font-weight:600;padding:0 8px}\n.sm-btn--ghost:hover{background:var(--accent-soft);color:var(--accent)}\n.sm-btn--danger{background:var(--surface-inset);color:var(--danger)}\n.sm-btn--danger:hover{background:var(--danger-soft);color:var(--danger)}\n.sm-btn--ok{background:var(--status-published-bg);color:var(--status-published-fg);font-weight:600;padding:0 14px}\n.sm-btn:disabled,.sm-btn[aria-disabled=\"true\"]{opacity:.5;cursor:default;pointer-events:none}\n.sm-btn:focus-visible,.sm-iconbtn:focus-visible,.sm-nav-item:focus-visible,\n.sm-tab:focus-visible,.sm-seg__opt:focus-visible,.sm-row:focus-visible{\n outline:2px solid var(--accent);outline-offset:1px\n}\n\n.sm-iconbtn{\n width:24px;height:24px;flex:none;display:grid;place-items:center;padding:0;\n border:0;border-radius:var(--radius-sm);background:transparent;color:var(--text-muted);\n font-size:12px;line-height:1;cursor:pointer;\n}\n.sm-iconbtn:hover{background:var(--hover);color:var(--text)}\n.sm-iconbtn--inverse{color:#EEF0F5}\n.sm-iconbtn--inverse:hover{background:rgba(255,255,255,.14);color:#fff}\n.sm-iconbtn:disabled{opacity:.3;cursor:default}\n.sm-iconbtn:disabled:hover{background:transparent}\n\n/* \u2500\u2500 fields \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.sm-field{display:flex;flex-direction:column;gap:6px;min-width:0}\n.sm-field__head{display:flex;align-items:baseline;justify-content:space-between;gap:8px}\n.sm-label{\n font-size:var(--text-2xs);font-weight:600;letter-spacing:var(--tracking-label);\n text-transform:uppercase;color:var(--text-muted);\n}\n.sm-counter,.sm-hint{font-size:var(--text-sm);color:var(--text-muted)}\n.sm-input,.sm-textarea,.sm-select{\n width:100%;min-width:0;border:1.5px solid transparent;border-radius:var(--radius-md);\n background:var(--surface-inset);color:var(--text);font-size:var(--text-md);\n outline:0;box-shadow:none;\n}\n.sm-input,.sm-select{height:var(--control-h);padding:0 10px}\n.sm-textarea{padding:8px 10px;line-height:1.5;resize:vertical;min-height:60px}\n.sm-input--lg{height:var(--control-h-lg);font-size:var(--text-2xl);font-weight:600}\n.sm-input--mono{font-family:var(--font-mono);font-size:var(--text-sm)}\n.sm-input:hover,.sm-textarea:hover,.sm-select:hover{background:var(--control-hover)}\n.sm-input:focus,.sm-textarea:focus,.sm-select:focus,\n.sm-inputwrap:focus-within{\n background:var(--input-bg);border-color:var(--selection);box-shadow:var(--focus-ring);\n}\n.sm-input:disabled,.sm-textarea:disabled,.sm-select:disabled{opacity:.6;cursor:default}\n.sm-select{\n appearance:none;-webkit-appearance:none;cursor:pointer;padding-right:26px;font-weight:500;\n background-image:linear-gradient(45deg,transparent 50%,var(--text-muted) 50%),\n linear-gradient(135deg,var(--text-muted) 50%,transparent 50%);\n background-position:calc(100% - 14px) 12px,calc(100% - 10px) 12px;\n background-size:4px 4px,4px 4px;background-repeat:no-repeat;\n}\n.sm-inputwrap{\n display:flex;align-items:center;gap:0;height:var(--control-h);padding:0 10px;\n border:1.5px solid transparent;border-radius:var(--radius-md);background:var(--surface-inset);\n}\n.sm-inputwrap>.sm-input{height:auto;padding:0;background:transparent;border:0;box-shadow:none}\n.sm-inputwrap>.sm-input:hover,.sm-inputwrap>.sm-input:focus{background:transparent;box-shadow:none}\n.sm-prefix{color:var(--text-muted);font-family:var(--font-mono);font-size:var(--text-sm);flex:none}\n.sm-error{color:var(--danger);font-size:var(--text-sm)}\n/* \u2500\u2500 richtext \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.sm-richtext{display:flex;flex-direction:column;gap:6px;min-width:0}\n.sm-richtext__bar{display:flex;flex-wrap:wrap;align-items:center;gap:2px}\n.sm-richtext__sep{width:1px;height:16px;margin:0 4px;background:var(--line)}\n.sm-richtext__bar .sm-iconbtn[aria-pressed=\"true\"]{background:var(--press);color:var(--text)}\n.sm-richtext__link{display:flex;align-items:center;gap:6px}\n.sm-richtext__content{\n min-height:110px;padding:8px 10px;border:1.5px solid transparent;border-radius:var(--radius-md);\n background:var(--surface-inset);color:var(--text);font-size:var(--text-md);line-height:1.5;outline:0;\n}\n.sm-richtext__content:hover{background:var(--control-hover)}\n.sm-richtext__content:focus{background:var(--input-bg);border-color:var(--selection);box-shadow:var(--focus-ring)}\n.sm-richtext__content>*+*{margin-top:.5em}\n.sm-richtext__content p,.sm-richtext__content h1,.sm-richtext__content h2,.sm-richtext__content h3,\n.sm-richtext__content h4,.sm-richtext__content h5,.sm-richtext__content h6,\n.sm-richtext__content ul,.sm-richtext__content ol,.sm-richtext__content blockquote{margin:0}\n.sm-richtext__content h1,.sm-richtext__content h2,.sm-richtext__content h3,\n.sm-richtext__content h4,.sm-richtext__content h5,.sm-richtext__content h6{font-weight:600;line-height:1.25}\n.sm-richtext__content h1{font-size:var(--text-3xl)}\n.sm-richtext__content h2{font-size:var(--text-2xl)}\n.sm-richtext__content h3{font-size:var(--text-xl)}\n.sm-richtext__content h4,.sm-richtext__content h5,.sm-richtext__content h6{font-size:var(--text-lg)}\n.sm-richtext__content ul,.sm-richtext__content ol{padding-left:18px}\n.sm-richtext__content li+li{margin-top:.25em}\n.sm-richtext__content blockquote{padding-left:10px;border-left:2px solid var(--line);color:var(--text-muted)}\n.sm-richtext__content a{color:var(--accent);text-decoration:underline}\n\n.sm-group{display:flex;flex-direction:column;gap:10px;padding:2px 0 0 10px;border-left:2px solid var(--line)}\n.sm-list{display:flex;flex-direction:column;gap:6px;align-items:stretch}\n.sm-listrow{border:1px solid var(--line);border-radius:var(--radius-md);min-width:0}\n.sm-listrow__head{display:flex;align-items:center;gap:2px;height:var(--control-h);padding:0 4px 0 4px}\n.sm-listrow__toggle{\n flex:1;min-width:0;display:flex;align-items:center;gap:6px;height:100%;padding:0 4px;\n border:0;background:transparent;color:var(--text);font:inherit;font-size:var(--text-md);font-weight:500;\n text-align:left;cursor:pointer;\n}\n.sm-listrow__toggle:hover{color:var(--accent)}\n.sm-listrow__summary{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}\n.sm-listrow__body{display:flex;flex-direction:column;gap:10px;padding:8px 10px 10px;border-top:1px solid var(--line)}\n\n/* \u2500\u2500 data \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.sm-table{display:flex;flex-direction:column;padding:0 16px 16px}\n.sm-table__head,.sm-row{display:grid;gap:12px;align-items:center;padding:0 8px}\n.sm-table__head{\n height:34px;border-bottom:var(--border);font-family:var(--font-mono);\n font-size:var(--text-2xs);letter-spacing:var(--tracking-tag);color:var(--text-muted);\n text-transform:uppercase;position:sticky;top:0;background:var(--surface);z-index:1;\n}\n.sm-table__body{display:flex;flex-direction:column;padding-top:4px}\n.sm-row{\n height:var(--row-h);border-radius:var(--radius-md);background:transparent;\n border:0;font:inherit;color:inherit;text-align:left;width:100%;\n}\n.sm-row--clickable{cursor:pointer}\n.sm-row--clickable:hover{background:var(--surface-inset)}\n.sm-cell{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}\n.sm-cell--muted{color:var(--text-muted)}\n.sm-cell--mono{font-family:var(--font-mono);font-size:var(--text-sm);color:var(--text-muted)}\n.sm-cell--title{font-weight:500}\n.sm-empty{padding:28px 8px;color:var(--text-muted)}\n.sm-chevron{\n width:12px;flex:none;border:0;background:none;padding:0;font:inherit;font-size:10px;\n color:var(--text-muted);cursor:pointer;line-height:1;\n}\n.sm-chevron:hover{color:var(--text)}\n.sm-cell--tree{display:flex;align-items:center;gap:6px;font-weight:500}\n.sm-cell--tree.sm-cell--parent{font-weight:600}\n.sm-rowmenu{\n border:0;background:none;padding:0 6px;font:inherit;color:var(--text-muted);cursor:pointer;\n border-radius:var(--radius-md);line-height:1;height:24px;\n}\n.sm-rowmenu:hover{background:var(--surface-inset);color:var(--text)}\n\n.sm-badge{\n display:inline-flex;align-items:center;height:20px;padding:0 8px;border-radius:var(--radius-md);\n font-size:var(--text-xs);font-weight:600;letter-spacing:.04em;white-space:nowrap;\n}\n.sm-savestate{\n display:inline-flex;align-items:center;gap:6px;padding:0 6px;\n font-size:var(--text-xs);font-weight:600;letter-spacing:.06em;text-transform:uppercase;\n}\n.sm-dot{width:5px;height:5px;border-radius:var(--radius-round);flex:none}\n\n/* \u2500\u2500 navigation bits \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.sm-tabs{display:flex;gap:18px;padding:0 14px;height:36px;align-items:stretch;border-bottom:var(--border);flex:none}\n.sm-tab{\n display:flex;align-items:center;border:0;background:transparent;padding:0;font:inherit;\n cursor:pointer;color:var(--text-muted);border-bottom:2px solid transparent;margin-bottom:-1px;\n}\n.sm-tab:hover{color:var(--text)}\n.sm-tab[aria-selected=\"true\"]{color:var(--accent);font-weight:600;border-bottom-color:var(--accent)}\n.sm-seg{display:flex;gap:2px;padding:2px;background:var(--surface-inset);border-radius:var(--radius-md)}\n.sm-seg--stretch{width:100%}\n.sm-seg--disabled{opacity:.6}\n.sm-seg__opt:disabled{cursor:default}\n.sm-seg__opt{\n flex:1;padding:3px 10px;border:0;border-radius:var(--radius-sm);background:transparent;\n color:var(--text-muted);font:inherit;cursor:pointer;white-space:nowrap;\n}\n.sm-seg__opt:hover{color:var(--text)}\n.sm-seg__opt[aria-pressed=\"true\"]{background:var(--surface);color:var(--text);font-weight:600;box-shadow:var(--shadow-seg)}\n.sm-divider{height:1px;background:var(--line);flex:none}\n.sm-sectionlabel{display:flex;align-items:center;justify-content:space-between;gap:8px}\n.sm-sectionlabel--mono>.sm-label{\n font-family:var(--font-mono);font-weight:400;letter-spacing:var(--tracking-tag);\n}\n.sm-banner{\n display:flex;gap:14px;align-items:flex-start;padding:10px 14px;border-radius:var(--radius-md);\n font-size:var(--text-md);\n}\n.sm-banner--error{background:var(--danger-soft);color:var(--danger)}\n.sm-banner--ok{background:var(--status-published-bg);color:var(--status-published-fg)}\n.sm-banner--warn{background:var(--warn-soft);color:var(--warn)}\n\n/* \u2500\u2500 editor \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.sm-editor{flex:1;min-height:0;display:grid;grid-template-columns:1fr var(--inspector-w)}\n.sm-canvas{display:flex;flex-direction:column;background:var(--bg);min-width:0;min-height:0;overflow:hidden}\n.sm-canvas__meta{\n display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 20px;flex:none;\n font-family:var(--font-mono);font-size:var(--text-xs);letter-spacing:.04em;color:var(--text-muted);\n}\n.sm-canvas__scroll{flex:1;min-height:0;overflow:auto;padding:0 20px 20px}\n.sm-page{\n position:relative;background:var(--page-bg);border-radius:var(--radius-md);\n box-shadow:var(--shadow-page);min-height:100%;\n}\n.sm-inspector{\n display:flex;flex-direction:column;min-height:0;overflow:hidden;\n border-left:var(--border);background:var(--surface);\n}\n.sm-inspector__head{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:12px 14px 10px;flex:none}\n.sm-inspector__body{display:flex;flex-direction:column;gap:14px;padding:14px;overflow:auto}\n.sm-blockglyph{width:14px;height:10px;border:1.5px solid var(--text-muted);border-radius:var(--radius-xs);flex:none}\n.sm-blocktag{\n position:absolute;top:0;left:0;background:var(--selection);color:#fff;\n font-family:var(--font-mono);font-size:var(--text-2xs);font-weight:700;\n letter-spacing:var(--tracking-tag);text-transform:uppercase;padding:3px 7px;\n border-radius:var(--radius-sm) 0 var(--radius-sm) 0;white-space:nowrap;\n}\n.sm-blocktoolbar{\n display:flex;gap:2px;padding:2px;background:#15181F;border-radius:var(--radius-md);\n box-shadow:var(--shadow-float);\n}\n.sm-plus{\n width:24px;height:24px;border-radius:var(--radius-round);border:0;background:var(--selection);\n color:#fff;display:grid;place-items:center;font-size:15px;line-height:1;cursor:pointer;\n box-shadow:0 2px 6px rgba(0,56,177,.35);padding:0;\n}\n.sm-plus:hover{background:var(--brand-500)}\n.sm-guide{height:1px;background:color-mix(in srgb,var(--selection) 35%,transparent)}\n.sm-tooltip{\n background:#15181F;color:#EEF0F5;font-size:var(--text-sm);padding:4px 8px;\n border-radius:var(--radius-sm);white-space:nowrap;\n}\n.sm-outline-row{\n display:flex;align-items:center;justify-content:space-between;gap:4px;\n height:var(--control-h);border-radius:var(--radius-md);padding:0 4px 0 10px;\n color:var(--text);font-weight:500;background:transparent;\n border:1px solid var(--line);\n}\n.sm-outline-row--clickable{cursor:pointer}\n.sm-outline-row--clickable:hover{background:var(--surface-inset)}\n/* Selected and locked rows are read by their fill, so they drop the hairline \u2014\n border-color, not border, so every row keeps the same 28px box. */\n.sm-outline-row--selected,.sm-outline-row--selected:hover{\n background:var(--nav-active-bg);color:var(--nav-active-fg);font-weight:600;border-color:transparent;\n}\n.sm-outline-row--empty{border:1px dashed var(--line-dashed);color:var(--text-muted);padding:0 10px}\n.sm-outline-row--locked{background:var(--surface-inset);color:var(--text-muted);padding:0 10px;border-color:transparent}\n.sm-outline-row__label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}\n\n.sm-picker{\n width:300px;background:var(--surface);border:var(--border);border-radius:var(--radius-md);\n padding:6px;box-shadow:var(--shadow-pop);\n}\n.sm-picker__item{\n display:flex;gap:10px;align-items:center;width:100%;text-align:left;background:transparent;\n border:0;border-radius:var(--radius-md);padding:8px 8px;cursor:pointer;color:var(--text);font:inherit;\n}\n.sm-picker__item:hover{background:var(--nav-active-bg)}\n.sm-picker__icon{\n width:26px;height:26px;flex:none;border-radius:var(--radius-sm);background:var(--accent-soft);\n color:var(--accent);display:grid;place-items:center;font-size:13px;\n}\n.sm-dashed{\n border:1px dashed var(--line-dashed);border-radius:var(--radius-md);\n display:flex;align-items:center;justify-content:center;gap:8px;color:var(--text-muted);\n}\n\n/* \u2500\u2500 media \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.sm-modal{position:fixed;inset:0;z-index:70;background:rgba(0,0,0,.45);display:grid;place-items:center;padding:24px}\n.sm-modal__panel{\n max-width:100%;max-height:100%;display:flex;flex-direction:column;background:var(--surface);\n border:var(--border);border-radius:var(--radius-md);box-shadow:var(--shadow-pop);overflow:hidden;\n}\n.sm-modal__head{display:flex;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:var(--border)}\n.sm-modal__body{padding:12px;overflow:auto;display:flex;flex-direction:column;gap:10px;min-height:0}\n.sm-modal__foot{display:flex;justify-content:flex-end;gap:8px;padding:10px 12px;border-top:var(--border)}\n.sm-picker-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap}\n.sm-dropzone{\n border:1px dashed var(--line-dashed);border-radius:var(--radius-md);color:var(--text-muted);\n display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:16px;text-align:center;\n}\n.sm-dropzone--bar{padding:8px;font-size:var(--text-sm)}\n.sm-mediagrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(132px,1fr));gap:10px}\n.sm-mediacard{\n display:flex;flex-direction:column;gap:4px;padding:6px;background:transparent;border:1.5px solid transparent;\n border-radius:var(--radius-md);cursor:pointer;text-align:left;color:var(--text);font:inherit;min-width:0;\n}\n.sm-mediacard:hover{background:var(--control-hover)}\n.sm-mediacard[aria-pressed=\"true\"]{border-color:var(--selection);background:var(--accent-soft)}\n.sm-mediacard__thumb{display:block;aspect-ratio:4/3;border-radius:var(--radius-sm);overflow:hidden;background:var(--surface-inset)}\n.sm-mediacard__img{width:100%;height:100%;object-fit:cover;display:block}\n.sm-thumb--chip{display:grid;place-items:center;width:100%;height:100%;font-weight:600;color:var(--text-muted);font-size:var(--text-sm)}\n.sm-mediacard__name{font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.sm-mediacard__meta{font-size:var(--text-sm);color:var(--text-muted)}\n.sm-media{display:flex;flex-direction:column;gap:6px}\n.sm-media__thumb{position:relative;overflow:hidden;border-radius:var(--radius-md);background:var(--surface-inset)}\n.sm-media__thumb>img,.sm-media__thumb>video{position:absolute;max-width:none;display:block}\n.sm-media__alt{font-size:var(--text-sm);color:var(--text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.sm-media__actions{display:flex;gap:6px}\n.sm-media__chip{display:flex;align-items:center;gap:8px;padding:6px 10px;background:var(--surface-inset);border-radius:var(--radius-md)}\n.sm-crop{display:grid;grid-template-columns:1fr 240px;gap:14px;min-height:0}\n.sm-crop__stage{position:relative;user-select:none;touch-action:none;background:var(--surface-inset);border-radius:var(--radius-md);overflow:hidden}\n.sm-crop__stage>img{display:block;width:100%;height:auto;pointer-events:none}\n.sm-crop__shade{position:absolute;inset:0;background:rgba(0,0,0,.45);pointer-events:none}\n.sm-crop__rect{position:absolute;outline:1.5px solid #fff;box-shadow:0 0 0 1px rgba(0,0,0,.4);cursor:move;background:transparent}\n.sm-crop__handle{position:absolute;width:12px;height:12px;background:#fff;border:1px solid rgba(0,0,0,.4);border-radius:2px}\n.sm-crop__handle--nw{left:-6px;top:-6px;cursor:nwse-resize}.sm-crop__handle--ne{right:-6px;top:-6px;cursor:nesw-resize}\n.sm-crop__handle--sw{left:-6px;bottom:-6px;cursor:nesw-resize}.sm-crop__handle--se{right:-6px;bottom:-6px;cursor:nwse-resize}\n.sm-crop__hotspot{position:absolute;width:22px;height:22px;margin:-11px 0 0 -11px;border-radius:50%;border:2px solid #fff;background:rgba(0,56,177,.55);cursor:grab;box-shadow:0 0 0 1px rgba(0,0,0,.4)}\n.sm-crop__side{display:flex;flex-direction:column;gap:10px}\n.sm-crop__preview{position:relative;overflow:hidden;border-radius:var(--radius-md);background:var(--surface-inset)}\n.sm-crop__preview>img{position:absolute;max-width:none;display:block}\n.sm-mediascreen{display:grid;grid-template-columns:1fr 300px;flex:1;min-height:0}\n.sm-mediascreen__grid{padding:12px;overflow:auto}\n.sm-mediascreen__inspector{border-left:var(--border);padding:14px;overflow:auto;display:flex;flex-direction:column;gap:10px}\n.sm-facts{display:grid;grid-template-columns:auto 1fr;gap:4px 10px;font-size:var(--text-sm);color:var(--text-muted)}\n.sm-usage{display:flex;flex-direction:column;gap:4px}\n";
1
+ export declare const ADMIN_CSS: string;
2
2
  /** Where the theme toggle's choice is remembered, browser-side only. */
3
3
  export declare const THEME_KEY = "smoodly-theme";
4
+ /** TEMPORARY, see the technical block: which look the developer is comparing. */
5
+ export declare const LOOK_KEY = "smoodly-look";
4
6
  /** One <style> element and the theme boot, rendered once by AdminLayout. */
5
7
  export declare function AdminStyles(): import("react").JSX.Element;