@visualli/react 0.2.4 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -259,6 +259,7 @@ var useRenderConfigStore = (0, import_zustand3.create)((set) => {
259
259
 
260
260
  // src/hooks/useKonvaRenderer.ts
261
261
  var import_react = require("react");
262
+ var useIsoLayoutEffect = typeof window !== "undefined" ? import_react.useLayoutEffect : import_react.useEffect;
262
263
  function useKonvaRenderer({
263
264
  containerRef,
264
265
  enabled = true
@@ -291,41 +292,28 @@ function useKonvaRenderer({
291
292
  if (fpsRafId.current !== null) cancelAnimationFrame(fpsRafId.current);
292
293
  };
293
294
  }, [enabled, autoAdjust]);
294
- (0, import_react.useEffect)(() => {
295
+ useIsoLayoutEffect(() => {
295
296
  if (!enabled) return;
296
- let rafId = null;
297
- const measure = () => {
298
- const el = containerRef.current;
299
- const w = el ? Math.round(el.getBoundingClientRect().width) : 800;
300
- const h = el ? Math.round(el.getBoundingClientRect().height) : 600;
301
- return { w, h };
302
- };
303
- const updateSize = () => {
304
- if (rafId) cancelAnimationFrame(rafId);
305
- rafId = requestAnimationFrame(() => {
306
- rafId = null;
307
- const { w, h } = measure();
308
- if (w > 0 && h > 0 && (cachedSizeRef.current.width !== w || cachedSizeRef.current.height !== h)) {
309
- cachedSizeRef.current = { width: w, height: h };
310
- setCanvasWidth(w);
311
- setCanvasHeight(h);
312
- updateCanvasSize(w, h);
313
- }
314
- });
315
- };
316
- updateSize();
317
- let ro = null;
318
- if (typeof ResizeObserver !== "undefined" && containerRef.current) {
319
- ro = new ResizeObserver(updateSize);
320
- ro.observe(containerRef.current);
321
- } else {
322
- window.addEventListener("resize", updateSize);
323
- }
324
- return () => {
325
- if (ro) ro.disconnect();
326
- else window.removeEventListener("resize", updateSize);
327
- if (rafId) cancelAnimationFrame(rafId);
297
+ const el = containerRef.current;
298
+ const apply = () => {
299
+ if (!el) return;
300
+ const r = el.getBoundingClientRect();
301
+ const w = Math.round(r.width), h = Math.round(r.height);
302
+ if (w > 0 && h > 0 && (cachedSizeRef.current.width !== w || cachedSizeRef.current.height !== h)) {
303
+ cachedSizeRef.current = { width: w, height: h };
304
+ setCanvasWidth(w);
305
+ setCanvasHeight(h);
306
+ updateCanvasSize(w, h);
307
+ }
328
308
  };
309
+ apply();
310
+ if (typeof ResizeObserver !== "undefined" && el) {
311
+ const ro = new ResizeObserver(apply);
312
+ ro.observe(el);
313
+ return () => ro.disconnect();
314
+ }
315
+ window.addEventListener("resize", apply);
316
+ return () => window.removeEventListener("resize", apply);
329
317
  }, [enabled, containerRef, updateCanvasSize]);
330
318
  const handleWheel = (0, import_react.useCallback)((e) => {
331
319
  e.evt.preventDefault();
@@ -672,10 +660,10 @@ var import_react3 = require("react");
672
660
  var import_core6 = require("@visualli/core");
673
661
 
674
662
  // src/generated/specCss.ts
675
- var SPEC_TOKENS_CSS = '/* Spec tokens for all 8 themes, scoped to the map (.vi-map) for renderers embedded in another page \u2014 generated from the Visualli design system (v0.3.0). Do not edit here: changes come in through PRs from the private design system. MIT license, see ../LICENSE. */\n.vi-map, :where([data-theme="light"]) .vi-map, .vi-map[data-theme] {\n --font-display: "Atkinson Hyperlegible Next", system-ui, sans-serif;\n --font-ui: "Atkinson Hyperlegible Next", system-ui, -apple-system, "Segoe UI", sans-serif;\n --font-hand: Kalam, "Atkinson Hyperlegible Next", cursive;\n --font-note: Caveat, Kalam, cursive;\n --font-mono: "Atkinson Hyperlegible Mono", ui-monospace, "SF Mono", Menlo, monospace;\n --canvas: #f6f3ee;\n --surface: #ffffff;\n --surface-sunken: #f0ece5;\n --surface-overlay: #ffffff;\n --line: #e4dfd6;\n --line-strong: #7a7f94;\n --ink: #182140;\n --ink-muted: #4f5671;\n --ink-subtle: #5f647c;\n --action: #182140;\n --on-action: #ffffff;\n --focus: #325e8c;\n --link: #2f5a87;\n --selection: rgba(18, 199, 211, 0.28);\n --node-ink: #182140;\n --edge: #2a3048;\n --edge-label: #4f5671;\n --topic-teal: #87f2f8;\n --topic-teal-ring: #00cdd5;\n --topic-harbor: #bcdbff;\n --topic-harbor-ring: #76b2f5;\n --topic-iris: #e0c8fe;\n --topic-iris-ring: #bd96ea;\n --topic-berry: #ffbdc4;\n --topic-berry-ring: #f47e8f;\n --topic-coral: #ffc7b2;\n --topic-coral-ring: #f38e65;\n --topic-amber: #ffcf9d;\n --topic-amber-ring: #eb9c38;\n --topic-sun: #ffea94;\n --topic-sun-ring: #dfbd04;\n --topic-stone: #e0dacf;\n --topic-stone-ring: #bab09f;\n --space-1: 4px;\n --space-2: 8px;\n --space-3: 12px;\n --space-4: 16px;\n --space-7: 48px;\n --radius-sm: 10px;\n --radius-md: 16px;\n --radius-lg: 24px;\n --radius-pill: 999px;\n --shadow-float: 0 1px 2px rgba(24, 33, 64, 0.06), 0 8px 24px rgba(24, 33, 64, 0.08);\n --shadow-pop: 0 2px 4px rgba(24, 33, 64, 0.08), 0 16px 40px rgba(24, 33, 64, 0.14);\n --node-width: 200px;\n --node-height: 148px;\n --node-stroke: 3px;\n --edge-width: 2.5px;\n --edge-gap: 15px;\n --edge-arc-max: 45px;\n --immersion-alpha: 0.15;\n --zoom-min: 0.3;\n --zoom-max: 5;\n --duration-instant: 100ms;\n --duration-quick: 160ms;\n --duration-base: 240ms;\n --duration-zoom: 500ms;\n --duration-reveal: 420ms;\n --stagger-reveal: 70ms;\n --ease-zoom: cubic-bezier(0.2, 0.8, 0.2, 1);\n --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);\n --ease-bloom: cubic-bezier(0.34, 1.4, 0.64, 1);\n --z-chrome: 10;\n --z-card: 20;\n --z-menu: 40;\n }\n:where([data-theme="dark"]) .vi-map, .vi-map[data-theme="dark"] {\n --canvas: #0f1220;\n --surface: #171b2b;\n --surface-sunken: #0f1220;\n --surface-overlay: #212639;\n --line: #2b3148;\n --line-strong: #6c7392;\n --ink: #eef0f7;\n --ink-muted: #b3b8cc;\n --ink-subtle: #8f95ae;\n --action: #f6f3ee;\n --on-action: #182140;\n --focus: #12c7d3;\n --link: #5fd8e1;\n --selection: rgba(18, 199, 211, 0.32);\n --edge: #c9cddc;\n --edge-label: #b3b8cc;\n --topic-teal-ring: #0fc5cd;\n --topic-harbor-ring: #73aceb;\n --topic-iris-ring: #b691e1;\n --topic-berry-ring: #eb798a;\n --topic-coral-ring: #ea8962;\n --topic-amber-ring: #e29737;\n --topic-sun-ring: #d8b70a;\n --topic-stone-ring: #b4a999;\n --shadow-float: 0 1px 2px rgba(0, 0, 0, 0.40), 0 8px 24px rgba(0, 0, 0, 0.35);\n --shadow-pop: 0 2px 4px rgba(0, 0, 0, 0.50), 0 16px 40px rgba(0, 0, 0, 0.50);\n }\n:where([data-theme="focus-light"]) .vi-map, .vi-map[data-theme="focus-light"] {\n --canvas: #f4f2ee;\n --edge: #5a6079;\n --edge-label: #5a6079;\n --topic-teal: #bce8ea;\n --topic-teal-ring: #67c6cb;\n --topic-harbor: #c9d9ec;\n --topic-harbor-ring: #8bb2de;\n --topic-iris: #d9cfe8;\n --topic-iris-ring: #b79ed4;\n --topic-berry: #edc6c9;\n --topic-berry-ring: #da9099;\n --topic-coral: #eecec1;\n --topic-coral-ring: #db9b82;\n --topic-amber: #eed4ba;\n --topic-amber-ring: #d6a570;\n --topic-sun: #f4eac1;\n --topic-sun-ring: #d2bf73;\n --topic-stone: #dddad5;\n }\n:where([data-theme="focus-dark"]) .vi-map, .vi-map[data-theme="focus-dark"] {\n --canvas: #12141c;\n --surface: #171b2b;\n --surface-sunken: #0f1220;\n --surface-overlay: #212639;\n --line: #2b3148;\n --line-strong: #6c7392;\n --ink: #eef0f7;\n --ink-muted: #b3b8cc;\n --ink-subtle: #8f95ae;\n --action: #f6f3ee;\n --on-action: #182140;\n --focus: #12c7d3;\n --link: #5fd8e1;\n --selection: rgba(18, 199, 211, 0.32);\n --edge: #a9aec0;\n --edge-label: #b3b8cc;\n --topic-teal: #bce8ea;\n --topic-teal-ring: #71bdc1;\n --topic-harbor: #c9d9ec;\n --topic-harbor-ring: #8cabce;\n --topic-iris: #d9cfe8;\n --topic-iris-ring: #ae9bc6;\n --topic-berry: #edc6c9;\n --topic-berry-ring: #cc8e95;\n --topic-coral: #eecec1;\n --topic-coral-ring: #cd9884;\n --topic-amber: #eed4ba;\n --topic-amber-ring: #c9a176;\n --topic-sun: #f4eac1;\n --topic-sun-ring: #c9b97a;\n --topic-stone: #dddad5;\n --topic-stone-ring: #b4a999;\n }\n:where([data-theme="colorsafe-light"]) .vi-map, .vi-map[data-theme="colorsafe-light"] {\n --topic-teal: #b0ecbc;\n --topic-teal-ring: #6cc982;\n --topic-harbor: #abdbfe;\n --topic-harbor-ring: #60b4ec;\n --topic-iris: #f6bde2;\n --topic-iris-ring: #dc87c0;\n --topic-berry: #feb79e;\n --topic-berry-ring: #ec7f58;\n --topic-coral: #fece96;\n --topic-coral-ring: #e49d41;\n --topic-amber: #b2e8fa;\n --topic-amber-ring: #5cc4e2;\n --topic-sun: #f6f097;\n --topic-sun-ring: #d0c637;\n --topic-stone: #dbdbdb;\n --topic-stone-ring: #b7afb1;\n }\n:where([data-theme="colorsafe-dark"]) .vi-map, .vi-map[data-theme="colorsafe-dark"] {\n --canvas: #0f1220;\n --surface: #171b2b;\n --surface-sunken: #0f1220;\n --surface-overlay: #212639;\n --line: #2b3148;\n --line-strong: #6c7392;\n --ink: #eef0f7;\n --ink-muted: #b3b8cc;\n --ink-subtle: #8f95ae;\n --action: #f6f3ee;\n --on-action: #182140;\n --focus: #12c7d3;\n --link: #5fd8e1;\n --selection: rgba(18, 199, 211, 0.32);\n --edge: #c9cddc;\n --edge-label: #b3b8cc;\n --topic-teal: #b0ecbc;\n --topic-teal-ring: #69c27e;\n --topic-harbor: #abdbfe;\n --topic-harbor-ring: #5eade2;\n --topic-iris: #f6bde2;\n --topic-iris-ring: #d383b9;\n --topic-berry: #feb79e;\n --topic-berry-ring: #e37a55;\n --topic-coral: #fece96;\n --topic-coral-ring: #db9840;\n --topic-amber: #b2e8fa;\n --topic-amber-ring: #5bbdda;\n --topic-sun: #f6f097;\n --topic-sun-ring: #c9bf37;\n --topic-stone: #dbdbdb;\n --topic-stone-ring: #b1a8aa;\n }\n:where([data-theme="contrast-light"]) .vi-map, .vi-map[data-theme="contrast-light"] {\n --canvas: #ffffff;\n --surface-sunken: #f2f2f2;\n --line: #5c6178;\n --line-strong: #000000;\n --ink: #000000;\n --ink-muted: #1f2433;\n --ink-subtle: #3a3f52;\n --action: #000000;\n --focus: #0b4a8f;\n --link: #0b4a8f;\n --node-ink: #000000;\n --edge: #000000;\n --edge-label: #1f2433;\n --topic-teal-ring: #006b70;\n --topic-harbor-ring: #0e599d;\n --topic-iris-ring: #683b92;\n --topic-berry-ring: #96173a;\n --topic-coral-ring: #923400;\n --topic-amber-ring: #7e4d00;\n --topic-sun-ring: #7a6600;\n --topic-stone-ring: #63594b;\n --shadow-float: none;\n --shadow-pop: none;\n }\n:where([data-theme="contrast-dark"]) .vi-map, .vi-map[data-theme="contrast-dark"] {\n --canvas: #000000;\n --surface: #000000;\n --surface-sunken: #0d0d0d;\n --surface-overlay: #0d0d0d;\n --line: #9aa0b8;\n --line-strong: #ffffff;\n --ink: #ffffff;\n --ink-muted: #e6e8f0;\n --ink-subtle: #c8ccda;\n --action: #ffffff;\n --on-action: #000000;\n --focus: #5fe8f0;\n --link: #7ff0f5;\n --selection: rgba(18, 199, 211, 0.32);\n --node-ink: #000000;\n --edge: #ffffff;\n --edge-label: #e6e8f0;\n --topic-teal-ring: #64eff7;\n --topic-harbor-ring: #b0d5fe;\n --topic-iris-ring: #dcbfff;\n --topic-berry-ring: #feb3bb;\n --topic-coral-ring: #ffbea5;\n --topic-amber-ring: #ffc78a;\n --topic-sun-ring: #ffe26d;\n --topic-stone-ring: #ddd3c2;\n --shadow-float: none;\n --shadow-pop: none;\n }\n:where([data-motion="reduced"]) .vi-map, .vi-map[data-motion="reduced"] {\n --duration-instant: 120ms;\n --duration-quick: 0ms;\n --duration-base: 0ms;\n --duration-zoom: 0ms;\n --duration-reveal: 0ms;\n --stagger-reveal: 0ms;\n }\n@media (prefers-reduced-motion: reduce) {\n :where(:root:not([data-motion="full"])) .vi-map {\n --duration-instant: 120ms;\n --duration-quick: 0ms;\n --duration-base: 0ms;\n --duration-zoom: 0ms;\n --duration-reveal: 0ms;\n --stagger-reveal: 0ms;\n }\n}\n\n/* Type styles */\n.vi-text-heading { font-family: var(--font-ui); font-size: 17px; line-height: 24px; font-weight: 700; }\n.vi-text-body { font-family: var(--font-ui); font-size: 15px; line-height: 22px; font-weight: 400; }\n.vi-text-body-sm { font-family: var(--font-ui); font-size: 13px; line-height: 18px; font-weight: 400; }\n.vi-text-label { font-family: var(--font-ui); font-size: 13px; line-height: 16px; font-weight: 600; letter-spacing: 0.01em; }\n.vi-text-caption { font-family: var(--font-ui); font-size: 12px; line-height: 16px; font-weight: 400; }\n.vi-text-node-root { font-family: var(--font-hand); font-size: 30px; line-height: 34px; font-weight: 700; }\n.vi-text-node-label { font-family: var(--font-hand); font-size: 22px; line-height: 26px; font-weight: 700; }\n.vi-text-edge-label { font-family: var(--font-note); font-size: 18px; line-height: 20px; font-weight: 500; }\n.vi-text-annotation { font-family: var(--font-note); font-size: 24px; line-height: 26px; font-weight: 600; }\n';
676
- var SPEC_COMPONENT_CSS = '/* Styles for the spec components (map, nodes, connectors, peek, terms, depth trail, controls) \u2014 generated from the Visualli design system (v0.3.0). Do not edit here: changes come in through PRs from the private design system. MIT license, see ../LICENSE. */\n.vi-map { --vi-fs-body: 15px; --vi-fs-sm: 13px; --vi-fs-cap: 12px; --vi-lh-body: 22px; --vi-label-scale: 1; }\n[data-theme$="dark"] :where([class^="vi-"], [class*=" vi-"]) { color-scheme: dark; }\n:where([data-scale="lg"]) .vi-map, .vi-map[data-scale="lg"] { --vi-fs-body: 17px; --vi-fs-sm: 15px; --vi-fs-cap: 14px; --vi-lh-body: 26px; --vi-label-scale: 1.15; }\n:where([data-type="readable"]) .vi-map, .vi-map[data-type="readable"] { --font-hand: "Atkinson Hyperlegible Next", system-ui, sans-serif; --font-note: "Atkinson Hyperlegible Next", system-ui, sans-serif; --font-display: "Atkinson Hyperlegible Next", system-ui, sans-serif; }\n[data-type="readable"] .vi-node__label, [data-type="readable"] .vi-edge__label, [data-type="readable"] .vi-map__group-label { font-family: var(--font-ui) !important; font-weight: 700; letter-spacing: 0.02em; font-style: normal; }\n.vi-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }\n.vi-icon { flex: none; display: block; }\n:where([class^="vi-"], [class*=" vi-"]):where(button, a, input, textarea, [tabindex]):focus-visible, :where([class^="vi-"], [class*=" vi-"]) :where(button, a, input, textarea, [tabindex]):focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }\n.vi-iconbtn { display: inline-grid; place-items: center; border-radius: var(--radius-sm); border: 1.5px solid transparent; background: transparent; color: var(--ink); cursor: pointer; transition: background var(--duration-instant); flex: none; }\n.vi-iconbtn--sm { width: 32px; height: 32px; }\n.vi-iconbtn--md { width: 40px; height: 40px; }\n.vi-iconbtn--lg { width: 48px; height: 48px; border-radius: var(--radius-md); }\n.vi-iconbtn:hover { background: var(--surface-sunken); }\n.vi-iconbtn--surface { background: var(--surface); border-color: var(--line); }\n.vi-iconbtn--surface:hover { border-color: var(--line-strong); }\n.vi-iconbtn--primary { background: var(--action); color: var(--on-action); border-radius: var(--radius-pill); }\n.vi-iconbtn.is-active { background: var(--action); color: var(--on-action); }\n[data-theme^="contrast"] .vi-ctrls { border: 2px solid var(--line-strong); }\n.vi-node { position: relative; display: inline-grid; place-items: center; border: 0; padding: 0; background: none; color: var(--node-ink); cursor: default; transition: transform var(--duration-base) var(--ease-bloom), opacity var(--duration-base); -webkit-tap-highlight-color: transparent; }\nbutton.vi-node { cursor: pointer; }\n.vi-node__svg { position: absolute; inset: 0; overflow: visible; }\n.vi-node__body { filter: drop-shadow(var(--vi-node-shadow, 0 6px 14px rgba(24, 33, 64, .10))); }\n[data-theme$="dark"] .vi-node__body { --vi-node-shadow: 0 0 22px rgba(255, 255, 255, .10); }\n[data-theme^="contrast"] .vi-node__body { filter: none; }\n.vi-node__rings { transform-origin: center; transition: transform var(--duration-base) var(--ease-standard); }\n@media (hover: hover) {\n button.vi-node:hover .vi-node__rings { transform: rotate(var(--vi-ring-turn, -2deg)) scale(var(--vi-ring-grow, 1.015)); }\n button.vi-node:hover { transform: translateY(calc(var(--vi-lift, 2px) * -1)); }\n}\n.vi-node { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; touch-action: manipulation; }\n.vi-node.is-selected { transform: translateY(-2px); }\n.vi-node.is-selected .vi-node__rings { transform: rotate(var(--vi-ring-turn, -2deg)) scale(var(--vi-ring-grow, 1.015)); }\n.vi-node.is-selected .vi-node__body { stroke-width: 4; }\n.vi-node:focus-visible { outline: none; }\n.vi-node:focus-visible .vi-node__body { stroke: var(--focus); stroke-width: 4; }\n.vi-node.is-selected .vi-node__body { stroke: var(--ink); }\n.vi-node__label { position: relative; text-align: center; color: var(--node-ink); display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; font-size: calc(var(--vi-nfs, 22px) * var(--vi-label-scale) * var(--vi-ninv, 1)); line-height: 1.15; padding-top: 2px; }\n.vi-node__label.node-root { --vi-nfs: 30px; }\n.vi-node--mini .vi-node__label { --vi-nfs: 15px; }\n.vi-node.is-dimmed { opacity: 1; }\n[data-theme^="focus"] .vi-node.is-dimmed { opacity: .4; }\n.vi-map.is-touch .vi-node.is-dimmed { opacity: .6; }\n[data-theme^="focus"] .vi-map.is-touch .vi-node.is-dimmed { opacity: .4; }\n.vi-edge__line { fill: none; stroke: var(--edge); stroke-width: 2.5; stroke-linecap: round; }\n.vi-edge.is-dashed .vi-edge__line { stroke-dasharray: 7 7; }\n.vi-edge__arrow { fill: none; stroke: var(--edge); stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }\n.vi-edge__label { font: 500 calc(18px * var(--vi-inv, 1)) / 1 var(--font-note); fill: var(--edge-label); paint-order: stroke; stroke: var(--canvas); stroke-width: 5px; stroke-linejoin: round; letter-spacing: .01em; }\n[data-type="readable"] .vi-edge__label { font-size: calc(14px * var(--vi-inv, 1)); font-weight: 600; }\n.vi-fact { position: relative; width: 300px; box-sizing: border-box; padding: var(--space-4) var(--space-4) var(--space-3); background: var(--surface-overlay); color: var(--ink); border: 2px solid var(--vi-fact-ring); border-radius: 14px 18px 12px 16px / 16px 12px 18px 14px; font: 400 var(--vi-fs-sm)/20px var(--font-ui); z-index: var(--z-card); animation: vi-pop var(--duration-quick) var(--ease-standard); }\n.vi-fact { box-shadow: 5px 6px 0 0 var(--vi-fact-fill), 5px 6px 0 2px var(--vi-fact-ring); }\n[data-theme^="contrast"] .vi-fact { border-color: var(--ink); }\n[data-theme^="contrast"] .vi-fact { box-shadow: none; }\n.vi-fact__title { font: 700 22px/26px var(--font-hand); margin-bottom: var(--space-1); }\n.vi-fact__body { color: var(--ink); }\n.vi-fact__foot { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); margin-top: var(--space-3); padding-top: var(--space-2); border-top: 1px dashed var(--line); }\n.vi-fact__acts { display: inline-flex; gap: var(--space-3); align-items: center; flex: none; }\n.vi-fact__link { color: var(--link); font: 600 var(--vi-fs-cap)/16px var(--font-ui); }\n.vi-fact__step { display: inline-flex; align-items: center; gap: 2px; border: 0; background: var(--action); color: var(--on-action); font: 600 var(--vi-fs-cap)/16px var(--font-ui); padding: 5px 8px 5px 10px; border-radius: var(--radius-pill); cursor: pointer; }\n.vi-trail ol { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; position: relative; }\n.vi-trail li { position: relative; }\n.vi-trail li + li::before { content: ""; position: absolute; left: 13px; top: -8px; height: 10px; border-left: 1.5px dashed var(--line-strong); }\n.vi-trail button { display: inline-flex; align-items: center; gap: 6px; border: 0; background: none; padding: 4px 8px 4px 5px; border-radius: var(--radius-sm); color: var(--ink-muted); font: 400 var(--vi-fs-cap)/16px var(--font-ui); cursor: pointer; }\n.vi-trail button span { display: inline-flex; align-items: center; gap: 4px; }\n.vi-trail button:hover:not(:disabled) { background: var(--surface-sunken); color: var(--ink); }\n.vi-trail li.is-current button { color: var(--ink); font-weight: 700; font-size: var(--vi-fs-sm); cursor: default; background: var(--surface); box-shadow: var(--shadow-float); }\n.vi-trail li.is-current button:disabled { opacity: 1; }\n.vi-ctrls { display: inline-flex; align-items: center; gap: 2px; padding: 4px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md); box-shadow: var(--shadow-float); }\n.vi-ctrls__pct { min-width: 44px; text-align: center; font: 600 var(--vi-fs-cap)/16px var(--font-ui); color: var(--ink-muted); font-variant-numeric: tabular-nums; }\n.vi-ctrls__div { width: 1px; height: 20px; background: var(--line); margin: 0 4px; }\n.vi-map { position: relative; overflow: hidden; background: var(--canvas); border-radius: inherit; isolation: isolate; }\n.vi-map__immersion { position: absolute; inset: 0; opacity: var(--immersion-alpha); transition: background var(--duration-base); z-index: -1; }\n.vi-map__world { position: absolute; left: 0; top: 0; transform-origin: 0 0; }\n.vi-map__edges { position: absolute; overflow: visible; pointer-events: none; }\n.vi-map__group rect { fill: none; stroke: var(--line-strong); stroke-width: 1.5; stroke-dasharray: 10 6; }\n.vi-map__group-label { position: absolute; transform: translate(-50%, -50%); z-index: 2; max-width: 420px; box-sizing: border-box; padding: 6px 22px 8px; background: var(--topic-stone); border: 1.5px solid var(--topic-stone-ring); border-radius: var(--radius-pill); font: 700 22px/1.1 var(--font-note); color: var(--node-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; pointer-events: none; font-size: calc(26px * var(--vi-label-scale) * var(--vi-ninv, 1)); }\n[data-theme^="contrast"] .vi-map__group-label { border: 2px solid var(--line-strong); }\n.vi-map__slot { position: absolute; transform: translate(-50%, -50%); }\n.vi-map.is-reveal .vi-map__slot { animation: vi-bloom var(--duration-reveal) var(--ease-bloom) backwards; }\n.vi-map.is-reveal .vi-edge { animation: vi-fade var(--duration-base) var(--ease-standard) backwards; }\n.vi-map.is-reveal .vi-edge__line { stroke-dasharray: 1; stroke-dashoffset: 1; animation: vi-line var(--vi-line-draw, .5s) var(--ease-standard) forwards; animation-delay: inherit; }\n.vi-map.is-reveal .vi-edge.is-dashed .vi-edge__line { stroke-dasharray: 7 7; stroke-dashoffset: 0; animation: vi-fade var(--vi-line-draw, .5s) var(--ease-standard) backwards; animation-delay: inherit; }\n.vi-map:not(.is-reveal) .vi-edge__line { stroke-dasharray: none; }\n.vi-map:not(.is-reveal) .vi-edge.is-dashed .vi-edge__line { stroke-dasharray: 7 7; }\n.vi-map__lens { position: absolute; inset: 0; transform-origin: var(--vi-ox) var(--vi-oy); }\n.vi-map.is-diving .vi-map__lens { transform: scale(var(--vi-dive-scale, 3.2)); opacity: 0; transition: transform var(--duration-zoom) var(--ease-zoom), opacity var(--vi-dive-fade, 160ms) var(--vi-dive-fade-delay, 150ms); }\n.vi-map.is-surfacing .vi-map__lens { transform: scale(var(--vi-surface-scale, .6)); opacity: 0; transform-origin: 50% 50%; transition: transform var(--vi-surface-dur, 220ms) var(--ease-zoom), opacity var(--vi-surface-fade, 200ms); }\n.vi-map.is-reveal .vi-map__lens { animation: vi-settle var(--vi-settle-dur, 260ms) var(--ease-standard); }\n[data-motion="reduced"] .vi-map__lens { animation: none !important; transform: none !important; }\n.vi-map__fact { position: absolute; }\n.vi-map.is-idle .vi-map__world { transition: transform var(--duration-base) var(--ease-standard); }\n.vi-map.is-panning, .vi-map.is-panning .vi-node { cursor: grabbing; }\n.vi-map__hint { position: absolute; left: var(--space-4); right: var(--space-4); margin: 0; text-align: center; color: var(--ink-muted); pointer-events: none; z-index: var(--z-chrome); animation: vi-fade var(--duration-base) var(--ease-standard); }\n.vi-fact__head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-2); }\n.vi-fact__back { display: inline-flex; align-items: center; gap: 2px; border: 0; background: none; padding: 2px 0; margin-bottom: var(--space-1); color: var(--link); font: 600 var(--vi-fs-cap)/16px var(--font-ui); cursor: pointer; }\n.vi-term { border: 0; background: none; padding: 0; font: inherit; color: var(--link); text-decoration: underline; text-decoration-style: dotted; text-decoration-thickness: 1.5px; text-underline-offset: 3px; cursor: pointer; }\n.vi-fact--sheet { position: absolute; left: 0; right: 0; bottom: 0; width: auto; max-width: 560px; margin: 0 auto; max-height: var(--vi-sheet-h, 300px); overflow-y: auto; overscroll-behavior: contain; padding: 0 var(--space-4) calc(var(--space-4) + env(safe-area-inset-bottom, 0px)); border: 0; border-top: 2px solid var(--vi-fact-ring); border-radius: var(--radius-lg) var(--radius-lg) 0 0; box-shadow: 0 -6px 0 0 var(--vi-fact-fill), var(--shadow-pop); font-size: var(--vi-fs-body); line-height: var(--vi-lh-body); animation: vi-sheet-in var(--duration-base) var(--ease-standard); transition: transform var(--duration-quick) var(--ease-standard), max-height var(--duration-base) var(--ease-standard); }\n.vi-fact--sheet.is-expanded { max-height: calc(100% - var(--space-7)); }\n.vi-fact__grip { position: sticky; top: 0; display: flex; justify-content: center; margin: 0 calc(var(--space-4) * -1); background: var(--surface-overlay); touch-action: none; cursor: grab; z-index: 1; }\n.vi-fact__handle { position: relative; width: 64px; height: 24px; border: 0; padding: 0; background: none; cursor: pointer; }\n.vi-fact__handle::after { content: ""; position: absolute; left: 12px; right: 12px; top: 10px; height: 5px; border-radius: var(--radius-pill); background: var(--line-strong); }\n.vi-fact--sheet .vi-fact__title { font-size: calc(24px * var(--vi-label-scale)); line-height: 1.15; padding-top: var(--space-1); }\n.vi-fact--sheet .vi-fact__close { margin: 0 calc(var(--space-2) * -1) 0 0; }\n.vi-fact--sheet .vi-fact__step { min-height: 44px; padding: 0 var(--space-3) 0 var(--space-4); font-size: var(--vi-fs-sm); }\n.vi-fact--sheet .vi-fact__link { font-size: var(--vi-fs-sm); min-height: 44px; display: inline-flex; align-items: center; }\n[data-motion="reduced"] .vi-fact--sheet { animation: vi-fade 120ms; }\n@media (prefers-reduced-motion: reduce) {\n .vi-fact--sheet { animation: vi-fade 120ms; }\n .vi-map.is-idle .vi-map__world { transition: none; }\n}\n.vi-map__trail { position: absolute; top: var(--space-4); left: var(--space-4); z-index: var(--z-chrome); }\n.vi-map__ctrls { position: absolute; top: var(--space-4); right: var(--space-4); z-index: var(--z-chrome); }\n[data-reveal="instant"] .vi-map.is-reveal .vi-map__slot, [data-motion="reduced"] .vi-map.is-reveal .vi-map__slot { animation: vi-fade var(--vi-reduced-fade, 120ms) backwards; animation-delay: 0ms !important; }\n[data-reveal="instant"] .vi-map.is-reveal .vi-edge, [data-reveal="instant"] .vi-map.is-reveal .vi-edge__line, [data-motion="reduced"] .vi-map.is-reveal .vi-edge, [data-motion="reduced"] .vi-map.is-reveal .vi-edge__line { animation: none; stroke-dashoffset: 0; }\n@media (prefers-reduced-motion: reduce) {\n .vi-map.is-reveal .vi-map__slot { animation: vi-fade var(--vi-reduced-fade, 120ms) backwards; animation-delay: 0ms !important; }\n .vi-map.is-reveal .vi-edge, .vi-map.is-reveal .vi-edge__line { animation: none; stroke-dashoffset: 0; }\n}\n[data-motion="reduced"] :where([class^="vi-"], [class*=" vi-"]), [data-motion="reduced"] :where([class^="vi-"], [class*=" vi-"])::before, [data-motion="reduced"] :where([class^="vi-"], [class*=" vi-"])::after { transition-duration: 1ms !important; }\n.vi-fact__acts { flex-wrap: wrap; justify-content: flex-end; }\n.vi-fact__link { display: inline-flex; align-items: center; gap: 2px; }\n.vi-anchor { position: relative; display: inline; }\n.vi-anchor.is-open .vi-term { background: var(--selection); border-radius: 3px; }\n.vi-anchor__pop { position: absolute; left: -12px; top: calc(100% + 6px); z-index: var(--z-menu); display: block; width: max-content; }\n.vi-anchor-card { position: relative; display: block; width: min(300px, calc(100vw - 32px)); box-sizing: border-box; padding: var(--space-3) var(--space-4) var(--space-3); background: var(--surface-overlay); color: var(--ink); text-align: left; white-space: normal; font: 400 var(--vi-fs-sm)/20px var(--font-ui); border: 2px solid var(--vi-fact-ring); border-radius: 12px 16px 10px 14px / 14px 10px 16px 12px; box-shadow: 4px 5px 0 0 var(--vi-fact-fill), 4px 5px 0 2px var(--vi-fact-ring), var(--shadow-pop); animation: vi-pop var(--duration-quick) var(--ease-standard); }\n.vi-anchor-card.is-in-sheet { width: auto; border: 0; box-shadow: none; padding: 0; animation: none; background: transparent; }\n[data-theme^="contrast"] .vi-anchor-card { border-color: var(--ink); box-shadow: none; }\n.vi-anchor-card__eyebrow { font: 400 11px/14px var(--font-display); letter-spacing: .14em; text-transform: uppercase; color: var(--ink-muted); }\n.vi-anchor-card__word { font: 700 20px/24px var(--font-hand); color: var(--ink); margin: 2px 0 var(--space-1); }\n.vi-fact--sheet .vi-anchor-card__word { font-size: calc(24px * var(--vi-label-scale)); line-height: 1.15; }\n.vi-anchor-card__desc { margin: 0; color: var(--ink); }\n.vi-anchor-card__note { margin: var(--space-2) 0 0; font: 400 var(--vi-fs-cap)/16px var(--font-ui); color: var(--ink-muted); }\n.vi-anchor-card__acts { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: var(--space-3); margin-top: var(--space-3); padding-top: var(--space-2); border-top: 1px dashed var(--line); }\n.vi-fact--sheet .vi-anchor-card__acts .vi-fact__link { min-height: 44px; }\n[data-type="readable"] .vi-anchor-card__word { font-family: var(--font-ui); }\n[data-motion="reduced"] .vi-anchor-card { animation: vi-fade 120ms; }\n@media (prefers-reduced-motion: reduce) {\n .vi-anchor-card { animation: vi-fade 120ms; }\n}\n.vi-peek .vi-fact__foot { flex-wrap: wrap; row-gap: var(--space-2); }\n.vi-peek .vi-fact__acts { margin-left: auto; }\n.vi-map.has-highlight .vi-node.is-dimmed { opacity: .35; transition: opacity var(--duration-quick) var(--ease-standard); }\n.vi-map.has-highlight .vi-node.is-selected .vi-node__body { stroke: var(--ink); }\n.vi-map { container-type: inline-size; }\n.vi-edge__hit { fill: none; stroke: transparent; stroke-width: 18; pointer-events: stroke; cursor: pointer; }\n.vi-edge__hit:hover + .vi-edge__line { stroke-width: 3.5; }\n.vi-edge.is-selected .vi-edge__line, .vi-edge.is-selected .vi-edge__arrow { stroke: var(--focus); stroke-width: 3.5; }\n.vi-map__group-hit { fill: none; stroke: transparent; stroke-width: 16; pointer-events: stroke; cursor: pointer; }\n.vi-map__group.is-selected rect:not(.vi-map__group-hit) { stroke: var(--focus); stroke-width: 2.5; stroke-dasharray: none; }\n.vi-map__group-label.is-selected { border-color: var(--focus); color: var(--focus); box-shadow: 0 0 0 1px var(--focus); }\n.vi-map { touch-action: pan-x pan-y; }\n.vi-map.is-app { touch-action: none; }\n.vi-map.is-pinching .vi-map__world { transition: none; }\n.vi-map.is-instant.is-reveal .vi-map__slot { animation: vi-fade var(--vi-reduced-fade, 120ms) backwards; animation-delay: 0ms !important; }\n.vi-map.is-instant.is-reveal .vi-edge, .vi-map.is-instant.is-reveal .vi-edge__line { animation: none; stroke-dashoffset: 0; }\n.vi-map.is-instant .vi-node, .vi-map.is-instant .vi-node__rings { transition: none; }\n.vi-map.is-detail-body .vi-node:not(.is-selected):not(:focus-visible):not(:hover) :is(.vi-node__rings, .vi-node__label), .vi-map.is-detail-speck .vi-node:not(.is-selected):not(:focus-visible):not(:hover) :is(.vi-node__rings, .vi-node__label) { display: none; }\n.vi-map.is-detail-body .vi-node:not(.is-selected):not(:focus-visible):not(:hover) .vi-node__body, .vi-map.is-detail-speck .vi-node:not(.is-selected):not(:focus-visible):not(:hover) .vi-node__body, .vi-map.no-shadows .vi-node__body { filter: none; }\n@keyframes vi-pop { from { opacity: 0; transform: translateY(-4px) scale(.98); } }\n@keyframes vi-fade { from { opacity: 0; } }\n@keyframes vi-bloom { from { opacity: 0; transform: translate(calc(-50% + var(--vi-dx)), calc(-50% + var(--vi-dy))) scale(var(--vi-bloom-from, .55)); } }\n@keyframes vi-line { to { stroke-dashoffset: 0; } }\n@keyframes vi-settle { from { transform: scale(var(--vi-settle-from, .94)); } }\n@keyframes vi-sheet-in { from { transform: translateY(100%); } }\n';
663
+ var SPEC_TOKENS_CSS = '/* Spec tokens for all 8 themes, scoped to the map (.vi-map) for renderers embedded in another page \u2014 generated from the Visualli design system (v0.3.1). Do not edit here: changes come in through PRs from the private design system. MIT license, see ../LICENSE. */\n.vi-map, .vi-map[data-theme] {\n --font-display: "Atkinson Hyperlegible Next", system-ui, sans-serif;\n --font-ui: "Atkinson Hyperlegible Next", system-ui, -apple-system, "Segoe UI", sans-serif;\n --font-hand: Kalam, "Atkinson Hyperlegible Next", cursive;\n --font-note: Caveat, Kalam, cursive;\n --font-mono: "Atkinson Hyperlegible Mono", ui-monospace, "SF Mono", Menlo, monospace;\n --canvas: #f6f3ee;\n --surface: #ffffff;\n --surface-sunken: #f0ece5;\n --surface-overlay: #ffffff;\n --line: #e4dfd6;\n --line-strong: #7a7f94;\n --ink: #182140;\n --ink-muted: #4f5671;\n --ink-subtle: #5f647c;\n --action: #182140;\n --on-action: #ffffff;\n --focus: #325e8c;\n --link: #2f5a87;\n --selection: rgba(18, 199, 211, 0.28);\n --node-ink: #182140;\n --edge: #2a3048;\n --edge-label: #4f5671;\n --topic-teal: #87f2f8;\n --topic-teal-ring: #00cdd5;\n --topic-harbor: #bcdbff;\n --topic-harbor-ring: #76b2f5;\n --topic-iris: #e0c8fe;\n --topic-iris-ring: #bd96ea;\n --topic-berry: #ffbdc4;\n --topic-berry-ring: #f47e8f;\n --topic-coral: #ffc7b2;\n --topic-coral-ring: #f38e65;\n --topic-amber: #ffcf9d;\n --topic-amber-ring: #eb9c38;\n --topic-sun: #ffea94;\n --topic-sun-ring: #dfbd04;\n --topic-stone: #e0dacf;\n --topic-stone-ring: #bab09f;\n --space-1: 4px;\n --space-2: 8px;\n --space-3: 12px;\n --space-4: 16px;\n --space-7: 48px;\n --radius-sm: 10px;\n --radius-md: 16px;\n --radius-lg: 24px;\n --radius-pill: 999px;\n --shadow-float: 0 1px 2px rgba(24, 33, 64, 0.06), 0 8px 24px rgba(24, 33, 64, 0.08);\n --shadow-pop: 0 2px 4px rgba(24, 33, 64, 0.08), 0 16px 40px rgba(24, 33, 64, 0.14);\n --node-width: 200px;\n --node-height: 148px;\n --node-stroke: 3px;\n --edge-width: 2.5px;\n --edge-gap: 15px;\n --edge-arc-max: 45px;\n --immersion-alpha: 0.15;\n --zoom-min: 0.3;\n --zoom-max: 5;\n --duration-instant: 100ms;\n --duration-quick: 160ms;\n --duration-base: 240ms;\n --duration-zoom: 500ms;\n --duration-reveal: 420ms;\n --stagger-reveal: 70ms;\n --ease-zoom: cubic-bezier(0.2, 0.8, 0.2, 1);\n --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);\n --ease-bloom: cubic-bezier(0.34, 1.4, 0.64, 1);\n --z-chrome: 10;\n --z-card: 20;\n --z-menu: 40;\n }\n:where([data-theme="light"]) .vi-map {\n --font-display: "Atkinson Hyperlegible Next", system-ui, sans-serif;\n --font-ui: "Atkinson Hyperlegible Next", system-ui, -apple-system, "Segoe UI", sans-serif;\n --font-hand: Kalam, "Atkinson Hyperlegible Next", cursive;\n --font-note: Caveat, Kalam, cursive;\n --font-mono: "Atkinson Hyperlegible Mono", ui-monospace, "SF Mono", Menlo, monospace;\n --canvas: #f6f3ee;\n --surface: #ffffff;\n --surface-sunken: #f0ece5;\n --surface-overlay: #ffffff;\n --line: #e4dfd6;\n --line-strong: #7a7f94;\n --ink: #182140;\n --ink-muted: #4f5671;\n --ink-subtle: #5f647c;\n --action: #182140;\n --on-action: #ffffff;\n --focus: #325e8c;\n --link: #2f5a87;\n --selection: rgba(18, 199, 211, 0.28);\n --node-ink: #182140;\n --edge: #2a3048;\n --edge-label: #4f5671;\n --topic-teal: #87f2f8;\n --topic-teal-ring: #00cdd5;\n --topic-harbor: #bcdbff;\n --topic-harbor-ring: #76b2f5;\n --topic-iris: #e0c8fe;\n --topic-iris-ring: #bd96ea;\n --topic-berry: #ffbdc4;\n --topic-berry-ring: #f47e8f;\n --topic-coral: #ffc7b2;\n --topic-coral-ring: #f38e65;\n --topic-amber: #ffcf9d;\n --topic-amber-ring: #eb9c38;\n --topic-sun: #ffea94;\n --topic-sun-ring: #dfbd04;\n --topic-stone: #e0dacf;\n --topic-stone-ring: #bab09f;\n --space-1: 4px;\n --space-2: 8px;\n --space-3: 12px;\n --space-4: 16px;\n --space-7: 48px;\n --radius-sm: 10px;\n --radius-md: 16px;\n --radius-lg: 24px;\n --radius-pill: 999px;\n --shadow-float: 0 1px 2px rgba(24, 33, 64, 0.06), 0 8px 24px rgba(24, 33, 64, 0.08);\n --shadow-pop: 0 2px 4px rgba(24, 33, 64, 0.08), 0 16px 40px rgba(24, 33, 64, 0.14);\n --node-width: 200px;\n --node-height: 148px;\n --node-stroke: 3px;\n --edge-width: 2.5px;\n --edge-gap: 15px;\n --edge-arc-max: 45px;\n --immersion-alpha: 0.15;\n --zoom-min: 0.3;\n --zoom-max: 5;\n --duration-instant: 100ms;\n --duration-quick: 160ms;\n --duration-base: 240ms;\n --duration-zoom: 500ms;\n --duration-reveal: 420ms;\n --stagger-reveal: 70ms;\n --ease-zoom: cubic-bezier(0.2, 0.8, 0.2, 1);\n --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);\n --ease-bloom: cubic-bezier(0.34, 1.4, 0.64, 1);\n --z-chrome: 10;\n --z-card: 20;\n --z-menu: 40;\n }\n.vi-map[data-theme="dark"] {\n --canvas: #0f1220;\n --surface: #171b2b;\n --surface-sunken: #0f1220;\n --surface-overlay: #212639;\n --line: #2b3148;\n --line-strong: #6c7392;\n --ink: #eef0f7;\n --ink-muted: #b3b8cc;\n --ink-subtle: #8f95ae;\n --action: #f6f3ee;\n --on-action: #182140;\n --focus: #12c7d3;\n --link: #5fd8e1;\n --selection: rgba(18, 199, 211, 0.32);\n --edge: #c9cddc;\n --edge-label: #b3b8cc;\n --topic-teal-ring: #0fc5cd;\n --topic-harbor-ring: #73aceb;\n --topic-iris-ring: #b691e1;\n --topic-berry-ring: #eb798a;\n --topic-coral-ring: #ea8962;\n --topic-amber-ring: #e29737;\n --topic-sun-ring: #d8b70a;\n --topic-stone-ring: #b4a999;\n --shadow-float: 0 1px 2px rgba(0, 0, 0, 0.40), 0 8px 24px rgba(0, 0, 0, 0.35);\n --shadow-pop: 0 2px 4px rgba(0, 0, 0, 0.50), 0 16px 40px rgba(0, 0, 0, 0.50);\n }\n:where([data-theme="dark"]) .vi-map {\n --canvas: #0f1220;\n --surface: #171b2b;\n --surface-sunken: #0f1220;\n --surface-overlay: #212639;\n --line: #2b3148;\n --line-strong: #6c7392;\n --ink: #eef0f7;\n --ink-muted: #b3b8cc;\n --ink-subtle: #8f95ae;\n --action: #f6f3ee;\n --on-action: #182140;\n --focus: #12c7d3;\n --link: #5fd8e1;\n --selection: rgba(18, 199, 211, 0.32);\n --edge: #c9cddc;\n --edge-label: #b3b8cc;\n --topic-teal-ring: #0fc5cd;\n --topic-harbor-ring: #73aceb;\n --topic-iris-ring: #b691e1;\n --topic-berry-ring: #eb798a;\n --topic-coral-ring: #ea8962;\n --topic-amber-ring: #e29737;\n --topic-sun-ring: #d8b70a;\n --topic-stone-ring: #b4a999;\n --shadow-float: 0 1px 2px rgba(0, 0, 0, 0.40), 0 8px 24px rgba(0, 0, 0, 0.35);\n --shadow-pop: 0 2px 4px rgba(0, 0, 0, 0.50), 0 16px 40px rgba(0, 0, 0, 0.50);\n }\n.vi-map[data-theme="focus-light"] {\n --canvas: #f4f2ee;\n --edge: #5a6079;\n --edge-label: #5a6079;\n --topic-teal: #bce8ea;\n --topic-teal-ring: #67c6cb;\n --topic-harbor: #c9d9ec;\n --topic-harbor-ring: #8bb2de;\n --topic-iris: #d9cfe8;\n --topic-iris-ring: #b79ed4;\n --topic-berry: #edc6c9;\n --topic-berry-ring: #da9099;\n --topic-coral: #eecec1;\n --topic-coral-ring: #db9b82;\n --topic-amber: #eed4ba;\n --topic-amber-ring: #d6a570;\n --topic-sun: #f4eac1;\n --topic-sun-ring: #d2bf73;\n --topic-stone: #dddad5;\n }\n:where([data-theme="focus-light"]) .vi-map {\n --canvas: #f4f2ee;\n --edge: #5a6079;\n --edge-label: #5a6079;\n --topic-teal: #bce8ea;\n --topic-teal-ring: #67c6cb;\n --topic-harbor: #c9d9ec;\n --topic-harbor-ring: #8bb2de;\n --topic-iris: #d9cfe8;\n --topic-iris-ring: #b79ed4;\n --topic-berry: #edc6c9;\n --topic-berry-ring: #da9099;\n --topic-coral: #eecec1;\n --topic-coral-ring: #db9b82;\n --topic-amber: #eed4ba;\n --topic-amber-ring: #d6a570;\n --topic-sun: #f4eac1;\n --topic-sun-ring: #d2bf73;\n --topic-stone: #dddad5;\n }\n.vi-map[data-theme="focus-dark"] {\n --canvas: #12141c;\n --surface: #171b2b;\n --surface-sunken: #0f1220;\n --surface-overlay: #212639;\n --line: #2b3148;\n --line-strong: #6c7392;\n --ink: #eef0f7;\n --ink-muted: #b3b8cc;\n --ink-subtle: #8f95ae;\n --action: #f6f3ee;\n --on-action: #182140;\n --focus: #12c7d3;\n --link: #5fd8e1;\n --selection: rgba(18, 199, 211, 0.32);\n --edge: #a9aec0;\n --edge-label: #b3b8cc;\n --topic-teal: #bce8ea;\n --topic-teal-ring: #71bdc1;\n --topic-harbor: #c9d9ec;\n --topic-harbor-ring: #8cabce;\n --topic-iris: #d9cfe8;\n --topic-iris-ring: #ae9bc6;\n --topic-berry: #edc6c9;\n --topic-berry-ring: #cc8e95;\n --topic-coral: #eecec1;\n --topic-coral-ring: #cd9884;\n --topic-amber: #eed4ba;\n --topic-amber-ring: #c9a176;\n --topic-sun: #f4eac1;\n --topic-sun-ring: #c9b97a;\n --topic-stone: #dddad5;\n --topic-stone-ring: #b4a999;\n }\n:where([data-theme="focus-dark"]) .vi-map {\n --canvas: #12141c;\n --surface: #171b2b;\n --surface-sunken: #0f1220;\n --surface-overlay: #212639;\n --line: #2b3148;\n --line-strong: #6c7392;\n --ink: #eef0f7;\n --ink-muted: #b3b8cc;\n --ink-subtle: #8f95ae;\n --action: #f6f3ee;\n --on-action: #182140;\n --focus: #12c7d3;\n --link: #5fd8e1;\n --selection: rgba(18, 199, 211, 0.32);\n --edge: #a9aec0;\n --edge-label: #b3b8cc;\n --topic-teal: #bce8ea;\n --topic-teal-ring: #71bdc1;\n --topic-harbor: #c9d9ec;\n --topic-harbor-ring: #8cabce;\n --topic-iris: #d9cfe8;\n --topic-iris-ring: #ae9bc6;\n --topic-berry: #edc6c9;\n --topic-berry-ring: #cc8e95;\n --topic-coral: #eecec1;\n --topic-coral-ring: #cd9884;\n --topic-amber: #eed4ba;\n --topic-amber-ring: #c9a176;\n --topic-sun: #f4eac1;\n --topic-sun-ring: #c9b97a;\n --topic-stone: #dddad5;\n --topic-stone-ring: #b4a999;\n }\n.vi-map[data-theme="colorsafe-light"] {\n --topic-teal: #b0ecbc;\n --topic-teal-ring: #6cc982;\n --topic-harbor: #abdbfe;\n --topic-harbor-ring: #60b4ec;\n --topic-iris: #f6bde2;\n --topic-iris-ring: #dc87c0;\n --topic-berry: #feb79e;\n --topic-berry-ring: #ec7f58;\n --topic-coral: #fece96;\n --topic-coral-ring: #e49d41;\n --topic-amber: #b2e8fa;\n --topic-amber-ring: #5cc4e2;\n --topic-sun: #f6f097;\n --topic-sun-ring: #d0c637;\n --topic-stone: #dbdbdb;\n --topic-stone-ring: #b7afb1;\n }\n:where([data-theme="colorsafe-light"]) .vi-map {\n --topic-teal: #b0ecbc;\n --topic-teal-ring: #6cc982;\n --topic-harbor: #abdbfe;\n --topic-harbor-ring: #60b4ec;\n --topic-iris: #f6bde2;\n --topic-iris-ring: #dc87c0;\n --topic-berry: #feb79e;\n --topic-berry-ring: #ec7f58;\n --topic-coral: #fece96;\n --topic-coral-ring: #e49d41;\n --topic-amber: #b2e8fa;\n --topic-amber-ring: #5cc4e2;\n --topic-sun: #f6f097;\n --topic-sun-ring: #d0c637;\n --topic-stone: #dbdbdb;\n --topic-stone-ring: #b7afb1;\n }\n.vi-map[data-theme="colorsafe-dark"] {\n --canvas: #0f1220;\n --surface: #171b2b;\n --surface-sunken: #0f1220;\n --surface-overlay: #212639;\n --line: #2b3148;\n --line-strong: #6c7392;\n --ink: #eef0f7;\n --ink-muted: #b3b8cc;\n --ink-subtle: #8f95ae;\n --action: #f6f3ee;\n --on-action: #182140;\n --focus: #12c7d3;\n --link: #5fd8e1;\n --selection: rgba(18, 199, 211, 0.32);\n --edge: #c9cddc;\n --edge-label: #b3b8cc;\n --topic-teal: #b0ecbc;\n --topic-teal-ring: #69c27e;\n --topic-harbor: #abdbfe;\n --topic-harbor-ring: #5eade2;\n --topic-iris: #f6bde2;\n --topic-iris-ring: #d383b9;\n --topic-berry: #feb79e;\n --topic-berry-ring: #e37a55;\n --topic-coral: #fece96;\n --topic-coral-ring: #db9840;\n --topic-amber: #b2e8fa;\n --topic-amber-ring: #5bbdda;\n --topic-sun: #f6f097;\n --topic-sun-ring: #c9bf37;\n --topic-stone: #dbdbdb;\n --topic-stone-ring: #b1a8aa;\n }\n:where([data-theme="colorsafe-dark"]) .vi-map {\n --canvas: #0f1220;\n --surface: #171b2b;\n --surface-sunken: #0f1220;\n --surface-overlay: #212639;\n --line: #2b3148;\n --line-strong: #6c7392;\n --ink: #eef0f7;\n --ink-muted: #b3b8cc;\n --ink-subtle: #8f95ae;\n --action: #f6f3ee;\n --on-action: #182140;\n --focus: #12c7d3;\n --link: #5fd8e1;\n --selection: rgba(18, 199, 211, 0.32);\n --edge: #c9cddc;\n --edge-label: #b3b8cc;\n --topic-teal: #b0ecbc;\n --topic-teal-ring: #69c27e;\n --topic-harbor: #abdbfe;\n --topic-harbor-ring: #5eade2;\n --topic-iris: #f6bde2;\n --topic-iris-ring: #d383b9;\n --topic-berry: #feb79e;\n --topic-berry-ring: #e37a55;\n --topic-coral: #fece96;\n --topic-coral-ring: #db9840;\n --topic-amber: #b2e8fa;\n --topic-amber-ring: #5bbdda;\n --topic-sun: #f6f097;\n --topic-sun-ring: #c9bf37;\n --topic-stone: #dbdbdb;\n --topic-stone-ring: #b1a8aa;\n }\n.vi-map[data-theme="contrast-light"] {\n --canvas: #ffffff;\n --surface-sunken: #f2f2f2;\n --line: #5c6178;\n --line-strong: #000000;\n --ink: #000000;\n --ink-muted: #1f2433;\n --ink-subtle: #3a3f52;\n --action: #000000;\n --focus: #0b4a8f;\n --link: #0b4a8f;\n --node-ink: #000000;\n --edge: #000000;\n --edge-label: #1f2433;\n --topic-teal-ring: #006b70;\n --topic-harbor-ring: #0e599d;\n --topic-iris-ring: #683b92;\n --topic-berry-ring: #96173a;\n --topic-coral-ring: #923400;\n --topic-amber-ring: #7e4d00;\n --topic-sun-ring: #7a6600;\n --topic-stone-ring: #63594b;\n --shadow-float: none;\n --shadow-pop: none;\n }\n:where([data-theme="contrast-light"]) .vi-map {\n --canvas: #ffffff;\n --surface-sunken: #f2f2f2;\n --line: #5c6178;\n --line-strong: #000000;\n --ink: #000000;\n --ink-muted: #1f2433;\n --ink-subtle: #3a3f52;\n --action: #000000;\n --focus: #0b4a8f;\n --link: #0b4a8f;\n --node-ink: #000000;\n --edge: #000000;\n --edge-label: #1f2433;\n --topic-teal-ring: #006b70;\n --topic-harbor-ring: #0e599d;\n --topic-iris-ring: #683b92;\n --topic-berry-ring: #96173a;\n --topic-coral-ring: #923400;\n --topic-amber-ring: #7e4d00;\n --topic-sun-ring: #7a6600;\n --topic-stone-ring: #63594b;\n --shadow-float: none;\n --shadow-pop: none;\n }\n.vi-map[data-theme="contrast-dark"] {\n --canvas: #000000;\n --surface: #000000;\n --surface-sunken: #0d0d0d;\n --surface-overlay: #0d0d0d;\n --line: #9aa0b8;\n --line-strong: #ffffff;\n --ink: #ffffff;\n --ink-muted: #e6e8f0;\n --ink-subtle: #c8ccda;\n --action: #ffffff;\n --on-action: #000000;\n --focus: #5fe8f0;\n --link: #7ff0f5;\n --selection: rgba(18, 199, 211, 0.32);\n --node-ink: #000000;\n --edge: #ffffff;\n --edge-label: #e6e8f0;\n --topic-teal-ring: #64eff7;\n --topic-harbor-ring: #b0d5fe;\n --topic-iris-ring: #dcbfff;\n --topic-berry-ring: #feb3bb;\n --topic-coral-ring: #ffbea5;\n --topic-amber-ring: #ffc78a;\n --topic-sun-ring: #ffe26d;\n --topic-stone-ring: #ddd3c2;\n --shadow-float: none;\n --shadow-pop: none;\n }\n:where([data-theme="contrast-dark"]) .vi-map {\n --canvas: #000000;\n --surface: #000000;\n --surface-sunken: #0d0d0d;\n --surface-overlay: #0d0d0d;\n --line: #9aa0b8;\n --line-strong: #ffffff;\n --ink: #ffffff;\n --ink-muted: #e6e8f0;\n --ink-subtle: #c8ccda;\n --action: #ffffff;\n --on-action: #000000;\n --focus: #5fe8f0;\n --link: #7ff0f5;\n --selection: rgba(18, 199, 211, 0.32);\n --node-ink: #000000;\n --edge: #ffffff;\n --edge-label: #e6e8f0;\n --topic-teal-ring: #64eff7;\n --topic-harbor-ring: #b0d5fe;\n --topic-iris-ring: #dcbfff;\n --topic-berry-ring: #feb3bb;\n --topic-coral-ring: #ffbea5;\n --topic-amber-ring: #ffc78a;\n --topic-sun-ring: #ffe26d;\n --topic-stone-ring: #ddd3c2;\n --shadow-float: none;\n --shadow-pop: none;\n }\n.vi-map[data-motion="reduced"] {\n --duration-instant: 120ms;\n --duration-quick: 0ms;\n --duration-base: 0ms;\n --duration-zoom: 0ms;\n --duration-reveal: 0ms;\n --stagger-reveal: 0ms;\n }\n:where([data-motion="reduced"]) .vi-map {\n --duration-instant: 120ms;\n --duration-quick: 0ms;\n --duration-base: 0ms;\n --duration-zoom: 0ms;\n --duration-reveal: 0ms;\n --stagger-reveal: 0ms;\n }\n@media (prefers-reduced-motion: reduce) {\n :where(:root:not([data-motion="full"])) .vi-map {\n --duration-instant: 120ms;\n --duration-quick: 0ms;\n --duration-base: 0ms;\n --duration-zoom: 0ms;\n --duration-reveal: 0ms;\n --stagger-reveal: 0ms;\n }\n}\n\n/* Type styles */\n.vi-text-heading { font-family: var(--font-ui); font-size: 17px; line-height: 24px; font-weight: 700; }\n.vi-text-body { font-family: var(--font-ui); font-size: 15px; line-height: 22px; font-weight: 400; }\n.vi-text-body-sm { font-family: var(--font-ui); font-size: 13px; line-height: 18px; font-weight: 400; }\n.vi-text-label { font-family: var(--font-ui); font-size: 13px; line-height: 16px; font-weight: 600; letter-spacing: 0.01em; }\n.vi-text-caption { font-family: var(--font-ui); font-size: 12px; line-height: 16px; font-weight: 400; }\n.vi-text-node-root { font-family: var(--font-hand); font-size: 30px; line-height: 34px; font-weight: 700; }\n.vi-text-node-label { font-family: var(--font-hand); font-size: 22px; line-height: 26px; font-weight: 700; }\n.vi-text-edge-label { font-family: var(--font-note); font-size: 18px; line-height: 20px; font-weight: 500; }\n.vi-text-annotation { font-family: var(--font-note); font-size: 24px; line-height: 26px; font-weight: 600; }\n';
664
+ var SPEC_COMPONENT_CSS = '/* Styles for the spec components (map, nodes, connectors, peek, terms, depth trail, controls) \u2014 generated from the Visualli design system (v0.3.1). Do not edit here: changes come in through PRs from the private design system. MIT license, see ../LICENSE. */\n.vi-map { --vi-fs-body: 15px; --vi-fs-sm: 13px; --vi-fs-cap: 12px; --vi-lh-body: 22px; --vi-label-scale: 1; }\n[data-theme$="dark"] :where([class^="vi-"], [class*=" vi-"]) { color-scheme: dark; }\n.vi-map[data-scale="lg"] { --vi-fs-body: 17px; --vi-fs-sm: 15px; --vi-fs-cap: 14px; --vi-lh-body: 26px; --vi-label-scale: 1.15; }\n:where([data-scale="lg"]) .vi-map { --vi-fs-body: 17px; --vi-fs-sm: 15px; --vi-fs-cap: 14px; --vi-lh-body: 26px; --vi-label-scale: 1.15; }\n.vi-map[data-type="readable"] { --font-hand: "Atkinson Hyperlegible Next", system-ui, sans-serif; --font-note: "Atkinson Hyperlegible Next", system-ui, sans-serif; --font-display: "Atkinson Hyperlegible Next", system-ui, sans-serif; }\n:where([data-type="readable"]) .vi-map { --font-hand: "Atkinson Hyperlegible Next", system-ui, sans-serif; --font-note: "Atkinson Hyperlegible Next", system-ui, sans-serif; --font-display: "Atkinson Hyperlegible Next", system-ui, sans-serif; }\n[data-type="readable"] .vi-node__label, [data-type="readable"] .vi-edge__label, [data-type="readable"] .vi-map__group-label { font-family: var(--font-ui) !important; font-weight: 700; letter-spacing: 0.02em; font-style: normal; }\n.vi-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }\n.vi-icon { flex: none; display: block; }\n:where([class^="vi-"], [class*=" vi-"]):where(button, a, input, textarea, [tabindex]):focus-visible, :where([class^="vi-"], [class*=" vi-"]) :where(button, a, input, textarea, [tabindex]):focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }\n.vi-iconbtn { display: inline-grid; place-items: center; border-radius: var(--radius-sm); border: 1.5px solid transparent; background: transparent; color: var(--ink); cursor: pointer; transition: background var(--duration-instant); flex: none; }\n.vi-iconbtn--sm { width: 32px; height: 32px; }\n.vi-iconbtn--md { width: 40px; height: 40px; }\n.vi-iconbtn--lg { width: 48px; height: 48px; border-radius: var(--radius-md); }\n.vi-iconbtn:hover { background: var(--surface-sunken); }\n.vi-iconbtn--surface { background: var(--surface); border-color: var(--line); }\n.vi-iconbtn--surface:hover { border-color: var(--line-strong); }\n.vi-iconbtn--primary { background: var(--action); color: var(--on-action); border-radius: var(--radius-pill); }\n.vi-iconbtn.is-active { background: var(--action); color: var(--on-action); }\n[data-theme^="contrast"] .vi-ctrls { border: 2px solid var(--line-strong); }\n.vi-node { position: relative; display: inline-grid; place-items: center; border: 0; padding: 0; background: none; color: var(--node-ink); cursor: default; transition: transform var(--duration-base) var(--ease-bloom), opacity var(--duration-base); -webkit-tap-highlight-color: transparent; }\nbutton.vi-node { cursor: pointer; }\n.vi-node__svg { position: absolute; top: 0; right: 0; bottom: 0; left: 0; overflow: visible; }\n.vi-node__body { filter: drop-shadow(var(--vi-node-shadow, 0 6px 14px rgba(24, 33, 64, .10))); }\n[data-theme$="dark"] .vi-node__body { --vi-node-shadow: 0 0 22px rgba(255, 255, 255, .10); }\n[data-theme^="contrast"] .vi-node__body { filter: none; }\n.vi-node__rings { transform-origin: center; transition: transform var(--duration-base) var(--ease-standard); }\n@media (hover: hover) {\n button.vi-node:hover .vi-node__rings { transform: rotate(var(--vi-ring-turn, -2deg)) scale(var(--vi-ring-grow, 1.015)); }\n button.vi-node:hover { transform: translateY(calc(var(--vi-lift, 2px) * -1)); }\n}\n.vi-node { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; touch-action: manipulation; }\n.vi-node.is-selected { transform: translateY(-2px); }\n.vi-node.is-selected .vi-node__rings { transform: rotate(var(--vi-ring-turn, -2deg)) scale(var(--vi-ring-grow, 1.015)); }\n.vi-node.is-selected .vi-node__body { stroke-width: 4; }\n.vi-node:focus-visible { outline: none; }\n.vi-node:focus-visible .vi-node__body { stroke: var(--focus); stroke-width: 4; }\n.vi-node.is-selected .vi-node__body { stroke: var(--ink); }\n.vi-node__label { position: relative; text-align: center; color: var(--node-ink); display: flex; flex-direction: column; align-items: center; font-size: calc(var(--vi-nfs, 22px) * var(--vi-label-scale) * var(--vi-ninv, 1)); line-height: 1.15; padding-top: 2px; }\n.vi-node__line { display: block; white-space: nowrap; }\n.vi-node--mini .vi-node__label { --vi-nfs: 15px; }\n.vi-node.is-dimmed { opacity: 1; }\n[data-theme^="focus"] .vi-node.is-dimmed { opacity: .4; }\n.vi-map.is-touch .vi-node.is-dimmed { opacity: .6; }\n[data-theme^="focus"] .vi-map.is-touch .vi-node.is-dimmed { opacity: .4; }\n.vi-edge__line { fill: none; stroke: var(--edge); stroke-width: 2.5; stroke-linecap: round; }\n.vi-edge.is-dashed .vi-edge__line { stroke-dasharray: 7 7; }\n.vi-edge__arrow { fill: none; stroke: var(--edge); stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }\n.vi-edge__label { font: 500 calc(18px * var(--vi-inv, 1)) / 1 var(--font-note); fill: var(--edge-label); paint-order: stroke; stroke: var(--canvas); stroke-width: 5px; stroke-linejoin: round; letter-spacing: .01em; }\n[data-type="readable"] .vi-edge__label { font-size: calc(14px * var(--vi-inv, 1)); font-weight: 600; }\n.vi-fact { position: relative; width: 300px; box-sizing: border-box; padding: var(--space-4) var(--space-4) var(--space-3); background: var(--surface-overlay); color: var(--ink); border: 2px solid var(--vi-fact-ring); border-radius: 14px 18px 12px 16px / 16px 12px 18px 14px; font: 400 var(--vi-fs-sm)/20px var(--font-ui); z-index: var(--z-card); animation: vi-pop var(--duration-quick) var(--ease-standard); }\n.vi-fact { box-shadow: 5px 6px 0 0 var(--vi-fact-fill), 5px 6px 0 2px var(--vi-fact-ring); }\n[data-theme^="contrast"] .vi-fact { border-color: var(--ink); }\n[data-theme^="contrast"] .vi-fact { box-shadow: none; }\n.vi-fact__title { font: 700 22px/26px var(--font-hand); margin-bottom: var(--space-1); }\n.vi-fact__body { color: var(--ink); }\n.vi-fact__foot { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); margin-top: var(--space-3); padding-top: var(--space-2); border-top: 1px dashed var(--line); }\n.vi-fact__acts { display: inline-flex; gap: var(--space-3); align-items: center; flex: none; }\n.vi-fact__link { color: var(--link); font: 600 var(--vi-fs-cap)/16px var(--font-ui); }\n.vi-fact__step { display: inline-flex; align-items: center; gap: 2px; border: 0; background: var(--action); color: var(--on-action); font: 600 var(--vi-fs-cap)/16px var(--font-ui); padding: 5px 8px 5px 10px; border-radius: var(--radius-pill); cursor: pointer; }\n.vi-trail ol { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; position: relative; }\n.vi-trail li { position: relative; }\n.vi-trail li + li::before { content: ""; position: absolute; left: 13px; top: -8px; height: 10px; border-left: 1.5px dashed var(--line-strong); }\n.vi-trail button { display: inline-flex; align-items: center; gap: 6px; border: 0; background: none; padding: 4px 8px 4px 5px; border-radius: var(--radius-sm); color: var(--ink-muted); font: 400 var(--vi-fs-cap)/16px var(--font-ui); cursor: pointer; }\n.vi-trail button span { display: inline-flex; align-items: center; gap: 4px; }\n.vi-trail button:hover:not(:disabled) { background: var(--surface-sunken); color: var(--ink); }\n.vi-trail li.is-current button { color: var(--ink); font-weight: 700; font-size: var(--vi-fs-sm); cursor: default; background: var(--surface); box-shadow: var(--shadow-float); }\n.vi-trail li.is-current button:disabled { opacity: 1; }\n.vi-ctrls { display: inline-flex; align-items: center; gap: 2px; padding: 4px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md); box-shadow: var(--shadow-float); }\n.vi-ctrls__pct { min-width: 44px; text-align: center; font: 600 var(--vi-fs-cap)/16px var(--font-ui); color: var(--ink-muted); font-variant-numeric: tabular-nums; }\n.vi-ctrls__div { width: 1px; height: 20px; background: var(--line); margin: 0 4px; }\n.vi-map { position: relative; overflow: hidden; background: var(--canvas); border-radius: inherit; isolation: isolate; }\n.vi-map__immersion { position: absolute; top: 0; right: 0; bottom: 0; left: 0; opacity: var(--immersion-alpha); transition: background var(--duration-base); z-index: -1; }\n.vi-map__world { position: absolute; left: 0; top: 0; transform-origin: 0 0; }\n.vi-map__edges { position: absolute; overflow: visible; pointer-events: none; }\n.vi-map__group rect { fill: none; stroke: var(--line-strong); stroke-width: 1.5; stroke-dasharray: 10 6; }\n.vi-map__group-label { position: absolute; transform: translate(-50%, -50%); z-index: 2; max-width: 420px; box-sizing: border-box; padding: 6px 22px 8px; background: var(--topic-stone); border: 1.5px solid var(--topic-stone-ring); border-radius: var(--radius-pill); font: 700 22px/1.1 var(--font-note); color: var(--node-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; pointer-events: none; font-size: calc(26px * var(--vi-label-scale) * var(--vi-ninv, 1)); }\n[data-theme^="contrast"] .vi-map__group-label { border: 2px solid var(--line-strong); }\n.vi-map__slot { position: absolute; transform: translate(-50%, -50%); }\n.vi-map.is-reveal .vi-map__slot { animation: vi-bloom var(--duration-reveal) var(--ease-bloom) backwards; }\n.vi-map.is-reveal .vi-edge { animation: vi-fade var(--duration-base) var(--ease-standard) backwards; }\n.vi-map.is-reveal .vi-edge__line { stroke-dasharray: 1; stroke-dashoffset: 1; animation: vi-line var(--vi-line-draw, .5s) var(--ease-standard) forwards; animation-delay: inherit; }\n.vi-map.is-reveal .vi-edge.is-dashed .vi-edge__line { stroke-dasharray: 7 7; stroke-dashoffset: 0; animation: vi-fade var(--vi-line-draw, .5s) var(--ease-standard) backwards; animation-delay: inherit; }\n.vi-map:not(.is-reveal) .vi-edge__line { stroke-dasharray: none; }\n.vi-map:not(.is-reveal) .vi-edge.is-dashed .vi-edge__line { stroke-dasharray: 7 7; }\n.vi-map__lens { position: absolute; top: 0; right: 0; bottom: 0; left: 0; transform-origin: var(--vi-ox) var(--vi-oy); }\n.vi-map.is-diving .vi-map__lens { transform: scale(var(--vi-dive-scale, 3.2)); opacity: 0; transition: transform var(--duration-zoom) var(--ease-zoom), opacity var(--vi-dive-fade, 160ms) var(--vi-dive-fade-delay, 150ms); }\n.vi-map.is-surfacing .vi-map__lens { transform: scale(var(--vi-surface-scale, .6)); opacity: 0; transform-origin: 50% 50%; transition: transform var(--vi-surface-dur, 220ms) var(--ease-zoom), opacity var(--vi-surface-fade, 200ms); }\n.vi-map.is-reveal .vi-map__lens { animation: vi-settle var(--vi-settle-dur, 260ms) var(--ease-standard); }\n[data-motion="reduced"] .vi-map__lens { animation: none !important; transform: none !important; }\n.vi-map__fact { position: absolute; }\n.vi-map.is-idle .vi-map__world { transition: transform var(--duration-base) var(--ease-standard); }\n.vi-map.is-panning, .vi-map.is-panning .vi-node { cursor: grabbing; }\n.vi-map__hint { position: absolute; left: var(--space-4); right: var(--space-4); margin: 0; text-align: center; color: var(--ink-muted); pointer-events: none; z-index: var(--z-chrome); animation: vi-fade var(--duration-base) var(--ease-standard); }\n.vi-fact__head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-2); }\n.vi-fact__back { display: inline-flex; align-items: center; gap: 2px; border: 0; background: none; padding: 2px 0; margin-bottom: var(--space-1); color: var(--link); font: 600 var(--vi-fs-cap)/16px var(--font-ui); cursor: pointer; }\n.vi-term { border: 0; background: none; padding: 0; font: inherit; color: var(--link); text-decoration: underline; text-decoration-style: dotted; text-decoration-thickness: 1.5px; text-underline-offset: 3px; cursor: pointer; }\n.vi-fact--sheet { position: absolute; left: 0; right: 0; bottom: 0; width: auto; max-width: 560px; margin: 0 auto; max-height: var(--vi-sheet-h, 300px); overflow-y: auto; overscroll-behavior: contain; padding: 0 var(--space-4) calc(var(--space-4) + env(safe-area-inset-bottom, 0px)); border: 0; border-top: 2px solid var(--vi-fact-ring); border-radius: var(--radius-lg) var(--radius-lg) 0 0; box-shadow: 0 -6px 0 0 var(--vi-fact-fill), var(--shadow-pop); font-size: var(--vi-fs-body); line-height: var(--vi-lh-body); animation: vi-sheet-in var(--duration-base) var(--ease-standard); transition: transform var(--duration-quick) var(--ease-standard), max-height var(--duration-base) var(--ease-standard); }\n.vi-fact--sheet.is-expanded { max-height: calc(100% - var(--space-7)); }\n.vi-fact__grip { position: sticky; top: 0; display: flex; justify-content: center; margin: 0 calc(var(--space-4) * -1); background: var(--surface-overlay); touch-action: none; cursor: grab; z-index: 1; }\n.vi-fact__handle { position: relative; width: 64px; height: 24px; border: 0; padding: 0; background: none; cursor: pointer; }\n.vi-fact__handle::after { content: ""; position: absolute; left: 12px; right: 12px; top: 10px; height: 5px; border-radius: var(--radius-pill); background: var(--line-strong); }\n.vi-fact--sheet .vi-fact__title { font-size: calc(24px * var(--vi-label-scale)); line-height: 1.15; padding-top: var(--space-1); }\n.vi-fact--sheet .vi-fact__close { margin: 0 calc(var(--space-2) * -1) 0 0; }\n.vi-fact--sheet .vi-fact__step { min-height: 44px; padding: 0 var(--space-3) 0 var(--space-4); font-size: var(--vi-fs-sm); }\n.vi-fact--sheet .vi-fact__link { font-size: var(--vi-fs-sm); min-height: 44px; display: inline-flex; align-items: center; }\n[data-motion="reduced"] .vi-fact--sheet { animation: vi-fade 120ms; }\n@media (prefers-reduced-motion: reduce) {\n .vi-fact--sheet { animation: vi-fade 120ms; }\n .vi-map.is-idle .vi-map__world { transition: none; }\n}\n.vi-map__trail { position: absolute; top: var(--space-4); left: var(--space-4); z-index: var(--z-chrome); }\n.vi-map__ctrls { position: absolute; top: var(--space-4); right: var(--space-4); z-index: var(--z-chrome); }\n[data-reveal="instant"] .vi-map.is-reveal .vi-map__slot, [data-motion="reduced"] .vi-map.is-reveal .vi-map__slot { animation: vi-fade var(--vi-reduced-fade, 120ms) backwards; animation-delay: 0ms !important; }\n[data-reveal="instant"] .vi-map.is-reveal .vi-edge, [data-reveal="instant"] .vi-map.is-reveal .vi-edge__line, [data-motion="reduced"] .vi-map.is-reveal .vi-edge, [data-motion="reduced"] .vi-map.is-reveal .vi-edge__line { animation: none; stroke-dashoffset: 0; }\n@media (prefers-reduced-motion: reduce) {\n .vi-map.is-reveal .vi-map__slot { animation: vi-fade var(--vi-reduced-fade, 120ms) backwards; animation-delay: 0ms !important; }\n .vi-map.is-reveal .vi-edge, .vi-map.is-reveal .vi-edge__line { animation: none; stroke-dashoffset: 0; }\n}\n[data-motion="reduced"] :where([class^="vi-"], [class*=" vi-"]), [data-motion="reduced"] :where([class^="vi-"], [class*=" vi-"])::before, [data-motion="reduced"] :where([class^="vi-"], [class*=" vi-"])::after { transition-duration: 1ms !important; }\n.vi-fact__acts { flex-wrap: wrap; justify-content: flex-end; }\n.vi-fact__link { display: inline-flex; align-items: center; gap: 2px; }\n.vi-anchor { position: relative; display: inline; }\n.vi-anchor.is-open .vi-term { background: var(--selection); border-radius: 3px; }\n.vi-anchor__pop { position: absolute; left: -12px; top: calc(100% + 6px); z-index: var(--z-menu); display: block; width: max-content; }\n.vi-anchor-card { position: relative; display: block; width: min(300px, calc(100vw - 32px)); box-sizing: border-box; padding: var(--space-3) var(--space-4) var(--space-3); background: var(--surface-overlay); color: var(--ink); text-align: left; white-space: normal; font: 400 var(--vi-fs-sm)/20px var(--font-ui); border: 2px solid var(--vi-fact-ring); border-radius: 12px 16px 10px 14px / 14px 10px 16px 12px; box-shadow: 4px 5px 0 0 var(--vi-fact-fill), 4px 5px 0 2px var(--vi-fact-ring), var(--shadow-pop); animation: vi-pop var(--duration-quick) var(--ease-standard); }\n.vi-anchor-card.is-in-sheet { width: auto; border: 0; box-shadow: none; padding: 0; animation: none; background: transparent; }\n[data-theme^="contrast"] .vi-anchor-card { border-color: var(--ink); box-shadow: none; }\n.vi-anchor-card__eyebrow { font: 400 11px/14px var(--font-display); letter-spacing: .14em; text-transform: uppercase; color: var(--ink-muted); }\n.vi-anchor-card__word { font: 700 20px/24px var(--font-hand); color: var(--ink); margin: 2px 0 var(--space-1); }\n.vi-fact--sheet .vi-anchor-card__word { font-size: calc(24px * var(--vi-label-scale)); line-height: 1.15; }\n.vi-anchor-card__desc { margin: 0; color: var(--ink); }\n.vi-anchor-card__note { margin: var(--space-2) 0 0; font: 400 var(--vi-fs-cap)/16px var(--font-ui); color: var(--ink-muted); }\n.vi-anchor-card__acts { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: var(--space-3); margin-top: var(--space-3); padding-top: var(--space-2); border-top: 1px dashed var(--line); }\n.vi-fact--sheet .vi-anchor-card__acts .vi-fact__link { min-height: 44px; }\n[data-type="readable"] .vi-anchor-card__word { font-family: var(--font-ui); }\n[data-motion="reduced"] .vi-anchor-card { animation: vi-fade 120ms; }\n@media (prefers-reduced-motion: reduce) {\n .vi-anchor-card { animation: vi-fade 120ms; }\n}\n.vi-peek .vi-fact__foot { flex-wrap: wrap; row-gap: var(--space-2); }\n.vi-peek .vi-fact__acts { margin-left: auto; }\n.vi-map.has-highlight .vi-node.is-dimmed { opacity: .35; transition: opacity var(--duration-quick) var(--ease-standard); }\n.vi-map.has-highlight .vi-node.is-selected .vi-node__body { stroke: var(--ink); }\n.vi-map { container-type: inline-size; }\n.vi-edge__hit { fill: none; stroke: transparent; stroke-width: 18; pointer-events: stroke; cursor: pointer; }\n.vi-edge__hit:hover + .vi-edge__line { stroke-width: 3.5; }\n.vi-edge.is-selected .vi-edge__line, .vi-edge.is-selected .vi-edge__arrow { stroke: var(--focus); stroke-width: 3.5; }\n.vi-map__group-hit { fill: none; stroke: transparent; stroke-width: 16; pointer-events: stroke; cursor: pointer; }\n.vi-map__group.is-selected rect:not(.vi-map__group-hit) { stroke: var(--focus); stroke-width: 2.5; stroke-dasharray: none; }\n.vi-map__group-label.is-selected { border-color: var(--focus); color: var(--focus); box-shadow: 0 0 0 1px var(--focus); }\n.vi-map { touch-action: pan-x pan-y; }\n.vi-map.is-app { touch-action: none; }\n.vi-map.is-pinching .vi-map__world { transition: none; }\n.vi-map.is-instant.is-reveal .vi-map__slot { animation: vi-fade var(--vi-reduced-fade, 120ms) backwards; animation-delay: 0ms !important; }\n.vi-map.is-instant.is-reveal .vi-edge, .vi-map.is-instant.is-reveal .vi-edge__line { animation: none; stroke-dashoffset: 0; }\n.vi-map.is-instant .vi-node, .vi-map.is-instant .vi-node__rings { transition: none; }\n.vi-map.is-detail-body .vi-node:not(.is-selected):not(:focus-visible):not(:hover) :is(.vi-node__rings, .vi-node__label), .vi-map.is-detail-speck .vi-node:not(.is-selected):not(:focus-visible):not(:hover) :is(.vi-node__rings, .vi-node__label) { display: none; }\n.vi-map.is-detail-body .vi-node:not(.is-selected):not(:focus-visible):not(:hover) .vi-node__body, .vi-map.is-detail-speck .vi-node:not(.is-selected):not(:focus-visible):not(:hover) .vi-node__body, .vi-map.no-shadows .vi-node__body { filter: none; }\n@keyframes vi-pop { from { opacity: 0; transform: translateY(-4px) scale(.98); } }\n@keyframes vi-fade { from { opacity: 0; } }\n@keyframes vi-bloom { from { opacity: 0; transform: translate(calc(-50% + var(--vi-dx)), calc(-50% + var(--vi-dy))) scale(var(--vi-bloom-from, .55)); } }\n@keyframes vi-line { to { stroke-dashoffset: 0; } }\n@keyframes vi-settle { from { transform: scale(var(--vi-settle-from, .94)); } }\n@keyframes vi-sheet-in { from { transform: translateY(100%); } }\n';
677
665
  var SPEC_FONTS_URL = "https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:ital,wght@0,200..800;1,200..800&family=Atkinson+Hyperlegible+Mono:wght@400;600&family=Kalam:wght@400;700&display=swap";
678
- var SPEC_DESIGN_SYSTEM_VERSION = "0.3.0";
666
+ var SPEC_DESIGN_SYSTEM_VERSION = "0.3.1";
679
667
  var SPEC_BUNDLED_FONTS = [
680
668
  {
681
669
  "family": "Caveat",
@@ -689,13 +677,13 @@ var STYLE_ID = "visualli-design-system";
689
677
  var LAYOUT_CSS = `
690
678
  .vi-map__ctrls { transition: bottom var(--duration-quick) var(--ease-standard); } /* controlsPosition 'bottom-right': rising above the sheet */
691
679
  .vi-map.is-compact .vi-trail li:not(:first-child):not(.is-current):not(:nth-last-child(2)) { display: none; }
692
- .vi-map.is-compact .vi-trail button span { max-width: 40vw; max-width: 40cqw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
693
- .vi-map.is-compact .vi-fact:not(.vi-fact--sheet) { width: min(300px, calc(100cqw - 2 * var(--space-4))); }
680
+ .vi-map.is-compact .vi-trail button span { max-width: calc(var(--vi-map-w, 100vw) * .4); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
681
+ .vi-map.is-compact .vi-fact:not(.vi-fact--sheet) { width: min(300px, calc(var(--vi-map-w, 100vw) - 2 * var(--space-4))); }
694
682
  .vi-map.is-touch .vi-trail button { min-height: 32px; }
695
683
  `;
696
684
  var FONTS_ID = "visualli-design-system-fonts";
697
685
  var CAVEAT_ID = "visualli-design-system-caveat";
698
- var SDK_VERSION = true ? "0.2.4" : "latest";
686
+ var SDK_VERSION = true ? "0.2.5" : "latest";
699
687
  var webFontsSheet = Promise.resolve();
700
688
  function ensureDesignSystemStyles(assets = {}) {
701
689
  if (typeof document === "undefined") return Promise.resolve();
@@ -852,7 +840,7 @@ var KonvaStage_default = KonvaStage;
852
840
 
853
841
  // src/components/KonvaNodeLayer.tsx
854
842
  var import_react6 = require("react");
855
- var import_core9 = require("@visualli/core");
843
+ var import_core10 = require("@visualli/core");
856
844
 
857
845
  // src/design/design.ts
858
846
  var import_core7 = require("@visualli/core");
@@ -900,7 +888,34 @@ function resolveDesign(props, env) {
900
888
  }
901
889
 
902
890
  // src/design/drawing.ts
891
+ var import_core9 = require("@visualli/core");
892
+
893
+ // src/design/measure.ts
903
894
  var import_core8 = require("@visualli/core");
895
+ var ctx;
896
+ var cache2 = /* @__PURE__ */ new Map();
897
+ var MAX_CACHED = 32768;
898
+ function ideaMeasure(d) {
899
+ if (ctx === void 0) ctx = typeof document !== "undefined" ? document.createElement("canvas").getContext("2d") : null;
900
+ const c = ctx;
901
+ if (!c) return import_core8.approximateMeasure;
902
+ const weight = import_core8.TYPE_STYLES["node-label"].weight;
903
+ const tracking = d.comfort.readableType ? 0.02 : 0;
904
+ return (text, px) => {
905
+ const font = `${weight} ${px}px ${d.fontHand}`;
906
+ const key2 = `${fontsEpoch()}|${font}|${text}`;
907
+ let w = cache2.get(key2);
908
+ if (w === void 0) {
909
+ c.font = font;
910
+ w = c.measureText(text).width + text.length * px * tracking;
911
+ if (cache2.size >= MAX_CACHED) cache2.clear();
912
+ cache2.set(key2, w);
913
+ }
914
+ return w;
915
+ };
916
+ }
917
+
918
+ // src/design/drawing.ts
904
919
  var DEG = Math.PI / 180;
905
920
  var MAX_CACHED_PATHS = 4096;
906
921
  var pathCache = /* @__PURE__ */ new Map();
@@ -909,7 +924,7 @@ function blobPath2D(shape, rx, ry) {
909
924
  let p = pathCache.get(key2);
910
925
  if (!p) {
911
926
  if (pathCache.size >= MAX_CACHED_PATHS) pathCache.delete(pathCache.keys().next().value);
912
- p = new Path2D((0, import_core8.blobPath)(shape, rx, ry));
927
+ p = new Path2D((0, import_core9.blobPath)(shape, rx, ry));
913
928
  pathCache.set(key2, p);
914
929
  }
915
930
  return p;
@@ -926,55 +941,32 @@ function dashOf(spec) {
926
941
  }
927
942
  return d;
928
943
  }
929
- var lineCache = /* @__PURE__ */ new Map();
930
- var MAX_CACHED_LAYOUTS = 8192;
931
- function layoutLabel(c, text, font, maxWidth, maxLines = Infinity) {
932
- const key2 = `${fontsEpoch()}|${font}|${maxWidth}|${text}`;
933
- const hit = lineCache.get(key2);
934
- if (hit) return hit;
935
- c.font = font;
936
- const words = text.split(/\s+/).filter(Boolean);
937
- const lines = [];
938
- let cur = "";
939
- const fit = (s) => c.measureText(s).width <= maxWidth;
940
- for (const w of words) {
941
- const next = cur ? `${cur} ${w}` : w;
942
- if (fit(next)) {
943
- cur = next;
944
- continue;
945
- }
946
- if (cur) lines.push(cur);
947
- let rest = w;
948
- while (!fit(rest) && rest.length > 1) {
949
- let n = rest.length - 1;
950
- while (n > 1 && !fit(rest.slice(0, n))) n--;
951
- lines.push(rest.slice(0, n));
952
- rest = rest.slice(n);
953
- }
954
- cur = rest;
955
- }
956
- if (cur) lines.push(cur);
957
- if (lines.length > maxLines) {
958
- lines.length = maxLines;
959
- let last = lines[maxLines - 1] + "\u2026";
960
- while (last.length > 1 && !fit(last)) last = last.slice(0, -2) + "\u2026";
961
- lines[maxLines - 1] = last;
944
+ var MAX_CACHED_LAYOUTS = 32768;
945
+ var IDLE = { hovered: false, pressed: false, selected: false, focused: false, dimmed: false };
946
+ var ideaColors = (d, n) => (0, import_core9.ideaStyle)(d.theme, n);
947
+ var ideaLabelPx = (n, d) => import_core9.IDEA.labelSize[n.kind ?? "node"] * d.labelScale;
948
+ var labelLines = /* @__PURE__ */ new Map();
949
+ function ideaLabelLayout(node, d, zoom) {
950
+ const kind = node.kind ?? "node", shape = (0, import_core9.shapeOfLevel)(node.level), base = ideaLabelPx({ kind }, d);
951
+ const px = base * (0, import_core9.labelGrowth)(zoom).idea, lineHeight = px * import_core9.IDEA.labelLineHeight;
952
+ const key2 = `${fontsEpoch()}|${d.fontHand}|${+d.comfort.readableType}|${d.labelScale}|${kind}|${shape}|${node.width}|${node.height}|${(0, import_core9.layoutGrowth)(zoom)}|${node.title}`;
953
+ let lines = labelLines.get(key2);
954
+ if (!lines) {
955
+ const m = ideaMeasure(d);
956
+ lines = (0, import_core9.labelLayout)(kind, node.title ?? "", (0, import_core9.nodeRadii)(node), zoom, (t, at) => m(t, base) * (at / base), d.labelScale, (0, import_core9.blobProfile)(shape)).lines;
957
+ if (labelLines.size >= MAX_CACHED_LAYOUTS) labelLines.clear();
958
+ labelLines.set(key2, lines);
962
959
  }
963
- if (lineCache.size >= MAX_CACHED_LAYOUTS) lineCache.clear();
964
- lineCache.set(key2, lines);
965
- return lines;
960
+ return { px, lineHeight, lines };
966
961
  }
967
- var IDLE = { hovered: false, pressed: false, selected: false, focused: false, dimmed: false };
968
- var ideaColors = (d, n) => (0, import_core8.ideaStyle)(d.theme, n);
969
- var ideaLabelPx = (n, d) => import_core8.IDEA.labelSize[n.kind ?? "node"] * d.labelScale;
970
962
  function drawIdea(c, node, d, zoom, st, opt) {
971
- const { rx, ry } = (0, import_core8.nodeRadii)(node);
963
+ const { rx, ry } = (0, import_core9.nodeRadii)(node);
972
964
  const { fill, ring } = ideaColors(d, node);
973
965
  const screenW = node.width * zoom;
974
966
  const base = opt.base;
975
- const detail = (0, import_core8.ideaDetail)(screenW, st.selected || st.focused || st.hovered);
967
+ const detail = (0, import_core9.ideaDetail)(screenW, st.selected || st.focused || st.hovered);
976
968
  if (detail === "speck") {
977
- const a = (st.dimmed ? opt.dimOpacity ?? import_core8.CANVAS_STYLE.node.dimmedOpacityFocus : 1) * (st.arrival ? st.arrival.alpha : 1);
969
+ const a = (st.dimmed ? opt.dimOpacity ?? import_core9.CANVAS_STYLE.node.dimmedOpacityFocus : 1) * (st.arrival ? st.arrival.alpha : 1);
978
970
  if (a !== 1) c.globalAlpha = a;
979
971
  c.fillStyle = fill;
980
972
  c.fillRect(node.x - rx, node.y - ry, rx * 2, ry * 2);
@@ -982,13 +974,13 @@ function drawIdea(c, node, d, zoom, st, opt) {
982
974
  return;
983
975
  }
984
976
  const detailed = detail === "full";
985
- const shape = (0, import_core8.shapeOfLevel)(node.level);
986
- const rings = detailed ? (0, import_core8.ringsFor)(node.branchCount) : NO_RINGS;
977
+ const shape = (0, import_core9.shapeOfLevel)(node.level);
978
+ const rings = detailed ? (0, import_core9.ringsFor)(node.branchCount) : NO_RINGS;
987
979
  const raised = st.hovered || st.pressed || st.selected;
988
980
  const lift = st.lift ?? (raised ? 1 : 0);
989
981
  const ringT = st.ring ?? (raised ? 1 : 0);
990
982
  const arr = st.arrival;
991
- const y = node.y + lift * import_core8.CANVAS_STYLE.node.hoverLift + (arr ? arr.dy : 0);
983
+ const y = node.y + lift * import_core9.CANVAS_STYLE.node.hoverLift + (arr ? arr.dy : 0);
992
984
  const x = node.x + (arr ? arr.dx : 0);
993
985
  let k = opt.k;
994
986
  if (k === void 0) {
@@ -1002,11 +994,11 @@ function drawIdea(c, node, d, zoom, st, opt) {
1002
994
  }
1003
995
  if (arr) c.scale(arr.scale, arr.scale);
1004
996
  c.lineJoin = "round";
1005
- const baseAlpha = (st.dimmed ? opt.dimOpacity ?? import_core8.CANVAS_STYLE.node.dimmedOpacityFocus : 1) * (arr ? arr.alpha : 1);
997
+ const baseAlpha = (st.dimmed ? opt.dimOpacity ?? import_core9.CANVAS_STYLE.node.dimmedOpacityFocus : 1) * (arr ? arr.alpha : 1);
1006
998
  c.globalAlpha = baseAlpha;
1007
999
  if (rings.length) {
1008
- const ringSpin = ringT * import_core8.CANVAS_STYLE.node.hoverRingsRotate;
1009
- const ringGrow = 1 + ringT * (import_core8.CANVAS_STYLE.node.hoverRingsScale - 1);
1000
+ const ringSpin = ringT * import_core9.CANVAS_STYLE.node.hoverRingsRotate;
1001
+ const ringGrow = 1 + ringT * (import_core9.CANVAS_STYLE.node.hoverRingsScale - 1);
1010
1002
  c.fillStyle = fill;
1011
1003
  c.strokeStyle = ring;
1012
1004
  let dashed = false;
@@ -1048,15 +1040,12 @@ function drawIdea(c, node, d, zoom, st, opt) {
1048
1040
  c.shadowOffsetY = 0;
1049
1041
  }
1050
1042
  c.strokeStyle = st.focused ? d.tokens["focus"] : st.selected ? d.tokens["ink"] : ring;
1051
- c.lineWidth = st.selected || st.focused ? import_core8.CANVAS_STYLE.node.selectedStrokeWidth : d.metrics.nodeStroke;
1043
+ c.lineWidth = st.selected || st.focused ? import_core9.CANVAS_STYLE.node.selectedStrokeWidth : d.metrics.nodeStroke;
1052
1044
  c.stroke(body);
1053
1045
  if (detailed && node.title) {
1054
- const px = ideaLabelPx(node, d);
1055
- const font = `${import_core8.TYPE_STYLES["node-label"].weight} ${px}px ${d.fontHand}`;
1056
- const lines = layoutLabel(c, node.title, font, node.width - import_core8.IDEA.labelInset);
1057
- const s = (0, import_core8.labelGrowth)(zoom).idea;
1058
- const lh = px * import_core8.IDEA.labelLineHeight;
1059
- c.scale(s, s);
1046
+ const lay = ideaLabelLayout(node, d, zoom);
1047
+ const px = lay.px, lines = lay.lines, lh = lay.lineHeight;
1048
+ const font = `${import_core9.TYPE_STYLES["node-label"].weight} ${px}px ${d.fontHand}`;
1060
1049
  c.font = font;
1061
1050
  c.fillStyle = d.tokens["node-ink"];
1062
1051
  c.textAlign = "center";
@@ -1096,18 +1085,18 @@ function drawConnector(c, g, d, zoom, dashed, label, arrival) {
1096
1085
  c.lineWidth = d.metrics.edgeWidth;
1097
1086
  c.lineCap = "round";
1098
1087
  c.lineJoin = "round";
1099
- if (dashed) c.setLineDash([...import_core8.CANVAS_STYLE.edge.dash]);
1088
+ if (dashed) c.setLineDash([...import_core9.CANVAS_STYLE.edge.dash]);
1100
1089
  else if (arrival && arrival.draw < 1) c.setLineDash([g.len * arrival.draw, g.len * 2]);
1101
1090
  if (!(arrival && arrival.draw <= 1e-3)) c.stroke(g.path);
1102
1091
  c.setLineDash([]);
1103
1092
  c.stroke(g.arrowPath);
1104
- const labelPx = label ? d.edgeLabel.size * (0, import_core8.labelGrowth)(zoom).connector : 0;
1093
+ const labelPx = label ? d.edgeLabel.size * (0, import_core9.labelGrowth)(zoom).connector : 0;
1105
1094
  if (label && labelPx * zoom >= LABEL_MIN_SCREEN_PX) {
1106
1095
  const px = labelPx;
1107
1096
  c.font = `${d.edgeLabel.weight} ${px}px ${d.fontNote}`;
1108
1097
  c.textAlign = "center";
1109
1098
  c.textBaseline = "middle";
1110
- c.lineWidth = import_core8.CANVAS_STYLE.edge.labelHaloWidth;
1099
+ c.lineWidth = import_core9.CANVAS_STYLE.edge.labelHaloWidth;
1111
1100
  c.strokeStyle = d.tokens["canvas"];
1112
1101
  c.strokeText(label, g.mid.x, g.mid.y);
1113
1102
  c.fillStyle = d.tokens["edge-label"];
@@ -1167,13 +1156,13 @@ function KonvaNodeLayer({
1167
1156
  const nodes = (0, import_react6.useMemo)(() => propNodes ?? Array.from(storeNodes.values()), [propNodes, storeNodes]);
1168
1157
  const index = (0, import_react6.useMemo)(() => {
1169
1158
  if (nodes.length < SPATIAL_INDEX_THRESHOLD || isDragging) return null;
1170
- const idx = new import_core9.RBushSpatialIndex();
1171
- idx.bulkLoad(nodes.map((n) => ({ nodeId: n.id, bounds: (0, import_core9.nodeBounds)(n) })));
1159
+ const idx = new import_core10.RBushSpatialIndex();
1160
+ idx.bulkLoad(nodes.map((n) => ({ nodeId: n.id, bounds: (0, import_core10.nodeBounds)(n) })));
1172
1161
  return idx;
1173
1162
  }, [nodes, isDragging]);
1174
1163
  const byId = (0, import_react6.useMemo)(() => index ? new Map(nodes.map((n) => [n.id, n])) : null, [index, nodes]);
1175
1164
  const highlighted = hoveredNodeId ?? peekedNodeId ?? selectedNodeId;
1176
- const N = import_core9.CANVAS_STYLE.node;
1165
+ const N = import_core10.CANVAS_STYLE.node;
1177
1166
  const focusTheme = design.theme.startsWith("focus");
1178
1167
  const dimOpacity = focusTheme ? touch ? N.dimmedOpacityFocusTouch : N.dimmedOpacityFocus : touch ? N.dimmedOpacityTouch : N.dimmedOpacity;
1179
1168
  const dimOthers = highlighted !== null && dimOpacity !== 1;
@@ -1184,8 +1173,8 @@ function KonvaNodeLayer({
1184
1173
  if (hoveredNodeId) raised.add(hoveredNodeId);
1185
1174
  if (pressedNodeId) raised.add(pressedNodeId);
1186
1175
  if (selectedNodeId) raised.add(selectedNodeId);
1187
- if (tweens.set(raised, design.comfort.reducedMotion || !(0, import_core9.layerBlooms)(nodes.length))) frames.run();
1188
- }, [hoveredNodeId, pressedNodeId, selectedNodeId, design.comfort.reducedMotion, (0, import_core9.layerBlooms)(nodes.length), tweens, frames]);
1176
+ if (tweens.set(raised, design.comfort.reducedMotion || !(0, import_core10.layerBlooms)(nodes.length))) frames.run();
1177
+ }, [hoveredNodeId, pressedNodeId, selectedNodeId, design.comfort.reducedMotion, (0, import_core10.layerBlooms)(nodes.length), tweens, frames]);
1189
1178
  (0, import_react6.useLayoutEffect)(() => clock?.subscribe(() => frames.run()), [clock, frames]);
1190
1179
  (0, import_react6.useLayoutEffect)(() => {
1191
1180
  layerRef.current?.batchDraw();
@@ -1198,10 +1187,10 @@ function KonvaNodeLayer({
1198
1187
  let visible;
1199
1188
  if (isDragging) visible = nodes;
1200
1189
  else if (index && byId) visible = index.query(view).map((id) => byId.get(id)).filter(Boolean);
1201
- else visible = nodes.filter((n) => (0, import_core9.intersectsViewport)(n, view));
1190
+ else visible = nodes.filter((n) => (0, import_core10.intersectsViewport)(n, view));
1202
1191
  const c = ctx2._context;
1203
1192
  const m = c.getTransform();
1204
- const opt = { shadows: !isDragging && (0, import_core9.shadowsShown)(visible.length), k: Math.hypot(m.a, m.b), base: m, dimOpacity };
1193
+ const opt = { shadows: !isDragging && (0, import_core10.shadowsShown)(visible.length), k: Math.hypot(m.a, m.b), base: m, dimOpacity };
1205
1194
  const st = { ...IDLE };
1206
1195
  const now = performance.now();
1207
1196
  const arrival = { alpha: 1, scale: 1, dx: 0, dy: 0 };
@@ -1232,7 +1221,7 @@ function KonvaNodeLayer({
1232
1221
 
1233
1222
  // src/components/KonvaEdgeLayer.tsx
1234
1223
  var import_react7 = require("react");
1235
- var import_core10 = require("@visualli/core");
1224
+ var import_core11 = require("@visualli/core");
1236
1225
  var import_jsx_runtime3 = require("react/jsx-runtime");
1237
1226
  var key = (n) => `${n.x},${n.y},${n.width},${n.level},${n.branchCount ?? 0}`;
1238
1227
  var MARGIN = 80;
@@ -1253,7 +1242,7 @@ function KonvaEdgeLayer({ connections, isDragging = false, design = DEFAULT_DESI
1253
1242
  const k = `${key(src)}|${key(tgt)}`;
1254
1243
  let hit = cache3.current.get(id);
1255
1244
  if (!hit || hit.k !== k) {
1256
- hit = { k, g: prepareConnector((0, import_core10.connectorGeometry)(src, tgt, design.metrics.edgeGap)) };
1245
+ hit = { k, g: prepareConnector((0, import_core11.connectorGeometry)(src, tgt, design.metrics.edgeGap)) };
1257
1246
  cache3.current.set(id, hit);
1258
1247
  }
1259
1248
  out.push({ g: hit.g, dashed: conn.style === "dashed", label: conn.label });
@@ -1291,8 +1280,8 @@ function KonvaEdgeLayer({ connections, isDragging = false, design = DEFAULT_DESI
1291
1280
  var import_react8 = require("react");
1292
1281
 
1293
1282
  // src/design/containers.ts
1294
- var import_core11 = require("@visualli/core");
1295
- var PILL = import_core11.CANVAS_STYLE.group;
1283
+ var import_core12 = require("@visualli/core");
1284
+ var PILL = import_core12.CANVAS_STYLE.group;
1296
1285
  var pillFont = (d, px) => `${PILL.labelWeight} ${px}px ${d.fontNote}`;
1297
1286
  var measureCtx = null;
1298
1287
  function measurer() {
@@ -1328,9 +1317,9 @@ function measurePill(text, d, k, c = measurer()) {
1328
1317
  };
1329
1318
  }
1330
1319
  function ideaBox(n) {
1331
- const { rx, ry } = (0, import_core11.nodeRadii)(n);
1332
- const rings = (0, import_core11.ringsFor)(n.branchCount);
1333
- const s = rings.length ? import_core11.RINGS[rings.length - 1].scale : 1;
1320
+ const { rx, ry } = (0, import_core12.nodeRadii)(n);
1321
+ const rings = (0, import_core12.ringsFor)(n.branchCount);
1322
+ const s = rings.length ? import_core12.RINGS[rings.length - 1].scale : 1;
1334
1323
  return { x0: n.x - rx * s, x1: n.x + rx * s, y0: n.y - ry * s, y1: n.y + ry * s };
1335
1324
  }
1336
1325
  function layoutContainers(containers, nodes, connections, d) {
@@ -1340,19 +1329,19 @@ function layoutContainers(containers, nodes, connections, d) {
1340
1329
  const lines = connections.flatMap((c) => {
1341
1330
  const a = nodes.get(c.from), b = nodes.get(c.to);
1342
1331
  if (!a || !b) return [];
1343
- const g = (0, import_core11.connectorGeometry)(a, b, d.metrics.edgeGap);
1344
- return [(0, import_core11.connectorSamples)(g.a, g.mid, g.b)];
1332
+ const g = (0, import_core12.connectorGeometry)(a, b, d.metrics.edgeGap);
1333
+ return [(0, import_core12.connectorSamples)(g.a, g.mid, g.b)];
1345
1334
  });
1346
1335
  const placed = [];
1347
1336
  const out = [];
1348
1337
  for (const c of containers) {
1349
1338
  const members = c.nodeIds.map((id) => nodes.get(id)).filter((n) => !!n);
1350
- const hull = (0, import_core11.containerHull)(members.map((n) => ({ x: n.x, y: n.y, ...(0, import_core11.nodeRadii)(n) })));
1339
+ const hull = (0, import_core12.containerHull)(members.map((n) => ({ x: n.x, y: n.y, ...(0, import_core12.nodeRadii)(n) })));
1351
1340
  if (!hull) continue;
1352
1341
  let pill = null;
1353
1342
  if (c.label) {
1354
1343
  const size = measurePill(c.label, d, 1);
1355
- const at = (0, import_core11.placeContainerLabel)(hull, size, { ideas, connectors: lines, labels: placed });
1344
+ const at = (0, import_core12.placeContainerLabel)(hull, size, { ideas, connectors: lines, labels: placed });
1356
1345
  pill = { ...size, x: at.x, y: at.y, side: at.side };
1357
1346
  placed.push({ x0: at.x - size.w / 2, x1: at.x + size.w / 2, y0: at.y - size.h / 2, y1: at.y + size.h / 2 });
1358
1347
  }
@@ -1371,7 +1360,7 @@ function drawHull(c, h, d) {
1371
1360
  c.restore();
1372
1361
  }
1373
1362
  function drawPill(c, p, d, zoom) {
1374
- const k = (0, import_core11.labelGrowth)(zoom).idea;
1363
+ const k = (0, import_core12.labelGrowth)(zoom).idea;
1375
1364
  const size = k === 1 ? p : measurePill(p.text, d, k, c);
1376
1365
  const contrast = d.theme.startsWith("contrast");
1377
1366
  const bw = contrast ? PILL.contrastLabelBorderWidth : PILL.labelBorderWidth;
@@ -1450,7 +1439,7 @@ var import_react11 = require("react");
1450
1439
 
1451
1440
  // src/components/Overlays.tsx
1452
1441
  var import_react10 = __toESM(require("react"), 1);
1453
- var import_core12 = require("@visualli/core");
1442
+ var import_core13 = require("@visualli/core");
1454
1443
  var import_jsx_runtime6 = require("react/jsx-runtime");
1455
1444
  var Icon = ({ children, size = 20 }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { className: "vi-icon", width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children });
1456
1445
  var MinusIcon = () => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Icon, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "5", y1: "12", x2: "19", y2: "12" }) });
@@ -1471,7 +1460,7 @@ var ExternalIcon = () => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Icon, { s
1471
1460
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polyline", { points: "15 3 21 3 21 9" }),
1472
1461
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "10", y1: "14", x2: "21", y2: "3" })
1473
1462
  ] });
1474
- var HomeIcon = () => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Icon, { size: import_core12.TRAIL.homeIconSize, children: [
1463
+ var HomeIcon = () => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Icon, { size: import_core13.TRAIL.homeIconSize, children: [
1475
1464
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }),
1476
1465
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polyline", { points: "9 22 9 12 15 12 15 22" })
1477
1466
  ] });
@@ -1485,8 +1474,8 @@ var ZoomControls = (0, import_react10.memo)(function ZoomControls2({ onFit, touc
1485
1474
  const setZoom = useViewportStore((s) => s.setZoom);
1486
1475
  const fitted = useViewportStore((s) => Math.abs(s.zoomLevel - s.fitScale) < 1e-6 && Math.abs(s.centerX - s.fitCenterX) < 0.5 && Math.abs(s.centerY - s.fitCenterY) < 0.5);
1487
1476
  const fit = useViewportStore((s) => s.fitScale);
1488
- const zoomIn = (0, import_react10.useCallback)(() => setZoom(zoom * import_core12.VIEW.zoomStep), [zoom, setZoom]);
1489
- const zoomOut = (0, import_react10.useCallback)(() => setZoom(zoom / import_core12.VIEW.zoomStep), [zoom, setZoom]);
1477
+ const zoomIn = (0, import_react10.useCallback)(() => setZoom(zoom * import_core13.VIEW.zoomStep), [zoom, setZoom]);
1478
+ const zoomOut = (0, import_react10.useCallback)(() => setZoom(zoom / import_core13.VIEW.zoomStep), [zoom, setZoom]);
1490
1479
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "vi-ctrls", role: "toolbar", "aria-label": "Canvas", "data-help": "zoom-controls", children: [
1491
1480
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: `vi-iconbtn ${size}`, "aria-label": "Zoom out", onClick: zoomOut, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MinusIcon, {}) }),
1492
1481
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "vi-ctrls__pct", "aria-live": "polite", children: [
@@ -1502,17 +1491,17 @@ var ZoomControls = (0, import_react10.memo)(function ZoomControls2({ onFit, touc
1502
1491
  });
1503
1492
  function paintVars(p) {
1504
1493
  if (p.custom) return { fill: p.custom.fill, ring: p.custom.ring ?? "var(--edge)" };
1505
- const t = p.topic ?? import_core12.TRAIL.rootTopic;
1494
+ const t = p.topic ?? import_core13.TRAIL.rootTopic;
1506
1495
  return { fill: `var(--topic-${t})`, ring: `var(--topic-${t}-ring)` };
1507
1496
  }
1508
1497
  var TrailDot = ({ level, paint, current }) => {
1509
1498
  const { fill, ring } = paintVars(paint);
1510
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { width: import_core12.TRAIL.dotBox, height: import_core12.TRAIL.dotBox, viewBox: `${-import_core12.TRAIL.dotBox / 2} ${-import_core12.TRAIL.dotBox / 2} ${import_core12.TRAIL.dotBox} ${import_core12.TRAIL.dotBox}`, "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: (0, import_core12.blobPath)((0, import_core12.shapeOfLevel)(level), import_core12.TRAIL.dotRadius, import_core12.TRAIL.dotRadius), fill, stroke: ring, strokeWidth: current ? import_core12.TRAIL.currentStroke : import_core12.TRAIL.stroke }) });
1499
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { width: import_core13.TRAIL.dotBox, height: import_core13.TRAIL.dotBox, viewBox: `${-import_core13.TRAIL.dotBox / 2} ${-import_core13.TRAIL.dotBox / 2} ${import_core13.TRAIL.dotBox} ${import_core13.TRAIL.dotBox}`, "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: (0, import_core13.blobPath)((0, import_core13.shapeOfLevel)(level), import_core13.TRAIL.dotRadius, import_core13.TRAIL.dotRadius), fill, stroke: ring, strokeWidth: current ? import_core13.TRAIL.currentStroke : import_core13.TRAIL.stroke }) });
1511
1500
  };
1512
1501
  var DepthTrail = (0, import_react10.memo)(function DepthTrail2({ stack, onNavigateBack }) {
1513
1502
  if (stack.length === 0) return null;
1514
1503
  const current = stack.length - 1;
1515
- const iconOnly = import_core12.TRAIL.rootLabel === "icon";
1504
+ const iconOnly = import_core13.TRAIL.rootLabel === "icon";
1516
1505
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("nav", { className: "vi-trail", "aria-label": "Depth trail", "data-help": "navigation-stack", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ol", { children: stack.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("li", { className: i === current ? "is-current" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1517
1506
  "button",
1518
1507
  {
@@ -1556,7 +1545,7 @@ var TermCard = ({ anchor, inSheet = false }) => /* @__PURE__ */ (0, import_jsx_r
1556
1545
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "vi-anchor-card__word", style: { display: "block" }, children: anchor.word }),
1557
1546
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "vi-anchor-card__desc", children: anchor.description }),
1558
1547
  anchor.knowMoreUrl && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "vi-anchor-card__acts", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("a", { className: "vi-fact__link", href: anchor.knowMoreUrl, target: "_blank", rel: "noopener noreferrer", children: [
1559
- import_core12.TERM.learnMoreLabel,
1548
+ import_core13.TERM.learnMoreLabel,
1560
1549
  " ",
1561
1550
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ExternalIcon, {})
1562
1551
  ] }) })
@@ -1583,7 +1572,7 @@ function TermAnchor({ anchor }) {
1583
1572
  setShift(0);
1584
1573
  return;
1585
1574
  }
1586
- const r = card.current.getBoundingClientRect(), vw = document.documentElement.clientWidth, m = import_core12.TERM.viewportMargin;
1575
+ const r = card.current.getBoundingClientRect(), vw = document.documentElement.clientWidth, m = import_core13.TERM.viewportMargin;
1587
1576
  setShift(r.right > vw - m ? vw - m - r.right : r.left < m ? m - r.left : 0);
1588
1577
  }, [open]);
1589
1578
  (0, import_react10.useEffect)(() => {
@@ -1612,14 +1601,14 @@ function TermAnchor({ anchor }) {
1612
1601
  className: `vi-anchor${open ? " is-open" : ""}`,
1613
1602
  "data-semantic-tooltip": "true",
1614
1603
  onPointerEnter: (e) => {
1615
- if (!(0, import_core12.hoverOpensTerm)(e.pointerType)) return;
1604
+ if (!(0, import_core13.hoverOpensTerm)(e.pointerType)) return;
1616
1605
  clearTimeout(timer.current);
1617
- timer.current = setTimeout(() => setOpen(true), import_core12.TERM.hoverOpen);
1606
+ timer.current = setTimeout(() => setOpen(true), import_core13.TERM.hoverOpen);
1618
1607
  },
1619
1608
  onPointerLeave: (e) => {
1620
- if (!(0, import_core12.hoverOpensTerm)(e.pointerType) || pinned.current) return;
1609
+ if (!(0, import_core13.hoverOpensTerm)(e.pointerType) || pinned.current) return;
1621
1610
  clearTimeout(timer.current);
1622
- timer.current = setTimeout(() => setOpen(false), import_core12.TERM.hoverClose);
1611
+ timer.current = setTimeout(() => setOpen(false), import_core13.TERM.hoverClose);
1623
1612
  },
1624
1613
  onBlur: (e) => {
1625
1614
  if (!wrap.current?.contains(e.relatedTarget)) hide();
@@ -1692,10 +1681,10 @@ var PeekSheet = (0, import_react10.memo)(function PeekSheet2({ node, anchors, on
1692
1681
  if (startY.current === null) return;
1693
1682
  const dy = e.clientY - startY.current;
1694
1683
  startY.current = null;
1695
- if (dy < -import_core12.SHEET.expandDrag) {
1684
+ if (dy < -import_core13.SHEET.expandDrag) {
1696
1685
  swiped.current = true;
1697
1686
  if (!expanded) onToggleExpanded();
1698
- } else if (dy > import_core12.SHEET.dismissDrag) {
1687
+ } else if (dy > import_core13.SHEET.dismissDrag) {
1699
1688
  swiped.current = true;
1700
1689
  if (expanded) onToggleExpanded();
1701
1690
  else onClose();
@@ -1743,7 +1732,7 @@ var NavigationStack_default = NavigationStack;
1743
1732
 
1744
1733
  // src/components/A11yLayer.tsx
1745
1734
  var import_react12 = require("react");
1746
- var import_core13 = require("@visualli/core");
1735
+ var import_core14 = require("@visualli/core");
1747
1736
  var import_jsx_runtime8 = require("react/jsx-runtime");
1748
1737
  var MAX_MIRRORED = 150;
1749
1738
  var A11yLayer = (0, import_react12.memo)(function A11yLayer2({ nodes, label, hasChildLayer, onActivate, onFocusNode, announcement }) {
@@ -1760,9 +1749,9 @@ var A11yLayer = (0, import_react12.memo)(function A11yLayer2({ nodes, label, has
1760
1749
  const items = (0, import_react12.useMemo)(() => {
1761
1750
  const halfW = view.w / 2 / view.zoom, halfH = view.h / 2 / view.zoom;
1762
1751
  const bounds = { minX: view.cx - halfW, maxX: view.cx + halfW, minY: view.cy - halfH, maxY: view.cy + halfH };
1763
- const visible = nodes.filter((n) => (0, import_core13.intersectsViewport)(n, bounds));
1752
+ const visible = nodes.filter((n) => (0, import_core14.intersectsViewport)(n, bounds));
1764
1753
  const picked = visible.length > MAX_MIRRORED ? visible.map((n) => ({ n, d: (n.x - view.cx) ** 2 + (n.y - view.cy) ** 2 })).sort((a, b) => a.d - b.d).slice(0, MAX_MIRRORED).map((e) => e.n) : visible;
1765
- const row = (n) => Math.round((0, import_core13.nodeBounds)(n).minY / 150);
1754
+ const row = (n) => Math.round((0, import_core14.nodeBounds)(n).minY / 150);
1766
1755
  return [...picked].sort((a, b) => row(a) - row(b) || a.x - b.x);
1767
1756
  }, [nodes, view]);
1768
1757
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
@@ -1812,31 +1801,6 @@ function useDesign(props) {
1812
1801
  );
1813
1802
  }
1814
1803
 
1815
- // src/design/measure.ts
1816
- var import_core14 = require("@visualli/core");
1817
- var ctx;
1818
- var cache2 = /* @__PURE__ */ new Map();
1819
- var MAX_CACHED = 8192;
1820
- function ideaMeasure(d) {
1821
- if (ctx === void 0) ctx = typeof document !== "undefined" ? document.createElement("canvas").getContext("2d") : null;
1822
- const c = ctx;
1823
- if (!c) return import_core14.approximateMeasure;
1824
- const weight = import_core14.TYPE_STYLES["node-label"].weight;
1825
- const tracking = d.comfort.readableType ? 0.02 : 0;
1826
- return (text, px) => {
1827
- const font = `${weight} ${px}px ${d.fontHand}`;
1828
- const key2 = `${fontsEpoch()}|${font}|${text}`;
1829
- let w = cache2.get(key2);
1830
- if (w === void 0) {
1831
- c.font = font;
1832
- w = c.measureText(text).width + text.length * px * tracking;
1833
- if (cache2.size >= MAX_CACHED) cache2.clear();
1834
- cache2.set(key2, w);
1835
- }
1836
- return w;
1837
- };
1838
- }
1839
-
1840
1804
  // src/VisualliCanvas.tsx
1841
1805
  var import_jsx_runtime9 = require("react/jsx-runtime");
1842
1806
  function resolveDocument(props, fileText) {
@@ -2024,7 +1988,9 @@ function VisualliCanvas(props) {
2024
1988
  cancelPan: rendererCancelPan,
2025
1989
  canvasWidth,
2026
1990
  canvasHeight
2027
- } = useKonvaRenderer({ containerRef, stageRef });
1991
+ // Enabled once the document is there: until then the canvas renders a placeholder without the container, and the
1992
+ // size observer must attach to the real container when it appears (a file loading from visualliFile).
1993
+ } = useKonvaRenderer({ containerRef, stageRef, enabled: !!doc });
2028
1994
  const _cw = canvasWidth || containerRef.current?.getBoundingClientRect().width || 800;
2029
1995
  const _ch = canvasHeight || containerRef.current?.getBoundingClientRect().height || 600;
2030
1996
  canvasSizeRef.current = { width: _cw, height: _ch };
@@ -2605,6 +2571,7 @@ function VisualliCanvas(props) {
2605
2571
  const pinnedHasChild = !!(pinnedNode && (0, import_core3.getChildLayerForNode)(doc, pinnedNode.id, currentLayerId ?? ""));
2606
2572
  const compact = (viewport.canvasWidth || canvasSizeRef.current.width || 1024) < 560;
2607
2573
  const medium = !compact && (viewport.canvasWidth || canvasSizeRef.current.width || 1024) < 900;
2574
+ const mapWidth = viewport.canvasWidth || canvasSizeRef.current.width || 0;
2608
2575
  const peekHasChild = !!(peekNode && doc && (0, import_core3.getChildLayerForNode)(doc, peekNode.id, currentLayerId ?? ""));
2609
2576
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2610
2577
  "div",
@@ -2624,7 +2591,7 @@ function VisualliCanvas(props) {
2624
2591
  onPointerMoveCapture: onPinchMove,
2625
2592
  onPointerUpCapture: onPinchUp,
2626
2593
  onPointerCancelCapture: onPinchUp,
2627
- style: { position: "relative", overflow: "hidden", userSelect: "none", width: "100%", height: "100%", outline: "none", ...(0, import_core15.motionVars)(), ...style },
2594
+ style: { position: "relative", overflow: "hidden", userSelect: "none", width: "100%", height: "100%", outline: "none", ...(0, import_core15.motionVars)(), ...mapWidth ? { ["--vi-map-w"]: `${mapWidth}px` } : {}, ...style },
2628
2595
  onMouseMove: handleCanvasMouseMove,
2629
2596
  onMouseLeave: () => {
2630
2597
  if (!tooltipHoverRef.current && !keepTooltipOpenRef.current) {