pmx-canvas 0.7.3 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/.github/extensions/pmx-canvas/THIRD_PARTY_LICENSES.txt +331 -0
  2. package/.github/extensions/pmx-canvas/extension.mjs +25 -172
  3. package/.github/extensions/pmx-canvas/mcp-bootstrap.ts +198 -0
  4. package/.github/extensions/pmx-canvas/runtime.d.mts +41 -0
  5. package/.github/extensions/pmx-canvas/runtime.json +3 -0
  6. package/.github/extensions/pmx-canvas/runtime.mjs +194 -0
  7. package/.github/extensions/pmx-canvas/start-mcp.mjs +51 -0
  8. package/.github/extensions/pmx-canvas/tools.json +2560 -0
  9. package/CHANGELOG.md +47 -0
  10. package/Readme.md +21 -7
  11. package/dist/canvas/fonts/OFL.txt +93 -0
  12. package/dist/canvas/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
  13. package/dist/canvas/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
  14. package/dist/canvas/fonts/ibm-plex-mono-latin-ext-400-normal.woff2 +0 -0
  15. package/dist/canvas/fonts/ibm-plex-mono-latin-ext-500-normal.woff2 +0 -0
  16. package/dist/canvas/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
  17. package/dist/canvas/fonts/ibm-plex-sans-latin-500-normal.woff2 +0 -0
  18. package/dist/canvas/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
  19. package/dist/canvas/fonts/ibm-plex-sans-latin-ext-400-normal.woff2 +0 -0
  20. package/dist/canvas/fonts/ibm-plex-sans-latin-ext-500-normal.woff2 +0 -0
  21. package/dist/canvas/fonts/ibm-plex-sans-latin-ext-600-normal.woff2 +0 -0
  22. package/dist/canvas/global.css +723 -672
  23. package/dist/canvas/index.js +90 -88
  24. package/dist/canvas/mcp-app.html +7878 -0
  25. package/dist/canvas/mermaid-entry.js +1 -1
  26. package/dist/canvas/surface-theme.css +151 -46
  27. package/dist/json-render/index.css +1 -1
  28. package/dist/types/cli/commands/record.d.ts +1 -1
  29. package/dist/types/client/canvas/NodeContextMark.d.ts +24 -0
  30. package/dist/types/client/canvas/SessionReceipt.d.ts +3 -1
  31. package/dist/types/client/icons.d.ts +1 -2
  32. package/dist/types/client/mcp-app/index.d.ts +1 -0
  33. package/dist/types/client/nodes/ax-node-actions.d.ts +2 -2
  34. package/dist/types/client/nodes/markdown-sanitize.d.ts +2 -0
  35. package/dist/types/client/nodes/use-workbench-asset.d.ts +2 -0
  36. package/dist/types/client/state/attention-store.d.ts +0 -5
  37. package/dist/types/client/state/context-status-store.d.ts +53 -0
  38. package/dist/types/client/state/intent-bridge.d.ts +3 -0
  39. package/dist/types/client/state/near-pin-store.d.ts +16 -0
  40. package/dist/types/client/state/presence-store.d.ts +5 -0
  41. package/dist/types/client/state/session-store.d.ts +33 -3
  42. package/dist/types/client/state/workbench-transport.d.ts +16 -0
  43. package/dist/types/client/theme/tokens.d.ts +1 -3
  44. package/dist/types/client/types.d.ts +9 -0
  45. package/dist/types/json-render/server.d.ts +1 -1
  46. package/dist/types/mcp/app-server.d.ts +3 -0
  47. package/dist/types/mcp/canvas-access.d.ts +1 -1
  48. package/dist/types/mcp/canvas-app.d.ts +4 -0
  49. package/dist/types/mcp/workbench-app.d.ts +4 -0
  50. package/dist/types/server/agent-presence.d.ts +1 -1
  51. package/dist/types/server/ax-state.d.ts +1 -1
  52. package/dist/types/server/canvas-db.d.ts +8 -0
  53. package/dist/types/server/canvas-geometry.d.ts +25 -0
  54. package/dist/types/server/canvas-operations.d.ts +1 -11
  55. package/dist/types/server/canvas-state.d.ts +25 -3
  56. package/dist/types/server/context-brief.d.ts +8 -1
  57. package/dist/types/server/context-reads.d.ts +30 -7
  58. package/dist/types/server/index.d.ts +6 -1
  59. package/dist/types/server/mcp-app-runtime.d.ts +1 -0
  60. package/dist/types/server/operations/http.d.ts +2 -0
  61. package/dist/types/server/operations/invoker.d.ts +4 -2
  62. package/dist/types/server/operations/ops/nodes.d.ts +0 -6
  63. package/dist/types/server/server.d.ts +1 -0
  64. package/dist/types/server/spatial-analysis.d.ts +2 -0
  65. package/dist/types/shared/agent-context.d.ts +22 -0
  66. package/dist/types/shared/agent-presence.d.ts +3 -10
  67. package/dist/types/shared/canvas-app.d.ts +54 -0
  68. package/dist/types/shared/themes.d.ts +1 -1
  69. package/docs/RELEASE.md +7 -3
  70. package/docs/agent-plugin.md +123 -0
  71. package/docs/ax-state-contract.md +40 -0
  72. package/docs/cli.md +7 -2
  73. package/docs/design/AgentContext.dc.html +186 -0
  74. package/docs/design/Before.dc.html +208 -0
  75. package/docs/design/BoardPins.dc.html +151 -0
  76. package/docs/design/Chrome.dc.html +208 -0
  77. package/docs/design/Context.dc.html +239 -0
  78. package/docs/design/Flows.dc.html +181 -0
  79. package/docs/design/Home.dc.html +209 -0
  80. package/docs/design/HomeGraph.dc.html +211 -0
  81. package/docs/design/HomeMap.dc.html +117 -0
  82. package/docs/design/HomeTeam.dc.html +176 -0
  83. package/docs/design/Light.dc.html +28 -0
  84. package/docs/design/Main.dc.html +280 -0
  85. package/docs/design/NearPin.dc.html +260 -0
  86. package/docs/design/Pane600.dc.html +120 -0
  87. package/docs/design/Relations.dc.html +297 -0
  88. package/docs/design/Themes.dc.html +121 -0
  89. package/docs/design/TokenSystem.dc.html +136 -0
  90. package/docs/design/Tokens.dc.html +163 -0
  91. package/docs/design/Types1.dc.html +177 -0
  92. package/docs/design/Types2.dc.html +226 -0
  93. package/docs/design/Types3.dc.html +195 -0
  94. package/docs/design/Uses.dc.html +152 -0
  95. package/docs/design/Vision.dc.html +116 -0
  96. package/docs/design/Zoom.dc.html +82 -0
  97. package/docs/design/canvas.json +274 -0
  98. package/docs/design.md +214 -0
  99. package/docs/environment.md +1 -1
  100. package/docs/http-api.md +80 -15
  101. package/docs/mcp.md +198 -3
  102. package/docs/node-types.md +34 -10
  103. package/docs/plans/plan-009-tech-debt-backlog.md +1 -1
  104. package/docs/plans/plan-015-design-foundation-wave-0.md +177 -0
  105. package/docs/plans/plan-016-context-made-visible-wave-1.md +148 -0
  106. package/docs/product-vision-2026-09.md +47 -7
  107. package/docs/screenshot.png +0 -0
  108. package/docs/sdk.md +1 -0
  109. package/package.json +10 -4
  110. package/plugins/pmx-canvas/LICENSE +21 -0
  111. package/plugins/pmx-canvas/THIRD_PARTY_LICENSES.txt +331 -0
  112. package/plugins/pmx-canvas/assets/preview.png +0 -0
  113. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/extension.mjs +930 -0
  114. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +3 -0
  115. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.mjs +194 -0
  116. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/steering-delivery.mjs +79 -0
  117. package/plugins/pmx-canvas/mcp.json +12 -0
  118. package/plugins/pmx-canvas/plugin.json +24 -0
  119. package/plugins/pmx-canvas/runtime.json +3 -0
  120. package/plugins/pmx-canvas/runtime.mjs +194 -0
  121. package/plugins/pmx-canvas/start-mcp.mjs +51 -0
  122. package/plugins/pmx-canvas/tools.json +2560 -0
  123. package/skills/pmx-canvas/SKILL.md +10 -7
  124. package/skills/pmx-canvas/references/full-reference.md +11 -7
  125. package/skills/pmx-canvas/references/github-copilot-app-adapter.md +20 -8
  126. package/skills/pmx-canvas/references/installing-pmx-canvas.md +6 -6
  127. package/skills/pmx-canvas-testing/SKILL.md +5 -4
  128. package/src/cli/commands/copilot.ts +5 -0
  129. package/src/cli/commands/view.ts +1 -1
  130. package/src/cli/index.ts +8 -3
  131. package/src/client/App.tsx +6 -1
  132. package/src/client/canvas/AgentPresenceLayer.tsx +3 -3
  133. package/src/client/canvas/AnnotationLayer.tsx +1 -1
  134. package/src/client/canvas/CanvasNode.tsx +32 -49
  135. package/src/client/canvas/CanvasViewport.tsx +0 -2
  136. package/src/client/canvas/CommandPalette.tsx +3 -2
  137. package/src/client/canvas/ContextMenu.tsx +11 -0
  138. package/src/client/canvas/EdgeLayer.tsx +1 -1
  139. package/src/client/canvas/ExportDialog.tsx +33 -6
  140. package/src/client/canvas/IntentLayer.tsx +2 -2
  141. package/src/client/canvas/Minimap.tsx +12 -7
  142. package/src/client/canvas/NodeContextMark.tsx +156 -0
  143. package/src/client/canvas/SessionPanel.tsx +4 -7
  144. package/src/client/canvas/SessionReceipt.tsx +114 -17
  145. package/src/client/canvas/ShortcutOverlay.tsx +6 -1
  146. package/src/client/canvas/SnapshotPanel.tsx +11 -2
  147. package/src/client/canvas/ToolRail.tsx +12 -9
  148. package/src/client/canvas/TopBar.tsx +16 -11
  149. package/src/client/icons.tsx +2 -12
  150. package/src/client/mcp-app/index.tsx +137 -0
  151. package/src/client/mcp-app/style.css +10 -0
  152. package/src/client/nodes/AttachmentNode.tsx +8 -1
  153. package/src/client/nodes/AxStepControls.tsx +1 -1
  154. package/src/client/nodes/ContextNode.tsx +9 -9
  155. package/src/client/nodes/ExtAppFrame.tsx +13 -8
  156. package/src/client/nodes/FileNode.tsx +8 -5
  157. package/src/client/nodes/ImageNode.tsx +4 -2
  158. package/src/client/nodes/InlineMarkdownEditor.tsx +2 -1
  159. package/src/client/nodes/LedgerNode.tsx +2 -2
  160. package/src/client/nodes/MarkdownNode.tsx +44 -9
  161. package/src/client/nodes/McpAppNode.tsx +6 -4
  162. package/src/client/nodes/PromptNode.tsx +5 -17
  163. package/src/client/nodes/ResponseNode.tsx +3 -15
  164. package/src/client/nodes/StatusNode.tsx +2 -2
  165. package/src/client/nodes/StatusSummary.tsx +3 -1
  166. package/src/client/nodes/TraceNode.tsx +6 -4
  167. package/src/client/nodes/WebpageNode.tsx +1 -1
  168. package/src/client/nodes/ax-node-actions.ts +2 -2
  169. package/src/client/nodes/iframe-document-url.ts +3 -2
  170. package/src/client/nodes/markdown-sanitize.ts +10 -0
  171. package/src/client/nodes/surface-url.ts +10 -0
  172. package/src/client/nodes/use-surface-frame.ts +3 -1
  173. package/src/client/nodes/use-workbench-asset.ts +26 -0
  174. package/src/client/state/attention-bridge.ts +0 -8
  175. package/src/client/state/attention-store.ts +0 -17
  176. package/src/client/state/canvas-store.ts +4 -2
  177. package/src/client/state/context-status-store.ts +66 -0
  178. package/src/client/state/human-store.ts +2 -0
  179. package/src/client/state/iframe-mode.ts +5 -0
  180. package/src/client/state/intent-bridge.ts +10 -4
  181. package/src/client/state/near-pin-store.ts +53 -0
  182. package/src/client/state/presence-store.ts +5 -10
  183. package/src/client/state/session-store.ts +106 -9
  184. package/src/client/state/sse-bridge.ts +26 -7
  185. package/src/client/state/theme-override.ts +1 -1
  186. package/src/client/state/workbench-transport.ts +36 -0
  187. package/src/client/theme/global.css +723 -672
  188. package/src/client/theme/surface-theme.css +151 -46
  189. package/src/client/theme/tokens.ts +7 -11
  190. package/src/client/types.ts +10 -0
  191. package/src/json-render/server.ts +71 -14
  192. package/src/mcp/app-server.ts +64 -0
  193. package/src/mcp/canvas-access.ts +14 -11
  194. package/src/mcp/canvas-app.ts +318 -0
  195. package/src/mcp/server.ts +1 -1
  196. package/src/mcp/workbench-app.ts +211 -0
  197. package/src/server/agent-presence.ts +45 -6
  198. package/src/server/ax-context.ts +1 -1
  199. package/src/server/ax-state.ts +1 -1
  200. package/src/server/board-export.ts +5 -2
  201. package/src/server/canvas-db.ts +46 -12
  202. package/src/server/canvas-geometry.ts +64 -0
  203. package/src/server/canvas-operations.ts +6 -24
  204. package/src/server/canvas-state.ts +105 -15
  205. package/src/server/canvas-validation.ts +19 -4
  206. package/src/server/context-brief.ts +39 -2
  207. package/src/server/context-reads.ts +86 -15
  208. package/src/server/demo-state.json +31 -31
  209. package/src/server/html-surface.ts +4 -5
  210. package/src/server/index.ts +19 -3
  211. package/src/server/mcp-app-runtime.ts +11 -0
  212. package/src/server/operations/composites.ts +4 -3
  213. package/src/server/operations/http.ts +4 -4
  214. package/src/server/operations/invoker.ts +13 -4
  215. package/src/server/operations/ops/ax-read.ts +1 -1
  216. package/src/server/operations/ops/ax-timeline.ts +37 -0
  217. package/src/server/operations/ops/boards.ts +1 -1
  218. package/src/server/operations/ops/canvas-wire.ts +3 -6
  219. package/src/server/operations/ops/intent.ts +31 -2
  220. package/src/server/operations/ops/json-render.ts +1 -1
  221. package/src/server/operations/ops/nodes.ts +8 -76
  222. package/src/server/operations/ops/query.ts +8 -2
  223. package/src/server/operations/ops/snapshots.ts +4 -1
  224. package/src/server/operations/ops/viewport.ts +29 -5
  225. package/src/server/operations/registry.ts +38 -4
  226. package/src/server/scope-fence.ts +55 -10
  227. package/src/server/server.ts +83 -5
  228. package/src/server/spatial-analysis.ts +2 -2
  229. package/src/server/workboard.ts +6 -1
  230. package/src/{server → shared}/agent-context.ts +2 -2
  231. package/src/shared/agent-presence.ts +3 -15
  232. package/src/shared/canvas-app.ts +62 -0
  233. package/src/shared/themes.ts +5 -5
  234. package/src/client/canvas/FocusFieldLayer.tsx +0 -116
  235. package/src/client/canvas/kind-colors.ts +0 -23
@@ -0,0 +1,120 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>The narrow end, 600 px</title>
6
+ <script src="./support.js"></script>
7
+ </head>
8
+ <body>
9
+ <x-dc>
10
+ <helmet>
11
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&amp;family=IBM+Plex+Sans:wght@400;500;600&amp;display=swap">
12
+ <style>
13
+ body{margin:0}
14
+ </style>
15
+ </helmet>
16
+ <div style="width: 1440px; height: 1000px; box-sizing: border-box; padding: 40px 56px; background: #050d17; color: #e6eef7; font-family: 'IBM Plex Sans', system-ui, sans-serif; font-size: 13px; line-height: 1.5; display: flex; flex-direction: column; gap: 16px">
17
+ <div><h1 style="margin: 0; font-size: 28px; font-weight: 600; letter-spacing: -0.015em">At 600 px — the narrow end</h1><p style="margin: 4px 0 0; font-size: 13px; color: #8ea3bd">The narrow end of the reference widths (600, 1024, 1920 px): an agent-host pane or a narrow side browser. One column, panels become drawers and sheets, nothing important hides behind a scrollbar. Every board above is the full-window end.</p></div>
18
+ <div style="display: flex; gap: 64px">
19
+
20
+ <div style="display: flex; flex-direction: column; gap: 8px">
21
+ <span style="font-size: 11px; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; color: #6b7d94">Home · Folders</span>
22
+ <div style="position: relative; width: 600px; height: 840px; border-radius: 12px; border: 1px solid #2a4566; overflow: hidden; background: #081524">
23
+ <header style="display: flex; align-items: center; gap: 8px; height: 48px; padding: 0 10px 0 12px; border-bottom: 1px solid #1b2c44; background: #0f1d31">
24
+ <button aria-label="Folders" style="{{p.ib}}"><svg width="18" height="18" viewBox="0 0 24 24" style="fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round"><path d="M4 7h16M4 12h16M4 17h10"></path></svg></button>
25
+ <span style="font-size: 13px; font-weight: 600; flex: 1">Library</span>
26
+ <div role="tablist" aria-label="View" style="display: flex; padding: 2px; border-radius: 8px; border: 1px solid #1b2c44"><button role="tab" aria-selected="true" style="{{p.tabOn}}">Folders</button><button role="tab" aria-selected="false" style="{{p.tab}}">Map</button><button role="tab" aria-selected="false" style="{{p.tab}}">Graph</button></div>
27
+ </header>
28
+ <div style="padding: 12px; display: flex; flex-direction: column; gap: 12px">
29
+ <div style="display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-radius: 8px; border: 1px solid rgba(75,188,255,0.3); background: rgba(75,188,255,0.08); font-size: 12px"><span style="width: 16px; height: 16px; border-radius: 999px; background: #4BBCFF; color: #081524; display: flex; align-items: center; justify-content: center"><svg width="9" height="9" viewBox="0 0 24 24" style="fill: none; stroke: currentColor; stroke-width: 2.8; stroke-linecap: round; stroke-linejoin: round"><path d="M9 4h6l-.8 5.5 2.8 3H7l2.8-3zM12 12.5V20"></path></svg></span><span style="color: #4BBCFF; font-weight: 600">In context: 3 boards</span><span style="color: #8ea3bd; flex: 1">≈ 21k tokens</span><span style="color: #8ea3bd">Show ▾</span></div>
30
+ <button style="align-self: flex-start; display: flex; align-items: center; gap: 8px; height: 30px; padding: 0 10px; border-radius: 6px; border: 1px solid #1b2c44; background: #0f1d31; color: #e6eef7; font-family: inherit; font-size: 13px; font-weight: 600"><span style="width: 9px; height: 9px; border-radius: 3px; background: #5FCFC0"></span>Tech Enabling<span style="color: #6b7d94; font-weight: 400">· 12 ▾</span></button>
31
+ <div style="display: flex; gap: 8px; overflow: hidden">
32
+ <sc-for list="{{chips}}" as="c" hint-placeholder-count="3"><span style="flex-shrink: 0; display: flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px; border-radius: 999px; border: 1px solid #1b2c44; background: #0f1d31; font-size: 12px; font-weight: 500"><svg width="13" height="13" viewBox="0 0 24 24" style="fill: none; stroke: #8ea3bd; stroke-width: 1.7; stroke-linejoin: round"><path d="M4 7a1 1 0 0 1 1-1h4l2 2h8a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1z"></path></svg>{{c}}</span></sc-for>
33
+ </div>
34
+ <sc-for list="{{rows}}" as="r" hint-placeholder-count="4">
35
+ <div style="display: flex; gap: 12px; padding: 10px; border-radius: 10px; border: {{r.border}}; background: #0f1d31; opacity: {{r.o}}">
36
+ <div style="position: relative; width: 96px; height: 64px; flex-shrink: 0; border-radius: 6px; background-color: #081524; background-image: radial-gradient(rgba(142,163,189,0.16) 1px, transparent 1.2px); background-size: 8px 8px; border: 1px solid #1b2c44"><span style="position: absolute; left: 8px; top: 8px; width: 30px; height: 20px; border-radius: 2px; border: 1px solid {{r.l1}}; background: #13243b"></span><span style="position: absolute; left: 44px; top: 8px; width: 40px; height: 20px; border-radius: 2px; border: 1px solid #2a4566; background: #13243b"></span><span style="position: absolute; left: 8px; top: 34px; width: 76px; height: 20px; border-radius: 2px; border: 1px solid #2a4566; background: #13243b"></span></div>
37
+ <div style="flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px">
38
+ <div style="display: flex; align-items: center; gap: 6px"><span style="font-size: 13px; font-weight: 600; flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis">{{r.name}}</span><sc-if value="{{r.pin}}" hint-placeholder-val="{{ false }}"><span style="width: 16px; height: 16px; border-radius: 999px; background: #4BBCFF; color: #081524; display: flex; align-items: center; justify-content: center"><svg width="9" height="9" viewBox="0 0 24 24" style="fill: none; stroke: currentColor; stroke-width: 2.8; stroke-linecap: round; stroke-linejoin: round"><path d="M9 4h6l-.8 5.5 2.8 3H7l2.8-3zM12 12.5V20"></path></svg></span></sc-if></div>
39
+ <span style="font-size: 11px; color: #b9c6d6; white-space: nowrap; overflow: hidden; text-overflow: ellipsis">{{r.readme}}</span>
40
+ <span style="font-size: 11px; color: #6b7d94">{{r.meta}}</span>
41
+ </div>
42
+ </div>
43
+ </sc-for>
44
+ </div>
45
+ <div style="position: absolute; left: 0; right: 0; bottom: 0; padding: 12px; border-top: 1px solid #2a4566; background: #0b1828; border-radius: 14px 14px 0 0; box-shadow: 0 -10px 30px rgba(0,0,0,0.5); display: flex; flex-direction: column; gap: 6px">
46
+ <span style="width: 36px; height: 4px; border-radius: 999px; background: #2a4566; align-self: center"></span>
47
+ <div style="display: flex; align-items: center; gap: 8px"><span style="font-size: 13px; font-weight: 600; flex: 1">Tech Enabling — overview</span><button style="height: 28px; padding: 0 12px; border: 0; border-radius: 6px; background: #4BBCFF; color: #081524; font-family: inherit; font-size: 12px; font-weight: 600">Open</button></div>
48
+ <span style="font-size: 11px; color: #8ea3bd">Details as a bottom sheet: README, 2 pinned cards, 4 links, in-context switch.</span>
49
+ </div>
50
+ </div>
51
+ </div>
52
+
53
+ <div style="display: flex; flex-direction: column; gap: 8px">
54
+ <span style="font-size: 11px; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; color: #6b7d94">A board</span>
55
+ <div style="position: relative; width: 600px; height: 840px; border-radius: 12px; border: 1px solid #2a4566; overflow: hidden; background-color: #081524; background-image: radial-gradient(rgba(142,163,189,0.14) 1px, transparent 1.2px); background-size: 24px 24px">
56
+ <header style="position: absolute; left: 44px; top: 0; right: 0; display: flex; align-items: center; gap: 6px; height: 44px; padding: 0 8px 0 10px; border-bottom: 1px solid #1b2c44; background: #0f1d31">
57
+ <span style="width: 7px; height: 7px; border-radius: 999px; background: #2fd07f"></span>
58
+ <span style="font-size: 13px; font-weight: 600; flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis">Pricing research ▾</span>
59
+ <span style="font-size: 12px; color: #8ea3bd">100%</span>
60
+ <button aria-label="More: export, present, zoom, fit" style="{{p.ib}}"><svg width="16" height="16" viewBox="0 0 24 24" style="fill: currentColor; stroke: none"><circle cx="6" cy="12" r="1.4"></circle><circle cx="12" cy="12" r="1.4"></circle><circle cx="18" cy="12" r="1.4"></circle></svg></button>
61
+ </header>
62
+ <nav aria-label="Tools" style="position: absolute; left: 0; top: 0; width: 44px; height: 840px; box-sizing: border-box; padding: 6px 0; background: #0f1d31; border-right: 1px solid #1b2c44; display: flex; flex-direction: column; align-items: center; gap: 2px">
63
+ <sc-for list="{{rail}}" as="ic" hint-placeholder-count="9"><span style="width: 32px; height: 32px; border-radius: 6px; display: flex; align-items: center; justify-content: center; color: {{ic.c}}; background: {{ic.bg}}; margin-top: {{ic.mt}}px"><svg width="16" height="16" viewBox="0 0 24 24" style="fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round"><path d="{{ic.d}}"></path></svg></span></sc-for>
64
+ </nav>
65
+ <svg width="600" height="840" viewBox="0 0 600 840" style="position: absolute; left: 0; top: 0; fill: none; stroke-linecap: round">
66
+ <path d="M 200 330 C 200 290, 250 280, 250 236" style="stroke: #5FCFC0; stroke-width: 2"></path><path d="M 250 232 L 245.5 241 L 254.5 241 Z" style="fill: #5FCFC0"></path>
67
+ <path d="M 420 330 C 420 290, 370 280, 370 236" style="stroke: #E59B6B; stroke-width: 2"></path><path d="M 370 232 L 365.5 241 L 374.5 241 Z" style="fill: #E59B6B"></path>
68
+ </svg>
69
+ <div style="{{p.node}} left: 180px; top: 120px; width: 260px; border: 1.5px solid #4BBCFF"><div style="{{p.head}} background: rgba(75,188,255,0.10)"><span style="{{p.title}}">Raise Pro to $24?</span><span style="{{p.mark}}"><svg width="11" height="11" viewBox="0 0 24 24" style="fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round"><path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6S2 12 2 12zM12 9.5a2.5 2.5 0 1 0 0 5a2.5 2.5 0 1 0 0-5"></path></svg>read</span><button aria-label="Expand (Enter)" style="width: 22px; height: 22px; border: 0; border-radius: 6px; padding: 0; background: transparent; color: #b9c6d6; display: flex; align-items: center; justify-content: center; flex-shrink: 0"><svg width="13" height="13" viewBox="0 0 24 24" style="fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round"><path d="M14 4h6v6M20 4l-7 7M10 20H4v-6M4 20l7-7"></path></svg></button><span style="width: 18px; height: 18px; border-radius: 999px; background: #4BBCFF; color: #081524; display: flex; align-items: center; justify-content: center"><svg width="10" height="10" viewBox="0 0 24 24" style="fill: none; stroke: currentColor; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round"><path d="M9 4h6l-.8 5.5 2.8 3H7l2.8-3zM12 12.5V20"></path></svg></span></div><div style="{{p.body}}">Should Pro move from $19 to $24 in 2027?</div></div>
70
+ <div style="{{p.node}} left: 64px; top: 330px; width: 250px"><div style="{{p.head}}"><span style="{{p.title}}">Churn flat after raise</span><span style="{{p.mark}}"><svg width="11" height="11" viewBox="0 0 24 24" style="fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round"><path d="M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8z"></path></svg>by Claude</span><button aria-label="Expand (Enter)" style="width: 22px; height: 22px; border: 0; border-radius: 6px; padding: 0; background: transparent; color: #b9c6d6; display: flex; align-items: center; justify-content: center; flex-shrink: 0"><svg width="13" height="13" viewBox="0 0 24 24" style="fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round"><path d="M14 4h6v6M20 4l-7 7M10 20H4v-6M4 20l7-7"></path></svg></button><button aria-label="Add to context (P)" style="width: 18px; height: 18px; box-sizing: border-box; border: 1.5px solid #6b7d94; border-radius: 999px; padding: 0; background: transparent; color: #b9c6d6; display: flex; align-items: center; justify-content: center; flex-shrink: 0"><svg width="10" height="10" viewBox="0 0 24 24" style="fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round"><path d="M9 4h6l-.8 5.5 2.8 3H7l2.8-3zM12 12.5V20"></path></svg></button></div><div style="{{p.body}}">Pro churn 2.1% → 2.2%.</div></div>
71
+ <div style="{{p.node}} left: 330px; top: 330px; width: 250px"><div style="position: absolute; left: 0; right: 0; top: 0; height: 2.5px; background: #B388FF"></div><div style="{{p.head}}"><span style="{{p.title}}">SMB is price-sensitive</span><span style="{{p.mark}}"><svg width="11" height="11" viewBox="0 0 24 24" style="fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round"><path d="M4 20l4.2-1L19 8.2 15.8 5 5 15.8z"></path></svg>edited</span><button aria-label="Expand (Enter)" style="width: 22px; height: 22px; border: 0; border-radius: 6px; padding: 0; background: transparent; color: #b9c6d6; display: flex; align-items: center; justify-content: center; flex-shrink: 0"><svg width="13" height="13" viewBox="0 0 24 24" style="fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round"><path d="M14 4h6v6M20 4l-7 7M10 20H4v-6M4 20l7-7"></path></svg></button><button aria-label="Add to context (P)" style="width: 18px; height: 18px; box-sizing: border-box; border: 1.5px solid #6b7d94; border-radius: 999px; padding: 0; background: transparent; color: #b9c6d6; display: flex; align-items: center; justify-content: center; flex-shrink: 0"><svg width="10" height="10" viewBox="0 0 24 24" style="fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round"><path d="M9 4h6l-.8 5.5 2.8 3H7l2.8-3zM12 12.5V20"></path></svg></button></div><div style="{{p.body}}">6 of 9 would downgrade.</div></div>
72
+ <div style="position: absolute; left: 56px; right: 12px; bottom: 12px; display: flex; flex-direction: column; gap: 8px">
73
+ <div style="display: flex; gap: 8px; flex-wrap: wrap">
74
+ <span style="display: flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px; border-radius: 999px; border: 1px solid rgba(75,188,255,0.3); background: #0f1d31; color: #4BBCFF; font-size: 12px; font-weight: 500">4 in context <span style="color: #f4c542">· 1 out of date</span></span>
75
+ <span style="display: flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px; border-radius: 999px; border: 1px solid rgba(179,136,255,0.35); background: #0f1d31; color: #B388FF; font-size: 12px; font-weight: 500"><span style="width: 7px; height: 7px; border-radius: 999px; background: #B388FF"></span>Claude · 4 read · 1 edited</span>
76
+ </div>
77
+ <span style="font-size: 11px; color: #6b7d94">Minimap off by default under 700 px; the legend sits behind a “Relations” chip.</span>
78
+ </div>
79
+ </div>
80
+ </div>
81
+
82
+ </div>
83
+ </div>
84
+ </x-dc>
85
+ <script type="text/x-dc" data-dc-script data-props='{"$preview":{"width":1440,"height":1000}}'>
86
+ class Component extends DCLogic {
87
+ renderVals() {
88
+ var p = {
89
+ ib: 'width:32px;height:32px;border:0;border-radius:6px;padding:0;background:transparent;color:#8ea3bd;display:flex;align-items:center;justify-content:center;',
90
+ tab: 'height:26px;padding:0 9px;border:0;border-radius:6px;background:transparent;color:#8ea3bd;font-family:inherit;font-size:12px;font-weight:500;',
91
+ tabOn: 'height:26px;padding:0 9px;border:0;border-radius:6px;background:#1b2c44;color:#e6eef7;font-family:inherit;font-size:12px;font-weight:500;',
92
+ node: 'position:absolute;height:92px;box-sizing:border-box;overflow:hidden;border-radius:10px;border:1px solid #1b2c44;background:#0f1d31;box-shadow:0 1px 2px rgba(0,0,0,0.4),0 6px 20px rgba(0,0,0,0.24);',
93
+ head: 'display:flex;align-items:center;gap:6px;height:36px;padding:0 8px 0 10px;border-bottom:1px solid #1b2c44;',
94
+ title: 'flex:1;min-width:0;font-size:12px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;',
95
+ mark: 'display:flex;align-items:center;gap:4px;height:20px;padding:0 7px;border-radius:999px;font-size:11px;font-weight:500;color:#B388FF;background:rgba(179,136,255,0.16);flex-shrink:0;',
96
+ body: 'padding:8px 10px;font-size:12px;color:#b9c6d6;'
97
+ };
98
+ var chips = ['OKRs · 3', 'Delivery metrics · 4', 'Product discovery · 2'];
99
+ var rows = [
100
+ { name: 'Tech Enabling — overview', readme: 'Who we are, what we own and the three outcomes for 2027.', meta: '18 cards · 4 links · today', pin: true, border: '1.5px solid #4BBCFF', l1: '#4BBCFF', o: 1 },
101
+ { name: 'Platform roadmap 2027', readme: 'Quarterly themes, bets and team dependencies.', meta: '26 cards · 2 links · 4d ago', pin: false, border: '1px solid #1b2c44', l1: '#2a4566', o: 1 },
102
+ { name: 'Q3 risks and dependencies', readme: 'Retired risks from Q3. Kept for the record.', meta: '9 cards · 1 link · 74d ago', pin: false, border: '1px solid #1b2c44', l1: '#2a4566', o: 0.6 }
103
+ ];
104
+ var rail = [
105
+ { d: 'M8 4h8a4 4 0 0 1 4 4v8a4 4 0 0 1-4 4H8a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4z', c: '#8ea3bd', bg: 'transparent', mt: 0 },
106
+ { d: 'M5 3.5l13 7.5-5.6 1.6L10.8 18z', c: '#4BBCFF', bg: 'rgba(75,188,255,0.14)', mt: 8 },
107
+ { d: 'M4 6a2 2 0 1 0 4 0a2 2 0 1 0-4 0M16 18a2 2 0 1 0 4 0a2 2 0 1 0-4 0M7.6 7.6l8.8 8.8', c: '#8ea3bd', bg: 'transparent', mt: 0 },
108
+ { d: 'M6 5h12a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2zM8 10h8M8 14h5', c: '#8ea3bd', bg: 'transparent', mt: 8 },
109
+ { d: 'M12 15V4M7.5 8.5L12 4l4.5 4.5M5 19.5h14', c: '#8ea3bd', bg: 'transparent', mt: 0 },
110
+ { d: 'M5.5 5h13a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-13a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2zM3.5 9h17', c: '#8ea3bd', bg: 'transparent', mt: 0 },
111
+ { d: 'M4 20l4.2-1L19 8.2 15.8 5 5 15.8z', c: '#8ea3bd', bg: 'transparent', mt: 0 },
112
+ { d: 'M5 11a6 6 0 1 0 12 0a6 6 0 1 0-12 0M20 20l-4.6-4.6', c: '#8ea3bd', bg: 'transparent', mt: 8 },
113
+ { d: 'M4 7h9M17 7h3M4 17h3M11 17h9M13 7a2 2 0 1 0 4 0a2 2 0 1 0-4 0M7 17a2 2 0 1 0 4 0a2 2 0 1 0-4 0', c: '#8ea3bd', bg: 'transparent', mt: 440 }
114
+ ];
115
+ return { p: p, chips: chips, rows: rows, rail: rail };
116
+ }
117
+ }
118
+ </script>
119
+ </body>
120
+ </html>
@@ -0,0 +1,297 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Relations</title>
6
+ <script src="./support.js"></script>
7
+ </head>
8
+ <body>
9
+ <x-dc>
10
+ <helmet>
11
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&amp;family=IBM+Plex+Sans:wght@400;500;600&amp;display=swap">
12
+ <style>
13
+ body{margin:0}
14
+ </style>
15
+ </helmet>
16
+ <div style="width: 1440px; height: 1340px; box-sizing: border-box; padding: 48px 56px; background: #081524; color: #e6eef7; font-family: 'IBM Plex Sans', system-ui, sans-serif; font-size: 13px; line-height: 1.5; display: flex; flex-direction: column; gap: 32px">
17
+
18
+ <div>
19
+ <h1 style="margin: 0; font-size: 28px; font-weight: 600; letter-spacing: -0.015em">Relations</h1>
20
+ <p style="margin: 4px 0 0; font-size: 13px; color: #8ea3bd">A research board: one question, two findings, the evidence and the data behind them. Relations should say what supports a claim, what contradicts it, and where each number came from.</p>
21
+ </div>
22
+
23
+ <section style="display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 40px">
24
+
25
+ <div style="display: flex; flex-direction: column; gap: 8px">
26
+ <span style="font-size: 11px; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; color: #6b7d94">Today — hovering the question</span>
27
+ <div style="position: relative; width: 644px; height: 600px; border-radius: 10px; border: 1px solid #1b2c44; overflow: hidden; background-color: #081524; background-image: radial-gradient(rgba(142,163,189,0.10) 1px, transparent 1.2px); background-size: 24px 24px; font-family: -apple-system, 'SF Pro Text', system-ui, sans-serif">
28
+ <svg width="644" height="600" viewBox="0 0 644 600" style="position: absolute; left: 0; top: 0; overflow: visible; fill: none; stroke-linecap: round">
29
+ <path d="{{t.e0.d}}" style="stroke: {{t.e0.c}}; stroke-width: 1.5; opacity: {{t.e0.o}}"></path><path d="{{t.e0.h}}" style="fill: {{t.e0.c}}; stroke: none; opacity: {{t.e0.ho}}"></path>
30
+ <path d="{{t.e1.d}}" style="stroke: {{t.e1.c}}; stroke-width: 1.5; opacity: {{t.e1.o}}"></path><path d="{{t.e1.h}}" style="fill: {{t.e1.c}}; stroke: none; opacity: {{t.e1.ho}}"></path>
31
+ <path d="{{t.e2.d}}" style="stroke: {{t.e2.c}}; stroke-width: 1.5; opacity: {{t.e2.o}}"></path><path d="{{t.e2.h}}" style="fill: {{t.e2.c}}; stroke: none; opacity: {{t.e2.ho}}"></path>
32
+ <path d="{{t.e3.d}}" style="stroke: {{t.e3.c}}; stroke-width: 1.5; opacity: {{t.e3.o}}"></path><path d="{{t.e3.h}}" style="fill: {{t.e3.c}}; stroke: none; opacity: {{t.e3.ho}}"></path>
33
+ <path d="{{t.e4.d}}" style="stroke: {{t.e4.c}}; stroke-width: 1.5; opacity: {{t.e4.o}}"></path><path d="{{t.e4.h}}" style="fill: {{t.e4.c}}; stroke: none; opacity: {{t.e4.ho}}"></path>
34
+ <path d="{{t.e5.d}}" style="stroke: {{t.e5.c}}; stroke-width: 1.5; opacity: {{t.e5.o}}"></path><path d="{{t.e5.h}}" style="fill: {{t.e5.c}}; stroke: none; opacity: {{t.e5.ho}}"></path>
35
+ <path d="{{t.e6.d}}" style="stroke: {{t.e6.c}}; stroke-width: 1.5; opacity: {{t.e6.o}}"></path><path d="{{t.e6.h}}" style="fill: {{t.e6.c}}; stroke: none; opacity: {{t.e6.ho}}"></path>
36
+ <path d="{{t.e7.d}}" style="stroke: {{t.e7.c}}; stroke-width: 1.5; opacity: {{t.e7.o}}"></path><path d="{{t.e7.h}}" style="fill: {{t.e7.c}}; stroke: none; opacity: {{t.e7.ho}}"></path>
37
+ <path d="{{t.e8.d}}" style="stroke: {{t.e8.c}}; stroke-width: 1.5; opacity: {{t.e8.o}}"></path><path d="{{t.e8.h}}" style="fill: {{t.e8.c}}; stroke: none; opacity: {{t.e8.ho}}"></path>
38
+ </svg>
39
+ <sc-for list="{{tLabels}}" as="l" hint-placeholder-count="9">
40
+ <span style="position: absolute; left: {{l.x}}px; top: {{l.y}}px; width: {{l.w}}px; height: 20px; box-sizing: border-box; border-radius: 4px; border: 0.5px solid #1b2c44; background: rgba(15,29,49,0.85); font-size: 11px; line-height: 19px; text-align: center; color: #e6eef7; opacity: {{l.o}}">{{l.text}}</span>
41
+ </sc-for>
42
+ <sc-for list="{{tNodes}}" as="nd" hint-placeholder-count="7">
43
+ <div style="position: absolute; left: {{nd.x}}px; top: {{nd.y}}px; width: 160px; height: 56px; box-sizing: border-box; border-radius: 12px 12px 8px 8px; border: 1px solid {{nd.border}}; background: #0f1d31; box-shadow: 0 4px 24px rgba(0,0,0,0.3); overflow: hidden">
44
+ <div style="height: 24px; padding: 0 8px; display: flex; align-items: center; background: #0b1828; border-bottom: 1px solid #1b2c44; font-size: 11px; font-weight: 600">{{nd.title}}</div>
45
+ <div style="padding: 3px 8px; font-size: 9.5px; color: #8ea3bd">{{nd.meta}}</div>
46
+ </div>
47
+ </sc-for>
48
+ </div>
49
+ <span style="font-size: 12px; color: #ff8a9b">Supports, contradicts and cites are the same grey line; only the pill text tells them apart. The two links between the first finding and the question sit exactly on top of each other.</span>
50
+ </div>
51
+
52
+ <div style="display: flex; flex-direction: column; gap: 8px">
53
+ <span style="font-size: 11px; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; color: #6b7d94">Proposed — hovering the question</span>
54
+ <div style="position: relative; width: 644px; height: 600px; border-radius: 10px; border: 1px solid #1b2c44; overflow: hidden; background-color: #081524; background-image: radial-gradient(rgba(142,163,189,0.14) 1px, transparent 1.2px); background-size: 24px 24px">
55
+ <div style="position: absolute; left: 14px; top: 12px; right: 14px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap">
56
+ <sc-for list="{{legend}}" as="lg" hint-placeholder-count="6">
57
+ <button style="display: flex; align-items: center; gap: 6px; height: 24px; padding: 0 9px; border-radius: 999px; border: 1px solid #1b2c44; background: #0f1d31; color: #e6eef7; font-family: inherit; font-size: 11px; font-weight: 500"><svg width="18" height="6" viewBox="0 0 18 6" style="fill: none; stroke: {{lg.c}}; stroke-width: 2; stroke-dasharray: {{lg.dash}}; stroke-linecap: round"><path d="M1 3 H17"></path></svg>{{lg.name}}<span style="color: #6b7d94; font-variant-numeric: tabular-nums">{{lg.n}}</span></button>
58
+ </sc-for>
59
+ </div>
60
+ <svg width="644" height="600" viewBox="0 0 644 600" style="position: absolute; left: 0; top: 0; overflow: visible; fill: none; stroke-linecap: round">
61
+ <path d="{{p.e0.d}}" style="stroke: {{p.e0.c}}; stroke-width: {{p.e0.w}}; opacity: {{p.e0.o}}; stroke-dasharray: {{p.e0.dash}}"></path><path d="{{p.e0.h}}" style="fill: {{p.e0.c}}; stroke: none; opacity: {{p.e0.o}}"></path>
62
+ <path d="{{p.e1.d}}" style="stroke: {{p.e1.c}}; stroke-width: {{p.e1.w}}; opacity: {{p.e1.o}}; stroke-dasharray: {{p.e1.dash}}"></path><path d="{{p.e1.h}}" style="fill: {{p.e1.c}}; stroke: none; opacity: {{p.e1.o}}"></path>
63
+ <path d="{{p.e2.d}}" style="stroke: {{p.e2.c}}; stroke-width: {{p.e2.w}}; opacity: {{p.e2.o}}; stroke-dasharray: {{p.e2.dash}}"></path><path d="{{p.e2.h}}" style="fill: {{p.e2.c}}; stroke: none; opacity: {{p.e2.o}}"></path>
64
+ <path d="{{p.e3.d}}" style="stroke: {{p.e3.c}}; stroke-width: {{p.e3.w}}; opacity: {{p.e3.o}}; stroke-dasharray: {{p.e3.dash}}"></path><path d="{{p.e3.h}}" style="fill: {{p.e3.c}}; stroke: none; opacity: {{p.e3.o}}"></path>
65
+ <path d="{{p.e4.d}}" style="stroke: {{p.e4.c}}; stroke-width: {{p.e4.w}}; opacity: {{p.e4.o}}; stroke-dasharray: {{p.e4.dash}}"></path><path d="{{p.e4.h}}" style="fill: {{p.e4.c}}; stroke: none; opacity: {{p.e4.o}}"></path>
66
+ <path d="{{p.e5.d}}" style="stroke: {{p.e5.c}}; stroke-width: {{p.e5.w}}; opacity: {{p.e5.o}}; stroke-dasharray: {{p.e5.dash}}"></path><path d="{{p.e5.h}}" style="fill: {{p.e5.c}}; stroke: none; opacity: {{p.e5.o}}"></path>
67
+ <path d="{{p.e6.d}}" style="stroke: {{p.e6.c}}; stroke-width: {{p.e6.w}}; opacity: {{p.e6.o}}; stroke-dasharray: {{p.e6.dash}}"></path><path d="{{p.e6.h}}" style="fill: {{p.e6.c}}; stroke: none; opacity: {{p.e6.o}}"></path>
68
+ <path d="{{p.e7.d}}" style="stroke: {{p.e7.c}}; stroke-width: {{p.e7.w}}; opacity: {{p.e7.o}}; stroke-dasharray: {{p.e7.dash}}"></path><path d="{{p.e7.h}}" style="fill: {{p.e7.c}}; stroke: none; opacity: {{p.e7.o}}"></path>
69
+ <path d="{{p.e8.d}}" style="stroke: {{p.e8.c}}; stroke-width: {{p.e8.w}}; opacity: {{p.e8.o}}; stroke-dasharray: {{p.e8.dash}}"></path><path d="{{p.e8.h}}" style="fill: {{p.e8.c}}; stroke: none; opacity: {{p.e8.o}}"></path>
70
+ </svg>
71
+ <span style="position: absolute; left: 512px; top: 300px; height: 18px; padding: 0 6px; border-radius: 999px; border: 1px solid #2a4566; background: #0f1d31; font-size: 11px; font-weight: 600; line-height: 16px; color: #e6eef7">2</span>
72
+ <sc-for list="{{pNodes}}" as="nd" hint-placeholder-count="7">
73
+ <div style="position: absolute; left: {{nd.x}}px; top: {{nd.y}}px; width: 160px; height: 56px; box-sizing: border-box; padding: 8px 12px; border-radius: 10px; border: {{nd.border}}; background: #0f1d31; box-shadow: {{nd.shadow}}; opacity: {{nd.o}}; display: flex; flex-direction: column; justify-content: center">
74
+ <span style="font-size: 12px; font-weight: 600; line-height: 1.3">{{nd.title}}</span>
75
+ <span style="font-size: 11px; color: #8ea3bd">{{nd.meta}}</span>
76
+ </div>
77
+ </sc-for>
78
+ <span style="position: absolute; left: 252px; top: 166px; height: 18px; padding: 0 6px; border-radius: 999px; border: 1px solid #2a4566; background: #0f1d31; font-size: 11px; font-weight: 600; line-height: 16px; color: #e6eef7">2</span>
79
+ <div style="position: absolute; left: 418px; top: 96px; width: 214px; box-sizing: border-box; padding: 10px 12px; border-radius: 10px; border: 1px solid #2a4566; background: #13243b; box-shadow: 0 10px 30px rgba(0,0,0,0.55); display: flex; flex-direction: column; gap: 4px">
80
+ <span style="font-size: 12px; font-weight: 600">SMB is price-sensitive → Raise Pro to $24?</span>
81
+ <span style="display: flex; align-items: center; gap: 6px; font-size: 11px; color: #E59B6B"><svg width="16" height="6" viewBox="0 0 16 6" style="fill: none; stroke: #E59B6B; stroke-width: 2; stroke-linecap: round"><path d="M1 3 H15"></path></svg>contradicts</span>
82
+ <span style="font-size: 11px; color: #b9c6d6">6 of 9 SMB interviewees would downgrade at $24.</span>
83
+ </div>
84
+ </div>
85
+ <span style="font-size: 12px; color: #8ea3bd">Hovering the question shows what supports it (teal) and what contradicts it (coral); the sources and data step back. Each link carries its reason.</span>
86
+ </div>
87
+
88
+ </section>
89
+
90
+ <section style="display: flex; flex-direction: column; gap: 12px">
91
+ <h2 style="margin: 0; font-size: 15px; font-weight: 600">Edge anatomy</h2>
92
+ <div style="display: grid; grid-template-columns: 100px repeat(5, minmax(0, 1fr)); column-gap: 12px; row-gap: 10px; align-items: center">
93
+ <span></span>
94
+ <span style="{{a.h}}">Two links, same pair</span>
95
+ <span style="{{a.h}}">Many into one</span>
96
+ <span style="{{a.h}}">Label</span>
97
+ <span style="{{a.h}}">Added by the agent</span>
98
+ <span style="{{a.h}}">Selected</span>
99
+
100
+ <span style="font-size: 12px; font-weight: 600">Today</span>
101
+ <div style="{{a.cell}}">
102
+ <svg width="220" height="110" viewBox="0 0 220 110" style="position: absolute; left: 0; top: 0; fill: none; stroke-linecap: round"><path d="M 60 55 C 100 55, 120 55, 160 55" style="stroke: #8ea3bd; stroke-width: 1.5; opacity: 0.85"></path></svg>
103
+ <div style="{{a.box}} left: 14px; top: 41px">A</div><div style="{{a.box}} left: 160px; top: 41px">B</div>
104
+ <span style="{{a.oldLabel}} left: 82px; top: 45px">supports</span><span style="{{a.oldLabel}} left: 86px; top: 47px">cites</span>
105
+ </div>
106
+ <div style="{{a.cell}}">
107
+ <svg width="220" height="110" viewBox="0 0 220 110" style="position: absolute; left: 0; top: 0; fill: none; stroke-linecap: round"><path d="M 40 30 C 40 60, 104 50, 108 78" style="stroke: #4BBCFF; stroke-width: 1.5; opacity: 0.85"></path><path d="M 110 30 C 110 50, 110 60, 110 78" style="stroke: #4BBCFF; stroke-width: 1.5; opacity: 0.85"></path><path d="M 180 30 C 180 60, 116 50, 112 78" style="stroke: #4BBCFF; stroke-width: 1.5; opacity: 0.85"></path><path d="M 106 72 L 110 80 L 114 72 Z" style="fill: #4BBCFF; stroke: none; opacity: 0.75"></path></svg>
108
+ <div style="{{a.box}} left: 20px; top: 8px">A</div><div style="{{a.box}} left: 90px; top: 8px">B</div><div style="{{a.box}} left: 160px; top: 8px">C</div><div style="{{a.box}} left: 80px; top: 80px; width: 60px">D</div>
109
+ </div>
110
+ <div style="{{a.cell}}">
111
+ <svg width="220" height="110" viewBox="0 0 220 110" style="position: absolute; left: 0; top: 0; fill: none; stroke-linecap: round"><path d="M 60 55 L 160 55" style="stroke: #8ea3bd; stroke-width: 1.5; opacity: 0.85"></path></svg>
112
+ <div style="{{a.box}} left: 14px; top: 41px">A</div><div style="{{a.box}} left: 160px; top: 41px">B</div>
113
+ <span style="{{a.oldLabel}} left: 72px; top: 45px; width: 76px">contradicts</span>
114
+ </div>
115
+ <div style="{{a.cell}}">
116
+ <svg width="220" height="110" viewBox="0 0 220 110" style="position: absolute; left: 0; top: 0; fill: none; stroke-linecap: round"><path d="M 60 55 L 158 55" style="stroke: #4BBCFF; stroke-width: 1.5; opacity: 0.85"></path><path d="M 152 51 L 160 55 L 152 59 Z" style="fill: #4BBCFF; stroke: none; opacity: 0.75"></path></svg>
117
+ <div style="{{a.box}} left: 14px; top: 41px">A</div><div style="{{a.box}} left: 160px; top: 41px">B</div>
118
+ <span style="position: absolute; left: 14px; bottom: 8px; font-size: 11px; color: #6b7d94">Looks like any other edge.</span>
119
+ </div>
120
+ <div style="{{a.cell}}">
121
+ <svg width="220" height="110" viewBox="0 0 220 110" style="position: absolute; left: 0; top: 0; fill: none; stroke-linecap: round"><path d="M 60 55 L 158 55" style="stroke: #4BBCFF; stroke-width: 6; opacity: 0.25"></path><path d="M 60 55 L 158 55" style="stroke: #4BBCFF; stroke-width: 3"></path></svg>
122
+ <div style="{{a.box}} left: 14px; top: 41px">A</div><div style="{{a.box}} left: 160px; top: 41px">B</div>
123
+ <span style="position: absolute; left: 14px; bottom: 8px; font-size: 11px; color: #6b7d94">Delete or double-click for a menu.</span>
124
+ </div>
125
+
126
+ <span style="font-size: 12px; font-weight: 600">Proposed</span>
127
+ <div style="{{a.cell}}">
128
+ <svg width="220" height="110" viewBox="0 0 220 110" style="position: absolute; left: 0; top: 0; fill: none; stroke-linecap: round"><path d="M 60 48 L 156 48" style="stroke: #5FCFC0; stroke-width: 1.75"></path><path d="M 152 44 L 160 48 L 152 52 Z" style="fill: #5FCFC0; stroke: none"></path><path d="M 60 62 L 156 62" style="stroke: #7C9CFF; stroke-width: 1.75"></path><path d="M 152 58 L 160 62 L 152 66 Z" style="fill: #7C9CFF; stroke: none"></path></svg>
129
+ <div style="{{a.box}} left: 14px; top: 41px">A</div><div style="{{a.box}} left: 160px; top: 41px">B</div>
130
+ <span style="position: absolute; left: 84px; top: 24px; font-size: 11px; color: #5FCFC0">supports</span><span style="position: absolute; left: 94px; top: 68px; font-size: 11px; color: #7C9CFF">cites</span>
131
+ </div>
132
+ <div style="{{a.cell}}">
133
+ <svg width="220" height="110" viewBox="0 0 220 110" style="position: absolute; left: 0; top: 0; fill: none; stroke-linecap: round"><path d="M 40 30 C 40 56, 92 52, 92 76" style="stroke: #5FCFC0; stroke-width: 1.75"></path><path d="M 88 72 L 92 80 L 96 72 Z" style="fill: #5FCFC0; stroke: none"></path><path d="M 110 30 L 110 76" style="stroke: #5FCFC0; stroke-width: 1.75"></path><path d="M 106 72 L 110 80 L 114 72 Z" style="fill: #5FCFC0; stroke: none"></path><path d="M 180 30 C 180 56, 128 52, 128 76" style="stroke: #E59B6B; stroke-width: 1.75"></path><path d="M 124 72 L 128 80 L 132 72 Z" style="fill: #E59B6B; stroke: none"></path></svg>
134
+ <div style="{{a.box}} left: 20px; top: 8px">A</div><div style="{{a.box}} left: 90px; top: 8px">B</div><div style="{{a.box}} left: 160px; top: 8px">C</div><div style="{{a.box}} left: 80px; top: 80px; width: 60px">D</div>
135
+ </div>
136
+ <div style="{{a.cell}}">
137
+ <svg width="220" height="110" viewBox="0 0 220 110" style="position: absolute; left: 0; top: 0; fill: none; stroke-linecap: round"><path d="M 60 40 L 156 40" style="stroke: #E59B6B; stroke-width: 1.75"></path><path d="M 152 36 L 160 40 L 152 44 Z" style="fill: #E59B6B; stroke: none"></path></svg>
138
+ <div style="{{a.box}} left: 14px; top: 26px">A</div><div style="{{a.box}} left: 160px; top: 26px">B</div>
139
+ <span style="position: absolute; left: 78px; top: 18px; font-size: 11px; color: #E59B6B">contradicts</span>
140
+ <div style="position: absolute; left: 14px; top: 62px; right: 14px; padding: 5px 8px; border-radius: 6px; background: #13243b; border: 1px solid #2a4566; font-size: 11px; color: #b9c6d6">Why: 6 of 9 SMB interviewees would downgrade.</div>
141
+ </div>
142
+ <div style="{{a.cell}}">
143
+ <svg width="220" height="110" viewBox="0 0 220 110" style="position: absolute; left: 0; top: 0; fill: none; stroke-linecap: round"><path d="M 60 55 L 156 55" style="stroke: #B388FF; stroke-width: 2; stroke-dasharray: 6 5"></path><path d="M 152 51 L 160 55 L 152 59 Z" style="fill: #B388FF; stroke: none"></path></svg>
144
+ <div style="{{a.box}} left: 14px; top: 41px">A</div><div style="{{a.box}} left: 160px; top: 41px">B</div>
145
+ <span style="position: absolute; left: 70px; top: 30px; height: 18px; padding: 0 7px; border-radius: 999px; background: rgba(179,136,255,0.16); color: #B388FF; font-size: 11px; font-weight: 600; line-height: 18px">suggested by agent</span>
146
+ <span style="position: absolute; left: 14px; bottom: 8px; font-size: 11px; color: #6b7d94">Accept or dismiss; then it takes its ink.</span>
147
+ </div>
148
+ <div style="{{a.cell}}">
149
+ <svg width="220" height="110" viewBox="0 0 220 110" style="position: absolute; left: 0; top: 0; fill: none; stroke-linecap: round"><path d="M 60 62 L 156 62" style="stroke: #5FCFC0; stroke-width: 2.5"></path><path d="M 151 57 L 160 62 L 151 67 Z" style="fill: #5FCFC0; stroke: none"></path></svg>
150
+ <div style="{{a.box}} left: 14px; top: 48px">A</div><div style="{{a.box}} left: 160px; top: 48px">B</div>
151
+ <span style="position: absolute; left: 56px; top: 58px; width: 8px; height: 8px; box-sizing: border-box; border-radius: 999px; background: #0f1d31; border: 2px solid #5FCFC0"></span>
152
+ <div style="position: absolute; left: 8px; right: 8px; top: 10px; display: flex; align-items: center; justify-content: space-between; gap: 2px; padding: 3px; border-radius: 8px; background: #13243b; border: 1px solid #2a4566; box-shadow: 0 10px 30px rgba(0,0,0,0.5); font-size: 11px"><span style="padding: 0 6px; color: #5FCFC0">supports ▾</span><span style="padding: 0 6px; color: #b9c6d6">Label</span><span style="padding: 0 6px; color: #b9c6d6">Why</span><span style="padding: 0 6px; color: #ff8a9b">Delete</span></div>
153
+ </div>
154
+ </div>
155
+ </section>
156
+
157
+ <section style="display: flex; flex-direction: column; gap: 12px">
158
+ <h2 style="margin: 0; font-size: 15px; font-weight: 600">Relation inks</h2>
159
+ <div style="display: flex; gap: 12px; flex-wrap: wrap">
160
+ <sc-for list="{{inks}}" as="ik" hint-placeholder-count="7">
161
+ <div style="width: 170px; box-sizing: border-box; padding: 10px 12px; border-radius: 10px; border: 1px solid #1b2c44; background: #0f1d31; display: flex; flex-direction: column; gap: 6px">
162
+ <svg width="146" height="10" viewBox="0 0 146 10" style="fill: none; stroke: {{ik.c}}; stroke-width: 2; stroke-dasharray: {{ik.dash}}; stroke-linecap: round"><path d="M2 5 H136"></path><path d="M136 1 L144 5 L136 9 Z" style="fill: {{ik.c}}; stroke: none"></path></svg>
163
+ <span style="font-size: 12px; font-weight: 600">{{ik.name}}</span>
164
+ <span style="font-size: 11px; color: #8ea3bd">{{ik.use}}</span>
165
+ </div>
166
+ </sc-for>
167
+ </div>
168
+ <span style="font-size: 12px; color: #8ea3bd">Relation inks never reuse the reserved colors: amber = warning (e.g. a stale source), accent blue = pinned and selected, violet = the agent.</span>
169
+ </section>
170
+
171
+ </div>
172
+ </x-dc>
173
+ <script type="text/x-dc" data-dc-script data-props='{"$preview":{"width":1440,"height":1340}}'>
174
+ class Component extends DCLogic {
175
+ renderVals() {
176
+ var W = 160, H = 56;
177
+ var N = {
178
+ Q: { x: 242, y: 80, title: 'Raise Pro to $24?', meta: 'question' },
179
+ F1: { x: 100, y: 220, title: 'Churn flat after last raise', meta: 'finding' },
180
+ F2: { x: 384, y: 220, title: 'SMB is price-sensitive', meta: 'finding' },
181
+ C: { x: 40, y: 360, title: 'Churn by plan', meta: 'chart' },
182
+ S1: { x: 242, y: 360, title: 'Competitor pricing', meta: 'webpage' },
183
+ S3: { x: 444, y: 360, title: 'Customer interviews', meta: 'notes · 9 calls' },
184
+ S2: { x: 40, y: 490, title: 'churn-2025.csv', meta: 'file · 14k rows' }
185
+ };
186
+ function r(v) { return Math.round(v * 10) / 10; }
187
+ function head(tip, from, len, half) {
188
+ var dx = tip.x - from.x, dy = tip.y - from.y, m = Math.sqrt(dx * dx + dy * dy) || 1;
189
+ var ux = dx / m, uy = dy / m, bx = tip.x - ux * len, by = tip.y - uy * len;
190
+ return 'M ' + r(tip.x) + ' ' + r(tip.y) + ' L ' + r(bx - uy * half) + ' ' + r(by + ux * half) + ' L ' + r(bx + uy * half) + ' ' + r(by - ux * half) + ' Z';
191
+ }
192
+ var OFF = { top: [0, -1], bottom: [0, 1], left: [-1, 0], right: [1, 0] };
193
+ // Today: EdgeLayer.tsx computeAnchor + sideControlPoint, curvature clamp(dist * 0.35, 32, 160)
194
+ function anchor(n, t) {
195
+ var cx = n.x + W / 2, cy = n.y + H / 2, tx = t.x + W / 2, ty = t.y + H / 2;
196
+ var dx = tx - cx, dy = ty - cy, hw = W / 2, hh = H / 2;
197
+ var tan = Math.abs(dy / (dx || 0.001)), ratio = hh / hw;
198
+ if (tan > ratio) { var sg = dy > 0 ? 1 : -1; return { x: cx + (hh / tan) * (dx > 0 ? 1 : -1), y: cy + hh * sg, side: sg > 0 ? 'bottom' : 'top' }; }
199
+ var s2 = dx > 0 ? 1 : -1; return { x: cx + hw * s2, y: cy + tan * hw * (dy > 0 ? 1 : -1), side: s2 > 0 ? 'right' : 'left' };
200
+ }
201
+ // relation = grey, undirected (no head); depends-on = amber, directed
202
+ function today(a, b, kind, dim, label) {
203
+ var s = anchor(N[a], N[b]), e = anchor(N[b], N[a]);
204
+ var dist = Math.sqrt(Math.pow(e.x - s.x, 2) + Math.pow(e.y - s.y, 2));
205
+ var c = Math.min(Math.max(dist * 0.35, 32), 160);
206
+ var c1 = { x: s.x + OFF[s.side][0] * c, y: s.y + OFF[s.side][1] * c };
207
+ var c2 = { x: e.x + OFF[e.side][0] * c, y: e.y + OFF[e.side][1] * c };
208
+ var mid = { x: 0.125 * s.x + 0.375 * c1.x + 0.375 * c2.x + 0.125 * e.x, y: 0.125 * s.y + 0.375 * c1.y + 0.375 * c2.y + 0.125 * e.y };
209
+ var wl = label.length * 7 + 16;
210
+ var directed = kind === 'depends-on';
211
+ return {
212
+ d: 'M ' + r(s.x) + ' ' + r(s.y) + ' C ' + r(c1.x) + ' ' + r(c1.y) + ', ' + r(c2.x) + ' ' + r(c2.y) + ', ' + r(e.x) + ' ' + r(e.y),
213
+ h: directed ? head(e, c2, 8, 3.2) : 'M 0 0', c: directed ? '#f4c542' : '#8ea3bd', o: dim ? 0.2 : 0.85, ho: dim ? 0.15 : 0.75,
214
+ label: { x: r(mid.x - wl / 2), y: r(mid.y - 10), w: wl, text: label, o: dim ? 0.35 : 1 }
215
+ };
216
+ }
217
+ var te = [
218
+ today('F1', 'Q', 'relation', false, 'supports'), today('F2', 'Q', 'relation', false, 'contradicts'),
219
+ today('F2', 'F1', 'relation', true, 'contradicts'), today('C', 'F1', 'relation', true, 'cites'),
220
+ today('S1', 'F1', 'relation', true, 'supports'), today('S3', 'F2', 'relation', true, 'quotes'),
221
+ today('S3', 'F2', 'relation', true, 'supports'), today('S2', 'C', 'depends-on', true, 'derived from'),
222
+ today('F1', 'Q', 'relation', false, 'cites')
223
+ ];
224
+ var t = {};
225
+ te.forEach(function (e, i) { t['e' + i] = e; });
226
+ var tLabels = te.map(function (e) { return e.label; });
227
+ var tNodes = Object.keys(N).map(function (k) { return { x: N[k].x, y: N[k].y, title: N[k].title, meta: N[k].meta, border: '#1b2c44' }; });
228
+
229
+ function port(k, side, frac) {
230
+ var n = N[k];
231
+ if (side === 'top') return { x: n.x + W * frac, y: n.y, side: side };
232
+ if (side === 'bottom') return { x: n.x + W * frac, y: n.y + H, side: side };
233
+ if (side === 'left') return { x: n.x, y: n.y + H * frac, side: side };
234
+ return { x: n.x + W, y: n.y + H * frac, side: side };
235
+ }
236
+ var INK = { supports: '#5FCFC0', contradicts: '#E59B6B', cites: '#7C9CFF', derived: '#D5DEEA' };
237
+ function prop(s, e, ink, dim, c, dash) {
238
+ var tip = { x: e.x + OFF[e.side][0] * 2, y: e.y + OFF[e.side][1] * 2 };
239
+ var dist = Math.sqrt(Math.pow(tip.x - s.x, 2) + Math.pow(tip.y - s.y, 2));
240
+ var k = c || Math.min(Math.max(dist * 0.45, 24), 110);
241
+ var c1 = { x: s.x + OFF[s.side][0] * k, y: s.y + OFF[s.side][1] * k };
242
+ var c2 = { x: tip.x + OFF[e.side][0] * k, y: tip.y + OFF[e.side][1] * k };
243
+ return {
244
+ d: 'M ' + r(s.x) + ' ' + r(s.y) + ' C ' + r(c1.x) + ' ' + r(c1.y) + ', ' + r(c2.x) + ' ' + r(c2.y) + ', ' + r(tip.x) + ' ' + r(tip.y),
245
+ h: head(tip, c2, 9, 4), c: ink, w: dim ? 1.5 : 2, o: dim ? 0.14 : 1, dash: dash || 'none'
246
+ };
247
+ }
248
+ var pe = [
249
+ prop(port('F1', 'top', 0.5), port('Q', 'bottom', 0.3), INK.supports, false),
250
+ prop(port('F2', 'top', 0.5), port('Q', 'bottom', 0.75), INK.contradicts, false),
251
+ prop(port('F2', 'left', 0.5), port('F1', 'right', 0.5), INK.contradicts, true),
252
+ prop(port('C', 'top', 0.5), port('F1', 'bottom', 0.35), INK.cites, true),
253
+ prop(port('S1', 'top', 0.5), port('F1', 'bottom', 0.8), INK.supports, true),
254
+ prop(port('S3', 'top', 0.4), port('F2', 'bottom', 0.55), INK.cites, true),
255
+ prop(port('S3', 'top', 0.6), port('F2', 'bottom', 0.75), INK.supports, true),
256
+ prop(port('S2', 'top', 0.5), port('C', 'bottom', 0.5), INK.derived, true),
257
+ prop(port('F1', 'top', 0.62), port('Q', 'bottom', 0.4), INK.cites, false)
258
+ ];
259
+ var p = {};
260
+ pe.forEach(function (e, i) { p['e' + i] = e; });
261
+ var neighbors = { Q: 1, F1: 1, F2: 1 };
262
+ var pNodes = Object.keys(N).map(function (k) {
263
+ var focus = k === 'Q';
264
+ return {
265
+ x: N[k].x, y: N[k].y, title: N[k].title, meta: N[k].meta,
266
+ o: neighbors[k] ? 1 : 0.35,
267
+ border: focus ? '1px solid #8ea3bd' : '1px solid #1b2c44',
268
+ shadow: focus ? '0 0 0 3px rgba(142,163,189,0.18), 0 10px 30px rgba(0,0,0,0.5)' : '0 1px 2px rgba(0,0,0,0.4), 0 6px 20px rgba(0,0,0,0.24)'
269
+ };
270
+ });
271
+ var legend = [
272
+ { name: 'supports', n: 3, c: INK.supports, dash: 'none' },
273
+ { name: 'contradicts', n: 2, c: INK.contradicts, dash: 'none' },
274
+ { name: 'cites', n: 3, c: INK.cites, dash: 'none' },
275
+ { name: 'derived from', n: 1, c: INK.derived, dash: 'none' }
276
+ ];
277
+ var inks = [
278
+ { name: 'supports', use: 'Evidence for a claim', c: INK.supports, dash: 'none' },
279
+ { name: 'contradicts', use: 'Evidence against it', c: INK.contradicts, dash: 'none' },
280
+ { name: 'cites', use: 'Where a claim or number comes from', c: INK.cites, dash: 'none' },
281
+ { name: 'derived from', use: 'Data lineage: chart ← table ← source', c: INK.derived, dash: 'none' },
282
+ { name: 'answers', use: 'Finding → the question it settles', c: '#E58FB0', dash: 'none' },
283
+ { name: 'related', use: 'Loose link; undirected', c: '#8ea3bd', dash: '2 5' },
284
+ { name: 'suggested by agent', use: 'Until you accept it', c: '#B388FF', dash: '6 5' }
285
+ ];
286
+ var a = {
287
+ h: 'font-size:12px;font-weight:600;',
288
+ cell: 'position:relative;height:110px;border-radius:8px;border:1px solid #1b2c44;background:#081524;overflow:hidden;',
289
+ box: 'position:absolute;width:46px;height:28px;box-sizing:border-box;border-radius:6px;border:1px solid #2a4566;background:#0f1d31;font-size:11px;font-weight:600;line-height:26px;text-align:center;color:#e6eef7;',
290
+ oldLabel: 'position:absolute;width:48px;height:20px;box-sizing:border-box;border-radius:4px;border:0.5px solid #1b2c44;background:rgba(15,29,49,0.85);font-family:-apple-system,system-ui,sans-serif;font-size:11px;line-height:19px;text-align:center;color:#e6eef7;'
291
+ };
292
+ return { t: t, tLabels: tLabels, tNodes: tNodes, p: p, pNodes: pNodes, legend: legend, inks: inks, a: a };
293
+ }
294
+ }
295
+ </script>
296
+ </body>
297
+ </html>
@@ -0,0 +1,121 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Meaning colours across all themes</title>
6
+ <script src="./support.js"></script>
7
+ </head>
8
+ <body>
9
+ <x-dc>
10
+ <helmet>
11
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&amp;family=IBM+Plex+Sans:wght@400;500;600&amp;display=swap">
12
+ <style>
13
+ body{margin:0}
14
+ </style>
15
+ </helmet>
16
+ <div style="width: 1440px; height: 1220px; box-sizing: border-box; padding: 48px 56px; background: #081524; color: #e6eef7; font-family: 'IBM Plex Sans', system-ui, sans-serif; font-size: 13px; line-height: 1.5; display: flex; flex-direction: column; gap: 24px">
17
+
18
+ <div style="display: flex; gap: 32px; align-items: flex-end">
19
+ <div style="flex: 1">
20
+ <h1 style="margin: 0; font-size: 28px; font-weight: 600; letter-spacing: -0.015em">Meaning colours across all nine themes</h1>
21
+ <p style="margin: 4px 0 0; font-size: 13px; color: #8ea3bd">Each theme keeps its own surfaces and accent (buttons, selection). The meaning colours — in context, agent, warning and the relation inks — keep the same hue in every theme, tuned only for lightness, and a token test checks they stay apart.</p>
22
+ </div>
23
+ <div style="display: flex; gap: 8px; font-size: 11px; color: #8ea3bd">
24
+ <span style="display: flex; align-items: center; gap: 6px"><span style="width: 10px; height: 10px; border-radius: 3px; background: #4BBCFF"></span>in context</span><span style="display: flex; align-items: center; gap: 6px"><span style="width: 10px; height: 10px; border-radius: 3px; background: #E879F9"></span>subagent</span>
25
+ <span style="display: flex; align-items: center; gap: 6px"><span style="width: 10px; height: 10px; border-radius: 3px; background: #B388FF"></span>agent</span>
26
+ <span style="display: flex; align-items: center; gap: 6px"><span style="width: 10px; height: 10px; border-radius: 3px; background: #f4c542"></span>warning</span>
27
+ <span style="display: flex; align-items: center; gap: 6px"><span style="width: 10px; height: 10px; border-radius: 3px; background: #5FCFC0"></span>supports</span>
28
+ <span style="display: flex; align-items: center; gap: 6px"><span style="width: 10px; height: 10px; border-radius: 3px; background: #E59B6B"></span>contradicts</span>
29
+ </div>
30
+ </div>
31
+
32
+ <div style="display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px">
33
+ <sc-for list="{{themes}}" as="t" hint-placeholder-count="9">
34
+ <section style="display: flex; flex-direction: column; gap: 8px; padding: 14px; border-radius: 12px; border: 1px solid #1b2c44; background: #0b1828">
35
+ <div style="display: flex; align-items: baseline; gap: 8px">
36
+ <span style="font-size: 15px; font-weight: 600">{{t.name}}</span>
37
+ <span style="font-size: 11px; color: #6b7d94">{{t.was}}</span>
38
+ <span style="flex: 1"></span>
39
+ <span style="font-size: 11px; padding: 0 7px; border-radius: 999px; border: 1px solid #2a4566; color: #b9c6d6">{{t.scheme}}</span>
40
+ </div>
41
+ <div style="position: relative; height: 200px; border-radius: 8px; overflow: hidden; background: {{t.bg}}; font-family: 'IBM Plex Sans', sans-serif">
42
+ <svg width="398" height="200" viewBox="0 0 398 200" style="position: absolute; left: 0; top: 0; fill: none; stroke-linecap: round">
43
+ <path d="M 100 112 C 100 92, 150 84, 150 70" style="stroke: {{t.sup}}; stroke-width: 2"></path><path d="M 150 66 L 145.5 75 L 154.5 75 Z" style="fill: {{t.sup}}"></path>
44
+ <path d="M 290 112 C 290 92, 240 84, 240 70" style="stroke: {{t.con}}; stroke-width: 2"></path><path d="M 240 66 L 235.5 75 L 244.5 75 Z" style="fill: {{t.con}}"></path>
45
+ </svg>
46
+ <div style="{{t.n1}}">
47
+ <div style="display: flex; align-items: center; gap: 6px; height: 26px; padding: 0 6px 0 9px; background: {{t.pinTint}}; border-bottom: 1px solid {{t.line}}"><span style="flex: 1; font-size: 11px; font-weight: 600; color: {{t.text}}">Raise Pro to $24?</span><span style="width: 16px; height: 16px; border-radius: 999px; background: {{t.pin}}; color: {{t.onPin}}; display: flex; align-items: center; justify-content: center"><svg width="9" height="9" viewBox="0 0 24 24" style="fill: none; stroke: currentColor; stroke-width: 2.8; stroke-linecap: round; stroke-linejoin: round"><path d="M9 4h6l-.8 5.5 2.8 3H7l2.8-3zM12 12.5V20"></path></svg></span></div>
48
+ <div style="padding: 3px 9px; font-size: 10px; color: {{t.muted}}">question · in context</div>
49
+ </div>
50
+ <div style="{{t.n2}}">
51
+ <div style="height: 2.5px; background: {{t.agent}}"></div>
52
+ <div style="display: flex; align-items: center; gap: 4px; height: 24px; padding: 0 6px 0 9px; border-bottom: 1px solid {{t.line}}"><span style="flex: 1; font-size: 11px; font-weight: 600; color: {{t.text}}; white-space: nowrap; overflow: hidden; text-overflow: ellipsis">Churn flat</span><span style="height: 16px; padding: 0 6px; border-radius: 999px; background: {{t.agentTint}}; color: {{t.agent}}; font-size: 10px; font-weight: 500; line-height: 16px">edited</span></div>
53
+ <div style="padding: 4px 9px; font-size: 10px; color: {{t.muted}}">supports the question</div>
54
+ </div>
55
+ <div style="{{t.n3}}">
56
+ <div style="display: flex; align-items: center; gap: 4px; height: 26px; padding: 0 6px 0 9px; border-bottom: 1px solid {{t.line}}"><span style="flex: 1; font-size: 11px; font-weight: 600; color: {{t.text}}">SMB interviews</span></div>
57
+ <div style="padding: 5px 9px; display: flex; align-items: center; gap: 6px"><span style="height: 16px; padding: 0 6px; border-radius: 999px; background: {{t.warnTint}}; color: {{t.warn}}; font-size: 10px; font-weight: 500; line-height: 16px">changed since read</span><span style="height: 18px; padding: 0 7px; border-radius: 5px; background: {{t.accent}}; color: {{t.onAccent}}; font-size: 10px; font-weight: 600; line-height: 18px">Open</span></div>
58
+ </div>
59
+ </div>
60
+ <div style="display: flex; gap: 4px">
61
+ <sc-for list="{{t.swatches}}" as="sw" hint-placeholder-count="7">
62
+ <span style="flex: 1; height: 8px; border-radius: 2px; background: {{sw}}"></span>
63
+ </sc-for>
64
+ </div>
65
+ <span style="font-size: 11px; line-height: 1.45; color: {{t.noteColor}}">{{t.note}}</span>
66
+ </section>
67
+ </sc-for>
68
+ </div>
69
+
70
+ </div>
71
+ </x-dc>
72
+ <script type="text/x-dc" data-dc-script data-props='{"$preview":{"width":1440,"height":1220}}'>
73
+ class Component extends DCLogic {
74
+ renderVals() {
75
+ function hexA(hex, a) {
76
+ var h = hex.replace('#', '');
77
+ var r = parseInt(h.slice(0, 2), 16), g = parseInt(h.slice(2, 4), 16), b = parseInt(h.slice(4, 6), 16);
78
+ return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
79
+ }
80
+ var DARK = { pin: '#4BBCFF', onPin: '#081524', agent: '#B388FF', subagent: '#E879F9', sup: '#5FCFC0', con: '#E59B6B', e1: '0 1px 2px rgba(0,0,0,0.4), 0 6px 20px rgba(0,0,0,0.24)' };
81
+ var LIGHT = { pin: '#1A7ABF', onPin: '#FFFFFF', agent: '#7C4DDB', subagent: '#A83BB0', sup: '#1E8C80', con: '#B8572A', e1: '0 1px 2px rgba(8,21,36,0.1), 0 6px 20px rgba(8,21,36,0.08)' };
82
+ function t(o) {
83
+ var base = o.scheme === 'light' ? LIGHT : DARK;
84
+ var x = Object.assign({}, base, o);
85
+ x.pinTint = hexA(x.pin, o.scheme === 'light' ? 0.10 : 0.12);
86
+ x.agentTint = hexA(x.agent, 0.16);
87
+ x.warnTint = hexA(x.warn, 0.14);
88
+ x.swatches = [x.bg, x.panel, x.accent, x.pin, x.agent, x.subagent, x.warn, x.sup];
89
+ var nodeBase = 'position:absolute;box-sizing:border-box;border-radius:9px;overflow:hidden;background:' + x.panel + ';box-shadow:' + x.e1 + ';';
90
+ x.n1 = nodeBase + 'left:100px;top:14px;width:198px;height:52px;border:1.5px solid ' + x.pin + ';';
91
+ x.n2 = nodeBase + 'left:14px;top:114px;width:172px;height:70px;border:1px solid ' + x.line + ';';
92
+ x.n3 = nodeBase + 'left:204px;top:114px;width:180px;height:70px;border:1px solid ' + x.line + ';';
93
+ x.noteColor = o.bad ? '#ff8a9b' : '#8ea3bd';
94
+ return x;
95
+ }
96
+ var themes = [
97
+ t({ name: 'Harbor', was: 'id harbor · default', scheme: 'dark', bg: '#081524', panel: '#0f1d31', line: '#1b2c44', text: '#e6eef7', muted: '#8ea3bd', accent: '#4BBCFF', onAccent: '#081524', warn: '#f4c542',
98
+ note: 'Accent and pin are the same blue here, so selection is told apart by its gap ring and handles.' }),
99
+ t({ name: 'Daylight', was: 'id daylight', scheme: 'light', bg: '#F6F7F9', panel: '#FFFFFF', line: '#DDE2E9', text: '#0B1726', muted: '#55657a', accent: '#1A7ABF', onAccent: '#FFFFFF', warn: '#94600A',
100
+ note: 'Neutral surfaces instead of beige, so it no longer looks like Sepia. Shadows are ink-tinted, not black.' }),
101
+ t({ name: 'High contrast', was: 'unchanged', scheme: 'dark', bg: '#000000', panel: '#0a0a0a', line: '#ffffff', text: '#ffffff', muted: '#cccccc', accent: '#00ffff', onAccent: '#000000', warn: '#ffff00', pin: '#00ffff', onPin: '#000000', agent: '#e040fb', subagent: '#ff99ff', sup: '#00e5c0', con: '#ff8a00',
102
+ note: 'Keeps a literal name. Meaning colours use the theme’s own pure hues.' }),
103
+ t({ name: 'Midnight', was: 'unchanged', scheme: 'dark', bg: '#0A0D1C', panel: '#12172E', line: '#232B4A', text: '#E6E9F7', muted: '#8D96BC', accent: '#8B96FF', onAccent: '#0A0D1C', warn: '#F5C452',
104
+ note: 'Accent periwinkle sits between pin blue and agent violet — pin can’t be the accent here.', bad: true }),
105
+ t({ name: 'Sepia', was: 'unchanged', scheme: 'light', bg: '#F2E7D5', panel: '#EBDCC2', line: '#D3C1A0', text: '#2E2416', muted: '#77664C', accent: '#B4632C', onAccent: '#FFFFFF', warn: '#8A5A00', pin: '#1F6FA8', agent: '#7046A6', con: '#A3461C',
106
+ note: 'Accent copper is close to the warning brown — another reason pin is its own colour. Shadows are ink-tinted.', bad: true }),
107
+ t({ name: 'Arctic', was: 'unchanged', scheme: 'dark', bg: '#20242E', panel: '#2A2F3C', line: '#3A4152', text: '#E9EDF4', muted: '#939DB1', accent: '#7FC8DE', onAccent: '#20242E', warn: '#E8C476', pin: '#6EC1F5',
108
+ note: 'Works as is; pin is a touch bluer than the accent ice.' }),
109
+ t({ name: 'Ember', was: 'unchanged', scheme: 'dark', bg: '#0B0B0C', panel: '#161618', line: '#29292C', text: '#EDEBE6', muted: '#96948D', accent: '#F34E1C', onAccent: '#0B0B0C', warn: '#F5B944',
110
+ note: 'Accent orange-red would read as an error if it marked “in context”.', bad: true }),
111
+ t({ name: 'Forest', was: 'unchanged', scheme: 'dark', bg: '#0C1712', panel: '#14231B', line: '#26402F', text: '#E4EFE7', muted: '#8BA695', accent: '#5CCF8F', onAccent: '#0C1712', warn: '#E0B84F', sup: '#4FC9D9',
112
+ note: 'Accent green equals “done” green; supports shifts to cyan so it isn’t lost.', bad: true }),
113
+ t({ name: 'Volt', was: 'unchanged', scheme: 'dark', bg: '#0B0D0B', panel: '#171A17', line: '#272A27', text: '#F6FFF5', muted: '#9CA49C', accent: '#DFDFC1', onAccent: '#0B0D0B', warn: '#E4C465',
114
+ note: 'Accent is near-white, so a pin in the accent would barely show.', bad: true })
115
+ ];
116
+ return { themes: themes };
117
+ }
118
+ }
119
+ </script>
120
+ </body>
121
+ </html>