@visualli/react 0.2.4 → 0.2.6

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.2). 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: #00949c;\n --topic-harbor: #bcdbff;\n --topic-harbor-ring: #4386c5;\n --topic-iris: #e0c8fe;\n --topic-iris-ring: #9570c1;\n --topic-berry: #ffbdc4;\n --topic-berry-ring: #cc5b6d;\n --topic-coral: #ffc7b2;\n --topic-coral-ring: #c56741;\n --topic-amber: #ffcf9d;\n --topic-amber-ring: #b97201;\n --topic-sun: #ffea94;\n --topic-sun-ring: #a18600;\n --topic-stone: #e0dacf;\n --topic-stone-ring: #8b8272;\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: #00949c;\n --topic-harbor: #bcdbff;\n --topic-harbor-ring: #4386c5;\n --topic-iris: #e0c8fe;\n --topic-iris-ring: #9570c1;\n --topic-berry: #ffbdc4;\n --topic-berry-ring: #cc5b6d;\n --topic-coral: #ffc7b2;\n --topic-coral-ring: #c56741;\n --topic-amber: #ffcf9d;\n --topic-amber-ring: #b97201;\n --topic-sun: #ffea94;\n --topic-sun-ring: #a18600;\n --topic-stone: #e0dacf;\n --topic-stone-ring: #8b8272;\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: #2c9298;\n --topic-harbor: #c9d9ec;\n --topic-harbor-ring: #5d85af;\n --topic-iris: #d9cfe8;\n --topic-iris-ring: #8e76aa;\n --topic-berry: #edc6c9;\n --topic-berry-ring: #b26c75;\n --topic-coral: #eecec1;\n --topic-coral-ring: #ae725b;\n --topic-amber: #eed4ba;\n --topic-amber-ring: #a67947;\n --topic-sun: #f4eac1;\n --topic-sun-ring: #98883f;\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: #2c9298;\n --topic-harbor: #c9d9ec;\n --topic-harbor-ring: #5d85af;\n --topic-iris: #d9cfe8;\n --topic-iris-ring: #8e76aa;\n --topic-berry: #edc6c9;\n --topic-berry-ring: #b26c75;\n --topic-coral: #eecec1;\n --topic-coral-ring: #ae725b;\n --topic-amber: #eed4ba;\n --topic-amber-ring: #a67947;\n --topic-sun: #f4eac1;\n --topic-sun-ring: #98883f;\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: #bbeec1;\n --topic-teal-ring: #389754;\n --topic-harbor: #9bc9ef;\n --topic-harbor-ring: #1d84b9;\n --topic-iris: #d7a9c7;\n --topic-iris-ring: #ae5d94;\n --topic-berry: #e7a692;\n --topic-berry-ring: #c05a36;\n --topic-coral: #f5c893;\n --topic-coral-ring: #b27212;\n --topic-amber: #a6edff;\n --topic-amber-ring: #1092af;\n --topic-sun: #fbf494;\n --topic-sun-ring: #938e00;\n --topic-stone: #dbdbdb;\n --topic-stone-ring: #898284;\n }\n:where([data-theme="colorsafe-light"]) .vi-map {\n --topic-teal: #bbeec1;\n --topic-teal-ring: #389754;\n --topic-harbor: #9bc9ef;\n --topic-harbor-ring: #1d84b9;\n --topic-iris: #d7a9c7;\n --topic-iris-ring: #ae5d94;\n --topic-berry: #e7a692;\n --topic-berry-ring: #c05a36;\n --topic-coral: #f5c893;\n --topic-coral-ring: #b27212;\n --topic-amber: #a6edff;\n --topic-amber-ring: #1092af;\n --topic-sun: #fbf494;\n --topic-sun-ring: #938e00;\n --topic-stone: #dbdbdb;\n --topic-stone-ring: #898284;\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: #bbeec1;\n --topic-teal-ring: #69c27e;\n --topic-harbor: #9bc9ef;\n --topic-harbor-ring: #5eade2;\n --topic-iris: #d7a9c7;\n --topic-iris-ring: #d383b9;\n --topic-berry: #e7a692;\n --topic-berry-ring: #e37a55;\n --topic-coral: #f5c893;\n --topic-coral-ring: #db9840;\n --topic-amber: #a6edff;\n --topic-amber-ring: #5bbdda;\n --topic-sun: #fbf494;\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: #bbeec1;\n --topic-teal-ring: #69c27e;\n --topic-harbor: #9bc9ef;\n --topic-harbor-ring: #5eade2;\n --topic-iris: #d7a9c7;\n --topic-iris-ring: #d383b9;\n --topic-berry: #e7a692;\n --topic-berry-ring: #e37a55;\n --topic-coral: #f5c893;\n --topic-coral-ring: #db9840;\n --topic-amber: #a6edff;\n --topic-amber-ring: #5bbdda;\n --topic-sun: #fbf494;\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.2). 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.2";
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.6" : "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;
@@ -1017,6 +1009,7 @@ function drawIdea(c, node, d, zoom, st, opt) {
1017
1009
  c.globalAlpha = baseAlpha * r.opacity;
1018
1010
  const path = blobPath2D(shape, rx * r.scale * ringGrow, ry * r.scale * ringGrow);
1019
1011
  c.fill(path);
1012
+ c.globalAlpha = baseAlpha * r.strokeOpacity;
1020
1013
  c.lineWidth = r.width;
1021
1014
  if (r.dash) {
1022
1015
  c.setLineDash(dashOf(r.dash));
@@ -1048,15 +1041,12 @@ function drawIdea(c, node, d, zoom, st, opt) {
1048
1041
  c.shadowOffsetY = 0;
1049
1042
  }
1050
1043
  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;
1044
+ c.lineWidth = st.selected || st.focused ? import_core9.CANVAS_STYLE.node.selectedStrokeWidth : d.metrics.nodeStroke;
1052
1045
  c.stroke(body);
1053
1046
  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);
1047
+ const lay = ideaLabelLayout(node, d, zoom);
1048
+ const px = lay.px, lines = lay.lines, lh = lay.lineHeight;
1049
+ const font = `${import_core9.TYPE_STYLES["node-label"].weight} ${px}px ${d.fontHand}`;
1060
1050
  c.font = font;
1061
1051
  c.fillStyle = d.tokens["node-ink"];
1062
1052
  c.textAlign = "center";
@@ -1096,18 +1086,18 @@ function drawConnector(c, g, d, zoom, dashed, label, arrival) {
1096
1086
  c.lineWidth = d.metrics.edgeWidth;
1097
1087
  c.lineCap = "round";
1098
1088
  c.lineJoin = "round";
1099
- if (dashed) c.setLineDash([...import_core8.CANVAS_STYLE.edge.dash]);
1089
+ if (dashed) c.setLineDash([...import_core9.CANVAS_STYLE.edge.dash]);
1100
1090
  else if (arrival && arrival.draw < 1) c.setLineDash([g.len * arrival.draw, g.len * 2]);
1101
1091
  if (!(arrival && arrival.draw <= 1e-3)) c.stroke(g.path);
1102
1092
  c.setLineDash([]);
1103
1093
  c.stroke(g.arrowPath);
1104
- const labelPx = label ? d.edgeLabel.size * (0, import_core8.labelGrowth)(zoom).connector : 0;
1094
+ const labelPx = label ? d.edgeLabel.size * (0, import_core9.labelGrowth)(zoom).connector : 0;
1105
1095
  if (label && labelPx * zoom >= LABEL_MIN_SCREEN_PX) {
1106
1096
  const px = labelPx;
1107
1097
  c.font = `${d.edgeLabel.weight} ${px}px ${d.fontNote}`;
1108
1098
  c.textAlign = "center";
1109
1099
  c.textBaseline = "middle";
1110
- c.lineWidth = import_core8.CANVAS_STYLE.edge.labelHaloWidth;
1100
+ c.lineWidth = import_core9.CANVAS_STYLE.edge.labelHaloWidth;
1111
1101
  c.strokeStyle = d.tokens["canvas"];
1112
1102
  c.strokeText(label, g.mid.x, g.mid.y);
1113
1103
  c.fillStyle = d.tokens["edge-label"];
@@ -1167,13 +1157,13 @@ function KonvaNodeLayer({
1167
1157
  const nodes = (0, import_react6.useMemo)(() => propNodes ?? Array.from(storeNodes.values()), [propNodes, storeNodes]);
1168
1158
  const index = (0, import_react6.useMemo)(() => {
1169
1159
  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) })));
1160
+ const idx = new import_core10.RBushSpatialIndex();
1161
+ idx.bulkLoad(nodes.map((n) => ({ nodeId: n.id, bounds: (0, import_core10.nodeBounds)(n) })));
1172
1162
  return idx;
1173
1163
  }, [nodes, isDragging]);
1174
1164
  const byId = (0, import_react6.useMemo)(() => index ? new Map(nodes.map((n) => [n.id, n])) : null, [index, nodes]);
1175
1165
  const highlighted = hoveredNodeId ?? peekedNodeId ?? selectedNodeId;
1176
- const N = import_core9.CANVAS_STYLE.node;
1166
+ const N = import_core10.CANVAS_STYLE.node;
1177
1167
  const focusTheme = design.theme.startsWith("focus");
1178
1168
  const dimOpacity = focusTheme ? touch ? N.dimmedOpacityFocusTouch : N.dimmedOpacityFocus : touch ? N.dimmedOpacityTouch : N.dimmedOpacity;
1179
1169
  const dimOthers = highlighted !== null && dimOpacity !== 1;
@@ -1184,8 +1174,8 @@ function KonvaNodeLayer({
1184
1174
  if (hoveredNodeId) raised.add(hoveredNodeId);
1185
1175
  if (pressedNodeId) raised.add(pressedNodeId);
1186
1176
  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]);
1177
+ if (tweens.set(raised, design.comfort.reducedMotion || !(0, import_core10.layerBlooms)(nodes.length))) frames.run();
1178
+ }, [hoveredNodeId, pressedNodeId, selectedNodeId, design.comfort.reducedMotion, (0, import_core10.layerBlooms)(nodes.length), tweens, frames]);
1189
1179
  (0, import_react6.useLayoutEffect)(() => clock?.subscribe(() => frames.run()), [clock, frames]);
1190
1180
  (0, import_react6.useLayoutEffect)(() => {
1191
1181
  layerRef.current?.batchDraw();
@@ -1198,10 +1188,10 @@ function KonvaNodeLayer({
1198
1188
  let visible;
1199
1189
  if (isDragging) visible = nodes;
1200
1190
  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));
1191
+ else visible = nodes.filter((n) => (0, import_core10.intersectsViewport)(n, view));
1202
1192
  const c = ctx2._context;
1203
1193
  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 };
1194
+ const opt = { shadows: !isDragging && (0, import_core10.shadowsShown)(visible.length), k: Math.hypot(m.a, m.b), base: m, dimOpacity };
1205
1195
  const st = { ...IDLE };
1206
1196
  const now = performance.now();
1207
1197
  const arrival = { alpha: 1, scale: 1, dx: 0, dy: 0 };
@@ -1232,7 +1222,7 @@ function KonvaNodeLayer({
1232
1222
 
1233
1223
  // src/components/KonvaEdgeLayer.tsx
1234
1224
  var import_react7 = require("react");
1235
- var import_core10 = require("@visualli/core");
1225
+ var import_core11 = require("@visualli/core");
1236
1226
  var import_jsx_runtime3 = require("react/jsx-runtime");
1237
1227
  var key = (n) => `${n.x},${n.y},${n.width},${n.level},${n.branchCount ?? 0}`;
1238
1228
  var MARGIN = 80;
@@ -1253,7 +1243,7 @@ function KonvaEdgeLayer({ connections, isDragging = false, design = DEFAULT_DESI
1253
1243
  const k = `${key(src)}|${key(tgt)}`;
1254
1244
  let hit = cache3.current.get(id);
1255
1245
  if (!hit || hit.k !== k) {
1256
- hit = { k, g: prepareConnector((0, import_core10.connectorGeometry)(src, tgt, design.metrics.edgeGap)) };
1246
+ hit = { k, g: prepareConnector((0, import_core11.connectorGeometry)(src, tgt, design.metrics.edgeGap)) };
1257
1247
  cache3.current.set(id, hit);
1258
1248
  }
1259
1249
  out.push({ g: hit.g, dashed: conn.style === "dashed", label: conn.label });
@@ -1291,8 +1281,8 @@ function KonvaEdgeLayer({ connections, isDragging = false, design = DEFAULT_DESI
1291
1281
  var import_react8 = require("react");
1292
1282
 
1293
1283
  // src/design/containers.ts
1294
- var import_core11 = require("@visualli/core");
1295
- var PILL = import_core11.CANVAS_STYLE.group;
1284
+ var import_core12 = require("@visualli/core");
1285
+ var PILL = import_core12.CANVAS_STYLE.group;
1296
1286
  var pillFont = (d, px) => `${PILL.labelWeight} ${px}px ${d.fontNote}`;
1297
1287
  var measureCtx = null;
1298
1288
  function measurer() {
@@ -1328,9 +1318,9 @@ function measurePill(text, d, k, c = measurer()) {
1328
1318
  };
1329
1319
  }
1330
1320
  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;
1321
+ const { rx, ry } = (0, import_core12.nodeRadii)(n);
1322
+ const rings = (0, import_core12.ringsFor)(n.branchCount);
1323
+ const s = rings.length ? import_core12.RINGS[rings.length - 1].scale : 1;
1334
1324
  return { x0: n.x - rx * s, x1: n.x + rx * s, y0: n.y - ry * s, y1: n.y + ry * s };
1335
1325
  }
1336
1326
  function layoutContainers(containers, nodes, connections, d) {
@@ -1340,19 +1330,19 @@ function layoutContainers(containers, nodes, connections, d) {
1340
1330
  const lines = connections.flatMap((c) => {
1341
1331
  const a = nodes.get(c.from), b = nodes.get(c.to);
1342
1332
  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)];
1333
+ const g = (0, import_core12.connectorGeometry)(a, b, d.metrics.edgeGap);
1334
+ return [(0, import_core12.connectorSamples)(g.a, g.mid, g.b)];
1345
1335
  });
1346
1336
  const placed = [];
1347
1337
  const out = [];
1348
1338
  for (const c of containers) {
1349
1339
  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) })));
1340
+ const hull = (0, import_core12.containerHull)(members.map((n) => ({ x: n.x, y: n.y, ...(0, import_core12.nodeRadii)(n) })));
1351
1341
  if (!hull) continue;
1352
1342
  let pill = null;
1353
1343
  if (c.label) {
1354
1344
  const size = measurePill(c.label, d, 1);
1355
- const at = (0, import_core11.placeContainerLabel)(hull, size, { ideas, connectors: lines, labels: placed });
1345
+ const at = (0, import_core12.placeContainerLabel)(hull, size, { ideas, connectors: lines, labels: placed });
1356
1346
  pill = { ...size, x: at.x, y: at.y, side: at.side };
1357
1347
  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
1348
  }
@@ -1371,7 +1361,7 @@ function drawHull(c, h, d) {
1371
1361
  c.restore();
1372
1362
  }
1373
1363
  function drawPill(c, p, d, zoom) {
1374
- const k = (0, import_core11.labelGrowth)(zoom).idea;
1364
+ const k = (0, import_core12.labelGrowth)(zoom).idea;
1375
1365
  const size = k === 1 ? p : measurePill(p.text, d, k, c);
1376
1366
  const contrast = d.theme.startsWith("contrast");
1377
1367
  const bw = contrast ? PILL.contrastLabelBorderWidth : PILL.labelBorderWidth;
@@ -1450,7 +1440,7 @@ var import_react11 = require("react");
1450
1440
 
1451
1441
  // src/components/Overlays.tsx
1452
1442
  var import_react10 = __toESM(require("react"), 1);
1453
- var import_core12 = require("@visualli/core");
1443
+ var import_core13 = require("@visualli/core");
1454
1444
  var import_jsx_runtime6 = require("react/jsx-runtime");
1455
1445
  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
1446
  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 +1461,7 @@ var ExternalIcon = () => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Icon, { s
1471
1461
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polyline", { points: "15 3 21 3 21 9" }),
1472
1462
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "10", y1: "14", x2: "21", y2: "3" })
1473
1463
  ] });
1474
- var HomeIcon = () => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Icon, { size: import_core12.TRAIL.homeIconSize, children: [
1464
+ var HomeIcon = () => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Icon, { size: import_core13.TRAIL.homeIconSize, children: [
1475
1465
  /* @__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
1466
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polyline", { points: "9 22 9 12 15 12 15 22" })
1477
1467
  ] });
@@ -1485,8 +1475,8 @@ var ZoomControls = (0, import_react10.memo)(function ZoomControls2({ onFit, touc
1485
1475
  const setZoom = useViewportStore((s) => s.setZoom);
1486
1476
  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
1477
  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]);
1478
+ const zoomIn = (0, import_react10.useCallback)(() => setZoom(zoom * import_core13.VIEW.zoomStep), [zoom, setZoom]);
1479
+ const zoomOut = (0, import_react10.useCallback)(() => setZoom(zoom / import_core13.VIEW.zoomStep), [zoom, setZoom]);
1490
1480
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "vi-ctrls", role: "toolbar", "aria-label": "Canvas", "data-help": "zoom-controls", children: [
1491
1481
  /* @__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
1482
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "vi-ctrls__pct", "aria-live": "polite", children: [
@@ -1502,17 +1492,17 @@ var ZoomControls = (0, import_react10.memo)(function ZoomControls2({ onFit, touc
1502
1492
  });
1503
1493
  function paintVars(p) {
1504
1494
  if (p.custom) return { fill: p.custom.fill, ring: p.custom.ring ?? "var(--edge)" };
1505
- const t = p.topic ?? import_core12.TRAIL.rootTopic;
1495
+ const t = p.topic ?? import_core13.TRAIL.rootTopic;
1506
1496
  return { fill: `var(--topic-${t})`, ring: `var(--topic-${t}-ring)` };
1507
1497
  }
1508
1498
  var TrailDot = ({ level, paint, current }) => {
1509
1499
  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 }) });
1500
+ 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
1501
  };
1512
1502
  var DepthTrail = (0, import_react10.memo)(function DepthTrail2({ stack, onNavigateBack }) {
1513
1503
  if (stack.length === 0) return null;
1514
1504
  const current = stack.length - 1;
1515
- const iconOnly = import_core12.TRAIL.rootLabel === "icon";
1505
+ const iconOnly = import_core13.TRAIL.rootLabel === "icon";
1516
1506
  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
1507
  "button",
1518
1508
  {
@@ -1556,7 +1546,7 @@ var TermCard = ({ anchor, inSheet = false }) => /* @__PURE__ */ (0, import_jsx_r
1556
1546
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "vi-anchor-card__word", style: { display: "block" }, children: anchor.word }),
1557
1547
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "vi-anchor-card__desc", children: anchor.description }),
1558
1548
  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,
1549
+ import_core13.TERM.learnMoreLabel,
1560
1550
  " ",
1561
1551
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ExternalIcon, {})
1562
1552
  ] }) })
@@ -1583,7 +1573,7 @@ function TermAnchor({ anchor }) {
1583
1573
  setShift(0);
1584
1574
  return;
1585
1575
  }
1586
- const r = card.current.getBoundingClientRect(), vw = document.documentElement.clientWidth, m = import_core12.TERM.viewportMargin;
1576
+ const r = card.current.getBoundingClientRect(), vw = document.documentElement.clientWidth, m = import_core13.TERM.viewportMargin;
1587
1577
  setShift(r.right > vw - m ? vw - m - r.right : r.left < m ? m - r.left : 0);
1588
1578
  }, [open]);
1589
1579
  (0, import_react10.useEffect)(() => {
@@ -1612,14 +1602,14 @@ function TermAnchor({ anchor }) {
1612
1602
  className: `vi-anchor${open ? " is-open" : ""}`,
1613
1603
  "data-semantic-tooltip": "true",
1614
1604
  onPointerEnter: (e) => {
1615
- if (!(0, import_core12.hoverOpensTerm)(e.pointerType)) return;
1605
+ if (!(0, import_core13.hoverOpensTerm)(e.pointerType)) return;
1616
1606
  clearTimeout(timer.current);
1617
- timer.current = setTimeout(() => setOpen(true), import_core12.TERM.hoverOpen);
1607
+ timer.current = setTimeout(() => setOpen(true), import_core13.TERM.hoverOpen);
1618
1608
  },
1619
1609
  onPointerLeave: (e) => {
1620
- if (!(0, import_core12.hoverOpensTerm)(e.pointerType) || pinned.current) return;
1610
+ if (!(0, import_core13.hoverOpensTerm)(e.pointerType) || pinned.current) return;
1621
1611
  clearTimeout(timer.current);
1622
- timer.current = setTimeout(() => setOpen(false), import_core12.TERM.hoverClose);
1612
+ timer.current = setTimeout(() => setOpen(false), import_core13.TERM.hoverClose);
1623
1613
  },
1624
1614
  onBlur: (e) => {
1625
1615
  if (!wrap.current?.contains(e.relatedTarget)) hide();
@@ -1692,10 +1682,10 @@ var PeekSheet = (0, import_react10.memo)(function PeekSheet2({ node, anchors, on
1692
1682
  if (startY.current === null) return;
1693
1683
  const dy = e.clientY - startY.current;
1694
1684
  startY.current = null;
1695
- if (dy < -import_core12.SHEET.expandDrag) {
1685
+ if (dy < -import_core13.SHEET.expandDrag) {
1696
1686
  swiped.current = true;
1697
1687
  if (!expanded) onToggleExpanded();
1698
- } else if (dy > import_core12.SHEET.dismissDrag) {
1688
+ } else if (dy > import_core13.SHEET.dismissDrag) {
1699
1689
  swiped.current = true;
1700
1690
  if (expanded) onToggleExpanded();
1701
1691
  else onClose();
@@ -1743,7 +1733,7 @@ var NavigationStack_default = NavigationStack;
1743
1733
 
1744
1734
  // src/components/A11yLayer.tsx
1745
1735
  var import_react12 = require("react");
1746
- var import_core13 = require("@visualli/core");
1736
+ var import_core14 = require("@visualli/core");
1747
1737
  var import_jsx_runtime8 = require("react/jsx-runtime");
1748
1738
  var MAX_MIRRORED = 150;
1749
1739
  var A11yLayer = (0, import_react12.memo)(function A11yLayer2({ nodes, label, hasChildLayer, onActivate, onFocusNode, announcement }) {
@@ -1760,9 +1750,9 @@ var A11yLayer = (0, import_react12.memo)(function A11yLayer2({ nodes, label, has
1760
1750
  const items = (0, import_react12.useMemo)(() => {
1761
1751
  const halfW = view.w / 2 / view.zoom, halfH = view.h / 2 / view.zoom;
1762
1752
  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));
1753
+ const visible = nodes.filter((n) => (0, import_core14.intersectsViewport)(n, bounds));
1764
1754
  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);
1755
+ const row = (n) => Math.round((0, import_core14.nodeBounds)(n).minY / 150);
1766
1756
  return [...picked].sort((a, b) => row(a) - row(b) || a.x - b.x);
1767
1757
  }, [nodes, view]);
1768
1758
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
@@ -1812,31 +1802,6 @@ function useDesign(props) {
1812
1802
  );
1813
1803
  }
1814
1804
 
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
1805
  // src/VisualliCanvas.tsx
1841
1806
  var import_jsx_runtime9 = require("react/jsx-runtime");
1842
1807
  function resolveDocument(props, fileText) {
@@ -2024,7 +1989,9 @@ function VisualliCanvas(props) {
2024
1989
  cancelPan: rendererCancelPan,
2025
1990
  canvasWidth,
2026
1991
  canvasHeight
2027
- } = useKonvaRenderer({ containerRef, stageRef });
1992
+ // Enabled once the document is there: until then the canvas renders a placeholder without the container, and the
1993
+ // size observer must attach to the real container when it appears (a file loading from visualliFile).
1994
+ } = useKonvaRenderer({ containerRef, stageRef, enabled: !!doc });
2028
1995
  const _cw = canvasWidth || containerRef.current?.getBoundingClientRect().width || 800;
2029
1996
  const _ch = canvasHeight || containerRef.current?.getBoundingClientRect().height || 600;
2030
1997
  canvasSizeRef.current = { width: _cw, height: _ch };
@@ -2605,6 +2572,7 @@ function VisualliCanvas(props) {
2605
2572
  const pinnedHasChild = !!(pinnedNode && (0, import_core3.getChildLayerForNode)(doc, pinnedNode.id, currentLayerId ?? ""));
2606
2573
  const compact = (viewport.canvasWidth || canvasSizeRef.current.width || 1024) < 560;
2607
2574
  const medium = !compact && (viewport.canvasWidth || canvasSizeRef.current.width || 1024) < 900;
2575
+ const mapWidth = viewport.canvasWidth || canvasSizeRef.current.width || 0;
2608
2576
  const peekHasChild = !!(peekNode && doc && (0, import_core3.getChildLayerForNode)(doc, peekNode.id, currentLayerId ?? ""));
2609
2577
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2610
2578
  "div",
@@ -2624,7 +2592,7 @@ function VisualliCanvas(props) {
2624
2592
  onPointerMoveCapture: onPinchMove,
2625
2593
  onPointerUpCapture: onPinchUp,
2626
2594
  onPointerCancelCapture: onPinchUp,
2627
- style: { position: "relative", overflow: "hidden", userSelect: "none", width: "100%", height: "100%", outline: "none", ...(0, import_core15.motionVars)(), ...style },
2595
+ 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
2596
  onMouseMove: handleCanvasMouseMove,
2629
2597
  onMouseLeave: () => {
2630
2598
  if (!tooltipHoverRef.current && !keepTooltipOpenRef.current) {