schematex 0.3.2 → 0.3.4
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/README.md +20 -1
- package/dist/ai/ai-sdk.cjs +25 -25
- package/dist/ai/ai-sdk.d.cts +1 -1
- package/dist/ai/ai-sdk.d.ts +1 -1
- package/dist/ai/ai-sdk.js +20 -20
- package/dist/ai/index.cjs +28 -28
- package/dist/ai/index.d.cts +1 -1
- package/dist/ai/index.d.ts +1 -1
- package/dist/ai/index.js +20 -20
- package/dist/browser.cjs +22 -22
- package/dist/browser.js +20 -20
- package/dist/{chunk-UK7JF5QB.cjs → chunk-3MJKJX27.cjs} +37 -38
- package/dist/chunk-3MJKJX27.cjs.map +1 -0
- package/dist/{chunk-HIQPEAL7.js → chunk-3RAVPXLN.js} +4 -4
- package/dist/{chunk-HIQPEAL7.js.map → chunk-3RAVPXLN.js.map} +1 -1
- package/dist/{chunk-LFZZ4NCP.cjs → chunk-3WNW5Y7P.cjs} +48 -8
- package/dist/chunk-3WNW5Y7P.cjs.map +1 -0
- package/dist/{chunk-XUEROLSB.cjs → chunk-4THC4VUA.cjs} +17 -17
- package/dist/chunk-4THC4VUA.cjs.map +1 -0
- package/dist/{chunk-K5QG53GT.cjs → chunk-5COUZTVA.cjs} +27 -27
- package/dist/{chunk-K5QG53GT.cjs.map → chunk-5COUZTVA.cjs.map} +1 -1
- package/dist/{chunk-F6OROIHS.js → chunk-5CTERELC.js} +4 -4
- package/dist/{chunk-F6OROIHS.js.map → chunk-5CTERELC.js.map} +1 -1
- package/dist/{chunk-ZNLEUL7T.js → chunk-5SKT7RYR.js} +5 -5
- package/dist/chunk-5SKT7RYR.js.map +1 -0
- package/dist/{chunk-75BMFCP5.cjs → chunk-5UCXMYE7.cjs} +44 -44
- package/dist/{chunk-75BMFCP5.cjs.map → chunk-5UCXMYE7.cjs.map} +1 -1
- package/dist/{chunk-GYTWJ6VB.cjs → chunk-6ULC3UHJ.cjs} +68 -49
- package/dist/chunk-6ULC3UHJ.cjs.map +1 -0
- package/dist/{chunk-3YXXZ4LT.cjs → chunk-6WORZSL3.cjs} +34 -34
- package/dist/{chunk-3YXXZ4LT.cjs.map → chunk-6WORZSL3.cjs.map} +1 -1
- package/dist/{chunk-6BKUD5EJ.js → chunk-6XGSEG3K.js} +4 -5
- package/dist/chunk-6XGSEG3K.js.map +1 -0
- package/dist/{chunk-OK5RYX55.cjs → chunk-CQP5YHVK.cjs} +26 -27
- package/dist/chunk-CQP5YHVK.cjs.map +1 -0
- package/dist/{chunk-2UJAVPA4.cjs → chunk-CS67UBP3.cjs} +27 -27
- package/dist/{chunk-2UJAVPA4.cjs.map → chunk-CS67UBP3.cjs.map} +1 -1
- package/dist/{chunk-YTEEZV6J.cjs → chunk-DBMZEZLF.cjs} +425 -425
- package/dist/chunk-DBMZEZLF.cjs.map +1 -0
- package/dist/{chunk-NFT6VW73.js → chunk-DHKKDIVT.js} +4 -4
- package/dist/chunk-DHKKDIVT.js.map +1 -0
- package/dist/{chunk-H4CJTKEH.js → chunk-DNZV4V4R.js} +5 -5
- package/dist/{chunk-H4CJTKEH.js.map → chunk-DNZV4V4R.js.map} +1 -1
- package/dist/{chunk-ZTSO3S4P.js → chunk-FO7BLCEW.js} +3 -3
- package/dist/{chunk-ZTSO3S4P.js.map → chunk-FO7BLCEW.js.map} +1 -1
- package/dist/{chunk-EYHD7LV3.cjs → chunk-H5JYZ5YS.cjs} +130 -130
- package/dist/chunk-H5JYZ5YS.cjs.map +1 -0
- package/dist/{chunk-RNGYXGHS.js → chunk-HKJRHXGZ.js} +22 -22
- package/dist/chunk-HKJRHXGZ.js.map +1 -0
- package/dist/{chunk-X4F6VVEJ.js → chunk-I6VEXSUK.js} +3 -4
- package/dist/chunk-I6VEXSUK.js.map +1 -0
- package/dist/{chunk-MKKFIPKU.cjs → chunk-IFR34VTL.cjs} +47 -47
- package/dist/chunk-IFR34VTL.cjs.map +1 -0
- package/dist/{chunk-BJ65PKDU.js → chunk-IURM4ZWE.js} +4 -4
- package/dist/chunk-IURM4ZWE.js.map +1 -0
- package/dist/{chunk-M5ZC3LFJ.js → chunk-IZMAZOQI.js} +4 -4
- package/dist/chunk-IZMAZOQI.js.map +1 -0
- package/dist/{chunk-RODV6PC4.cjs → chunk-J3Y2QDJR.cjs} +7 -7
- package/dist/{chunk-RODV6PC4.cjs.map → chunk-J3Y2QDJR.cjs.map} +1 -1
- package/dist/{chunk-BUN3CRMP.cjs → chunk-K6RAUXRQ.cjs} +65 -66
- package/dist/chunk-K6RAUXRQ.cjs.map +1 -0
- package/dist/{chunk-IT2TVXC7.js → chunk-LPKVIRYT.js} +4 -5
- package/dist/chunk-LPKVIRYT.js.map +1 -0
- package/dist/{chunk-W7GIQTJV.cjs → chunk-MIKCKV27.cjs} +22 -22
- package/dist/chunk-MIKCKV27.cjs.map +1 -0
- package/dist/{chunk-KUXOHLGC.cjs → chunk-O5POCPXZ.cjs} +52 -53
- package/dist/chunk-O5POCPXZ.cjs.map +1 -0
- package/dist/{chunk-BJWMPPEA.js → chunk-PWI4NFYR.js} +4 -4
- package/dist/chunk-PWI4NFYR.js.map +1 -0
- package/dist/{chunk-WHJXRLFD.js → chunk-SYYBKDL7.js} +48 -8
- package/dist/chunk-SYYBKDL7.js.map +1 -0
- package/dist/{chunk-L2KUGWFR.cjs → chunk-T47A6XUK.cjs} +47 -47
- package/dist/chunk-T47A6XUK.cjs.map +1 -0
- package/dist/{chunk-SFSZUOFT.js → chunk-TWAC2IIS.js} +4 -4
- package/dist/chunk-TWAC2IIS.js.map +1 -0
- package/dist/{chunk-XI6JOG76.cjs → chunk-V4OVUBLA.cjs} +46 -46
- package/dist/chunk-V4OVUBLA.cjs.map +1 -0
- package/dist/{chunk-2J2QWNGI.js → chunk-VRE5VWDQ.js} +27 -8
- package/dist/chunk-VRE5VWDQ.js.map +1 -0
- package/dist/{chunk-SE23X5OE.js → chunk-WHNIK4LK.js} +4 -5
- package/dist/chunk-WHNIK4LK.js.map +1 -0
- package/dist/{chunk-C3IVD7DI.cjs → chunk-WIFGBWET.cjs} +25 -26
- package/dist/chunk-WIFGBWET.cjs.map +1 -0
- package/dist/{chunk-7MVDN5UC.cjs → chunk-XSNNGYXZ.cjs} +36 -36
- package/dist/chunk-XSNNGYXZ.cjs.map +1 -0
- package/dist/{chunk-7BEJHG43.js → chunk-YWDODCW2.js} +4 -4
- package/dist/{chunk-7BEJHG43.js.map → chunk-YWDODCW2.js.map} +1 -1
- package/dist/{chunk-M3R6RCXY.js → chunk-ZKDL6Y3O.js} +4 -4
- package/dist/chunk-ZKDL6Y3O.js.map +1 -0
- package/dist/{chunk-56LXBM45.js → chunk-ZQZNWAHH.js} +4 -5
- package/dist/chunk-ZQZNWAHH.js.map +1 -0
- package/dist/diagrams/blockdiagram/index.cjs +6 -6
- package/dist/diagrams/blockdiagram/index.d.cts +1 -1
- package/dist/diagrams/blockdiagram/index.d.ts +1 -1
- package/dist/diagrams/blockdiagram/index.js +2 -2
- package/dist/diagrams/circuit/index.cjs +8 -8
- package/dist/diagrams/circuit/index.d.cts +1 -1
- package/dist/diagrams/circuit/index.d.ts +1 -1
- package/dist/diagrams/circuit/index.js +2 -2
- package/dist/diagrams/ecomap/index.cjs +8 -8
- package/dist/diagrams/ecomap/index.d.cts +1 -1
- package/dist/diagrams/ecomap/index.d.ts +1 -1
- package/dist/diagrams/ecomap/index.js +3 -3
- package/dist/diagrams/entity/index.cjs +6 -6
- package/dist/diagrams/entity/index.d.cts +1 -1
- package/dist/diagrams/entity/index.d.ts +1 -1
- package/dist/diagrams/entity/index.js +2 -2
- package/dist/diagrams/fishbone/index.cjs +8 -8
- package/dist/diagrams/fishbone/index.d.cts +1 -1
- package/dist/diagrams/fishbone/index.d.ts +1 -1
- package/dist/diagrams/fishbone/index.js +2 -2
- package/dist/diagrams/flowchart/index.cjs +8 -8
- package/dist/diagrams/flowchart/index.d.cts +2 -2
- package/dist/diagrams/flowchart/index.d.ts +2 -2
- package/dist/diagrams/flowchart/index.js +2 -2
- package/dist/diagrams/genogram/index.cjs +10 -10
- package/dist/diagrams/genogram/index.d.cts +1 -1
- package/dist/diagrams/genogram/index.d.ts +1 -1
- package/dist/diagrams/genogram/index.js +3 -3
- package/dist/diagrams/ladder/index.cjs +6 -6
- package/dist/diagrams/ladder/index.d.cts +1 -1
- package/dist/diagrams/ladder/index.d.ts +1 -1
- package/dist/diagrams/ladder/index.js +2 -2
- package/dist/diagrams/logic/index.cjs +6 -6
- package/dist/diagrams/logic/index.d.cts +1 -1
- package/dist/diagrams/logic/index.d.ts +1 -1
- package/dist/diagrams/logic/index.js +2 -2
- package/dist/diagrams/orgchart/index.cjs +7 -7
- package/dist/diagrams/orgchart/index.d.cts +1 -1
- package/dist/diagrams/orgchart/index.d.ts +1 -1
- package/dist/diagrams/orgchart/index.js +2 -2
- package/dist/diagrams/pedigree/index.cjs +8 -8
- package/dist/diagrams/pedigree/index.d.cts +1 -1
- package/dist/diagrams/pedigree/index.d.ts +1 -1
- package/dist/diagrams/pedigree/index.js +3 -3
- package/dist/diagrams/phylo/index.cjs +7 -7
- package/dist/diagrams/phylo/index.d.cts +1 -1
- package/dist/diagrams/phylo/index.d.ts +1 -1
- package/dist/diagrams/phylo/index.js +2 -2
- package/dist/diagrams/sld/index.cjs +6 -6
- package/dist/diagrams/sld/index.d.cts +1 -1
- package/dist/diagrams/sld/index.d.ts +1 -1
- package/dist/diagrams/sld/index.js +2 -2
- package/dist/diagrams/sociogram/index.cjs +7 -7
- package/dist/diagrams/sociogram/index.d.cts +1 -1
- package/dist/diagrams/sociogram/index.d.ts +1 -1
- package/dist/diagrams/sociogram/index.js +3 -3
- package/dist/diagrams/timing/index.cjs +5 -5
- package/dist/diagrams/timing/index.d.cts +1 -1
- package/dist/diagrams/timing/index.d.ts +1 -1
- package/dist/diagrams/timing/index.js +2 -2
- package/dist/diagrams/venn/index.cjs +9 -9
- package/dist/diagrams/venn/index.d.cts +1 -1
- package/dist/diagrams/venn/index.d.ts +1 -1
- package/dist/diagrams/venn/index.js +2 -2
- package/dist/export.cjs +1 -1
- package/dist/export.cjs.map +1 -1
- package/dist/export.d.cts +1 -1
- package/dist/export.d.ts +1 -1
- package/dist/export.js +1 -1
- package/dist/export.js.map +1 -1
- package/dist/{index-dWDwG6BW.d.ts → index-D3u6vcA4.d.ts} +1 -1
- package/dist/{index-BrLxEzSQ.d.cts → index-bdfj6FpQ.d.cts} +1 -1
- package/dist/index.cjs +41 -41
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +19 -19
- package/dist/react.cjs +20 -20
- package/dist/react.js +19 -19
- package/dist/{types-BtiUg7Gx.d.cts → types-DnU2UlWz.d.cts} +2 -0
- package/dist/{types-BtiUg7Gx.d.ts → types-DnU2UlWz.d.ts} +2 -0
- package/package.json +1 -1
- package/dist/chunk-2J2QWNGI.js.map +0 -1
- package/dist/chunk-56LXBM45.js.map +0 -1
- package/dist/chunk-6BKUD5EJ.js.map +0 -1
- package/dist/chunk-7MVDN5UC.cjs.map +0 -1
- package/dist/chunk-BJ65PKDU.js.map +0 -1
- package/dist/chunk-BJWMPPEA.js.map +0 -1
- package/dist/chunk-BUN3CRMP.cjs.map +0 -1
- package/dist/chunk-C3IVD7DI.cjs.map +0 -1
- package/dist/chunk-EYHD7LV3.cjs.map +0 -1
- package/dist/chunk-GYTWJ6VB.cjs.map +0 -1
- package/dist/chunk-IT2TVXC7.js.map +0 -1
- package/dist/chunk-KUXOHLGC.cjs.map +0 -1
- package/dist/chunk-L2KUGWFR.cjs.map +0 -1
- package/dist/chunk-LFZZ4NCP.cjs.map +0 -1
- package/dist/chunk-M3R6RCXY.js.map +0 -1
- package/dist/chunk-M5ZC3LFJ.js.map +0 -1
- package/dist/chunk-MKKFIPKU.cjs.map +0 -1
- package/dist/chunk-NFT6VW73.js.map +0 -1
- package/dist/chunk-OK5RYX55.cjs.map +0 -1
- package/dist/chunk-RNGYXGHS.js.map +0 -1
- package/dist/chunk-SE23X5OE.js.map +0 -1
- package/dist/chunk-SFSZUOFT.js.map +0 -1
- package/dist/chunk-UK7JF5QB.cjs.map +0 -1
- package/dist/chunk-W7GIQTJV.cjs.map +0 -1
- package/dist/chunk-WHJXRLFD.js.map +0 -1
- package/dist/chunk-X4F6VVEJ.js.map +0 -1
- package/dist/chunk-XI6JOG76.cjs.map +0 -1
- package/dist/chunk-XUEROLSB.cjs.map +0 -1
- package/dist/chunk-YTEEZV6J.cjs.map +0 -1
- package/dist/chunk-ZNLEUL7T.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ai/registry.ts","../src/ai/errors.ts","../src/ai/_generated.ts","../src/ai/examples.ts","../src/ai/syntax.ts","../src/ai/tools.ts"],"names":["parse","render"],"mappings":"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","file":"chunk-RODV6PC4.cjs","sourcesContent":["/**\n * Diagram registry — metadata for every diagram type Schematex supports.\n *\n * This is the index an LLM sees when calling `listDiagrams()`. Descriptions\n * are tuned to help the model pick the right type for a user request.\n */\n\nimport type { DiagramType } from \"../core/types\";\n\nexport type DiagramCluster =\n | \"relationships\"\n | \"electrical-industrial\"\n | \"corporate-legal\"\n | \"causality-analysis\"\n | \"generic\"\n | \"strategy\"\n | \"knowledge\"\n | \"behavior-modeling\";\n\nexport interface DiagramMeta {\n /** Canonical type id — matches `DiagramType` and plugin keys. */\n type: DiagramType;\n /** Human-readable name. */\n name: string;\n /** One-sentence tagline. */\n tagline: string;\n /** When should an LLM pick this diagram? Written in \"use X when …\" form. */\n useWhen: string;\n /** Domain cluster for grouping. */\n cluster: DiagramCluster;\n /** Published standard the parser and layout follow. */\n standard: string;\n /** Path to the syntax doc key in the generated content bundle. */\n syntaxKey: string;\n}\n\nexport const DIAGRAM_REGISTRY: readonly DiagramMeta[] = [\n // ── Relationships ────────────────────────────────────────────\n {\n type: \"genogram\",\n name: \"Genogram\",\n tagline: \"Family diagram with emotional, medical, and generational notation.\",\n useWhen:\n \"Use for family therapy, social-work case notes, or medical family history. Handles 3+ generations with deaths, cutoffs, hostility, closeness, and the proband marker.\",\n cluster: \"relationships\",\n standard: \"McGoldrick, Gerson & Petry (2020) + GenoPro emotional taxonomy\",\n syntaxKey: \"genogram\",\n },\n {\n type: \"ecomap\",\n name: \"Ecomap\",\n tagline: \"Radial diagram of a client's connections to external systems.\",\n useWhen:\n \"Use for social work intake to visualise a client's support network — school, work, healthcare, faith, extended family — with strong/weak/stressful tie variants.\",\n cluster: \"relationships\",\n standard: \"Hartman (1978) + NSGC\",\n syntaxKey: \"ecomap\",\n },\n {\n type: \"pedigree\",\n name: \"Pedigree chart\",\n tagline: \"Clinical genetic-counselling pedigree with affected/carrier states.\",\n useWhen:\n \"Use for genetic counselling or medical genetics education — mendelian inheritance, carrier status, consanguinity, deceased generations. Follows NSGC conventions.\",\n cluster: \"relationships\",\n standard: \"NSGC Pedigree Standardization (Bennett 2008)\",\n syntaxKey: \"pedigree\",\n },\n {\n type: \"phylo\",\n name: \"Phylogenetic tree\",\n tagline: \"Rectangular cladogram from a Newick/NHX tree string.\",\n useWhen:\n \"Use for evolutionary biology, taxonomy, or species relationships. Accepts standard Newick input with optional branch lengths and clade highlighting.\",\n cluster: \"relationships\",\n standard: \"Newick + NHX extensions\",\n syntaxKey: \"phylo\",\n },\n {\n type: \"sociogram\",\n name: \"Sociogram\",\n tagline: \"Force-directed social network graph with edge types and weights.\",\n useWhen:\n \"Use for classroom sociometry, team influence mapping, or organisational network analysis. Edges can be directed, weighted, positive, negative, or reciprocal.\",\n cluster: \"relationships\",\n standard: \"Moreno (1934) sociometry\",\n syntaxKey: \"sociogram\",\n },\n // ── Electrical & Industrial ──────────────────────────────────\n {\n type: \"timing\",\n name: \"Timing / waveform diagram\",\n tagline: \"Digital signal timing diagram with clocks, buses, and annotations.\",\n useWhen:\n \"Use for digital-logic or bus-protocol documentation (SPI/I²C/AXI). Supports clock, data, bus, and gap signals with transition annotations.\",\n cluster: \"electrical-industrial\",\n standard: \"WaveDrom-compatible signal description\",\n syntaxKey: \"timing\",\n },\n {\n type: \"logic\",\n name: \"Logic gate netlist\",\n tagline: \"IEEE 91 logic-gate diagram from a gate-list DSL.\",\n useWhen:\n \"Use for combinational / sequential logic design — AND/OR/XOR/NAND/NOR/NOT/MUX/latches. Auto-routes via DAG topological sort.\",\n cluster: \"electrical-industrial\",\n standard: \"IEEE Std 91/91a-1991\",\n syntaxKey: \"logic\",\n },\n {\n type: \"circuit\",\n name: \"Circuit schematic\",\n tagline: \"Positional circuit schematic with resistors, sources, transistors.\",\n useWhen:\n \"Use for analogue/mixed-signal schematics — voltage/current sources, passives, diodes, BJT/MOSFET, op-amps. Uses an explicit positional DSL, not auto-layout.\",\n cluster: \"electrical-industrial\",\n standard: \"IEEE Std 315 / ANSI Y32.2\",\n syntaxKey: \"circuit\",\n },\n {\n type: \"blockdiagram\",\n name: \"Control-systems block diagram\",\n tagline: \"Transfer-function block diagram with summing junctions and feedback.\",\n useWhen:\n \"Use for classical control theory — plants, controllers, sensors, summing junctions, feedback loops. Nested feedback supported.\",\n cluster: \"electrical-industrial\",\n standard: \"Ogata / standard controls textbook convention\",\n syntaxKey: \"block\",\n },\n {\n type: \"ladder\",\n name: \"Ladder logic\",\n tagline: \"IEC 61131-3 ladder-logic program with rungs, contacts, coils.\",\n useWhen:\n \"Use for PLC / industrial-automation programs — normally-open/closed contacts, output coils, timers, counters. Renders with fixed power-rail layout.\",\n cluster: \"electrical-industrial\",\n standard: \"IEC 61131-3 Ladder Diagram\",\n syntaxKey: \"ladder\",\n },\n {\n type: \"sld\",\n name: \"Single-line diagram\",\n tagline: \"Electrical power distribution single-line (one-line) diagram.\",\n useWhen:\n \"Use for facility / industrial / utility power systems — utility, generator, transformer, ATS, bus, breaker, load. Top-to-bottom power flow.\",\n cluster: \"electrical-industrial\",\n standard: \"IEEE Std 315 + ANSI device numbering\",\n syntaxKey: \"sld\",\n },\n {\n type: \"pid\",\n name: \"P&ID (Piping & Instrumentation)\",\n tagline: \"ISA-5.1 process equipment, valves, and instrument bubbles.\",\n useWhen:\n \"Use for chemical / petrochemical / pharmaceutical / water-treatment process diagrams — vessels, columns, heat exchangers, pumps, valves, and instrument loops with ISA tag codes (FT/FIC/PT/etc.). Equipment + piping + instrumentation in one diagram.\",\n cluster: \"electrical-industrial\",\n standard: \"ANSI/ISA-5.1-2009 + ISO 10628-1:2014\",\n syntaxKey: \"pid\",\n },\n // ── Corporate / Legal ────────────────────────────────────────\n {\n type: \"entity\",\n name: \"Entity structure\",\n tagline: \"Corporate ownership hierarchy with percentage rollup.\",\n useWhen:\n \"Use for legal entity structures, holdco/opco charts, international tax charts, Series-A cap-table snapshots. Tiered layout with ownership percentages.\",\n cluster: \"corporate-legal\",\n standard: \"Tier-based ownership hierarchy\",\n syntaxKey: \"entity\",\n },\n // ── Causality / Analysis ─────────────────────────────────────\n {\n type: \"fishbone\",\n name: \"Fishbone (Ishikawa)\",\n tagline: \"Ishikawa cause-and-effect diagram with categorised root causes.\",\n useWhen:\n \"Use for root-cause analysis, post-mortems, quality investigations. Categories branch off the spine; each cause is a bone.\",\n cluster: \"causality-analysis\",\n standard: \"Ishikawa (1968) cause-and-effect\",\n syntaxKey: \"fishbone\",\n },\n {\n type: \"venn\",\n name: \"Venn / Euler\",\n tagline: \"Set-theoretic Venn / Euler diagram with 2, 3, or 4 sets.\",\n useWhen:\n \"Use to visualise set overlaps, commonalities, or category intersections. Supports 2/3-set Venn and Euler (non-overlapping) arrangements.\",\n cluster: \"causality-analysis\",\n standard: \"Venn (1880) / Euler diagrams\",\n syntaxKey: \"venn\",\n },\n {\n type: \"decisiontree\",\n name: \"Decision tree\",\n tagline: \"Decision/classification tree with splits, probabilities, leaves.\",\n useWhen:\n \"Use for decision analysis (Howard-Raiffa EV rollback), ML decision trees, or taxonomy classification. Binary or multi-way splits.\",\n cluster: \"causality-analysis\",\n standard: \"Howard-Raiffa / CART-sklearn / taxonomy\",\n syntaxKey: \"decisiontree\",\n },\n // ── Behavior modeling ────────────────────────────────────────\n {\n type: \"state\",\n name: \"State diagram\",\n tagline: \"UML 2.5 / Harel statechart with composite states and pseudo-states.\",\n useWhen:\n \"Use for modeling reactive system behavior — finite state machines, lifecycle states, controller modes, UI workflows. Supports simple states, composite (nested) states, fork/join, choice, history, and full Mermaid `stateDiagram-v2` syntax.\",\n cluster: \"behavior-modeling\",\n standard: \"OMG UML 2.5.1 §14 + Harel (1987) statechart\",\n syntaxKey: \"state\",\n },\n // ── Generic process / flow ───────────────────────────────────\n {\n type: \"flowchart\",\n name: \"Flowchart\",\n tagline: \"Generic flowchart with start/end/decision/process nodes.\",\n useWhen:\n \"Use for process flows, decision flows, or algorithms when no more specific diagram fits. Sugiyama layered layout with orthogonal routing.\",\n cluster: \"generic\",\n standard: \"Sugiyama layered DAG + orthogonal routing\",\n syntaxKey: \"flowchart\",\n },\n // ── Strategy / analysis ──────────────────────────────────────\n {\n type: \"matrix\",\n name: \"Matrix / quadrant\",\n tagline: \"2×2 / 3×3 / N×M matrix diagrams (Eisenhower, BCG, heatmap).\",\n useWhen:\n \"Use for prioritisation (Eisenhower urgent/important), portfolio (BCG growth/share), or any 2-axis categorisation.\",\n cluster: \"strategy\",\n standard: \"2×2 / N×M quadrant convention\",\n syntaxKey: \"matrix\",\n },\n {\n type: \"orgchart\",\n name: \"Organisation chart\",\n tagline: \"Corporate or team reporting-line hierarchy.\",\n useWhen:\n \"Use for reporting lines, team structure, or organisational design. Tidy-tree layout (not to be confused with legal `entity` ownership).\",\n cluster: \"corporate-legal\",\n standard: \"Reingold-Tilford tidy tree\",\n syntaxKey: \"orgchart\",\n },\n // ── Knowledge / brainstorming ────────────────────────────────\n {\n type: \"mindmap\",\n name: \"Mindmap\",\n tagline: \"Radial or markmap-style mindmap from markdown headings.\",\n useWhen:\n \"Use for brainstorming, note structures, concept maps, or outline visualisation. Accepts markdown-headings input.\",\n cluster: \"knowledge\",\n standard: \"Buzan radial + markmap-compat tree\",\n syntaxKey: \"mindmap\",\n },\n {\n type: \"timeline\",\n name: \"Timeline\",\n tagline: \"Horizontal or vertical timeline with events, eras, milestones.\",\n useWhen:\n \"Use for historical sequences, project milestones, product roadmaps. Horizontal or vertical orientation.\",\n cluster: \"generic\",\n standard: \"Timeline convention with era bands\",\n syntaxKey: \"timeline\",\n },\n] as const;\n\nexport function getDiagramMeta(type: string): DiagramMeta | undefined {\n return DIAGRAM_REGISTRY.find((d) => d.type === type);\n}\n\nexport function getAllDiagramTypes(): DiagramType[] {\n return DIAGRAM_REGISTRY.map((d) => d.type);\n}\n","/**\n * Structured error type returned by the AI tool layer.\n *\n * The underlying per-diagram parsers each throw their own error class\n * (genogram.ParseError, SLDParseError, PedigreeParseError, ...). Some\n * carry line/column, some don't. {@link extractError} normalises any\n * thrown value into this shape via structural extraction — no parser\n * refactor required.\n */\nexport interface SchematexValidationError {\n /** 1-based line number where the error occurred, if the parser reported it. */\n line?: number;\n /** 1-based column, if reported. */\n column?: number;\n /** Source snippet from the offending line, if the parser captured it. */\n source?: string;\n /** Human-readable error message. */\n message: string;\n /** Optional remediation hint. */\n hint?: string;\n}\n\n/**\n * Extract a {@link SchematexValidationError} from any thrown value.\n *\n * Works across all per-diagram parser error classes because it reads\n * `.line`, `.column`, `.source` structurally when present. Unknown\n * throws (non-Error values) are coerced to a message-only error.\n */\nexport function extractError(err: unknown): SchematexValidationError {\n if (err instanceof Error) {\n const anyErr = err as Error & {\n line?: number;\n column?: number;\n source?: string;\n hint?: string;\n };\n return {\n line: typeof anyErr.line === \"number\" ? anyErr.line : undefined,\n column: typeof anyErr.column === \"number\" ? anyErr.column : undefined,\n source: typeof anyErr.source === \"string\" ? anyErr.source : undefined,\n message: err.message,\n hint: typeof anyErr.hint === \"string\" ? anyErr.hint : undefined,\n };\n }\n return { message: String(err) };\n}\n","/**\n * AUTO-GENERATED by scripts/build-ai-content.mjs — do not edit by hand.\n * Regenerate with: npm run build:ai\n *\n * Compiled content bundle for the AI tool layer. Keeps MDX content\n * available to the published npm package without runtime fs access.\n */\n\nexport interface GeneratedExample {\n slug: string;\n diagram: string;\n title: string;\n description: string;\n standard: string;\n tags: readonly string[];\n complexity: number;\n featured: boolean;\n dsl: string;\n notes: string;\n}\n\nexport interface GeneratedSyntax {\n title: string;\n content: string;\n}\n\nexport const EXAMPLES: readonly GeneratedExample[] = [\n {\n \"slug\": \"block-pid-loop\",\n \"diagram\": \"block\",\n \"title\": \"PID control loop\",\n \"description\": \"Closed-loop PID block diagram with summing junction, controller, and plant — rendered from a signal-flow description without manual layout.\",\n \"standard\": \"Ogata control systems\",\n \"tags\": [\n \"PID\",\n \"closed-loop\",\n \"feedback\",\n \"summing-junction\",\n \"signal-flow\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"blockdiagram \\\"PID control loop\\\"\\nC = block(\\\"PID C(s)\\\") [role: controller]\\nG = block(\\\"Plant G(s)\\\") [role: plant]\\nerr = sum(+r, -y)\\nr = signal(\\\"r (setpoint)\\\")\\ny = signal(\\\"y (output)\\\")\\nin -> r\\nr -> err\\nerr -> C\\nC -> G\\nG -> y\\nG -> err\",\n \"notes\": \"## Scenario\\n\\nThe standard closed-loop PID block diagram appears in every control systems textbook (Ogata, Franklin, Åström) and every control system design spec sheet. Schematex renders it from a signal-flow description — not a generic flowchart — using proper summing junction symbols and automatic feedback routing.\\n\\n## Annotation key\\n\\n- `block(\\\"label\\\") [role: ...]` — transfer function block; `role: controller` and `role: plant` affect visual styling\\n- `sum(+r, -y)` — summing junction: adds the `+r` (reference) signal and subtracts the `-y` (output feedback)\\n- `signal(\\\"label\\\")` — named signal node\\n- `G -> err` — the feedback path: plant output `y` routes back to the summing junction\\n\\n## How to read\\n\\nThe setpoint `r` enters the summing junction `err`, which subtracts the plant output `y` to compute the error signal. The PID controller `C(s)` processes the error and drives the plant `G(s)`. The plant output `y` is both the system output and the feedback signal. The loop is closed when `G -> err` feeds `y` back to the summing junction.\"\n },\n {\n \"slug\": \"circuit-ce-amplifier\",\n \"diagram\": \"circuit\",\n \"title\": \"NPN common-emitter amplifier\",\n \"description\": \"NPN common-emitter amplifier rendered from a five-line SPICE netlist with automatic component placement per IEEE 315.\",\n \"standard\": \"IEEE 315\",\n \"tags\": [\n \"SPICE\",\n \"netlist\",\n \"BJT\",\n \"amplifier\",\n \"auto-layout\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"circuit \\\"CE Amp (netlist)\\\" netlist\\nV1 vcc 0 9V\\nRc vcc c 2.2k\\nRb vcc b 100k\\nQ1 c b e npn\\nRe e 0 1k\",\n \"notes\": \"## Scenario\\n\\nThe NPN common-emitter amplifier is the first transistor circuit every electronics student builds. Schematex renders it from a five-line SPICE netlist — the same format used in LTspice, ngspice, and Cadence — with automatic component placement and rail routing, so the diagram matches the hand-drawn textbook version without any manual layout.\\n\\n## Annotation key\\n\\n- `circuit \\\"...\\\" netlist` — enables netlist parsing mode (SPICE syntax)\\n- `V1 vcc 0 9V` — voltage source named V1, positive terminal at node `vcc`, negative at `0` (ground), value 9V\\n- `Rc vcc c 2.2k` — resistor Rc between nodes `vcc` and `c` with value 2.2 kΩ\\n- `Rb vcc b 100k` — base bias resistor between `vcc` and node `b`\\n- `Q1 c b e npn` — NPN BJT transistor: collector=c, base=b, emitter=e\\n- `Re e 0 1k` — emitter degeneration resistor between node `e` and ground\\n\\n## How to read\\n\\nThe supply rail (Vcc = 9 V) connects to the top of both resistors. Rc is the collector load; the output signal is taken across it. Rb biases the base into the active region. Re provides emitter degeneration for stability. Q1 amplifies a small base current into a large collector-to-emitter current flow. The auto-layout positions Vcc at top, ground at bottom, and Q1 in the center.\"\n },\n {\n \"slug\": \"decisiontree-investment-analysis\",\n \"diagram\": \"decisiontree\",\n \"title\": \"Investment decision analysis\",\n \"description\": \"Decision-analysis tree evaluating build-vs-buy vs hybrid for a platform choice — chance nodes with probabilities, automatic expected-value rollback.\",\n \"standard\": \"Raiffa & Schlaifer (1961)\",\n \"tags\": [\n \"decisiontree\",\n \"decision-analysis\",\n \"expected-value\",\n \"investment\",\n \"buildvbuy\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"decisiontree:decision \\\"Platform Vendor Choice\\\"\\n\\ndecision \\\"Which vendor?\\\"\\n choice \\\"Build in-house\\\"\\n chance \\\"Project outcome\\\"\\n prob 0.6 end \\\"On-time delivery\\\" payoff=900000\\n prob 0.4 end \\\"Over budget / delayed\\\" payoff=150000\\n choice \\\"Managed SaaS vendor\\\"\\n end \\\"Predictable cost\\\" payoff=500000\\n choice \\\"Hybrid approach\\\"\\n chance \\\"Integration complexity\\\"\\n prob 0.5 end \\\"Smooth integration\\\" payoff=700000\\n prob 0.5 end \\\"Integration rework\\\" payoff=300000\",\n \"notes\": \"## Scenario\\n\\nA CTO is deciding how to stand up a new data-platform layer: build internally, buy managed SaaS, or take a hybrid. Each path has outcomes with probabilities and net-value estimates. The decision-analysis tree rolls the expected value back to the decision node so the optimal branch is identified automatically.\\n\\n## Annotation key\\n\\n- `decision \\\"…\\\"` — actor chooses a branch\\n- `chance \\\"…\\\"` — nature chooses; `prob N` on each child (must sum to 1)\\n- `end \\\"…\\\" payoff=N` — terminal payoff\\n- `choice \\\"label\\\"` — label on an outgoing decision branch\\n- EV rollback: chance = probability-weighted sum; decision = max child EV\\n\\n## How to read\\n\\nEvaluate each branch's expected value. Build in-house: 0.6 × 900k + 0.4 × 150k = **600k**. Managed SaaS: flat **500k**. Hybrid: 0.5 × 700k + 0.5 × 300k = **500k**. Under these estimates the optimal branch is *Build in-house* — the parser flags it on render. The chart's real value is in forcing stakeholders to state probabilities explicitly; sensitivity to them is where the interesting argument happens.\"\n },\n {\n \"slug\": \"decisiontree-support-triage\",\n \"diagram\": \"decisiontree\",\n \"title\": \"Support ticket triage\",\n \"description\": \"Taxonomy-mode decision tree for front-line support — routes an incoming ticket through outage, billing, and reproducibility gates to the right team.\",\n \"standard\": \"Clinical/support decision flow\",\n \"tags\": [\n \"decisiontree\",\n \"support\",\n \"triage\",\n \"routing\",\n \"runbook\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"decisiontree \\\"Customer Support Triage\\\"\\ndirection: top-down\\n\\nquestion \\\"Is the service completely down?\\\"\\n yes: question \\\"Outage confirmed on status page?\\\"\\n yes: answer \\\"Follow incident protocol — page on-call\\\"\\n no: answer \\\"Check monitoring — open severity-1 ticket\\\"\\n no: question \\\"Is the issue affecting billing?\\\"\\n yes: answer \\\"Escalate to billing team — SLA breach risk\\\"\\n no: question \\\"Can user reproduce consistently?\\\"\\n yes: answer \\\"Collect HAR trace — file bug report\\\"\\n no: answer \\\"Ask for screenshot — watch for recurrence\\\"\",\n \"notes\": \"## Scenario\\n\\nThe support lead bakes this tree into the agent runbook so new hires can triage in week one without a buddy. Three gates — outage, billing, reproducibility — route 90% of tickets deterministically. The remaining 10% (edge cases, multi-category) get flagged for human escalation instead of guessed at.\\n\\n## Annotation key\\n\\n- `question \\\"text\\\"` — internal decision node\\n- `answer \\\"text\\\"` — terminal outcome\\n- `yes:` / `no:` — branch label\\n- Indentation (2 spaces) — parent-child nesting\\n\\n## How to read\\n\\nStart at the root. At each `question` node the agent answers yes or no and follows the matching branch. Every path terminates in an `answer` — a concrete next action, not another decision. The tree is deliberately shallow (max depth 3) so agents can hold it in their head; deeper branches would push into a runbook rather than a decision tree.\"\n },\n {\n \"slug\": \"ecomap-refugee-resettlement\",\n \"diagram\": \"ecomap\",\n \"title\": \"Refugee family resettlement\",\n \"description\": \"Ecomap of a refugee family's support network — IRC office, school, clinic, and sponsor family — categorized by resource type per Hartman 1978.\",\n \"standard\": \"Hartman 1978\",\n \"tags\": [\n \"resettlement\",\n \"cultural\",\n \"category\",\n \"arrow-direction\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"ecomap \\\"Nguyen Family Resettlement\\\"\\n center: family [label: \\\"Nguyen Family\\\"]\\n resettlement [label: \\\"IRC Office\\\", category: government]\\n school [label: \\\"Lincoln Elementary\\\", category: education]\\n esl [label: \\\"Adult ESL Class\\\", category: education]\\n clinic [label: \\\"Community Clinic\\\", category: health]\\n caseworker [label: \\\"Ms. Patel\\\", category: mental-health]\\n temple [label: \\\"Vietnamese Temple\\\", category: cultural]\\n neighbors [label: \\\"Sponsor Family\\\", category: community]\\n employer [label: \\\"Warehouse Job\\\", category: work]\\n cousins [label: \\\"Cousins (CA)\\\", category: family]\\n family === resettlement [label: \\\"active case\\\"]\\n family === school\\n family --- esl [label: \\\"twice weekly\\\"]\\n clinic --> family [label: \\\"vaccinations\\\"]\\n caseworker <-> family [label: \\\"weekly\\\"]\\n family === temple [label: \\\"anchor\\\"]\\n neighbors === family [label: \\\"housing host\\\"]\\n family --- employer [label: \\\"new, part-time\\\"]\\n cousins == family [label: \\\"phone support\\\"]\",\n \"notes\": \"## Scenario\\n\\nA resettlement caseworker at the IRC maps the Nguyen family's ecological support network during an initial home visit. The diagram surfaces which systems are strong anchors (temple, sponsor family) and which are fragile (new job, ESL class), guiding case prioritization and resource allocation.\\n\\n## Annotation key\\n\\n- `center: family [...]` — designates the central node (the family unit)\\n- `category: government / education / health / cultural / community / work / family` — color-codes each system node by domain\\n- `===` — strong, supportive tie; `==` — moderately strong; `---` — tenuous\\n- `-->` — unidirectional support (clinic delivers vaccinations to family)\\n- `<->` — reciprocal relationship (caseworker and family both invest in the relationship)\\n\\n## How to read\\n\\nThe family sits at the center with nine surrounding systems. The IRC and temple are the two strongest connections (===). The employer and ESL class are tenuous (---), representing early-stage relationships. The cousins in California provide moderate phone support (==) but no in-person resources. The caseworker's bidirectional arrow marks the primary professional support relationship.\"\n },\n {\n \"slug\": \"ecomap-substance-recovery\",\n \"diagram\": \"ecomap\",\n \"title\": \"Substance abuse recovery\",\n \"description\": \"Ecomap charting a client's recovery support network — AA group, family, probation, and therapist — with relationship strength and directional connections.\",\n \"standard\": \"Hartman 1978\",\n \"tags\": [\n \"recovery\",\n \"relationships\",\n \"line-types\",\n \"arrow-direction\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"ecomap \\\"Substance Abuse Recovery\\\"\\n center: client [male, age: 28, label: \\\"James\\\"]\\n aa [label: \\\"AA Group\\\", category: substance, importance: major]\\n sponsor [label: \\\"Bill (Sponsor)\\\", category: substance]\\n employer [label: \\\"Warehouse Job\\\", category: work]\\n mother [label: \\\"Mom\\\", category: family]\\n exwife [label: \\\"Ex-wife\\\", category: family]\\n kids [label: \\\"Children (2)\\\", category: family]\\n dealer [label: \\\"Old Friends\\\", category: substance]\\n probation [label: \\\"P.O. Johnson\\\", category: legal]\\n therapist [label: \\\"CBT Therapist\\\", category: mental-health]\\n client === aa\\n sponsor --> client\\n client --- employer [label: \\\"new, probationary\\\"]\\n client == mother [label: \\\"supportive\\\"]\\n client ~~~ exwife [label: \\\"custody conflict\\\"]\\n client - - kids [label: \\\"supervised visits\\\"]\\n client -/- dealer [label: \\\"trying to cut off\\\"]\\n probation --> client\\n therapist <-> client [label: \\\"weekly\\\"]\",\n \"notes\": \"## Scenario\\n\\nA case manager at a substance abuse recovery center creates this ecomap during an initial biopsychosocial assessment. The diagram externalises the client's full ecological field — every system pressing on his recovery — and immediately surfaces the imbalance: most energy flows inward (legal supervision, sponsor), almost nothing flows outward. The visual becomes the conversation starter for the treatment plan.\\n\\n## Annotation key\\n\\n- `===` — strong, supportive connection\\n- `==` — moderately strong connection\\n- `---` — tenuous or fragile connection\\n- `~~~` — stressful, conflicted connection\\n- `-/-` — severing or cut-off relationship\\n- `- - ` — currently restricted (supervised visits shown with spaces)\\n- `-->` — one-way energy or support flow (giver → receiver)\\n- `<->` — reciprocal, mutual relationship\\n- `category: mental-health / family / legal / work / substance` — colors the node by domain\\n- `importance: major` — increases node prominence in the layout\\n\\n## How to read\\n\\nThe client sits at the center. Arrow directions show who gives energy to whom: the probation officer and sponsor both point *toward* James (external pressure / support), while James gives energy *toward* the AA group. The therapist is the only `<->` reciprocal relationship — the treatment alliance. The `-/-` (severing) line to the dealer marks the highest-risk edge in early recovery. Supervised visits (`- -`) with the children shows the family system is strained but not severed.\"\n },\n {\n \"slug\": \"ecomap-teen-client\",\n \"diagram\": \"ecomap\",\n \"title\": \"Teen client intake\",\n \"description\": \"Quick intake ecomap for a 15-year-old showing family, school, soccer peers, and therapist — drawn in under five minutes during a counseling session.\",\n \"standard\": \"Hartman 1978\",\n \"tags\": [\n \"adolescent\",\n \"intake\",\n \"minimal\",\n \"school\",\n \"peers\"\n ],\n \"complexity\": 1,\n \"featured\": false,\n \"dsl\": \"ecomap \\\"Marcus, age 15\\\"\\n center: client [label: \\\"Marcus\\\"]\\n mom [label: \\\"Mother\\\", category: family]\\n dad [label: \\\"Father (divorced)\\\", category: family]\\n school [label: \\\"East High School\\\", category: education]\\n coach [label: \\\"Soccer Coach\\\", category: community]\\n peers [label: \\\"Soccer team\\\", category: community]\\n therapist [label: \\\"Ms. Chen\\\", category: mental-health]\\n mom === client [label: \\\"primary caregiver\\\"]\\n dad --- client [label: \\\"EOW weekends\\\"]\\n school === client\\n coach --> client [label: \\\"mentor\\\"]\\n peers === client\\n therapist <-> client [label: \\\"weekly\\\"]\",\n \"notes\": \"## Scenario\\n\\nA school-based counselor draws this ecomap during an initial intake session with Marcus, a 15-year-old referred for behavioral issues. The diagram takes under five minutes and immediately shows where Marcus's support is concentrated (peers, soccer team) and where it's fragile (divorced father, every-other-weekend contact).\\n\\n## Annotation key\\n\\n- `center: client` — the identified young person\\n- `===` — strong connection; `---` — tenuous connection\\n- `-->` — mentor/support flows toward client\\n- `<->` — reciprocal therapeutic alliance\\n- `EOW weekends` — edge label capturing the visitation schedule\\n\\n## How to read\\n\\nMarcus's strongest systems are his mother (primary caregiver), school, and soccer peers — all strong ties (===). His father is a tenuous connection (---). The therapist and soccer coach have supportive directional relationships pointing toward Marcus. The soccer team appears to be the central protective factor in this adolescent's life.\"\n },\n {\n \"slug\": \"entity-holding-company\",\n \"diagram\": \"entity\",\n \"title\": \"Multi-jurisdiction holding company\",\n \"description\": \"Multi-jurisdiction holding structure with a Delaware corp, UK subsidiary, and Cayman growth fund — the first document in any M&A due diligence package.\",\n \"standard\": \"Tier ownership\",\n \"tags\": [\n \"holding\",\n \"multi-jurisdiction\",\n \"trust\",\n \"fund\",\n \"Delaware\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"entity-structure \\\"Acme Holdings\\\"\\nentity trust_a \\\"Founder Trust\\\" trust@SD\\nentity acme_inc \\\"Acme Inc.\\\" corp@DE\\nentity acme_uk \\\"Acme UK Ltd.\\\" llc@UK\\nentity acme_fund \\\"Acme Growth Fund LP\\\" fund@KY\\ntrust_a -> acme_inc : 100%\\nacme_inc -> acme_uk : 100%\\nacme_inc -> acme_fund : 60%\",\n \"notes\": \"## Scenario\\n\\nAn M&A attorney or corporate counsel draws this structure for a client engagement letter, board minutes, or due diligence data room. The entity diagram is the first document requested in any M&A transaction, debt financing, or regulatory review — it maps the corporate family tree with jurisdiction and ownership percentages.\\n\\n## Annotation key\\n\\n- `entity id \\\"Display Name\\\" type@jurisdiction` — creates an entity node\\n- `corp` — C-corporation or equivalent; `llc` — limited liability company; `trust` — trust entity; `fund` — investment fund/LP\\n- `@SD / @DE / @UK / @KY` — state or country jurisdiction\\n- `->` — equity ownership relationship\\n- `: 100% / 60%` — percentage label on the ownership line\\n\\n## How to read\\n\\nThe South Dakota founder trust owns 100% of the Delaware C-corp (the operating parent). The Delaware entity owns 100% of the UK subsidiary and 60% of the Cayman Islands fund (implying 40% external LP interest). This is a typical private equity or founder-led holding structure for multi-market operations.\"\n },\n {\n \"slug\": \"entity-international-tax\",\n \"diagram\": \"entity\",\n \"title\": \"International tax holding structure\",\n \"description\": \"Cross-border tax holding structure with Irish IP company, Dutch distribution, and APAC entity — per OECD BEPS transfer-pricing documentation requirements.\",\n \"standard\": \"Tier ownership\",\n \"tags\": [\n \"cross-border\",\n \"IP-license\",\n \"royalty\",\n \"BEPS\",\n \"holding\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"entity-structure \\\"Acme Global Holdings\\\"\\nentity parent \\\"Acme Global, Inc.\\\" corp@US [note: \\\"Ultimate Parent\\\"]\\nentity ie-holdco \\\"Acme Ireland Holdings\\\" corp@IE\\nentity ie-ip \\\"Acme IP Ltd\\\" corp@KY [note: \\\"Holds group IP\\\"]\\nentity nl-bv \\\"Acme EU Distribution\\\" corp@NL\\nentity sg-apac \\\"Acme APAC Trading\\\" corp@SG\\nparent -> ie-holdco : 100%\\nie-holdco -> ie-ip : 100%\\nie-holdco -> nl-bv : 100%\\nie-ip -~-> nl-bv [label: \\\"IP License · royalty\\\"]\\nparent -> sg-apac : 100%\",\n \"notes\": \"## Scenario\\n\\nCorporate counsel or a Big Four tax team draws this structure during cross-border M&A due diligence or transfer-pricing documentation. The OECD BEPS framework requires taxpayers to document the substance and economic rationale of every intercompany flow — this diagram is the first attachment to the TP master file and the tax opinion memo.\\n\\n## Annotation key\\n\\n- `entity name \\\"Display Name\\\" corp@US` — creates an entity node; `corp` is the entity type, `US` is the jurisdiction\\n- `corp / llc / fund / trust` — entity type; determines the symbol rendered\\n- `@US / @IE / @KY / @NL / @SG` — ISO country code for jurisdiction labeling\\n- `[note: \\\"...\\\"]` — adds a subsidiary note below the entity name\\n- `->` — solid directed line; represents an equity ownership relationship\\n- `-~->` — dashed directed line; represents a contractual (non-equity) relationship such as an IP license or intercompany loan\\n- `: 100%` — label on a directed edge; shows the ownership percentage\\n\\n## How to read\\n\\nThe US parent owns 100% of the Irish holdco, which in turn holds both the Cayman IP entity and the Dutch distribution subsidiary. The dashed arrow from the IP entity to the Dutch entity represents the IP license — royalties flow from the Netherlands up to Cayman, shifting taxable income to a low-rate jurisdiction. Singapore APAC is a parallel branch for Asia-Pacific operations, owned directly by the US parent.\"\n },\n {\n \"slug\": \"entity-series-a-cap-table\",\n \"diagram\": \"entity\",\n \"title\": \"Series A cap table\",\n \"description\": \"Post-Series A cap table showing founders, seed fund, lead VC, angel group, and ESOP pool with ownership percentages — for 409A valuations and board consents.\",\n \"standard\": \"Tier ownership\",\n \"tags\": [\n \"cap-table\",\n \"Series-A\",\n \"ESOP\",\n \"VC\",\n \"founders\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"entity-structure \\\"Acme Inc. — post Series A\\\"\\nentity acme \\\"Acme Inc.\\\" corp@DE\\nentity founders \\\"Founders (2)\\\" individual\\nentity seed \\\"Seed Fund I\\\" lp@DE\\nentity lead \\\"Sequoia Series A\\\" lp@DE\\nentity angels \\\"Angel group\\\" individual\\nentity esop \\\"Employee Option Pool\\\" trust@DE\\nfounders -> acme : 45%\\nseed -> acme : 12%\\nlead -> acme : 22%\\nangels -> acme : 6%\\nesop -> acme : 15%\",\n \"notes\": \"## Scenario\\n\\nA startup attorney or CFO documents the post-Series A ownership table for a 409A valuation, board consent, or investor report. The cap table diagram makes the dilution story visual — founders can immediately see their post-money percentage, and the VC can verify their ownership stake before signing the term sheet.\\n\\n## Annotation key\\n\\n- `-> acme : 45%` — ownership arrow with percentage label; all percentages should sum to 100%\\n- `individual` — natural person (founder, angel)\\n- `lp` — institutional investor entity (fund/LP)\\n- `trust` — the ESOP/option pool (typically a Delaware trust or reserved pool)\\n- `corp@DE` — the issuer (Delaware C-corp)\\n\\n## How to read\\n\\nAcme Inc. (Delaware C-corp) sits at the bottom as the issuer. Five shareholder classes flow down with their ownership arrows: founders at 45%, the lead Series A investor at 22%, the employee option pool at 15%, the seed fund at 12%, and angels at 6%. Percentages sum to 100%, representing a clean fully-diluted cap table on the day of Series A close.\"\n },\n {\n \"slug\": \"fishbone-website-traffic\",\n \"diagram\": \"fishbone\",\n \"title\": \"Website traffic drop root-cause analysis\",\n \"description\": \"Ishikawa fishbone for a website traffic drop — six causal categories covering content, technical SEO, backlinks, UX, competition, and algorithm changes.\",\n \"standard\": \"Ishikawa 1968\",\n \"tags\": [\n \"root-cause\",\n \"Ishikawa\",\n \"six-categories\",\n \"growth\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"fishbone \\\"Fishbone diagram — Website traffic drop\\\"\\n\\neffect \\\"Traffic decline\\\"\\n\\ncategory content \\\"Content\\\"\\ncategory tech \\\"Technical\\\"\\ncategory links \\\"Backlinks\\\"\\ncategory ux \\\"UX\\\"\\ncategory competition \\\"Competition\\\"\\ncategory algo \\\"Algorithm\\\"\\n\\ncontent : \\\"Publishing frequency down\\\"\\ncontent : \\\"Content too generic\\\"\\ncontent : \\\"Keyword gaps\\\"\\ncontent : \\\"Low-quality AI content\\\"\\n\\ntech : \\\"Core Web Vitals failing\\\"\\ntech : \\\"Crawl coverage drop\\\"\\ntech : \\\"Crawler blocked by WAF\\\"\\ntech : \\\"Missing structured data\\\"\\n\\nlinks : \\\"High-quality backlinks lost\\\"\\nlinks : \\\"High ratio of low-quality links\\\"\\nlinks : \\\"Referring domain growth stalled\\\"\\nlinks : \\\"Low anchor text diversity\\\"\\n\\nux : \\\"Bounce rate rising\\\"\\nux : \\\"Poor mobile experience\\\"\\nux : \\\"Slow above-fold load\\\"\\nux : \\\"Excessive popup ads\\\"\\n\\ncompetition : \\\"New competitors entering\\\"\\ncompetition : \\\"AI tools replacing search\\\"\\ncompetition : \\\"Weakening brand recall\\\"\\ncompetition : \\\"Competitors publishing faster\\\"\\n\\nalgo : \\\"Core Update penalty\\\"\\nalgo : \\\"Weak E-E-A-T signals\\\"\\nalgo : \\\"AI Overviews / SGE cutoff\\\"\\nalgo : \\\"Search intent drift\\\"\",\n \"notes\": \"## Scenario\\n\\nAn ops lead runs a growth post-mortem after a 30% organic traffic drop. The Ishikawa (fishbone) diagram structures the team's brainstorm into six standard causal categories, preventing the meeting from fixating on the most vocal hypothesis while ignoring systemic causes. The completed diagram becomes the project brief for the remediation sprint.\\n\\n## Annotation key\\n\\n- `effect \\\"...\\\"` — the problem statement, placed at the fish's head (right side)\\n- `category id \\\"Label\\\"` — defines a major causal branch (a \\\"bone\\\"); use a short `id` to assign causes\\n- `id : \\\"cause text\\\"` — assigns a cause string to the named category branch\\n- Each category renders as a diagonal rib pointing toward the effect\\n- Sub-causes (second-order) can be added by nesting if the DSL supports it\\n\\n## How to read\\n\\nThe effect (traffic decline) sits at the right. Six causal ribs branch from the spine: Content, Technical, Backlinks, UX, Competition, and Algorithm. Each rib lists four specific hypotheses. In a workshop, the team votes on each cause, color-codes high-confidence ones, and converts the highest-priority items into action items. The diagram serves as both a brainstorming artifact and a living post-mortem document.\"\n },\n {\n \"slug\": \"flowchart-cicd-pipeline\",\n \"diagram\": \"flowchart\",\n \"title\": \"CI/CD pipeline with gated deploy\",\n \"description\": \"Flowchart of a trunk-based CI/CD pipeline — build, test, security scan, staging gate, and production deploy with automatic rollback on failed smoke tests.\",\n \"standard\": \"ISO 5807:1985\",\n \"tags\": [\n \"cicd\",\n \"devops\",\n \"pipeline\",\n \"deployment\",\n \"rollback\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"flowchart TD\\n commit([Push to main]) --> build[Build artifact]\\n build --> unit{Unit tests pass?}\\n unit -->|No| fail([Fail build])\\n unit -->|Yes| scan[Security scan]\\n scan --> vuln{High-severity CVEs?}\\n vuln -->|Yes| fail\\n vuln -->|No| stage[Deploy to staging]\\n stage --> smoke{Smoke tests green?}\\n smoke -->|No| fail\\n smoke -->|Yes| approve{Manual approval?}\\n approve -->|No| wait([Await approver])\\n approve -->|Yes| prod[Deploy to production]\\n prod --> health{Post-deploy health check?}\\n health -->|Yes| done([Release complete])\\n health -->|No| rollback[Automatic rollback]\\n rollback --> done\",\n \"notes\": \"## Scenario\\n\\nA platform engineer is documenting the team's trunk-based pipeline for a new-hire runbook. The diagram makes the four automated gates (tests → scan → smoke → post-deploy health) and the single human gate (manual approval) obvious at a glance, and shows that every failure path terminates the pipeline rather than silently continuing.\\n\\n## Annotation key\\n\\n- `([…])` — stadium; start and terminal nodes\\n- `{…}` — diamond; automated or manual gate\\n- `[…]` — rectangle; build / deploy / scan step\\n- `-->|Yes/No|` — branch labels on each gate\\n\\n## How to read\\n\\nStart at *Push to main*. Every diamond is a gate — a *No* on any of unit tests, CVE scan, or smoke tests terminates at *Fail build*. Manual approval is the only human gate; it can park the pipeline at *Await approver* without failing. The post-deploy health check guards production: a failure triggers automatic rollback, which still completes at *Release complete* because the rollback itself is a successful outcome.\"\n },\n {\n \"slug\": \"flowchart-order-fulfillment\",\n \"diagram\": \"flowchart\",\n \"title\": \"E-commerce order fulfillment\",\n \"description\": \"Flowchart mapping the full order-to-delivery path with inventory and payment decision gates, exception handling, and a single End terminal.\",\n \"standard\": \"ISO 5807:1985\",\n \"tags\": [\n \"process\",\n \"decision\",\n \"e-commerce\",\n \"operations\",\n \"exception-handling\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"flowchart LR\\n start([New order received])\\n start --> validate{Inventory available?}\\n validate -->|Yes| reserve[Reserve items]\\n validate -->|No| notify[Notify customer]\\n notify --> done([End])\\n reserve --> payment{Payment authorized?}\\n payment -->|Yes| ship[Ship order]\\n payment -->|No| cancel[Cancel & release]\\n ship --> confirm[Send confirmation email]\\n confirm --> done\\n cancel --> done\",\n \"notes\": \"## Scenario\\n\\nA product ops lead circulates this flowchart during an ops-review meeting to align engineering, customer support, and fulfillment on the single source of truth for what happens when a new order comes in. It surfaces the two decision gates (inventory, payment) and the three exception paths (out-of-stock notification, payment failure with released hold, successful ship with confirmation).\\n\\n## Annotation key\\n\\n- `([…])` — stadium / terminal; used for Start and End\\n- `{…}` — diamond; decision node\\n- `[…]` — rectangle; process step\\n- `-->|label|` — edge with a branch label (`Yes` / `No`)\\n\\n## How to read\\n\\nStart at the top-left terminal. Inventory check gates the first branch — a \\\"No\\\" routes straight to the End after notification. A \\\"Yes\\\" reserves stock then hits the payment gate. Payment failure releases the reservation and goes to End; success ships and emails the customer. Every path terminates at the same End node, so nothing dangles.\"\n },\n {\n \"slug\": \"genogram-brca-cancer\",\n \"diagram\": \"genogram\",\n \"title\": \"Hereditary cancer (BRCA1) family\",\n \"description\": \"Three-generation BRCA1 family genogram with hereditary breast/ovarian cancer conditions — captured at intake before formal clinical pedigree analysis.\",\n \"standard\": \"McGoldrick 2020\",\n \"tags\": [\n \"hereditary-cancer\",\n \"four-generation\",\n \"deceased\",\n \"BRCA\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"genogram \\\"BRCA1 Family\\\"\\n I_1 [male, 1930, 1995, deceased]\\n I_2 [female, 1932, 1990, deceased, conditions: ovarian_cancer(full, #B388FF)]\\n I_1 -- I_2\\n II_1 [female, 1955, conditions: breast_cancer(full, #EC407A)]\\n II_2 [male, 1958]\\n II_3 [female, 1960]\\n II_1 -- II_4 [male, 1954]\\n III_1 [female, 1985, index, conditions: breast_cancer(full, #EC407A)]\\n III_2 [male, 1988]\",\n \"notes\": \"## Scenario\\n\\nA family history genogram for hereditary breast/ovarian cancer, documented at the initial genetic counseling intake before formal pedigree analysis. For standardized clinical pedigree notation (NSGC), use the Pedigree diagram type instead.\\n\\n## Annotation key\\n\\n- `conditions: ovarian_cancer(full)` / `conditions: breast_cancer(full)` — medical conditions filling the symbol; color is optional hex\\n- `deceased` with birth and death years — marks individuals with a slash and date range\\n- `index` — marks the proband who triggered the clinical referral\\n\\n## How to read\\n\\nThe maternal grandmother (I_2) had ovarian cancer and is deceased. Her daughter (II_1) developed breast cancer. The proband (III_1, index) is a third-generation female with breast cancer — the inheritance pattern spanning three generations justifies BRCA genetic testing.\"\n },\n {\n \"slug\": \"genogram-foster-care\",\n \"diagram\": \"genogram\",\n \"title\": \"Foster care / child protection\",\n \"description\": \"Foster-care genogram for a real LATAM child-protection case — biological parents (cohabitation ended), abuse, current foster placement (dotted secondary link), unknown-count siblings, and a maternal uncle as known-relative-with-unknown-ancestry.\",\n \"standard\": \"McGoldrick 2020 + Bennett 2022 (adopted-out / dual-parent convention)\",\n \"tags\": [\n \"foster-care\",\n \"dual-parent\",\n \"abuse\",\n \"sibling-of\",\n \"unknown-siblings\",\n \"latam\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"genogram \\\"Familia Isaías\\\"\\n victor [male, label: \\\"Víctor Seguel\\\"]\\n monica [female, label: \\\"Mónica Barrientos\\\"]\\n victor ~/~ monica\\n ?\\n isaias [male, 2020, age: 6, label: \\\"Isaías\\\", index]\\n pablo_sr [male, label: \\\"Don Pablo\\\"]\\n priscila [female, label: \\\"Doña Priscila\\\"]\\n pablo_sr -- priscila\\n pablo_jr [male, label: \\\"Pablo (jr)\\\"]\\n alanis [female, label: \\\"Alanis\\\"]\\n isaias [foster]\\n tio_materno [male, label: \\\"Tío materno\\\", sibling-of: monica]\\n victor -physical-abuse-> isaias\\n monica -physical-abuse-> isaias\\n tio_materno -nevermet- isaias\",\n \"notes\": \"## Scenario\\n\\nA foster-care social worker in Chile is preparing the case file for Isaías, a 6-year-old boy removed from his biological parents (Víctor and Mónica) due to physical abuse from both. He currently lives with foster parents Don Pablo and Doña Priscila, who have two biological children of their own. The case file mentions Isaías has siblings whose names and ages are unknown, and a maternal uncle who is a potential reunification resource but currently has no contact.\\n\\nA judge or psychologist receiving this diagram must, at a glance, correctly conclude:\\n\\n1. Isaías is the **biological son** of Víctor and Mónica — solid parent-child line down from the bio couple.\\n2. He **currently lives** with Don Pablo and Doña Priscila as a foster child — *secondary dotted link* from the foster couple, drawn without pulling Isaías away from his bio-parent position.\\n3. He was **removed due to physical abuse** from both bio parents — directional red zigzag arrows.\\n4. The **maternal uncle** is Mónica's brother (`sibling-of: monica`) with **no current relationship** to Isaías — dashed bracket between Mónica and Tío + `nevermet` line.\\n5. Isaías has **unknown-count siblings** still with the bio parents — single `?` diamond placeholder.\\n6. **Isaías is the index person** — concentric outer border highlight.\\n\\n## Annotation key\\n\\n- `~/~` — cohabitation ended (never-married); standard for LATAM caseloads where bio parents lived together unmarried and the relationship has broken. Distinct from `-x-` divorce (no marriage) and `-/-` separation (still married).\\n- Re-declaring `isaias [foster]` under the foster couple after declaring him under the bio couple → engine treats the second declaration as a **secondary \\\"current caregiver\\\" link** (dotted), preserving all attributes from the first declaration.\\n- `?` on a child line → a single diamond with `?` glyph meaning \\\"≥1 siblings, count and identities unknown\\\" (standard pedigree convention).\\n- `[sibling-of: monica]` → places Tío materno on Mónica's generation with a dashed bracket between them, **without** synthesizing phantom maternal grandparents.\\n- `-physical-abuse->` → directional red arrow; the `>` indicates the perpetrator (left side) and victim (right side).\\n\\n## Why this matters\\n\\nA genogram engine that quietly rendered Isaías as a third biological child of Don Pablo + Doña Priscila — or dropped his sex and label when the `[foster]` redeclaration overwrote the original — would invert the case story. This example exercises every fix from the 2026-04 foster-care brief: dual-parent rendering, same-id merge, sibling-of, cohabiting-ended, and the unknown-siblings placeholder.\"\n },\n {\n \"slug\": \"genogram-medical-history\",\n \"diagram\": \"genogram\",\n \"title\": \"Multi-generation medical history\",\n \"description\": \"Three-generation family medical history genogram with multi-condition color annotations using fill zones — heart disease, diabetes, cancer, hypertension.\",\n \"standard\": \"McGoldrick 2020\",\n \"tags\": [\n \"conditions\",\n \"multicolor\",\n \"three-generation\",\n \"inheritance\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"genogram \\\"Medical History\\\"\\n grandfather [male, 1930, 1990, deceased, conditions: heart-disease(full, #e74c3c) + diabetes(half-left, #ff9800)]\\n grandmother [female, 1935, conditions: cancer(half-right, #9c27b0)]\\n grandfather -- grandmother\\n father [male, 1960, conditions: heart-disease(quad-tl, #e74c3c) + hypertension(quad-tr, #2196f3)]\\n uncle [male, 1963, conditions: diabetes(full, #ff9800)]\\n mother [female, 1962]\\n father -- mother\\n patient [male, 1988, index, conditions: hypertension(half-left, #2196f3)]\\n sister [female, 1991]\",\n \"notes\": \"## Scenario\\n\\nA clinical social worker or genetic counselor captures three generations of family medical history at intake. The `conditions()` annotation lets each person carry multiple diagnoses simultaneously — and the fill geometry (full, half, quadrant) encodes severity or inheritance proportion at a glance, without cluttering the diagram with text labels.\\n\\n## Annotation key\\n\\n- `conditions: X(fill, color)` — paints a shape inside the genogram symbol using the named fill zone and hex color\\n- `full` — entire symbol filled; indicates fully affected\\n- `half-left` / `half-right` — left or right half filled; often used for one of two conditions side-by-side\\n- `quad-tl` / `quad-tr` — top-left or top-right quadrant; allows up to four distinct conditions per person\\n- `+ diabetes(...)` — chain multiple conditions on the same person with `+`\\n- `deceased` — draws a diagonal slash through the symbol\\n- `index` — marks the proband with an arrow\\n\\n## How to read\\n\\nThe grandfather's full red fill (heart disease) and half-orange fill (diabetes) are visually inherited by the father, who carries both — encoded as top-left and top-right quadrant fills. The patient (index) shows only hypertension in the left half, indicating partial inheritance. Tracing any color across generations immediately reveals the inheritance chain.\"\n },\n {\n \"slug\": \"genogram-nuclear-family\",\n \"diagram\": \"genogram\",\n \"title\": \"Nuclear family (minimal template)\",\n \"description\": \"Minimal nuclear family genogram — married couple, one child, marriage date — the clinical intake starting template per McGoldrick 2020 notation.\",\n \"standard\": \"McGoldrick 2020\",\n \"tags\": [\n \"starter\",\n \"minimal\",\n \"marriage-date\",\n \"index\"\n ],\n \"complexity\": 1,\n \"featured\": false,\n \"dsl\": \"genogram \\\"Smith Family\\\"\\n john [male, 1975]\\n mary [female, 1977]\\n john -- mary \\\"m. 2002\\\"\\n alice [female, 2005, index]\",\n \"notes\": \"## Scenario\\n\\nThe simplest genogram that is clinically useful — a married couple with one child. Used as a session intake template and as the starting point when teaching genogram notation to new practitioners.\\n\\n## Annotation key\\n\\n- `--` — standard marriage/union line\\n- `\\\"m. 2002\\\"` — marriage year label\\n- `index` — marks Alice as the identified patient\\n\\n## How to read\\n\\nTwo parents connected by a union line with a marriage date; their child Alice (marked as the index person) hangs below. Extend this template by adding siblings, grandparents, or emotional relationship lines.\"\n },\n {\n \"slug\": \"genogram-potter-family\",\n \"diagram\": \"genogram\",\n \"title\": \"The Potter family\",\n \"description\": \"Three-generation Potter family genogram with emotional relationship lines — cutoff, hostile, and close — illustrating McGoldrick relational notation.\",\n \"standard\": \"McGoldrick 2020\",\n \"tags\": [\n \"emotional-relationships\",\n \"three-generation\",\n \"deceased\",\n \"cutoff\",\n \"hostile\",\n \"close\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"genogram \\\"The Potter Family\\\"\\n fleamont [male, 1909, 1979, deceased]\\n euphemia [female, 1920, 1979, deceased]\\n fleamont -- euphemia\\n james [male, 1960, 1981, deceased]\\n mr_evans [male, 1925, deceased]\\n mrs_evans [female, 1928, deceased]\\n mr_evans -- mrs_evans\\n lily [female, 1960, 1981, deceased]\\n petunia [female, 1958]\\n james -- lily \\\"m. 1978\\\"\\n harry [male, 1980, index]\\n petunia -- vernon [male, 1951]\\n dudley [male, 1980]\\n harry -cutoff- petunia\\n harry -hostile- dudley\\n harry -close- lily\",\n \"notes\": \"## Scenario\\n\\nA teaching example for social work students learning genogram notation. The Potter family is fictional but emotionally rich — death years, a marriage date, cross-family emotional relationships, and three distinct relational patterns (cutoff, hostile, close) all in one diagram.\\n\\n## Annotation key\\n\\n- `[male/female, birth_year, death_year, deceased]` — person with death marker\\n- `\\\"m. 1978\\\"` — marriage date label on the union line\\n- `index` — marks Harry as the identified patient (proband)\\n- `-cutoff-` — estrangement; drawn as two parallel bars across the relationship line\\n- `-hostile-` — conflict; drawn as zigzag line\\n- `-close-` — enmeshment/closeness; drawn as double parallel line\\n\\n## How to read\\n\\nRead each indented block as a family unit. James and Lily (index generation) both died in 1981. Harry's emotional world is defined by three relational lines: cutoff from Aunt Petunia, hostility toward cousin Dudley, and closeness to his deceased mother.\"\n },\n {\n \"slug\": \"ladder-mode-selection\",\n \"diagram\": \"ladder\",\n \"title\": \"System mode selection (Set/Reset)\",\n \"description\": \"IEC 61131-3 ladder logic for HMI-driven Auto/Manual mode selection using Set/Reset (OTL/OTU) coils with system fault interlocks.\",\n \"standard\": \"IEC 61131-3\",\n \"tags\": [\n \"OTL\",\n \"OTU\",\n \"Set-Reset\",\n \"parallel-outputs\",\n \"interlocks\",\n \"Allen-Bradley\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"ladder \\\"System Mode Selection\\\"\\n\\nrung 1 \\\"Set system Auto mode, reset Manual\\\":\\n XIC(AUTO_HMIPB, \\\"BIT 5.10\\\", name=\\\"Auto Mode HMI Pushbutton\\\")\\n XIO(MANL_HMIPB, \\\"BIT 5.11\\\", name=\\\"Manual Mode HMI Pushbutton\\\")\\n XIO(SYS_FAULT, \\\"BIT 3.0\\\", name=\\\"System Fault\\\")\\n parallel:\\n branch:\\n OTL(SYS_AUTO, \\\"BIT 3.1\\\", name=\\\"System Auto Mode\\\")\\n branch:\\n OTU(SYS_MANUAL, \\\"BIT 3.2\\\", name=\\\"System Manual Mode\\\")\\n\\nrung 2 \\\"Set Manual, reset Auto (with Home seal-in)\\\":\\n parallel:\\n branch:\\n XIC(MANL_HMIPB, \\\"BIT 5.11\\\", name=\\\"Manual Mode HMI Pushbutton\\\")\\n branch:\\n XIC(SYS_HOMECMD, \\\"BIT 3.5\\\", name=\\\"System Home Command\\\")\\n XIO(AUTO_HMIPB, \\\"BIT 5.10\\\", name=\\\"Auto Mode HMI Pushbutton\\\")\\n XIO(SYS_FAULT, \\\"BIT 3.0\\\", name=\\\"System Fault\\\")\\n parallel:\\n branch:\\n OTL(SYS_MANUAL, \\\"BIT 3.2\\\", name=\\\"System Manual Mode\\\")\\n branch:\\n OTU(SYS_AUTO, \\\"BIT 3.1\\\", name=\\\"System Auto Mode\\\")\",\n \"notes\": \"## Scenario\\n\\nAn Allen-Bradley PLC program for a machine that requires mutually exclusive Auto and Manual operating modes, with a Home command that can trigger a manual mode entry as a safety fallback. The latched Set/Reset coil pattern is standard for retained-state mode selection that survives a power cycle.\\n\\n## Annotation key\\n\\n- `OTL(tag, addr, name=...)` — Output Latch (Set): energizes and *latches* the bit high; bit stays high even when the rung loses power\\n- `OTU(tag, addr, name=...)` — Output Unlatch (Reset): clears a latched bit back to 0\\n- `parallel: branch:` — output-side parallel branches execute simultaneously when the rung is true\\n- Rung 1 sets Auto and simultaneously resets Manual; Rung 2 does the inverse\\n- The `SYS_FAULT` XIO contact appears in both rungs as a master interlock — no mode change is allowed during a fault\\n\\n## How to read\\n\\nRung 1 fires when the operator presses the Auto HMI button AND Manual is not pressed AND no fault exists. It simultaneously latches `SYS_AUTO` ON and unlatches `SYS_MANUAL`. Rung 2 is the mirror: Manual button OR Home command, guarded by Auto-not-pressed and no-fault, sets Manual and resets Auto. The latched coils mean the last-pressed mode persists through PLC power cycles.\"\n },\n {\n \"slug\": \"ladder-motor-start-stop\",\n \"diagram\": \"ladder\",\n \"title\": \"Motor start/stop seal-in circuit\",\n \"description\": \"Classic three-wire motor start/stop seal-in circuit in IEC 61131-3 ladder logic — the foundational pattern taught in every PLC certification course.\",\n \"standard\": \"IEC 61131-3\",\n \"tags\": [\n \"seal-in\",\n \"motor\",\n \"XIC\",\n \"XIO\",\n \"OTE\",\n \"parallel\"\n ],\n \"complexity\": 1,\n \"featured\": true,\n \"dsl\": \"ladder \\\"Motor Start/Stop\\\"\\nrung 1 \\\"Seal-in circuit\\\":\\n parallel:\\n branch:\\n XIC(START_PB, \\\"IN 1.0\\\", name=\\\"Start Button\\\")\\n branch:\\n XIC(MOTOR_AUX, \\\"BIT 3.0\\\", name=\\\"Aux Contact\\\")\\n XIO(STOP_PB, \\\"IN 1.1\\\", name=\\\"Stop Button\\\")\\n OTE(MOTOR_CMD, \\\"OUT 2.0\\\", name=\\\"Motor Command\\\")\",\n \"notes\": \"## Scenario\\n\\nEvery controls engineer learns the three-wire motor start/stop circuit before writing their first PLC program. It appears verbatim in IEC 61131-3 training materials, Allen-Bradley certification exams, and factory acceptance tests worldwide. The seal-in contact latches the motor ON after the momentary start pushbutton is released — the fundamental pattern for any maintained-output logic.\\n\\n## Annotation key\\n\\n- `XIC(tag, address, name=...)` — Examine If Closed: contact passes power when the referenced bit is `1` (true)\\n- `XIO(tag, address, name=...)` — Examine If Open: contact passes power when the referenced bit is `0` (false); normal for stop buttons wired N.C.\\n- `OTE(tag, address, name=...)` — Output Energize: coil energizes the referenced bit when rung has power\\n- `parallel: branch:` — models a parallel contact branch (logical OR)\\n- The `MOTOR_AUX` contact in the parallel branch is the seal-in: once the motor output energizes, the aux contact closes and holds the rung true even after the START_PB releases\\n\\n## How to read\\n\\nThe rung reads left to right. Power flows if *either* the start button (XIC START_PB) *or* the aux contact (XIC MOTOR_AUX) is closed, *and* the stop button (XIO STOP_PB) is not pressed. When the output coil (OTE MOTOR_CMD) energizes the motor, it also drives the aux contact bit — latching the rung high. Pressing STOP breaks the series path and de-energizes the rung, dropping the motor and the seal-in simultaneously.\"\n },\n {\n \"slug\": \"logic-full-adder\",\n \"diagram\": \"logic\",\n \"title\": \"1-bit full adder\",\n \"description\": \"1-bit full adder built from XOR, AND, and OR gates — the foundational building block of every arithmetic logic unit, from a functional description.\",\n \"standard\": \"IEEE 91\",\n \"tags\": [\n \"XOR\",\n \"AND\",\n \"OR\",\n \"combinational\",\n \"ALU\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"logic \\\"1-bit Full Adder\\\"\\ninput A, B, Cin\\noutput Sum, Cout\\ns1 = XOR(A, B)\\nSum = XOR(s1, Cin)\\nc1 = AND(A, B)\\nc2 = AND(s1, Cin)\\nCout = OR(c1, c2)\",\n \"notes\": \"## Scenario\\n\\nThe 1-bit full adder is the foundational building block of every arithmetic logic unit. Digital logic students derive it in lecture; FPGA engineers instantiate it in RTL. Schematex renders it from a purely functional description — no manual gate placement, no wire routing — making it easy to embed in textbooks, datasheets, or AI-generated hardware documentation.\\n\\n## Annotation key\\n\\n- `input A, B, Cin` — declare named input ports\\n- `output Sum, Cout` — declare named output ports\\n- `s1 = XOR(A, B)` — intermediate signal `s1` is the XOR of inputs A and B\\n- `Sum = XOR(s1, Cin)` — the sum bit is the XOR of the partial sum and carry-in\\n- `c1 = AND(A, B)` — carry generated when both A and B are 1\\n- `c2 = AND(s1, Cin)` — carry propagated when partial sum is 1 and Cin is 1\\n- `Cout = OR(c1, c2)` — carry-out is 1 if either generate or propagate carry is active\\n\\n## How to read\\n\\nThe diagram renders two XOR gates for the sum path (A⊕B, then ⊕Cin) and two AND gates feeding an OR for the carry-out (the standard generate/propagate structure). The layout is automatically ranked so data flows left to right, inputs on the left edge, outputs on the right. Every 4-bit or 8-bit ripple-carry adder in textbooks is just this circuit chained together.\"\n },\n {\n \"slug\": \"matrix-bcg-portfolio\",\n \"diagram\": \"matrix\",\n \"title\": \"BCG product portfolio\",\n \"description\": \"BCG matrix plotting five product lines by market share and growth rate — stars, cash cows, question marks, and one dog — for annual investment planning.\",\n \"standard\": \"BCG Growth-Share (1970)\",\n \"tags\": [\n \"matrix\",\n \"bcg\",\n \"portfolio\",\n \"strategy\",\n \"investment\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"matrix bcg \\\"Product Portfolio — FY26\\\"\\n\\\"Platform SDK\\\" at (0.8, 0.8) size: 5 highlight: true category: star\\n\\\"Legacy API\\\" at (0.85, 0.15) size: 4 category: cashcow\\n\\\"Mobile SDK\\\" at (0.25, 0.85) size: 3 category: question\\n\\\"Self-serve billing\\\" at (0.35, 0.75) size: 2 category: question\\n\\\"On-prem installer\\\" at (0.2, 0.15) size: 1 category: dog\",\n \"notes\": \"## Scenario\\n\\nA VP of product strategy presents this at the annual planning offsite. The Platform SDK is the clear star — keep investing. The Legacy API is a cash cow that funds new bets. Two question marks (Mobile SDK, Self-serve billing) get the hard conversation: which one earns the next round of engineering spend? The on-prem installer is a dog — sunset candidate.\\n\\n## Annotation key\\n\\n- `matrix bcg` — preset axes (market share ← → low; low → high growth)\\n- `\\\"Label\\\" at (x, y)` — share (0–1) × growth (0–1)\\n- `size:` — relative revenue contribution\\n- `category:` — BCG quadrant tag; drives colour\\n\\n## How to read\\n\\nBCG uses a *reversed* x-axis: high market share is on the left, low on the right. That quirk puts cash cows in the bottom-left (high share, low growth) and stars in the top-left (high share, high growth). The right half holds low-share products: top-right = question marks (decide: invest or kill), bottom-right = dogs (usually kill). Bubble size shows current revenue — don't prematurely kill a cow that funds a star.\"\n },\n {\n \"slug\": \"matrix-eisenhower-week\",\n \"diagram\": \"matrix\",\n \"title\": \"Eisenhower week prioritization\",\n \"description\": \"2×2 Eisenhower matrix plotting a week's to-dos by urgency and importance, with size encoding estimated hours and a highlighted must-do.\",\n \"standard\": \"Eisenhower (1954)\",\n \"tags\": [\n \"matrix\",\n \"eisenhower\",\n \"prioritization\",\n \"productivity\",\n \"planning\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"matrix eisenhower \\\"This Week\\\"\\n\\\"Ship hotfix\\\" at (0.1, 0.9) size: 5 highlight: true\\n\\\"Team 1:1s\\\" at (0.1, 0.7) size: 3\\n\\\"Write Q3 OKRs\\\" at (0.8, 0.85) size: 4\\n\\\"Customer demo prep\\\" at (0.2, 0.75) size: 3\\n\\\"Refactor auth layer\\\" at (0.75, 0.45) size: 4\\n\\\"Inbox zero\\\" at (0.15, 0.25) size: 2\\n\\\"LinkedIn updates\\\" at (0.85, 0.15) size: 1\",\n \"notes\": \"## Scenario\\n\\nAn engineering manager uses this chart at Monday planning to triage her week. The highlighted \\\"Ship hotfix\\\" is the unambiguous top item (urgent + important). \\\"Write Q3 OKRs\\\" sits in the important-but-not-urgent quadrant — the trap quadrant Eisenhower called out: it silently slips until it becomes urgent, at which point it's badly done.\\n\\n## Annotation key\\n\\n- `matrix eisenhower` — template with preset axes (urgency × importance) and quadrant labels\\n- `\\\"Label\\\" at (x, y)` — x is urgency (0=low, 1=high), y is importance\\n- `size:` — estimated effort (scales the bubble)\\n- `highlight: true` — red ring around the must-do\\n\\n## How to read\\n\\nTop-right is urgent + important — do first. Top-left (important, not urgent) is where careful planning lives; the Q3 OKRs and refactor belong here. Bottom-right (urgent, not important) is the delegation zone. Bottom-left (neither) is the \\\"delete\\\" zone — anything here is a candidate for saying no. Bubble size shows time cost; use it to sanity-check that your top-right doesn't exceed the week's available hours.\"\n },\n {\n \"slug\": \"mindmap-product-launch\",\n \"diagram\": \"mindmap\",\n \"title\": \"Product launch plan mindmap\",\n \"description\": \"Radial mind map for a product launch — market readiness, engineering, go-to-market, and success metrics — used as a kickoff whiteboard.\",\n \"standard\": \"Buzan (1970s)\",\n \"tags\": [\n \"mindmap\",\n \"product-launch\",\n \"planning\",\n \"gtm\",\n \"brainstorming\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"mindmap\\n\\n# Product Launch Plan\\n\\n## Market readiness\\n### Competitive analysis\\n- Direct competitors\\n- Pricing benchmarks\\n### Target segments\\n- SMB customers\\n- Enterprise pilot\\n\\n## Engineering\\n### Feature freeze\\n- Core API complete\\n- Edge cases resolved\\n### Infrastructure\\n- Load testing\\n- CDN configuration\\n - Cache rules\\n - Geo routing\\n\\n## Go-to-market\\n- Landing page live\\n- Email campaign\\n- Press outreach\\n - TechCrunch pitch\\n - Newsletter sponsors\\n\\n## Success metrics\\n- Week 1 signups\\n- Activation rate\\n- NPS at day 30\",\n \"notes\": \"## Scenario\\n\\nThe launch lead opens the kickoff meeting with this mindmap on a shared whiteboard. Four branches name the four owners (product, engineering, marketing, analytics) and every leaf is a checkable deliverable. The radial layout gives each owner roughly equal visual real estate — no function's work feels like an afterthought.\\n\\n## Annotation key\\n\\n- `#` — root (exactly one)\\n- `##`, `###` — branch depth; each extra `#` nests one level deeper\\n- `-` bullets — leaf items; 2-space indent adds another level\\n\\n## How to read\\n\\nStart at the centre. Each `##` heading is a top-level workstream with its own owner. `###` headings group sub-areas; bullet lists capture concrete deliverables. Indented bullets (e.g. *Cache rules* under *CDN configuration*) are sub-tasks owned by the same person who owns the parent. Anything without a bullet-or-heading is not tracked — the mindmap is the source of truth.\"\n },\n {\n \"slug\": \"mindmap-quarterly-okrs\",\n \"diagram\": \"mindmap\",\n \"title\": \"Quarterly OKRs mindmap\",\n \"description\": \"Company OKRs organized as a mindmap — three objectives, each with measurable key results — suited for the all-hands kickoff of a new quarter.\",\n \"standard\": \"Buzan (1970s)\",\n \"tags\": [\n \"mindmap\",\n \"okrs\",\n \"planning\",\n \"company\",\n \"quarterly\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"mindmap\\n\\n# Q4 Company OKRs\\n\\n## Grow ARR 30%\\n### Expand enterprise pipeline\\n- 10 new qualified logos\\n- Win rate ≥ 25%\\n### Increase expansion\\n- Net revenue retention ≥ 120%\\n- Seat adoption +40%\\n\\n## Ship Platform v2\\n### Core migration\\n- 100% API coverage\\n- Zero-downtime cutover\\n### Developer experience\\n- Sub-5-min quickstart\\n- 95% doc satisfaction\\n\\n## Strengthen team\\n### Hiring\\n- 8 senior engineers\\n- 2 staff PMs\\n### Retention\\n- Voluntary attrition < 5%\\n- eNPS ≥ 40\",\n \"notes\": \"## Scenario\\n\\nThe chief of staff projects this during the Q4 all-hands. Three objectives radiate from the centre; every key result is a leaf with a specific number. The mindmap format reads fast — every person in the company can find their team's objective within three seconds — and it tolerates mid-quarter edits without disturbing other branches.\\n\\n## Annotation key\\n\\n- `#` — root; company-level frame\\n- `##` — objective (qualitative direction)\\n- `###` — key-result cluster\\n- `-` bullets — specific measurable KRs\\n\\n## How to read\\n\\nThe root names the quarter. The three `##` branches are the objectives — the things that will be judged at the end of the quarter. Each `###` groups key results by theme; the bullets are the actual measurable targets. If a KR can't be reduced to a number, it probably belongs in a planning doc rather than on this mindmap.\"\n },\n {\n \"slug\": \"orgchart-matrix-reporting\",\n \"diagram\": \"orgchart\",\n \"title\": \"Scale-up with matrix reporting\",\n \"description\": \"Org chart for a 60-person scale-up with two product lines — solid-line functional reporting plus dotted-line product-manager reporting into each engineering lead.\",\n \"standard\": \"HR convention\",\n \"tags\": [\n \"orgchart\",\n \"matrix-reporting\",\n \"scale-up\",\n \"engineering\",\n \"organization\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"orgchart \\\"Scaleup — Matrixed Product Lines\\\"\\nceo: \\\"Jamie Torres\\\" | CEO [role: ceo]\\n cto: \\\"Raj Patel\\\" | CTO [role: cto]\\n lead_core: \\\"Priya Nair\\\" | Eng Lead | Core [role: engineer]\\n eng1: \\\"Alex Kim\\\" | Senior Engineer [role: engineer]\\n eng2: \\\"Jordan Lee\\\" | Engineer [role: engineer]\\n lead_growth: \\\"Omar Hassan\\\" | Eng Lead | Growth [role: engineer]\\n eng3: \\\"Yuki Tanaka\\\" | Staff Engineer [role: engineer]\\n eng4: \\\"Maya Patel\\\" | Engineer [role: engineer]\\n cpo: \\\"Ellen Wu\\\" | CPO [role: cpo]\\n pm_core: \\\"Tyler Brooks\\\" | PM | Core [role: product]\\n pm_growth: \\\"Suki Ito\\\" | PM | Growth [role: product]\\n cdo: \\\"Liu Wei\\\" | CDO [role: design]\\n des_core: \\\"Ana Rossi\\\" | Designer | Core [role: design]\\n des_growth: \\\"Kai Park\\\" | Designer | Growth [role: design]\\npm_core -.-> lead_core\\npm_growth -.-> lead_growth\\ndes_core -.-> lead_core\\ndes_growth -.-> lead_growth\",\n \"notes\": \"## Scenario\\n\\nThe head of engineering is explaining the matrix structure to a new eng lead. Functional managers (CTO, CPO, CDO) own career growth; product-line leads coordinate day-to-day work. The dotted lines from PMs and designers into the two eng leads make this split visible without implying a change in reporting chain.\\n\\n## Annotation key\\n\\n- Solid line (indentation) — functional / HR reporting\\n- `A -.-> B` — dotted line; secondary / product reporting\\n- `[role: …]` — colour-coded by function\\n\\n## How to read\\n\\nSolid lines (from indentation) answer \\\"who owns my performance review and career.\\\" Dotted lines answer \\\"whose roadmap am I aligned to this quarter.\\\" PMs and designers both report functionally into their chiefs but are dotted-lined into the engineering lead whose product line they're embedded in — a typical scale-up pattern that balances functional excellence with product-team velocity.\"\n },\n {\n \"slug\": \"orgchart-tech-startup\",\n \"diagram\": \"orgchart\",\n \"title\": \"Series-A tech startup org\",\n \"description\": \"Three-level org chart for a ~30-person Series-A startup — CEO with engineering, product, and ops directs, showing open roles and a board advisor.\",\n \"standard\": \"HR convention\",\n \"tags\": [\n \"orgchart\",\n \"startup\",\n \"hiring\",\n \"headcount\",\n \"series-a\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"orgchart \\\"Acme — Series A Team\\\"\\nceo: \\\"Jamie Torres\\\" | CEO [role: ceo]\\n cto: \\\"Raj Patel\\\" | CTO [role: cto]\\n lead_fe: \\\"Priya Nair\\\" | Eng Lead | Frontend [role: engineer]\\n eng1: \\\"Alex Kim\\\" | Senior Engineer [role: engineer]\\n eng2: \\\"Jordan Lee\\\" | Engineer [role: engineer, status: new]\\n open1: open \\\"TBH\\\" | Frontend Engineer [role: engineer]\\n lead_be: \\\"Omar Hassan\\\" | Eng Lead | Backend [role: engineer]\\n eng3: \\\"Yuki Tanaka\\\" | Staff Engineer [role: engineer]\\n draft1: draft \\\"TBH\\\" | Senior Engineer [role: engineer]\\n cpo: \\\"Ellen Wu\\\" | CPO [role: cpo]\\n pm1: \\\"Tyler Brooks\\\" | Product Lead | Core [role: product]\\n pm2: \\\"Suki Ito\\\" | Product Lead | Growth [role: product]\\n coo: \\\"Maria Santos\\\" | COO [role: ops]\\n fin1: \\\"Nour Ahmed\\\" | Finance Manager [role: ops]\\nadvisor adv1: \\\"Dr. Alan Ford\\\" | Board Advisor [role: advisor]\",\n \"notes\": \"## Scenario\\n\\nThe founder is preparing a hiring plan for the next two quarters and uses this chart in a board update. It shows the current team, the one confirmed open req (Frontend Engineer), one planned-but-not-recruiting slot (Staff Engineer backend), and the board advisor relationship. Indentation communicates reporting lines without drawing edges.\\n\\n## Annotation key\\n\\n- `id: \\\"Name\\\" | \\\"Title\\\" | \\\"Department\\\"` — a person node\\n- Indentation (2 spaces) — reporting hierarchy\\n- `open …` / `draft …` — unfilled / planned roles\\n- `advisor …` — external board or advisor relationship\\n- `[role: …]` — colour-coded by function\\n\\n## How to read\\n\\nThe single root is the CEO. Each two-space indent step moves one level down the reporting tree. Two kinds of \\\"ghost\\\" slots appear: `open` nodes (Frontend Engineer) are reqs you are actively hiring for; `draft` nodes (Staff Backend) are next-quarter plans. The advisor sits outside the tree — not in the reporting chain but formally associated with the org.\"\n },\n {\n \"slug\": \"pedigree-brca1\",\n \"diagram\": \"pedigree\",\n \"title\": \"BRCA1 hereditary cancer (four-generation)\",\n \"description\": \"Four-generation BRCA1 pedigree distinguishing affected, carrier, and presymptomatic individuals — per NSGC standard for cascade testing and insurance pre-authorization.\",\n \"standard\": \"NSGC\",\n \"tags\": [\n \"BRCA\",\n \"four-generation\",\n \"carrier\",\n \"presymptomatic\",\n \"proband\",\n \"NSGC\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"pedigree \\\"BRCA1 Family — Hereditary Breast/Ovarian Cancer\\\"\\n I-1 [male, unaffected]\\n I-2 [female, affected, deceased]\\n I-1 -- I-2\\n II-1 [female, affected]\\n II-2 [male, unaffected]\\n II-3 [female, carrier]\\n II-1 -- II-4 [male, unaffected]\\n III-1 [female, affected, proband]\\n III-2 [male, unaffected]\\n III-3 [female, presymptomatic]\\n II-3 -- II-6 [male, unaffected]\\n III-6 [female, carrier]\\n III-7 [male, unaffected]\",\n \"notes\": \"## Scenario\\n\\nA genetic counselor documents a four-generation BRCA1 pedigree for a patient referred after a personal diagnosis of breast cancer at age 35. The NSGC-standard pedigree distinguishes affected, carrier, and presymptomatic individuals — critical for insurance pre-authorization and cascade testing recommendations for at-risk relatives.\\n\\n## Annotation key\\n\\n- `affected` — full fill; individual has been diagnosed with the condition\\n- `carrier` — half fill; individual carries the mutation but is currently asymptomatic\\n- `presymptomatic` — quarter fill or dot; positive genetic test but no clinical diagnosis yet\\n- `proband` — triangle marker; the individual who triggered clinical investigation\\n- `deceased` — diagonal slash through the symbol\\n\\n## How to read\\n\\nThe pattern of affected females across three generations (I-2, II-1, III-1) is the red flag for hereditary BRCA1. The proband (III-1) is the entry point. Her aunt (II-3) is a carrier who has already passed the mutation to III-6. The presymptomatic sibling (III-3) has tested positive but is not yet diagnosed — she receives enhanced surveillance recommendations.\"\n },\n {\n \"slug\": \"pedigree-cystic-fibrosis\",\n \"diagram\": \"pedigree\",\n \"title\": \"Cystic fibrosis (autosomal recessive)\",\n \"description\": \"Classic autosomal recessive cystic fibrosis pedigree with two carrier parents — illustrates the 25% recurrence risk for genetic counseling and patient education.\",\n \"standard\": \"NSGC\",\n \"tags\": [\n \"autosomal-recessive\",\n \"carrier\",\n \"Mendelian\",\n \"proband\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"pedigree \\\"CF family — autosomal recessive\\\"\\n I-1 [male, carrier]\\n I-2 [female, carrier]\\n I-1 -- I-2\\n II-1 [male, affected, proband]\\n II-2 [female, carrier]\\n II-3 [male, unaffected]\\n II-2 -- II-4 [male, carrier]\\n III-1 [female, affected]\\n III-2 [male, unaffected]\",\n \"notes\": \"## Scenario\\n\\nThe classic textbook pedigree for autosomal recessive inheritance — used in genetics courses, patient education, and clinical counseling to illustrate the 25% recurrence risk when both parents are carriers.\\n\\n## Annotation key\\n\\n- `carrier` — half-filled symbol; one normal allele and one mutant allele (Aa)\\n- `affected` — fully filled symbol; two mutant alleles (aa)\\n- `unaffected` — open symbol; either homozygous normal (AA) or carrier (Aa) — cannot distinguish clinically without testing\\n- `proband` — triangle arrow; the first affected individual identified in the family\\n\\n## How to read\\n\\nBoth Generation I parents are carriers. Their Generation II children follow the expected 1:2:1 Mendelian ratio: one affected son (proband), one carrier daughter, one unaffected son. In Generation III, carrier daughter II-2 married a carrier II-4 — producing another affected granddaughter (III-1), reinforcing the recessive inheritance pattern.\"\n },\n {\n \"slug\": \"pedigree-hemophilia\",\n \"diagram\": \"pedigree\",\n \"title\": \"Hemophilia A (X-linked recessive)\",\n \"description\": \"Three-generation hemophilia A pedigree showing X-linked recessive inheritance with carrier females and affected males per NSGC clinical notation.\",\n \"standard\": \"NSGC\",\n \"tags\": [\n \"x-linked\",\n \"carrier\",\n \"three-generation\",\n \"NSGC\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"pedigree \\\"Hemophilia A\\\"\\n I-1 [male, unaffected]\\n I-2 [female, carrier-x]\\n I-1 -- I-2\\n II-1 [male, affected]\\n II-2 [female, carrier-x]\\n II-3 [male, unaffected]\\n II-4 [female, unaffected]\\n II-2 -- II-5 [male, unaffected]\\n III-1 [male, affected]\\n III-2 [female, carrier-x]\\n III-3 [male, unaffected]\",\n \"notes\": \"## Scenario\\n\\nA genetic counselor documents a three-generation hemophilia A pedigree during a prenatal consultation. The X-linked recessive pattern — carrier females who show no symptoms but pass the mutated allele — must be clearly distinguished from affected males. NSGC standard notation is required for clinical records and insurance pre-authorization.\\n\\n## Annotation key\\n\\n- `carrier-x` — female carrier of an X-linked recessive allele; rendered as a circle with a centre dot per NSGC convention\\n- `affected` — fully filled symbol; individual expresses the condition\\n- `unaffected` — open (unfilled) symbol; no clinical presentation\\n- `proband` — the index case who prompted clinical referral (not used here, but add `proband` to any individual)\\n- `I-1 -- I-2` followed by indented children — defines a mating pair and their offspring\\n\\n## How to read\\n\\nGeneration I: unaffected father, carrier mother. Generation II: one affected son (II-1), one carrier daughter (II-2), two unaffected children. Generation III: carrier daughter II-2 married an unaffected man; they produced another affected son (III-1) and another carrier daughter (III-2) — demonstrating the classic X-linked skip-generation pattern where the trait disappears in daughters only to re-emerge in grandsons.\"\n },\n {\n \"slug\": \"phylo-bacterial-diversity\",\n \"diagram\": \"phylo\",\n \"title\": \"Bacterial diversity (ten-taxon tree)\",\n \"description\": \"Ten-taxon bacterial phylogenetic tree from a Newick/NHX string with bootstrap support values, three colored clade arcs, and a branch-length scale bar.\",\n \"standard\": \"Newick/NHX\",\n \"tags\": [\n \"Newick\",\n \"NHX\",\n \"bootstrap\",\n \"clade\",\n \"color-coding\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"phylo \\\"Bacterial Diversity\\\"\\n newick: \\\"((((Ecoli:0.1,Salmonella:0.12):0.05[&&NHX:B=98],Vibrio:0.2):0.08[&&NHX:B=85],((Bacillus:0.15,Staph:0.18):0.06[&&NHX:B=92],Listeria:0.22):0.1):0.15,((Myco_tb:0.3,Myco_leprae:0.28):0.12[&&NHX:B=100],(Strepto:0.25,Lactobacillus:0.2):0.08[&&NHX:B=78]):0.2);\\\"\\n clade Gamma = (Ecoli, Salmonella, Vibrio) [color: \\\"#1E88E5\\\", label: \\\"γ-Proteobacteria\\\"]\\n clade Firmi = (Bacillus, Staph, Listeria, Strepto, Lactobacillus) [color: \\\"#E53935\\\", label: \\\"Firmicutes\\\"]\\n clade Actino = (Myco_tb, Myco_leprae) [color: \\\"#43A047\\\", label: \\\"Actinobacteria\\\"]\\n scale \\\"substitutions/site\\\"\",\n \"notes\": \"## Scenario\\n\\nA microbiologist or bioinformatician pastes a Newick tree string exported from RAxML, IQ-TREE, or MEGA and immediately gets a publication-ready SVG with clade highlights and a branch-length scale bar — no manual layout required.\\n\\n## Annotation key\\n\\n- `newick: \\\"...\\\"` — standard Newick format tree string; branch lengths follow `:` after each taxon name\\n- `[&&NHX:B=98]` — NHX annotation; `B=` is the bootstrap support value (0–100), rendered on internal nodes\\n- `clade id = (taxon, ...)` — defines a named clade by listing its leaf members\\n- `[color: \\\"#hex\\\", label: \\\"...\\\"]` — colors the clade's subtree and adds a labeled arc\\n- `scale \\\"...\\\"` — draws a calibrated scale bar with the given unit label\\n\\n## How to read\\n\\nThe tree shows three major bacterial clades. Blue (γ-Proteobacteria): *E. coli*, *Salmonella*, and *Vibrio* cluster with 98% bootstrap support. Red (Firmicutes): *Bacillus*, *Staph*, *Listeria*, *Streptococcus*, and *Lactobacillus*. Green (Actinobacteria): the two *Mycobacterium* species form a highly supported clade (bootstrap 100). Branch lengths represent substitutions per site — longer branches indicate faster evolutionary rates.\"\n },\n {\n \"slug\": \"sld-generator-ats\",\n \"diagram\": \"sld\",\n \"title\": \"Generator + ATS backup power\",\n \"description\": \"Single-line diagram for a utility + emergency generator ATS transfer system feeding critical loads on a 480 V bus — per IEEE 315 for facility design review.\",\n \"standard\": \"IEEE 315\",\n \"tags\": [\n \"ATS\",\n \"generator\",\n \"backup-power\",\n \"bus\",\n \"breaker\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"sld \\\"Utility + Generator Backup\\\"\\nUTIL = utility [voltage: \\\"480V\\\", label: \\\"Utility\\\"]\\nGEN = generator [rating: \\\"500 kW\\\", voltage: \\\"480V\\\", label: \\\"Emergency Gen\\\"]\\nATS1 = ats [rating: \\\"800A\\\", label: \\\"ATS-1\\\"]\\nBUS1 = bus [voltage: \\\"480V\\\", label: \\\"Critical Load Bus\\\"]\\nCB1 = breaker [rating: \\\"200A\\\"]\\nCB2 = breaker [rating: \\\"200A\\\"]\\nL1 = load [rating: \\\"100A\\\", label: \\\"Critical Load 1\\\"]\\nL2 = load [rating: \\\"100A\\\", label: \\\"Critical Load 2\\\"]\\nUTIL -> ATS1\\nGEN -> ATS1\\nATS1 -> BUS1\\nBUS1 -> CB1\\nBUS1 -> CB2\\nCB1 -> L1\\nCB2 -> L2\",\n \"notes\": \"## Scenario\\n\\nA facility engineer draws this one-line during the design review of a data-center UPS bypass or hospital emergency power system. The single-line diagram (SLD) is the first document a utility inspector or commissioning engineer asks for — it must show every source, switching device, bus, and load path in a single horizontal view without wiring details.\\n\\n## Annotation key\\n\\n- `utility` — mains supply; drawn as the IEEE 315 utility symbol (three-line source)\\n- `generator` — diesel or gas genset; drawn as rotating-machine circle with winding symbol\\n- `ats` — Automatic Transfer Switch; drawn as the NEMA/IEEE transfer-switch symbol\\n- `bus` — horizontal bus bar; all connected devices share the same voltage rail\\n- `breaker` — molded-case or air circuit breaker; drawn as the IEEE 315 breaker symbol\\n- `load` — end-consumer device or feeder\\n- `UTIL -> ATS1` — directed line representing the power path from source to device\\n\\n## How to read\\n\\nTwo sources (utility and generator) feed into the ATS. The ATS selects the live source and connects it to the 480 V critical load bus. From the bus, two independently-fused circuit breakers (CB1, CB2) feed their respective critical loads. If utility power fails, the ATS senses the loss, the generator starts, and the ATS transfers within seconds — all without interrupting the bus downstream.\"\n },\n {\n \"slug\": \"sld-substation-13kv\",\n \"diagram\": \"sld\",\n \"title\": \"13.8 kV utility substation\",\n \"description\": \"13.8 kV distribution substation single-line diagram with 138 kV grid input, 15 MVA step-down transformer, and three feeder breakers per IEEE 315.\",\n \"standard\": \"IEEE 315\",\n \"tags\": [\n \"substation\",\n \"transformer\",\n \"bus\",\n \"feeder\",\n \"HV\",\n \"MV\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"sld \\\"13.8 kV Substation\\\"\\nutility = utility [label: \\\"Grid 138 kV\\\"]\\nxfmr1 = transformer [kva: 15000, primary: 138, secondary: 13.8]\\nbus_hv = bus [voltage: 138]\\nbus_mv = bus [voltage: 13.8]\\nbrk1 = breaker [amps: 1200]\\nbrk2 = breaker [amps: 1200]\\nbrk3 = breaker [amps: 1200]\\nfeeder1 = load [label: \\\"Feeder 1\\\"]\\nfeeder2 = load [label: \\\"Feeder 2\\\"]\\nfeeder3 = load [label: \\\"Feeder 3\\\"]\\nutility -> bus_hv\\nbus_hv -> xfmr1\\nxfmr1 -> bus_mv\\nbus_mv -> brk1\\nbrk1 -> feeder1\\nbus_mv -> brk2\\nbrk2 -> feeder2\\nbus_mv -> brk3\\nbrk3 -> feeder3\",\n \"notes\": \"## Scenario\\n\\nA power systems engineer documents a distribution substation design for a utility interconnection application or a facility's electrical permit drawings. The single-line diagram is the first deliverable in any power system project — required by IEEE, NFPA 70E, and utility interconnection standards before detailed engineering begins.\\n\\n## Annotation key\\n\\n- `utility = utility [label: \\\"...\\\"]` — utility supply source (three-phase symbol)\\n- `[type: transformer, kva:..., primary:..., secondary:...]` — step-down transformer with rated kVA and voltage levels\\n- `[type: bus, voltage:...]` — horizontal bus bar at the specified voltage level\\n- `[type: breaker, amps:...]` — rated circuit breaker\\n- `[type: load, label:...]` — load or feeder destination\\n- `->` — directed power path from source to load\\n\\n## How to read\\n\\nThe 138 kV grid source feeds the high-voltage bus, which connects to the primary of the 15 MVA step-down transformer. The transformer secondary feeds the 13.8 kV medium-voltage bus. Three 1200 A circuit breakers fan out from the MV bus to three distribution feeders — each breaker isolates its feeder independently.\"\n },\n {\n \"slug\": \"sociogram-playground-dynamics\",\n \"diagram\": \"sociogram\",\n \"title\": \"Playground dynamics\",\n \"description\": \"Moreno sociogram of classroom playground dynamics — mutual friendships, one-way choices, and peer conflicts mapped by gender group using force-directed layout.\",\n \"standard\": \"Moreno 1934\",\n \"tags\": [\n \"groups\",\n \"force-directed\",\n \"mutual\",\n \"conflict\",\n \"classroom\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"sociogram \\\"Playground Dynamics\\\"\\n config: layout = force-directed\\n config: coloring = group\\n\\n group boys [label: \\\"Boys\\\", color: \\\"#42A5F5\\\"]\\n tom; jack; mike; leo\\n\\n group girls [label: \\\"Girls\\\", color: \\\"#EF5350\\\"]\\n anna; beth; chloe; diana\\n\\n tom <-> jack\\n tom -> mike\\n jack -> leo\\n mike -x> leo [label: \\\"conflict\\\"]\\n anna <-> beth\\n anna <-> chloe\\n beth <-> chloe\\n anna -> diana\\n diana -.- tom\\n leo -.- anna\",\n \"notes\": \"## Scenario\\n\\nA school counselor administers a sociometric survey to a class and maps the results to identify social stars, isolates, and conflict pairs. The force-directed layout naturally clusters tight friendship groups and surfaces bridging individuals — the counselor can immediately see who is at social risk and which cross-group connections are worth nurturing.\\n\\n## Annotation key\\n\\n- `group id [label:..., color:...]` — defines a named group; members listed below with `;` separator\\n- `<->` — mutual/reciprocal choice (both children named each other)\\n- `->` — one-way positive nomination (A chose B, B did not choose A)\\n- `-x>` — conflict or rejection edge; rendered with an X marker\\n- `-.-` — neutral / weak tie; neither positive nor negative nomination\\n- `config: layout = force-directed` — uses physics simulation to position nodes; tightly connected nodes cluster naturally\\n- `config: coloring = group` — colors each node by its assigned group\\n\\n## How to read\\n\\nThe diagram shows two tight cliques: the blue boys' group (Tom–Jack mutual friendship, with Leo drifting at the edge) and the red girls' group (Anna–Beth–Chloe triangle). Leo and Mike have a conflict edge — an immediate flag for the counselor. Diana sits between groups with only a weak tie to Tom, suggesting social isolation risk. Anna has the most outward nominations, making her a social star worth engaging as a peer ally.\"\n },\n {\n \"slug\": \"sociogram-team-influence\",\n \"diagram\": \"sociogram\",\n \"title\": \"Engineering team influence mapping\",\n \"description\": \"Informal influence map of an engineering team showing tech leads, senior ICs, and junior members — reveals bridging nodes and isolated individuals.\",\n \"standard\": \"Moreno 1934\",\n \"tags\": [\n \"force-directed\",\n \"groups\",\n \"bridging\",\n \"stars\",\n \"isolates\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"sociogram \\\"Engineering team — informal influence\\\"\\n config: layout = force-directed\\n group leads [label: \\\"Tech leads\\\", color: \\\"#1976D2\\\"]\\n alex; sam\\n group sr [label: \\\"Senior ICs\\\", color: \\\"#66BB6A\\\"]\\n priya; jordan; kim; tao\\n group jr [label: \\\"Junior\\\", color: \\\"#FFA726\\\"]\\n lee; ravi; nina; dev\\n alex <-> sam\\n alex -> priya\\n sam -> jordan\\n priya <-> kim\\n jordan <-> tao\\n kim -> lee\\n priya -> ravi\\n tao -> nina\\n dev -.- lee\\n nina -.- priya\",\n \"notes\": \"## Scenario\\n\\nAn engineering manager runs an informal network analysis survey (\\\"Who do you go to when you're stuck?\\\") and maps the results to identify knowledge hubs, bridging individuals between seniority tiers, and team members who are drifting toward isolation before performance reviews surface the issue.\\n\\n## Annotation key\\n\\n- `group id [label:..., color:...]` — assigns individuals to organizational tiers, color-coded\\n- `<->` — mutual influence; both nominated each other\\n- `->` — one-way influence nomination\\n- `-.-` — weak tie; neither party nominated the other in the survey\\n- The force-directed layout clusters mutual-nomination groups and separates isolates\\n\\n## How to read\\n\\nAlex and Sam (tech leads) are mutually influential. Alex bridges down to Priya, Sam to Jordan — healthy knowledge flow across tiers. Priya and Kim form a strong senior IC hub. Dev and Nina have only weak ties (--. to the network), suggesting integration risk. Dev's only connection is a weak tie to Lee — a coaching opportunity before the next performance cycle.\"\n },\n {\n \"slug\": \"timeline-company-milestones\",\n \"diagram\": \"timeline\",\n \"title\": \"Company milestone history\",\n \"description\": \"Lollipop timeline of a company's first five years — fundraising rounds, key hires, product GAs — suited for an investor deck or anniversary blog post.\",\n \"standard\": \"Timeline convention\",\n \"tags\": [\n \"timeline\",\n \"milestones\",\n \"fundraising\",\n \"company-history\",\n \"pitch\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"timeline \\\"Acme — First Five Years\\\"\\nconfig: style = lollipop\\n\\n2020-06: \\\"Founders meet at Y Combinator\\\" [side: below]\\n2020-11: milestone \\\"Incorporation + pre-seed $1M\\\" [side: above, color: #1565C0]\\n2021-04: \\\"First engineer hired\\\" [side: below]\\n2021-09: milestone \\\"Product beta — 50 design partners\\\" [side: above, color: #2E7D32]\\n2022-03: milestone \\\"Seed round — $6M\\\" [side: above, color: #1565C0]\\n2022-11: \\\"Team reaches 20 people\\\" [side: below]\\n2023-05: milestone \\\"Platform v1 GA\\\" [side: above, color: #6A1B9A]\\n2023-10: milestone \\\"Series A — $22M\\\" [side: above, color: #1565C0]\\n2024-06: \\\"100th enterprise customer\\\" [side: below]\\n2025-01: milestone \\\"Platform v2 launched\\\" [side: above, color: #6A1B9A]\",\n \"notes\": \"## Scenario\\n\\nThe founder drops this into the first page of the fundraising deck. Funding rounds, product GAs, and growth markers alternate above/below the axis, which makes the parallel story — \\\"we raised capital and shipped on time\\\" — visible in one glance. Reviewers who only read the top of the page still get the two-line story.\\n\\n## Annotation key\\n\\n- `style = lollipop` — dot-on-stick markers alternating above/below axis\\n- `milestone` — diamond marker for headline events\\n- `[side: above|below]` — explicit placement\\n- `[color: #hex]` — colour-code category (fundraising / product / team)\\n\\n## How to read\\n\\nTime runs left to right. Each marker is a single dated event; *milestone* markers are the diamond-shaped headline items (fundraising, GAs). Colour carries category: blue = fundraising, purple = product, green = early commercial traction. Events below the axis are supporting context (people, growth stats); events above are the announceable headlines.\"\n },\n {\n \"slug\": \"timeline-product-launch\",\n \"diagram\": \"timeline\",\n \"title\": \"Product launch timeline\",\n \"description\": \"Gantt-style timeline for a three-month product launch — overlapping workstreams, two milestones, and a freeze window, used for exec status updates.\",\n \"standard\": \"Timeline / Gantt convention\",\n \"tags\": [\n \"timeline\",\n \"gantt\",\n \"product-launch\",\n \"scheduling\",\n \"program\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"timeline \\\"Platform v2 Launch\\\"\\nconfig: style = gantt\\n\\n2025-07-01 - 2025-08-15: \\\"Engineering build\\\" [category: \\\"engineering\\\"]\\n2025-07-15 - 2025-08-31: \\\"Design polish\\\" [category: \\\"design\\\"]\\n2025-08-01 - 2025-09-10: \\\"Marketing collateral\\\" [category: \\\"marketing\\\"]\\n2025-08-20: milestone \\\"Feature freeze\\\" [color: #E53935]\\n2025-08-20 - 2025-09-05: \\\"QA hardening\\\" [category: \\\"engineering\\\"]\\n2025-09-01 - 2025-09-12: \\\"Press embargo outreach\\\" [category: \\\"marketing\\\"]\\n2025-09-15: milestone \\\"Public launch\\\" [color: #2E7D32]\",\n \"notes\": \"## Scenario\\n\\nThe launch PM shares this in weekly exec status. Overlapping bars show where workstreams parallelize (design polishing while engineering still builds) and the feature-freeze diamond makes the handoff between build and QA unmissable. The second milestone (public launch) anchors the entire timeline and is the reason every other bar exists.\\n\\n## Annotation key\\n\\n- `DATE - DATE: \\\"Label\\\"` — range (bar) event\\n- `DATE: milestone \\\"Label\\\"` — point milestone (diamond)\\n- `[category: …]` — group colour in the gantt legend\\n- `[color: #hex]` — explicit marker colour\\n\\n## How to read\\n\\nTime flows left to right. Horizontal bars are continuous work; diamonds are instantaneous events. Overlapping bars mean two teams are working simultaneously — fine, so long as they coordinate. The red *Feature freeze* marks the transition from net-new work to hardening; any engineering bar extending past it needs an exception. The green *Public launch* is the terminal milestone every other bar is serving.\"\n },\n {\n \"slug\": \"timing-spi-transaction\",\n \"diagram\": \"timing\",\n \"title\": \"SPI transaction timing diagram\",\n \"description\": \"WaveDrom-compatible SPI timing diagram for an 8-byte master-to-slave transaction with clock, chip-select, MOSI, and MISO signals for firmware documentation.\",\n \"standard\": \"WaveDrom / IEEE 1497\",\n \"tags\": [\n \"SPI\",\n \"digital\",\n \"clock\",\n \"chip-select\",\n \"MOSI\",\n \"MISO\",\n \"WaveDrom\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"timing \\\"SPI Transaction\\\"\\nCLK: pppppppp\\nCS_N: 10000001\\nMOSI: x======= data: [\\\"0xAB\\\",\\\"0xCD\\\",\\\"0xEF\\\",\\\"0x01\\\",\\\"0x02\\\",\\\"0x03\\\",\\\"0x04\\\",\\\"0x05\\\"]\\nMISO: zzzz==== data: [\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"0xFF\\\",\\\"0x12\\\",\\\"0x34\\\",\\\"0x56\\\"]\",\n \"notes\": \"## Scenario\\n\\nA firmware engineer or hardware designer documents an 8-byte SPI master-to-slave transaction for a device driver review or datasheet. The WaveDrom-compatible syntax means the same DSL can be pasted directly into WaveDrom's online editor or embedded in documentation pipelines.\\n\\n## Annotation key\\n\\n- `p` — clock pulse (high period followed by low); each `p` is one clock cycle\\n- `1` / `0` — logic high / logic low\\n- `=` — data bus: stable data (value unchanged from previous cycle)\\n- `x` — don't-care or undefined state (transition state)\\n- `z` — high-impedance (floating / tri-state)\\n- `data: [...]` — optional data labels for each stable segment, rendered inside the bus bar\\n- `CS_N` — active-low chip select; `1` = deselected, `0` = selected\\n\\n## How to read\\n\\nThe clock runs for 8 cycles. CS_N goes low at cycle 1 and returns high at cycle 8, framing the transaction. MOSI (master out) sends 8 bytes starting at cycle 1. MISO (slave in) is high-Z for the first 4 cycles (slave preparing the response) then transitions to stable data bytes 5–8. The transaction completes when CS_N de-asserts.\"\n },\n {\n \"slug\": \"venn-customer-segments\",\n \"diagram\": \"venn\",\n \"title\": \"Customer segment overlap\",\n \"description\": \"Three-set Venn showing email subscriber, paid-user, and mobile-app-user overlap with counts for every region — useful for lifecycle marketing planning.\",\n \"standard\": \"Venn (1880)\",\n \"tags\": [\n \"segmentation\",\n \"marketing\",\n \"analytics\",\n \"venn\",\n \"audience\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"venn \\\"Customer Segments — Q3 2025\\\"\\nset email \\\"Email subscribers\\\" [color: \\\"#1E88E5\\\"]\\nset paid \\\"Paid users\\\" [color: \\\"#E53935\\\"]\\nset mobile \\\"Mobile app users\\\" [color: \\\"#43A047\\\"]\\nemail & paid : 1840\\nemail & mobile : 920\\npaid & mobile : 2100\\nemail & paid & mobile : 650\\nemail only : 12400\\npaid only : 3200\\nmobile only : 8700\",\n \"notes\": \"## Scenario\\n\\nA lifecycle marketer is planning Q4 campaigns and needs to see which audiences overlap before deciding where to spend budget. The 650-strong triple intersection is the highest-LTV segment; the 12.4k email-only group is the biggest conversion opportunity. Putting the numbers on one Venn makes the gaps and overlaps argue for themselves in a 30-second leadership review.\\n\\n## Annotation key\\n\\n- `set ID \\\"Label\\\"` — declare a circle\\n- `A & B : n` — count in the intersection of A and B\\n- `A only : n` — count exclusive to A\\n\\n## How to read\\n\\nEach circle is a total audience; each overlap is people who belong to multiple audiences. The triple intersection (email ∩ paid ∩ mobile, 650 users) is your most engaged cohort — the obvious group to upsell. The *email only* and *mobile only* exclusive regions are your largest activation opportunities because each represents users who have not yet crossed into the other channels.\"\n },\n {\n \"slug\": \"venn-programming-paradigms\",\n \"diagram\": \"venn\",\n \"title\": \"Programming paradigm overlap\",\n \"description\": \"Venn diagram showing the intersection of object-oriented, functional, and logic programming paradigms with language counts — a teaching aid for CS curricula.\",\n \"standard\": \"Venn (1880)\",\n \"tags\": [\n \"education\",\n \"programming\",\n \"paradigms\",\n \"venn\",\n \"teaching\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"venn \\\"Programming Paradigms\\\"\\nset oop \\\"Object-Oriented\\\" [color: \\\"#1E88E5\\\"]\\nset fp \\\"Functional\\\" [color: \\\"#E53935\\\"]\\nset logic \\\"Logic\\\" [color: \\\"#43A047\\\"]\\noop & fp : 180\\noop & logic : 45\\nfp & logic : 90\\noop & fp & logic : 12\\noop only : 620\\nfp only : 340\\nlogic only : 95\",\n \"notes\": \"## Scenario\\n\\nA CS instructor opens a lecture on programming paradigms with this Venn. Counts come from a language survey — how many languages offer strong OOP, FP, and logic support. The tiny triple-overlap (12) tells students that languages supporting all three well are rare; the large OOP-only region shows industry gravity; the FP ∩ logic overlap (90) is where languages like Prolog-with-lambdas sit.\\n\\n## Annotation key\\n\\n- `set ID \\\"Label\\\"` — paradigm circle\\n- `A & B : n` — number of languages supporting both paradigms strongly\\n- `A only : n` — languages committed to a single paradigm\\n\\n## How to read\\n\\nEach circle represents a paradigm. Overlapping regions count multi-paradigm languages. The `oop & fp` intersection (180) is where modern mainstream languages — Scala, Kotlin, Swift, TypeScript — sit. The tiny triple intersection (12) is a reminder that truly multi-paradigm language design is expensive; the larger exclusive regions show how few languages commit to logic programming at all.\"\n }\n];\n\nexport const SYNTAX: Readonly<Record<string, GeneratedSyntax>> = {\n \"genogram\": {\n \"title\": \"Genogram\",\n \"content\": \"## 1. Your first genogram\\n\\nThe smallest clinically useful genogram: two parents, one child.\\n\\n```\\ngenogram\\n alice [female, 1980]\\n bob [male, 1978]\\n alice -- bob \\\"m. 2005\\\"\\n carol [female, 2008]\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with the keyword `genogram`, optionally followed by a quoted title.\\n2. Declare each person on their own line: `id [attributes]`. Attributes go in square brackets, comma-separated.\\n3. Connect two people with a **couple operator** — `--` (marriage) here; see §4.1 for all six. A trailing quoted string is the relationship label.\\n4. **Indent under the couple line** to add their children.\\n\\n> Comments must be on their own line, starting with `#`. Trailing inline comments (`bob [male, 1978] # ...`) are not supported and will break the parser — see §8.\\n\\n---\\n\\n## 2. Individuals\\n\\nAn individual line is `id [attr1, attr2, …]`. Attributes are comma-separated, order-independent, all optional.\\n\\n**ID rules.** Must match `[a-zA-Z][a-zA-Z0-9_-]*`. IDs are case-insensitive internally but preserve their original casing as the display label (override with `label:\\\"…\\\"`).\\n\\n**Attributes accepted by the parser today:**\\n\\n| Attribute | Values | Effect |\\n|---|---|---|\\n| Sex | `male`, `female`, `unknown`, `other` | Shape: square, circle, diamond, diamond |\\n| Status | `deceased`, `stillborn`, `miscarriage`, `abortion` | Visual modifier (X-out, scaled shape, etc.) |\\n| Birth year | 4-digit number, e.g. `1980` | First 4-digit token = birth year |\\n| Death year | 4-digit number after birth, e.g. `1980, 2055` | Second 4-digit token = death year |\\n| `index` | flag | Concentric shape = identified patient |\\n| `unknown-siblings` | flag | Diamond with `?` — placeholder for ≥1 siblings of unknown count |\\n| `age:N` | e.g. `age:42` | Age shown inside shape |\\n| `death:YYYY` | e.g. `death:2020` | Explicit death year |\\n| `label:\\\"…\\\"` | e.g. `label:\\\"Dr. Smith\\\"` | Display label override |\\n| `sibling-of:<id>` | e.g. `sibling-of:monica` | Pins same generation as the referenced sibling, draws a dashed bracket — for known relatives with unknown ancestry. |\\n| `conditions:…` | see §5 | Medical/psychological conditions |\\n| `key:value` | any custom | Stored as metadata |\\n\\n```\\ngenogram\\n grandma [female, 1920, 2002, deceased]\\n dad [male, 1950, age:74]\\n me [male, 1985, index]\\n daughter [female, 2012, label:\\\"Em\\\"]\\n```\\n\\n---\\n\\n## 3. Shapes\\n\\n| Visual | Sex value | Meaning |\\n|---|---|---|\\n| ☐ Square | `male` | Male |\\n| ○ Circle | `female` | Female |\\n| ◇ Diamond | `unknown`, `other`, *or* attribute omitted | Unknown / unspecified |\\n\\nStatus modifiers layer on top of the base shape:\\n\\n```\\ngenogram\\n alive [male, 1960]\\n passed [male, 1930, 2010, deceased]\\n stillborn_child [unknown, stillborn]\\n lost [unknown, miscarriage]\\n```\\n\\n---\\n\\n## 4. Connections\\n\\n### 4.1 Couple operators\\n\\nThe parser tries these in order. The first one that matches wins — so `-x-` beats `--`.\\n\\n| Operator | Type | Example | Meaning |\\n|---|---|---|---|\\n| `-x-` | divorced | `a -x- b` | Divorce |\\n| `-/-` | separated | `a -/- b` | Separation (married) |\\n| `-//` | separated | `a -// b` | Separation (alias for `-/-`) |\\n| `-o-` | engaged | `a -o- b` | Engagement |\\n| `==` | consanguineous | `a == b` | Blood-related couple |\\n| `--` | married | `a -- b` | Marriage |\\n| `~` | cohabiting | `a ~ b` | Cohabiting / LTR (current) |\\n| `~/~` | cohabiting-ended | `a ~/~ b` | Cohabitation has ended (never-married). Common in LATAM child-protection caseloads where biological parents lived together unmarried and the relationship has since broken — distinct from `-x-` divorce (no marriage) and `-/-` separation (still married). |\\n\\nA trailing quoted string becomes the relationship label (`a -- b \\\"m. 2005\\\"`).\\n\\n### 4.2 Inline individual on the right side\\n\\nIf the right-hand person hasn't been declared yet, you can declare them in-place:\\n\\n```\\ngenogram\\n ann [female, 1970]\\n ann -- ben [male, 1968] \\\"m. 1995\\\"\\n kim [female, 1997]\\n```\\n\\n### 4.3 Children (indented under a couple)\\n\\nIndentation under a couple line = \\\"these are the children of this couple.\\\" Any indent greater than the couple's indent works; by convention use 2 more spaces. Children are rendered in order of declaration (render also sorts by birth year when present).\\n\\n```\\ngenogram\\n dad [male, 1950]\\n mom [female, 1952]\\n dad -- mom\\n eldest [male, 1975]\\n middle [female, 1978, adopted]\\n twin_a [male, 1985, twin-identical]\\n twin_b [male, 1985, twin-identical]\\n```\\n\\n**Special child attributes:**\\n\\n| Attribute | Effect |\\n|---|---|\\n| `adopted` | Adoption line style |\\n| `foster` | Foster relationship |\\n| `guardian` | Guardianship by a non-parent relative (e.g. grandparent custody). Same primitive as `foster` — drawn as a secondary \\\"current caregiver\\\" link when biological parents are also declared. |\\n| `twin-identical` | Grouped with other `twin-identical` children of the same couple |\\n| `twin-fraternal` | Grouped with other `twin-fraternal` children |\\n| `unknown-siblings` | Single diamond with `?` glyph — \\\"≥1 siblings, count and identities unknown\\\" (pedigree convention). |\\n\\n### 4.3.1 Dual-parent families (foster, adoption, guardianship)\\n\\nChildren placed with a non-biological caregiver while biological parents are still part of the case can be declared under both couples. **Declare the child with full attributes the first time** (under the biological couple), then **redeclare with just `[foster]` / `[adopted]` / `[guardian]`** under the current caregiver. The first declaration wins layout; the second is drawn as a secondary dotted \\\"current caregiver\\\" link that does not pull the child away from their biological position.\\n\\n```\\ngenogram \\\"Foster placement\\\"\\n bp1 [male, label: \\\"Bio dad\\\"]\\n bp2 [female, label: \\\"Bio mom\\\"]\\n bp1 ~/~ bp2\\n child [male, 2018, index]\\n fp1 [male, label: \\\"Foster dad\\\"]\\n fp2 [female, label: \\\"Foster mom\\\"]\\n fp1 -- fp2\\n own [male, 2010]\\n child [foster]\\n```\\n\\nThe same primitive serves adoption (closed/open), foster placement, and guardianship by a relative — only the keyword differs. Re-declaration **merges** non-conflicting attributes (sex, birth year, label, `index` marker) into the original; declaring a conflicting `male` vs `female` raises a parse error rather than silently overwriting.\\n\\n### 4.3.2 Unknown-count siblings\\n\\nWhen a case file mentions \\\"the child has siblings\\\" without naming them, use either the `?` shorthand on its own line, or `[unknown-siblings]` on a regular id. Both render as a single diamond with a \\\"?\\\" glyph — the standard pedigree marker for \\\"one or more siblings, identities unknown.\\\"\\n\\n```\\ngenogram\\n dad [male]\\n mom [female]\\n dad -- mom\\n ?\\n known_kid [male, 2018]\\n```\\n\\n### 4.3.3 Sibling-of (known relative, unknown ancestry)\\n\\nTo express \\\"X is a sibling of Y\\\" without inventing parents, use the `sibling-of: <id>` property. The renderer pins X to Y's generation and draws a dashed bracket above the two — the standard pedigree convention for a known relative whose ancestry is not part of the case.\\n\\n```\\ngenogram\\n monica [female, 1990]\\n uncle [male, label: \\\"Tío materno\\\", sibling-of: monica]\\n```\\n\\n### 4.4 Emotional relationships\\n\\nSeparate line, parser pattern `A -TYPE- B` (non-directional) or `A -TYPE-> B` (directional). An optional quoted label goes at the end. **Both individuals must already be declared** before the emotional line.\\n\\n```\\nharry -cutoff- petunia # non-directional\\nharry -hostile- dudley \\\"since 1991\\\"\\nuncle -abuse-> nephew # directional (arrow)\\n```\\n\\nAll 32 types the parser accepts today:\\n\\n| Category | Types |\\n|---|---|\\n| Positive / close | `harmony`, `close`, `bestfriends`, `love`, `inlove`, `friendship` |\\n| Negative / hostile | `hostile`, `conflict`, `enmity`, `distant-hostile`, `cutoff` |\\n| Ambivalent | `close-hostile`, `fused`, `fused-hostile` |\\n| Distance | `distant`, `normal`, `nevermet` |\\n| Abuse *(directional)* | `abuse`, `physical-abuse`, `emotional-abuse`, `sexual-abuse`, `neglect` |\\n| Control *(directional)* | `manipulative`, `controlling`, `jealous` |\\n| Special | `focused`, `focused-neg`, `distrust`, `admirer`, `limerence` |\\n\\n```\\ngenogram\\n dad [male, 1950]\\n son [male, 1985]\\n daughter [female, 1988]\\n dad -close- daughter\\n dad -conflict- son\\n son -cutoff- dad \\\"since 2010\\\"\\n```\\n\\n---\\n\\n## 5. Medical conditions\\n\\nSyntax: `conditions: name(fill) [+ name(fill, #color)]…`\\n\\n```\\nfather [male, 1945, conditions: heart(full, #E53935)]\\nmother [female, 1948, conditions: diabetes(half-left) + anxiety(half-right, #26A69A)]\\n```\\n\\n- **`name`** — any identifier you choose (displayed in legend/tooltip).\\n- **`fill`** — required, controls which region of the shape is colored. See table below.\\n- **`color`** — optional hex. Default depends on the renderer theme.\\n- Multiple conditions are joined with `+`. Each needs its own `(fill)`.\\n\\n**Fill positions:**\\n\\n| `fill` value | Region |\\n|---|---|\\n| `full` | Entire shape |\\n| `half-left` / `half-right` | Left / right half |\\n| `half-top` / `half-bottom` | Top / bottom half |\\n| `quad-tl` / `quad-tr` / `quad-bl` / `quad-br` | One quadrant |\\n| `striped` | Diagonal stripe pattern (asymptomatic carrier) |\\n| `dotted` | Dot pattern |\\n\\n```\\ngenogram\\n dad [male, 1950, conditions: heart(full, #E53935)]\\n mom [female, 1952, conditions: diabetes(half-left) + depression(half-right, #5C6BC0)]\\n dad -- mom\\n son [male, 1980, conditions: carrier(striped)]\\n```\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Title:** `genogram \\\"Smith Family\\\"` — first line only.\\n- **Person label override:** `alice [female, label:\\\"Dr. Alice Smith\\\"]`.\\n- **Relationship label:** trailing quoted string on a couple or emotional line — `alice -- bob \\\"m. 2005\\\"`.\\n- **Comments:** `#` at the start of a line (after leading whitespace). Inline comments are **not** supported.\\n\\n```\\ngenogram \\\"Smith Family\\\"\\n # this line is a comment — fine\\n alice [female, 1980] # ← THIS trailing comment breaks the parser\\n```\\n\\n---\\n\\n## 7. Reserved words & escaping\\n\\n**Reserved at line start:** `genogram` (header keyword).\\n\\n**Reserved operator tokens** inside a line — avoid using these sequences in IDs:\\n`--`, `~`, `~/~`, `==`, `-x-`, `-/-`, `-//`, `-o-`, and any `-<type>-` / `-<type>->` matching an emotional-relationship type.\\n\\n**Reserved id `?`** — bare `?` on a child line auto-generates a synthetic placeholder with the `unknown-siblings` marker. Do not use `?` as a real id.\\n\\n**Strings with spaces** must be double-quoted: titles, labels, `label:\\\"…\\\"`. Single quotes and backticks are not recognized.\\n\\n---\\n\\n## 8. Common mistakes\\n\\nReal parser errors, what triggers them, and how to fix.\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `alex [nonbinary, 1995]` | `Unknown property 'nonbinary'` | Use `unknown` or `other` (nonbinary is §13 Roadmap) |\\n| `alice [female, transgender]` | `Unknown property 'transgender'` | Not yet parseable (§13 Roadmap) |\\n| `dad -- mom` ← followed by `child [male, 2010]` at the **same indent** | Child parsed as a new top-level individual, not as their child | Indent the child line deeper than the couple line (2 spaces is enough) |\\n| `A -- B` where `A` was never declared | `Unknown individual 'A'` | Declare `A [sex, year]` on a line above |\\n| `father -- mother \\\"married\\\"` on line 1 (no `genogram` header) | `Expected \\\"genogram\\\" header` | Start the file with `genogram` or `genogram \\\"Title\\\"` |\\n| `conditions: diabetes + cancer` (no parens) | `Invalid condition format 'diabetes'` | Add fill: `conditions: diabetes(half-left) + cancer(half-right)` |\\n| `[triplet-identical]` | `Unknown property 'triplet-identical'` | Triplets not yet parseable (§13 Roadmap) |\\n| `dad -- mom # first marriage` | Trailing inline `#` comment is treated as part of the label / errors | Move the comment to its own line |\\n| Same id declared twice with different sex (`x [male]` then `x [female]`) | `Conflicting sex for 'x': previously 'male', now 'female'` | Pick one or rename one of the ids |\\n| `child [foster]` redeclared but biological parents never declared | `child` becomes the foster couple's regular child (no secondary link drawn) | This is intentional — secondary links require an existing primary parent-child rel from a prior declaration |\\n\\n---\\n\\n## 9. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | individual | couple-block | emotional)*\\n\\nheader = \\\"genogram\\\" ( WS quoted-string )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nindividual = INDENT id ( \\\"[\\\" attrs \\\"]\\\" )? NEWLINE\\ncouple-block = INDENT id WS coupleOp WS right-side ( WS quoted-string )? NEWLINE\\n ( deeper-indent child )*\\nchild = INDENT id ( \\\"[\\\" attrs \\\"]\\\" )? NEWLINE\\n | INDENT \\\"?\\\" NEWLINE // unknown-count sibling shorthand\\nright-side = id ( \\\"[\\\" attrs \\\"]\\\" )?\\n\\nemotional = INDENT id WS \\\"-\\\" type \\\"-\\\" id ( WS quoted-string )? NEWLINE\\n | INDENT id WS \\\"-\\\" type \\\"->\\\" id ( WS quoted-string )? NEWLINE\\n\\ncoupleOp = \\\"~/~\\\" | \\\"-//\\\" | \\\"-x-\\\" | \\\"-/-\\\" | \\\"-o-\\\" | \\\"==\\\" | \\\"--\\\" | \\\"~\\\"\\ntype = \\\"harmony\\\" | \\\"close\\\" | \\\"bestfriends\\\" | \\\"love\\\" | \\\"inlove\\\"\\n | \\\"friendship\\\" | \\\"hostile\\\" | \\\"conflict\\\" | \\\"enmity\\\"\\n | \\\"distant-hostile\\\" | \\\"cutoff\\\" | \\\"close-hostile\\\" | \\\"fused\\\"\\n | \\\"fused-hostile\\\" | \\\"distant\\\" | \\\"normal\\\" | \\\"nevermet\\\"\\n | \\\"abuse\\\" | \\\"physical-abuse\\\" | \\\"emotional-abuse\\\"\\n | \\\"sexual-abuse\\\" | \\\"neglect\\\" | \\\"manipulative\\\" | \\\"controlling\\\"\\n | \\\"jealous\\\" | \\\"focused\\\" | \\\"focused-neg\\\" | \\\"distrust\\\"\\n | \\\"admirer\\\" | \\\"limerence\\\"\\n\\nid = [a-zA-Z] [a-zA-Z0-9_-]*\\nattrs = attr (\\\",\\\" attr)*\\nattr = \\\"male\\\" | \\\"female\\\" | \\\"unknown\\\" | \\\"other\\\"\\n | \\\"deceased\\\" | \\\"stillborn\\\" | \\\"miscarriage\\\" | \\\"abortion\\\"\\n | \\\"adopted\\\" | \\\"foster\\\" | \\\"guardian\\\"\\n | \\\"twin-identical\\\" | \\\"twin-fraternal\\\"\\n | \\\"index\\\" | \\\"unknown-siblings\\\"\\n | digit digit digit digit // year\\n | \\\"age\\\" \\\":\\\" digits\\n | \\\"death\\\" \\\":\\\" digit digit digit digit\\n | \\\"label\\\" \\\":\\\" quoted-string\\n | \\\"sibling-of\\\" \\\":\\\" id\\n | \\\"conditions\\\" \\\":\\\" condition (\\\"+\\\" condition)*\\n | key \\\":\\\" value // custom\\ncondition = name \\\"(\\\" fill (\\\",\\\" \\\"#\\\" hex)? \\\")\\\"\\nfill = \\\"full\\\" | \\\"half-left\\\" | \\\"half-right\\\" | \\\"half-top\\\" | \\\"half-bottom\\\"\\n | \\\"quad-tl\\\" | \\\"quad-tr\\\" | \\\"quad-bl\\\" | \\\"quad-br\\\"\\n | \\\"striped\\\" | \\\"dotted\\\"\\n\\ncomment = INDENT \\\"#\\\" any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/genogram/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"ecomap\": {\n \"title\": \"Ecomap\",\n \"content\": \"## 1. Your first ecomap\\n\\nThe smallest useful ecomap: one center and three outside systems.\\n\\n```\\necomap\\n center: client [label: \\\"Maria\\\"]\\n mom [label: \\\"Mother\\\", category: family]\\n work [label: \\\"Tech Corp\\\", category: work]\\n therapist [label: \\\"Dr. Patel\\\", category: mental-health]\\n mom === client\\n work --- client\\n therapist <-> client [label: \\\"weekly\\\"]\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with the keyword `ecomap`, optionally followed by a quoted title.\\n2. Declare the center on its own line: `center: id [label: \\\"…\\\"]`. Exactly one center per diagram.\\n3. Declare each outside system on its own line: `id [label: \\\"…\\\", category: …]`.\\n4. Connect any two declared IDs with a **connection operator** — `===` (strong), `---` (normal), `<->` (reciprocal), etc. See §3 for the full table. A trailing `[label: \\\"…\\\"]` adds an edge label.\\n\\n> Comments must be on their own line, starting with `#`. Inline trailing comments will break the parser.\\n\\n---\\n\\n## 2. Center and outside systems\\n\\nEvery ecomap has one **center** and any number of **outside systems**. Both use the same `id [attrs]` syntax; the only difference is the `center:` prefix.\\n\\n**ID rules.** Must match `[a-zA-Z][a-zA-Z0-9_-]*`. IDs are case-insensitive; the original token is kept as the default label.\\n\\n**Attributes accepted by the parser today:**\\n\\n| Attribute | Values | Effect |\\n|---|---|---|\\n| `label:\\\"…\\\"` | any quoted string | Display label override |\\n| `category:…` | see §2.1 | Color / grouping of a system node |\\n| `size:…` | `small`, `medium`, `large` | Node size |\\n| `importance:…` | `major`, `moderate`, `minor` | Visual weight |\\n| `sector:…` | `top`, `right`, `bottom`, `left` | Hint for which side of the center the system sits on |\\n| `age:N` | e.g. `age:34` | Age shown inside a person-typed center |\\n| `male` / `female` / `unknown` | flag | Sex for a person-typed center |\\n\\n### 2.1 System categories\\n\\nCategories color-code outside systems by the life domain they belong to. The parser accepts any string — these are the values the renderer has themed palettes for:\\n\\n| Category | Typical examples |\\n|---|---|\\n| `family` | Extended family, in-laws, cousins |\\n| `friends` | Friends, neighbors |\\n| `work` | Employer, coworkers |\\n| `education` | School, college, training program |\\n| `health` | Primary care, specialist, hospital |\\n| `mental-health` | Therapist, psychiatrist, support group |\\n| `religion` | Church, temple, spiritual community |\\n| `recreation` | Sports, hobbies, clubs |\\n| `legal` | Lawyer, probation, court |\\n| `government` | Social services, housing, immigration |\\n| `financial` | Bank, benefits, financial aid |\\n| `community` | Neighborhood groups, sponsors |\\n| `cultural` | Cultural/ethnic organizations |\\n| `substance` | Recovery programs or, if negative, active-use sources |\\n| `technology` | Online community, support forum |\\n| `pet` | Pets, service animals |\\n\\n```\\necomap\\n center: client [label: \\\"Marcus, age 15\\\"]\\n mom [label: \\\"Mother\\\", category: family]\\n dad [label: \\\"Father (divorced)\\\", category: family]\\n school [label: \\\"East High School\\\", category: education]\\n coach [label: \\\"Soccer Coach\\\", category: community]\\n therapist [label: \\\"Ms. Chen\\\", category: mental-health]\\n mom === client\\n dad --- client [label: \\\"EOW weekends\\\"]\\n school === client\\n coach --> client [label: \\\"mentor\\\"]\\n therapist <-> client [label: \\\"weekly\\\"]\\n```\\n\\n---\\n\\n## 3. Connections\\n\\nA connection is one line: `fromId OP toId` optionally followed by `[label: \\\"…\\\"]`. Both IDs must already be declared (center counts).\\n\\n### 3.1 Relationship-quality operators\\n\\n| Operator | Type | Meaning |\\n|---|---|---|\\n| `===` | strong | Close, supportive, high-frequency |\\n| `==` | moderate | Positive, moderate involvement |\\n| `---` | normal | Neutral / average |\\n| `- -` | weak | Tenuous, fragile, early-stage |\\n| `~~~` | stressful | Stressful relationship |\\n| `~=~` | stressful-strong | Close *and* stressful |\\n| `~x~` | conflictual | Active conflict |\\n| `-/-` | broken | Severed, estranged, cutoff |\\n\\n### 3.2 Energy-flow operators\\n\\nLayer arrow direction onto strong or normal lines:\\n\\n| Operator | Meaning |\\n|---|---|\\n| `-->` | One-way: energy flows from center to system |\\n| `<--` | One-way: energy flows from system to center |\\n| `<->` | Reciprocal / bidirectional |\\n| `===>` | Strong one-way outflow (draining) |\\n| `<===` | Strong one-way inflow (nourishing) |\\n| `<=>` | Strong reciprocal |\\n| `==>` | Moderate one-way outflow |\\n| `<==` | Moderate one-way inflow |\\n\\nThe parser normalizes direction so arrows read relative to the center. Writing `family === resettlement` and `resettlement === family` produces the same diagram; writing `clinic --> family` vs `family <-- clinic` likewise produces the same arrow pointing *from* the clinic *to* the family.\\n\\n```\\necomap\\n center: client [label: \\\"Rosa\\\"]\\n mom [category: family]\\n ex [category: family]\\n aa [label: \\\"AA Group\\\", category: substance]\\n job [label: \\\"Warehouse\\\", category: work]\\n mom === client [label: \\\"daily calls\\\"]\\n ex ~x~ client [label: \\\"custody disputes\\\"]\\n aa <== client [label: \\\"sponsor support\\\"]\\n job - - client [label: \\\"unstable hours\\\"]\\n```\\n\\n### 3.3 Edge labels\\n\\nA trailing `[label: \\\"…\\\"]` is the only attribute a connection line accepts. Put schedule, nature, or a short note here:\\n\\n```\\nfamily === temple [label: \\\"weekly service\\\"]\\nclinic --> family [label: \\\"vaccinations\\\"]\\ncaseworker <-> family [label: \\\"every Tuesday\\\"]\\n```\\n\\n---\\n\\n## 4. Labels & comments\\n\\n- **Title:** `ecomap \\\"Nguyen Family\\\"` — first line only.\\n- **Node label override:** `family [label: \\\"The Nguyens\\\"]`.\\n- **Edge label:** trailing `[label: \\\"…\\\"]` on a connection line.\\n- **Comments:** `#` or `//` at the start of a line (after leading whitespace). Inline trailing comments are **not** supported.\\n\\n```\\necomap \\\"Marcus Intake\\\"\\n # caseworker's notes — fine\\n // also fine\\n mom [category: family] # ← THIS trailing comment breaks the parser\\n```\\n\\n---\\n\\n## 5. Reserved words & escaping\\n\\n**Reserved at line start:** `ecomap`, `center:`.\\n\\n**Reserved operator tokens** inside a line — avoid using these sequences in IDs:\\n`===`, `==`, `---`, `- -`, `~~~`, `~=~`, `~x~`, `-/-`, and the directional variants listed in §3.2.\\n\\n**Strings with spaces** must be double-quoted: titles and any label. Single quotes and backticks are not recognized.\\n\\n---\\n\\n## 6. Common mistakes\\n\\nReal parser errors, what triggers them, and how to fix.\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `family -- school` | `Unexpected: family -- school` | Ecomap uses `===`/`---`/`<->` etc. `--` is a genogram/pedigree operator |\\n| `family === school` where `school` was never declared | Silently creates an empty `school` node with no label/category | Declare systems above their connection lines |\\n| No `center:` anywhere | Renders but with no visual center anchor | Every ecomap needs exactly one `center:` line |\\n| Two `center:` lines | Only the first is treated as center; the second becomes a regular system | Pick one |\\n| `family==school` (no spaces) | `Unexpected: family==school` | Operators require a space on each side |\\n| `family === school [weekly]` | Bare token `weekly` is parsed as a property flag, no label shown | Use `[label: \\\"weekly\\\"]` |\\n| `family === school # daily` | Trailing `#` is consumed as part of the line | Move the comment above |\\n\\n---\\n\\n## 7. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | center | system | connection)*\\n\\nheader = \\\"ecomap\\\" ( WS quoted-string )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\ncenter = \\\"center:\\\" WS id ( \\\"[\\\" attrs \\\"]\\\" )? NEWLINE\\nsystem = id ( \\\"[\\\" attrs \\\"]\\\" )? NEWLINE\\nconnection = id WS op WS id ( WS \\\"[\\\" \\\"label:\\\" quoted-string \\\"]\\\" )? NEWLINE\\n\\nop = \\\"===\\\" | \\\"==\\\" | \\\"---\\\" | \\\"- -\\\" | \\\"~~~\\\" | \\\"~=~\\\" | \\\"~x~\\\" | \\\"-/-\\\"\\n | \\\"===>\\\" | \\\"<===\\\" | \\\"<=>\\\" | \\\"==>\\\" | \\\"<==\\\"\\n | \\\"-->\\\" | \\\"<--\\\" | \\\"<->\\\"\\n\\nid = [a-zA-Z] [a-zA-Z0-9_-]*\\nattrs = attr (\\\",\\\" attr)*\\nattr = \\\"label\\\" \\\":\\\" quoted-string\\n | \\\"category\\\" \\\":\\\" category\\n | \\\"size\\\" \\\":\\\" (\\\"small\\\" | \\\"medium\\\" | \\\"large\\\")\\n | \\\"importance\\\" \\\":\\\" (\\\"major\\\" | \\\"moderate\\\" | \\\"minor\\\")\\n | \\\"sector\\\" \\\":\\\" (\\\"top\\\" | \\\"right\\\" | \\\"bottom\\\" | \\\"left\\\")\\n | \\\"age\\\" \\\":\\\" digits\\n | \\\"male\\\" | \\\"female\\\" | \\\"unknown\\\"\\n | key \\\":\\\" value // custom, stored as metadata\\n\\ncategory = \\\"family\\\" | \\\"friends\\\" | \\\"work\\\" | \\\"education\\\" | \\\"health\\\"\\n | \\\"mental-health\\\" | \\\"religion\\\" | \\\"recreation\\\" | \\\"legal\\\"\\n | \\\"government\\\" | \\\"financial\\\" | \\\"community\\\" | \\\"cultural\\\"\\n | \\\"substance\\\" | \\\"technology\\\" | \\\"pet\\\" | \\\"other\\\"\\n\\ncomment = ( \\\"#\\\" | \\\"//\\\" ) any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/ecomap/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"pedigree\": {\n \"title\": \"Pedigree\",\n \"content\": \"## 1. Your first pedigree\\n\\nThe smallest clinically useful pedigree: two parents and their affected child.\\n\\n```\\npedigree\\n I-1 [male, carrier]\\n I-2 [female, carrier]\\n I-1 -- I-2\\n II-1 [male, affected, proband]\\n II-2 [female, unaffected]\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with the keyword `pedigree`, optionally followed by a quoted title.\\n2. Declare each individual on their own line: `id [attributes]`. Conventionally IDs are `I-1`, `II-3`, etc. — Roman-numeral generation, dash, position within the generation.\\n3. Connect two individuals with a **couple operator** — `--` (mated), `==` (consanguineous), `-/-` (separated), `~` (no offspring). See §4.\\n4. **Indent under the couple line** to add their children. Any deeper indent works; two spaces is conventional.\\n\\n> Comments must be on their own line, starting with `#`. Inline trailing comments will break the parser.\\n\\n---\\n\\n## 2. Individuals\\n\\nAn individual line is `id [attr1, attr2, …]`. Attributes are comma-separated, order-independent, all optional.\\n\\n**ID rules.** Must match `[a-zA-Z][a-zA-Z0-9_-]*`. IDs are case-insensitive internally but preserve their original casing as the display label (override with `label:\\\"…\\\"`).\\n\\n**Attributes accepted by the parser today:**\\n\\n| Attribute | Values | Effect |\\n|---|---|---|\\n| Sex | `male`, `female`, `unknown`, `amab`, `afab`, `uaab` | Shape: square, circle, diamond (see §3) |\\n| Genetic status | `unaffected`, `affected`, `carrier`, `carrier-x`, `obligate-carrier`, `presymptomatic` | Fill / inner marker (see §3) |\\n| Marker | `proband`, `consultand`, `evaluated` | Arrow + letter annotation (see §3.3) |\\n| Life status | `deceased`, `stillborn`, `pregnancy`, `sab`, `tab`, `ectopic` | Visual modifier |\\n| Birth year | 4-digit number, e.g. `1958` | Shown below shape |\\n| `label:\\\"…\\\"` | any quoted string | Display label override |\\n| `affected: trait1+trait2` | see §5 | Multi-trait quadrant fill |\\n\\n```\\npedigree\\n I-1 [male, 1942, deceased]\\n I-2 [female, 1945, affected, deceased]\\n II-1 [female, affected, proband, label: \\\"Jane (42)\\\"]\\n II-2 [male, evaluated]\\n II-3 [female, presymptomatic]\\n```\\n\\n---\\n\\n## 3. Shapes, status, markers\\n\\n### 3.1 Shapes (Bennett 2022)\\n\\n| Visual | Sex value | Meaning |\\n|---|---|---|\\n| ☐ Square | `male` or `amab` | Assigned male at birth |\\n| ○ Circle | `female` or `afab` | Assigned female at birth |\\n| ◇ Diamond | `unknown`, `uaab`, or omitted | Unknown / DSD / not disclosed / in utero |\\n\\nBennett 2022 formalized that square and circle represent **assigned sex at birth**, not gender identity. If gender identity differs, record it in the label (`[female, label: \\\"Trans man (AFAB)\\\"]`) — do not change the shape.\\n\\n### 3.2 Genetic status (fill)\\n\\n| Status | Meaning |\\n|---|---|\\n| (default, no status token) | Unaffected — empty shape |\\n| `unaffected` | Explicit unaffected |\\n| `affected` | Fully filled shape |\\n| `carrier` | Half-filled — autosomal carrier |\\n| `carrier-x` | Center dot — X-linked carrier female |\\n| `obligate-carrier` | Center dot — inferred from pedigree structure |\\n| `presymptomatic` | Vertical line through shape — tested positive, no clinical signs yet |\\n\\n```\\npedigree\\n I-1 [male, unaffected]\\n I-2 [female, affected]\\n I-3 [female, carrier]\\n I-4 [female, carrier-x]\\n I-5 [male, obligate-carrier]\\n I-6 [female, presymptomatic]\\n```\\n\\n### 3.3 Markers\\n\\n| Marker | Meaning |\\n|---|---|\\n| `proband` | Arrow + \\\"P\\\" — the index case who triggered the referral |\\n| `consultand` | Arrow + \\\"C\\\" — the person who sought genetic counseling |\\n| `evaluated` | \\\"E\\\" — evaluated but no positive finding recorded |\\n\\n### 3.4 Life status\\n\\n| Value | Meaning |\\n|---|---|\\n| `deceased` | Diagonal slash across the shape |\\n| `stillborn` | Small shape + \\\"SB\\\" label |\\n| `pregnancy` | Shape + \\\"P\\\" label, or diamond if sex unknown |\\n| `sab` | Small triangle — spontaneous abortion |\\n| `tab` | Small triangle with bar — terminated pregnancy |\\n| `ectopic` | Small triangle + \\\"ECT\\\" label |\\n\\nMultiple tokens combine: `[female, affected, deceased]`, `[male, sab]`, `[unknown, pregnancy, presymptomatic]`.\\n\\n---\\n\\n## 4. Couples and children\\n\\n### 4.1 Couple operators\\n\\nThe parser tries these in order. The first match wins — so `-/-` beats `--`.\\n\\n| Operator | Type | Example | Meaning |\\n|---|---|---|---|\\n| `-/-` | separated | `a -/- b` | Mated pair, no longer together |\\n| `==` | consanguineous | `a == b` | Blood-related union (clinically critical) |\\n| `--` | married | `a -- b` | Mated pair with offspring |\\n| `~` | cohabiting | `a ~ b` | Partners without offspring |\\n\\n### 4.2 Inline individual on the right side\\n\\nIf the right-hand individual has not been declared yet, declare them in place:\\n\\n```\\npedigree\\n I-1 [female, carrier]\\n I-1 -- I-2 [male, unaffected]\\n II-1 [female, affected, proband]\\n II-2 [male, carrier]\\n```\\n\\n### 4.3 Children (indented under a couple)\\n\\nIndentation under a couple line = \\\"these are the children of this couple.\\\" Any indent greater than the couple's indent works; two spaces is conventional.\\n\\n```\\npedigree \\\"Cystic Fibrosis — autosomal recessive\\\"\\n I-1 [male, carrier]\\n I-2 [female, carrier]\\n I-1 -- I-2\\n II-1 [male, affected, proband]\\n II-2 [female, carrier]\\n II-3 [male, unaffected]\\n```\\n\\n### 4.4 Consanguineous unions\\n\\nConsanguinity is rendered as a double line and must be made visible — it is the single most load-bearing piece of information on many pedigrees.\\n\\n```\\npedigree \\\"Consanguineous union\\\"\\n I-1 [male, carrier]\\n I-2 [female, unaffected]\\n I-1 -- I-2\\n II-1 [male, carrier]\\n I-3 [male, unaffected]\\n I-4 [female, carrier]\\n I-3 -- I-4\\n II-3 [female, carrier]\\n II-1 == II-3\\n III-1 [male, affected, proband]\\n```\\n\\n---\\n\\n## 5. Multi-trait pedigrees\\n\\nFor families that carry more than one heritable condition, use `legend:` lines to define which quadrant of the shape represents which trait, then tag individuals with `affected: trait1+trait2`.\\n\\n```\\npedigree \\\"Cancer Family Syndrome\\\"\\n legend: breast = \\\"Breast cancer\\\" (fill: quad-tl)\\n legend: ovarian = \\\"Ovarian cancer\\\" (fill: quad-tr)\\n legend: prostate = \\\"Prostate cancer\\\" (fill: quad-bl)\\n legend: colon = \\\"Colon cancer\\\" (fill: quad-br)\\n\\n I-1 [male, affected: prostate, deceased]\\n I-2 [female, affected: breast, deceased]\\n I-1 -- I-2\\n II-1 [female, affected: breast+ovarian]\\n II-2 [male, unaffected]\\n```\\n\\n**Legend syntax:** `legend: id = \\\"Human label\\\" (fill: POSITION)`.\\n\\n| `fill` position | Region |\\n|---|---|\\n| `full` | Entire shape (default if `(fill: …)` omitted) |\\n| `quad-tl` / `quad-tr` / `quad-bl` / `quad-br` | Top-left / top-right / bottom-left / bottom-right quadrant |\\n| `half-left` / `half-right` / `half-top` / `half-bottom` | A half of the shape |\\n\\nIndividuals use the legend trait IDs: `[affected: breast]`, `[affected: breast+ovarian]`. The `+` joins traits; quadrants fill cumulatively.\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Title:** `pedigree \\\"BRCA1 Family\\\"` — first line only.\\n- **Individual label override:** `II-1 [female, affected, label: \\\"Jane Smith (42)\\\"]`.\\n- **Legend entry:** `legend: id = \\\"Label\\\" (fill: POSITION)` — see §5.\\n- **Comments:** `#` or `//` at the start of a line (after leading whitespace). Inline trailing comments are **not** supported.\\n\\n---\\n\\n## 7. Reserved words & escaping\\n\\n**Reserved at line start:** `pedigree` (header), `legend:` (legend entry).\\n\\n**Reserved operator tokens** inside a line — avoid using these sequences in IDs:\\n`--`, `==`, `-/-`, `~`.\\n\\n**Reserved attribute tokens** inside `[…]` — the parser will interpret these regardless of position: sex tokens (`male`, `female`, `unknown`, `amab`, `afab`, `uaab`), genetic statuses (`affected`, `carrier`, `carrier-x`, `obligate-carrier`, `presymptomatic`, `unaffected`), markers (`proband`, `consultand`, `evaluated`), and life statuses (`deceased`, `stillborn`, `pregnancy`, `sab`, `tab`, `ectopic`).\\n\\n**Strings with spaces** must be double-quoted. Single quotes and backticks are not recognized.\\n\\n---\\n\\n## 8. Common mistakes\\n\\nReal parser errors, what triggers them, and how to fix.\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `II-1 -- II-4` where `II-4` was never declared | `Unknown individual 'II-4'` | Declare `II-4` above, or use inline form: `II-1 -- II-4 [male, unaffected]` |\\n| `II-1 [nonbinary]` | Silently stored as a custom property; shape stays diamond | Bennett 2022 distinguishes assigned sex from gender — use `amab`/`afab`/`uaab` and record identity in `label:` |\\n| `II-3 [twin-mz]` | Stored as a custom property; no twin line rendered | Twin notation is §10 Roadmap |\\n| `I-1 -- I-2` followed by `II-1 [male]` at the **same indent** | Child parsed as a new top-level individual, not as offspring | Indent the child line deeper than the couple line |\\n| `I-1 [affected: breast]` with no matching `legend:` | Trait ID stored but no legend-keyed fill is rendered | Add `legend: breast = \\\"…\\\" (fill: quad-tl)` above |\\n| `II-1[affected]` (no space, no attrs split) | Works for a single token; breaks when a second attribute is added | Always separate `id` and `[…]` with a space |\\n| Line 1 is `I-1 [male]` with no `pedigree` header | `Expected \\\"pedigree\\\" header` | Start with `pedigree` or `pedigree \\\"Title\\\"` |\\n\\n---\\n\\n## 9. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | legend | individual | couple-block)*\\n\\nheader = \\\"pedigree\\\" ( WS quoted-string )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nlegend = INDENT \\\"legend:\\\" WS id WS \\\"=\\\" WS quoted-string\\n ( WS \\\"(\\\" \\\"fill:\\\" fill-value \\\")\\\" )? NEWLINE\\n\\nindividual = INDENT id ( \\\"[\\\" attrs \\\"]\\\" )? NEWLINE\\ncouple-block = INDENT id WS coupleOp WS right-side NEWLINE\\n ( deeper-indent child )*\\nchild = INDENT id ( \\\"[\\\" attrs \\\"]\\\" )? NEWLINE\\nright-side = id ( \\\"[\\\" attrs \\\"]\\\" )?\\n\\ncoupleOp = \\\"-/-\\\" | \\\"==\\\" | \\\"--\\\" | \\\"~\\\"\\n\\nid = [a-zA-Z] [a-zA-Z0-9_-]*\\nattrs = attr (\\\",\\\" attr)*\\nattr = sex\\n | genetic-status\\n | marker\\n | life-status\\n | digit digit digit digit // birth year\\n | \\\"label\\\" \\\":\\\" quoted-string\\n | \\\"affected\\\" \\\":\\\" trait-id ( \\\"+\\\" trait-id )*\\n | key \\\":\\\" value // custom\\n\\nsex = \\\"male\\\" | \\\"female\\\" | \\\"unknown\\\" | \\\"amab\\\" | \\\"afab\\\" | \\\"uaab\\\"\\ngenetic-status = \\\"unaffected\\\" | \\\"affected\\\" | \\\"carrier\\\" | \\\"carrier-x\\\"\\n | \\\"obligate-carrier\\\" | \\\"presymptomatic\\\"\\nmarker = \\\"proband\\\" | \\\"consultand\\\" | \\\"evaluated\\\"\\nlife-status = \\\"deceased\\\" | \\\"stillborn\\\" | \\\"pregnancy\\\"\\n | \\\"sab\\\" | \\\"tab\\\" | \\\"ectopic\\\"\\nfill-value = \\\"full\\\" | \\\"half-left\\\" | \\\"half-right\\\" | \\\"half-top\\\" | \\\"half-bottom\\\"\\n | \\\"quad-tl\\\" | \\\"quad-tr\\\" | \\\"quad-bl\\\" | \\\"quad-br\\\"\\n\\ncomment = INDENT ( \\\"#\\\" | \\\"//\\\" ) any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/pedigree/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"phylo\": {\n \"title\": \"Phylogenetic tree\",\n \"content\": \"## 1. Your first phylogenetic tree\\n\\nThe smallest useful tree: four taxa, two clades.\\n\\n```\\nphylo \\\"Vertebrates\\\"\\n newick: \\\"((Human:0.1,Chimp:0.08):0.03,(Dog:0.35,Cat:0.30):0.2);\\\"\\n```\\n\\nThree rules cover 80% of usage:\\n\\n1. Start with `phylo`, optionally followed by a quoted title and bracket props.\\n2. Provide the tree topology in `newick:` format — the standard Newick string, quoted, on one line. The trailing `;` is optional.\\n3. Optionally define **clade** highlight groups and a **scale** label below the newick line.\\n\\n> Comments must start with `#` on their own line. Inline trailing comments are not supported.\\n\\n---\\n\\n## 2. Input formats\\n\\n### 2.1 Newick format\\n\\nNewick is the primary input. The full grammar is:\\n\\n```\\n(A,B,(C,D)); # topology only\\n(A:0.1,B:0.2,(C:0.3,D:0.4):0.5); # with branch lengths\\n((A:0.1,B:0.2):0.05[&&NHX:B=98],(C,D):0.08); # NHX bootstrap\\n('Homo sapiens':0.1,'Mus musculus':0.2); # quoted names with spaces\\n```\\n\\nBranch lengths follow the node name after a colon. Internal node support values can appear as plain brackets `[95]` or as NHX `[&&NHX:B=95]`.\\n\\n```\\nphylo \\\"Newick examples\\\"\\n newick: \\\"((A:0.1,B:0.2):0.05[&&NHX:B=98],(C:0.3,D:0.4):0.08[&&NHX:B=87]);\\\"\\n```\\n\\n**Newick rules the parser accepts:**\\n\\n| Feature | Syntax | Notes |\\n|---|---|---|\\n| Leaf name | `A`, `Homo_sapiens` | No spaces — use `_` or quote |\\n| Quoted leaf name | `'Homo sapiens'` | Single quotes; `''` is a literal quote inside |\\n| Branch length | `:0.035` after name | Float; optional |\\n| Internal node name | `(A,B)ancestor` | After closing `)` |\\n| Bootstrap (plain) | `(A,B)[95]` | Integer or float in brackets |\\n| Bootstrap (NHX) | `(A,B)[&&NHX:B=95]` | `B=` field; other NHX fields stored but not rendered |\\n| Semicolon | `;` at end | Optional — parser strips it |\\n| Polytomy | `(A,B,C)` | More than 2 children |\\n\\n### 2.2 Indent DSL\\n\\nFor hand-written or small trees, Schematex offers an indentation-based alternative that is easier to read and edit than raw Newick:\\n\\n```\\nphylo \\\"Vertebrates (indent DSL)\\\" [mode: phylogram]\\nroot:\\n :0.03\\n Human: 0.1\\n Chimp: 0.08\\n :0.2\\n Dog: 0.35\\n Cat: 0.30\\nscale \\\"substitutions/site\\\"\\n```\\n\\n**Indent DSL rules:**\\n\\n| Syntax | Meaning |\\n|---|---|\\n| `Name: length` | Leaf node with branch length |\\n| `: length` | Unnamed internal node with branch length |\\n| `Name` | Leaf node, no branch length (cladogram) |\\n| `Name [N]` | Node with support value N |\\n| Deeper indent | Child of the node above at a shallower indent |\\n| `#` line | Comment, ignored |\\n\\nThe first line that ends with `:` and has no spaces triggers indent-tree mode (e.g. `root:`). The name before the colon becomes the root label; all indented lines below become its children.\\n\\n---\\n\\n## 3. Layout\\n\\nSet the layout in the header brackets: `phylo \\\"Title\\\" [layout: rectangular]`.\\n\\n| Layout | Value | Description |\\n|---|---|---|\\n| Rectangular | `rectangular` | Default. L-shaped branches; root on left, tips on right |\\n| Slanted | `slanted` | Diagonal lines from parent to child; more compact |\\n| Circular | `circular` | Root at center, tips around the circumference |\\n| Unrooted | `unrooted` | Equal-angle radial; emphasizes distance, not ancestry |\\n\\n`[unrooted]` as a bare flag is equivalent to `[layout: unrooted]`.\\n\\n**Circular** — root at center, tips fanning outward. Most visually striking for many-taxa trees with clade highlights.\\n\\n```\\nphylo \\\"Vertebrates — circular\\\" [layout: circular]\\n newick: \\\"((Human:0.1,Chimp:0.08,Gorilla:0.12):0.15,(Dog:0.35,Cat:0.30,Wolf:0.32):0.2,(Salmon:0.5,Zebrafish:0.45):0.3);\\\"\\n clade Primates = (Human, Chimp, Gorilla) [color: \\\"#1E88E5\\\", label: \\\"Primates\\\", highlight: both]\\n clade Carnivora = (Dog, Cat, Wolf) [color: \\\"#E53935\\\", label: \\\"Carnivora\\\", highlight: both]\\n```\\n\\n**Rectangular** — L-shaped branches; root on the left, tips on the right. The classic phylogram form for published figures.\\n\\n```\\nphylo \\\"Bacterial Diversity\\\" [layout: rectangular, mode: phylogram]\\n newick: \\\"((((Ecoli:0.1,Salmonella:0.12):0.05[&&NHX:B=98],Vibrio:0.2):0.08,((Bacillus:0.15,Staph:0.18):0.06[&&NHX:B=92],Listeria:0.22):0.1):0.15,((Myco_tb:0.3,Myco_leprae:0.28):0.12[&&NHX:B=100],(Strepto:0.25,Lactobacillus:0.2):0.08):0.2);\\\"\\n clade Gamma = (Ecoli, Salmonella, Vibrio) [color: \\\"#1E88E5\\\", label: \\\"γ-Proteobacteria\\\"]\\n clade Firmi = (Bacillus, Staph, Listeria) [color: \\\"#E53935\\\", label: \\\"Firmicutes\\\"]\\n scale \\\"substitutions/site\\\"\\n```\\n\\n**Slanted** — diagonal lines from parent to child; more compact than rectangular, same left-to-right reading direction.\\n\\n```\\nphylo \\\"Vertebrates — slanted\\\" [layout: slanted]\\n newick: \\\"((Human:0.1,Chimp:0.08,Gorilla:0.12):0.15,(Dog:0.35,Cat:0.30,Wolf:0.32):0.2,(Salmon:0.5,Zebrafish:0.45):0.3);\\\"\\n clade Primates = (Human, Chimp, Gorilla) [color: \\\"#1E88E5\\\", label: \\\"Primates\\\"]\\n clade Carnivora = (Dog, Cat, Wolf) [color: \\\"#E53935\\\", label: \\\"Carnivora\\\"]\\n scale \\\"substitutions/site\\\"\\n```\\n\\n**Unrooted** — equal-angle radial layout; de-emphasizes the root, emphasizes pairwise distance between all taxa.\\n\\n```\\nphylo \\\"Vertebrates — unrooted\\\" [unrooted]\\n newick: \\\"((Human:0.1,Chimp:0.08,Gorilla:0.12):0.15,(Dog:0.35,Cat:0.30,Wolf:0.32):0.2,(Salmon:0.5,Zebrafish:0.45):0.3);\\\"\\n clade Primates = (Human, Chimp, Gorilla) [color: \\\"#1E88E5\\\", label: \\\"Primates\\\"]\\n clade Carnivora = (Dog, Cat, Wolf) [color: \\\"#E53935\\\", label: \\\"Carnivora\\\"]\\n```\\n\\n---\\n\\n## 4. Mode\\n\\nSet with `[mode: …]` in the header (or in a `style [mode: …]` line).\\n\\n| Mode | Value | Branch length meaning |\\n|---|---|---|\\n| Phylogram | `phylogram` | Default. Proportional to evolutionary distance (substitutions/site) |\\n| Cladogram | `cladogram` | Ignored — tips align; only topology matters |\\n| Chronogram | `chronogram` | Proportional to divergence time; all tips align to \\\"present\\\" |\\n\\nChronogram requires branch lengths in units of time plus `[mrsd: \\\"YYYY\\\"]` (most-recent sampling date) in the header so the renderer can align tips to present.\\n\\n```\\nphylo \\\"SARS-CoV-2 variants\\\" [mode: chronogram, mrsd: \\\"2023\\\"]\\n newick: \\\"((Alpha:0.5,Delta:0.4):0.3,Omicron:0.8);\\\"\\n scale \\\"years\\\"\\n```\\n\\n---\\n\\n## 5. Clade highlighting\\n\\nA `clade` line marks a monophyletic group with a color, an optional label, and an optional highlight mode.\\n\\n```\\nclade ID = (member1, member2, ...) [color: \\\"#hex\\\", label: \\\"text\\\", highlight: mode]\\n```\\n\\n| Prop | Values | Effect |\\n|---|---|---|\\n| `color:` | hex string e.g. `\\\"#1E88E5\\\"` | Branch and/or background color |\\n| `label:` | quoted string | Clade label shown at right margin |\\n| `highlight:` | `branch`, `background`, `both` | `branch` colors lines; `background` shades the region; `both` does both |\\n\\nMembers are tip (leaf) IDs from the Newick string. The renderer computes the MRCA of the listed tips and highlights the entire subtree rooted there.\\n\\n```\\nphylo \\\"Mammal clades\\\" [layout: rectangular]\\n newick: \\\"(((Human:0.1,Chimp:0.08,Gorilla:0.12):0.15,Mouse:0.45):0.05,(Dog:0.35,(Cat:0.30,Tiger:0.32):0.1):0.2);\\\"\\n clade Primates = (Human, Chimp, Gorilla) [color: \\\"#1E88E5\\\", label: \\\"Primates\\\", highlight: both]\\n clade Carnivora = (Dog, Cat, Tiger) [color: \\\"#E53935\\\", label: \\\"Carnivora\\\", highlight: branch]\\n```\\n\\n---\\n\\n## 6. Scale bar and outgroup\\n\\n**Scale bar:** `scale \\\"label\\\"` — adds a bar at the bottom. The label describes the unit (e.g. `\\\"substitutions/site\\\"`, `\\\"Mya\\\"`). Omit for cladogram mode where branch lengths have no meaning.\\n\\n**Outgroup:** `outgroup: taxonId` — records the outgroup for documentation; the renderer may use it to visually mark the outgroup taxon.\\n\\n```\\nphylo \\\"Vertebrates\\\"\\n newick: \\\"((Human:0.1,Chimp:0.08):0.03,Lamprey:0.8);\\\"\\n outgroup: Lamprey\\n scale \\\"substitutions/site\\\"\\n```\\n\\n---\\n\\n## 7. Header props reference\\n\\nAll options go inside `[…]` on the `phylo` header line, or in a `style […]` line anywhere in the body.\\n\\n| Prop | Values | Default | Effect |\\n|---|---|---|---|\\n| `layout:` | `rectangular`, `slanted`, `circular`, `unrooted` | `rectangular` | Tree layout |\\n| `mode:` | `phylogram`, `cladogram`, `chronogram` | `phylogram` | Branch length semantics |\\n| `unrooted` | (flag) | — | Equivalent to `layout: unrooted` |\\n| `branch-width:` | number | `1.5` | Stroke width of branches |\\n| `openAngle:` | number (degrees) | `0` | Fan gap for circular layout (0 = full 360°) |\\n| `mrsd:` | quoted year string | — | Most-recent sampling date for chronograms |\\n\\n---\\n\\n## 8. Labels & comments\\n\\n- **Title:** `phylo \\\"Tree of Life\\\"` — first line only.\\n- **Scale label:** `scale \\\"substitutions/site\\\"` — one per document.\\n- **Clade label:** `[label: \\\"Primates\\\"]` inside a `clade` line.\\n- **Comments:** `#` at the start of a line (after leading whitespace). Inline trailing comments are not supported.\\n\\n---\\n\\n## 9. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `newick: (A,B,C);` (unquoted) | `PhyloParseError: Phylo document must start with 'phylo'` | Quote the Newick string: `newick: \\\"(A,B,C);\\\"` |\\n| Tip name with a space: `Homo sapiens:0.1` | Parsed as `Homo` — space terminates an unquoted name | Use underscore (`Homo_sapiens`) or single-quote (`'Homo sapiens'`) |\\n| Leaf ID in `clade` doesn't match Newick name | Clade silently has 0 members; no highlight | Copy names exactly as they appear in the Newick string |\\n| `clade X = (A, B)` with no `newick:` or indent tree | `PhyloParseError: No tree definition found` | Add a `newick:` line or an indent tree block |\\n| `mode: chronogram` with no branch lengths | Renderer treats all lengths as 0; tips overlap at root | Add `:length` to every edge in the Newick string |\\n| `root:` line not detected | If the `root:` line has a space in the name (e.g. `My root:`) the indent tree is not triggered | Use a single-word root label or `root:` |\\n| Newick with internal node names: `(A,B)ancestor:0.5` | Parses fine — `ancestor` is the internal node label | Supported; internal names appear on internal nodes |\\n\\n---\\n\\n## 10. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | newick-line | scale-line\\n | outgroup-line | clade-line | style-line | indent-line)*\\n\\nheader = \\\"phylo\\\" ( WS quoted-string )? ( WS \\\"[\\\" props \\\"]\\\" )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nnewick-line = \\\"newick:\\\" WS quoted-newick NEWLINE\\nscale-line = \\\"scale\\\" ( WS quoted-string )? NEWLINE\\noutgroup-line = \\\"outgroup:\\\" WS id NEWLINE\\nclade-line = \\\"clade\\\" WS id WS \\\"=\\\" WS \\\"(\\\" id (\\\",\\\" id)* \\\")\\\"\\n ( WS \\\"[\\\" clade-props \\\"]\\\" )? NEWLINE\\nstyle-line = \\\"style\\\" WS \\\"[\\\" props \\\"]\\\" NEWLINE\\n\\n// Indent tree — triggered by a line ending in \\\":\\\" with no spaces\\nindent-tree = root-line indent-node*\\nroot-line = id \\\":\\\" NEWLINE\\nindent-node = INDENT ( id \\\":\\\" length | \\\":\\\" length | id ) ( WS \\\"[\\\" number \\\"]\\\" )? NEWLINE\\n\\nprops = prop (\\\",\\\" prop)*\\nprop = \\\"layout:\\\" layout-value\\n | \\\"mode:\\\" mode-value\\n | \\\"unrooted\\\"\\n | \\\"branch-width:\\\" number\\n | \\\"openAngle:\\\" number\\n | \\\"mrsd:\\\" quoted-string\\n\\nclade-props = clade-prop (\\\",\\\" clade-prop)*\\nclade-prop = \\\"color:\\\" quoted-string\\n | \\\"label:\\\" quoted-string\\n | \\\"highlight:\\\" ( \\\"branch\\\" | \\\"background\\\" | \\\"both\\\" )\\n\\nlayout-value = \\\"rectangular\\\" | \\\"slanted\\\" | \\\"circular\\\" | \\\"unrooted\\\"\\nmode-value = \\\"phylogram\\\" | \\\"cladogram\\\" | \\\"chronogram\\\"\\n\\n// Newick grammar (embedded, parsed separately)\\nnewick = subtree \\\";\\\"?\\nsubtree = leaf | internal\\ninternal = \\\"(\\\" subtree (\\\",\\\" subtree)* \\\")\\\" name? nhx? length?\\nleaf = name nhx? length?\\nname = unquoted-name | \\\"'\\\" single-quoted \\\"'\\\")\\nlength = \\\":\\\" number\\nnhx = \\\"[\\\" number \\\"]\\\" // plain bootstrap\\n | \\\"[&&NHX:\\\" nhx-pair (\\\":\\\" nhx-pair)* \\\"]\\\"\\nnhx-pair = key \\\"=\\\" value\\n\\nid = [a-zA-Z] [a-zA-Z0-9_-]*\\nnumber = /[+-]?[0-9]+(\\\\.[0-9]+)?([eE][+-]?[0-9]+)?/\\ncomment = INDENT \\\"#\\\" any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/phylo/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"sociogram\": {\n \"title\": \"Sociogram\",\n \"content\": \"## 1. Your first sociogram\\n\\nThe smallest useful sociogram: four people, three different relationship types.\\n\\n```\\nsociogram \\\"Study group\\\"\\n alice [label: \\\"Alice\\\"]\\n bob [label: \\\"Bob\\\"]\\n carol [label: \\\"Carol\\\"]\\n dave [label: \\\"Dave\\\"]\\n alice <-> bob [label: \\\"lab partners\\\"]\\n carol -> alice\\n dave -x> bob [label: \\\"rivalry\\\"]\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with the keyword `sociogram`, optionally followed by a quoted title.\\n2. Each person is a **node** — declared explicitly with `id [label: \\\"…\\\"]` or auto-created the first time they appear in an edge.\\n3. Connect two nodes with an **edge operator** — `<->` (mutual), `->` (one-way), `-x>` (rejection), `-.-` (neutral). See §3.\\n4. Optionally declare **groups** and **config** lines to control layout and coloring.\\n\\n> Comments must start with `#` on their own line.\\n\\n---\\n\\n## 2. Nodes\\n\\nA node line is `id [attr: value, …]`. Nodes are also created implicitly when first referenced in an edge — but explicit declaration lets you set labels, groups, and roles.\\n\\n**ID rules.** Must match `[a-zA-Z][a-zA-Z0-9_-]*`. The ID is used internally; the `label:` attribute sets the display name.\\n\\n**Node attributes:**\\n\\n| Attribute | Values | Effect |\\n|---|---|---|\\n| `label: \\\"…\\\"` | quoted string | Display name (defaults to the ID) |\\n| `group: id` | group ID | Associates the node with a group for coloring |\\n| `role: …` | `star`, `isolate`, `bridge`, `neglectee`, `rejected` | Explicit sociometric role annotation |\\n| `size: …` | `small`, `medium`, `large` | Node size override |\\n\\n```\\nsociogram \\\"Group roles demo\\\"\\n config: layout = circular\\n dr_park [label: \\\"Dr. Park\\\", role: star]\\n james [label: \\\"James\\\"]\\n nina [label: \\\"Nina\\\", role: neglectee]\\n loner [label: \\\"Alex\\\", role: isolate]\\n bridge [label: \\\"Sam\\\", role: bridge, size: large]\\n james -> dr_park\\n james -> bridge\\n bridge -> dr_park\\n bridge -> nina\\n nina -> james\\n```\\n\\n---\\n\\n## 3. Edges\\n\\nAn edge line is `leftId OP rightId` optionally followed by `[label: \\\"…\\\", weight: N]`. Both IDs are auto-registered as nodes if not yet declared.\\n\\n### 3.1 Direction and valence\\n\\n```\\nsociogram \\\"Edge types\\\"\\n config: layout = circular\\n a [label: \\\"A\\\"]\\n b [label: \\\"B\\\"]\\n c [label: \\\"C\\\"]\\n d [label: \\\"D\\\"]\\n e [label: \\\"E\\\"]\\n f [label: \\\"F\\\"]\\n # Positive\\n a -> b [label: \\\"chose B\\\"]\\n b <-> c [label: \\\"mutual\\\"]\\n # Negative\\n c -x> d [label: \\\"rejects D\\\"]\\n d <x-> e [label: \\\"mutual reject\\\"]\\n # Neutral\\n e -.- f [label: \\\"indifferent\\\"]\\n f <.-> a [label: \\\"mutual neutral\\\"]\\n```\\n\\n| Operator | Direction | Valence | Meaning |\\n|---|---|---|---|\\n| `A -> B` | one-way | positive | A chose B |\\n| `A <- B` | one-way | positive | B chose A (same as `B -> A`) |\\n| `A <-> B` | mutual | positive | Both chose each other |\\n| `A -- B` | undirected | positive | Relationship known; direction not recorded |\\n| `A -x> B` | one-way | negative | A rejects B |\\n| `A <x- B` | one-way | negative | B rejects A |\\n| `A <x-> B` | mutual | negative | Mutual rejection |\\n| `A -x- B` | undirected | negative | Conflict; direction unknown |\\n| `A -.> B` | one-way | neutral | A is indifferent toward B |\\n| `A <.-> B` | mutual | neutral | Mutual indifference |\\n| `A -.- B` | undirected | neutral | Neutral relationship |\\n\\n### 3.2 Weight / strength\\n\\nHigher weight = thicker line. Use the shorthand operators or override explicitly with `[weight: N]`.\\n\\n| Weight | Shorthand | Direction | Meaning |\\n|---|---|---|---|\\n| 2 (default) | `->` `<->` `--` `-x>` `-.-` | any | Standard connection |\\n| 3 | `==>` `<==` `<==>` `===` | one-way / mutual / undirected | Strong |\\n| 4 | `===>` `<===` `<===>` | one-way / mutual | Very strong |\\n| custom | `[weight: N]` | — | Any integer |\\n\\n```\\nsociogram \\\"Relationship strengths\\\"\\n config: layout = circular\\n a [label: \\\"A\\\"]\\n b [label: \\\"B\\\"]\\n c [label: \\\"C\\\"]\\n d [label: \\\"D\\\"]\\n a -> b [label: \\\"weight 2 (default)\\\"]\\n b ==> c [label: \\\"weight 3 (strong)\\\"]\\n c ===> d [label: \\\"weight 4 (very strong)\\\"]\\n d -> a [weight: 1, label: \\\"weight 1 (weak)\\\"]\\n```\\n\\n### 3.3 Edge labels\\n\\n`A -> B [label: \\\"best friend\\\"]` — the label appears on the connecting line.\\n\\n---\\n\\n## 4. Groups\\n\\nA `group` block collects nodes into a named subgroup for coloring and layout clustering.\\n\\n**Group syntax:**\\n- `group id [label: \\\"…\\\", color: \\\"#hex\\\"]` — the group header line.\\n- Member lines follow, each indented **at least 4 spaces**, one node per line.\\n- A non-indented line (or the next `group`) closes the current group.\\n- Members can carry their own props: `anna [label: \\\"Anna K.\\\", size: large]`.\\n\\nNodes can also be assigned inline: `alice [group: girls]`.\\n\\n```\\nsociogram \\\"Classroom dynamics\\\"\\n config: layout = force-directed\\n group boys [label: \\\"Boys\\\", color: \\\"#42A5F5\\\"]\\n tom [label: \\\"Tom\\\"]\\n jack [label: \\\"Jack\\\"]\\n mike [label: \\\"Mike\\\"]\\n leo [label: \\\"Leo\\\"]\\n group girls [label: \\\"Girls\\\", color: \\\"#EF5350\\\"]\\n anna [label: \\\"Anna\\\"]\\n beth [label: \\\"Beth\\\"]\\n chloe [label: \\\"Chloe\\\"]\\n diana [label: \\\"Diana\\\"]\\n tom <-> jack\\n anna <-> beth\\n anna <-> chloe\\n beth <-> chloe\\n mike -x> leo [label: \\\"conflict\\\"]\\n diana -> anna\\n tom -> anna [label: \\\"cross-group\\\"]\\n```\\n\\n---\\n\\n## 5. Configuration\\n\\n`config:` lines tune layout and visual encoding. Each is its own line.\\n\\n| Config key | Values | Default | Effect |\\n|---|---|---|---|\\n| `layout` | `circular`, `force-directed`, `concentric` | `circular` | Placement algorithm |\\n| `sizing` | `uniform`, `in-degree`, `betweenness` | `uniform` | Node size by metric |\\n| `coloring` | `default`, `group`, `role` | `default` | Node color scheme |\\n| `highlight` | comma list: `stars`, `isolates`, `cliques` | `stars,isolates` | Which patterns to annotate |\\n\\n**Layout notes:**\\n- `circular` — nodes evenly spaced on a ring. Best for small groups (≤15).\\n- `force-directed` — spring model; clusters emerge automatically. Best for medium-sized groups with distinct subgroups.\\n- `concentric` — inner rings hold high-in-degree nodes. Best for showing core-periphery structure.\\n\\n**Circular** — uniform ring placement; every node equally visible. Best for small, tightly-knit groups.\\n\\n```\\nsociogram \\\"Therapy group — circular\\\"\\n config: layout = circular\\n dr_park [label: \\\"Dr. Park\\\", role: star]\\n james [label: \\\"James\\\"]\\n maria [label: \\\"Maria\\\"]\\n lee [label: \\\"Lee\\\"]\\n sarah [label: \\\"Sarah\\\"]\\n tom [label: \\\"Tom\\\"]\\n nina [label: \\\"Nina\\\", role: neglectee]\\n james -> dr_park\\n james <-> maria [weight: 3]\\n james -> lee\\n maria -> dr_park\\n lee -> dr_park\\n lee -x> nina\\n sarah <-> nina\\n sarah -> dr_park\\n tom -> dr_park\\n nina -> maria\\n```\\n\\n**Force-directed** — spring physics pulls connected nodes together and pushes disconnected ones apart. Subgroups cluster organically.\\n\\n```\\nsociogram \\\"Classroom dynamics — force-directed\\\"\\n config: layout = force-directed\\n group boys [label: \\\"Boys\\\", color: \\\"#42A5F5\\\"]\\n tom [label: \\\"Tom\\\"]\\n jack [label: \\\"Jack\\\"]\\n mike [label: \\\"Mike\\\"]\\n leo [label: \\\"Leo\\\"]\\n group girls [label: \\\"Girls\\\", color: \\\"#EF5350\\\"]\\n anna [label: \\\"Anna\\\"]\\n beth [label: \\\"Beth\\\"]\\n chloe [label: \\\"Chloe\\\"]\\n diana [label: \\\"Diana\\\"]\\n tom <-> jack\\n anna <-> beth\\n anna <-> chloe\\n beth <-> chloe\\n mike -x> leo [label: \\\"conflict\\\"]\\n diana -> anna\\n tom -> anna [label: \\\"cross-group\\\"]\\n jack -> beth\\n```\\n\\n**Concentric** — nodes sorted by in-degree; high-centrality nodes appear on inner rings, peripheral nodes on outer rings.\\n\\n```\\nsociogram \\\"Informal influence — concentric\\\"\\n config: layout = concentric\\n config: sizing = in-degree\\n vp [label: \\\"VP Eng\\\"]\\n lead_a [label: \\\"Lead A\\\"]\\n lead_b [label: \\\"Lead B\\\"]\\n alice [label: \\\"Alice\\\"]\\n bob [label: \\\"Bob\\\"]\\n carol [label: \\\"Carol\\\"]\\n dave [label: \\\"Dave\\\"]\\n alice -> lead_a\\n bob -> lead_a\\n carol -> lead_a\\n carol -> lead_b\\n dave -> lead_b\\n lead_a -> vp\\n lead_b -> vp\\n alice <-> bob\\n carol <-> dave\\n```\\n\\n---\\n\\n## 6. Sociometric roles\\n\\nThe parser stores role annotations on nodes. The renderer uses them to apply visual badges — a star marker for `star`, a dashed border for `isolate`, and so on.\\n\\n| Role | Meaning |\\n|---|---|\\n| `star` | Central figure chosen by many (high in-degree) |\\n| `isolate` | No connections in or out |\\n| `neglectee` | Reaches out to others but receives no choices |\\n| `rejected` | Receives rejection edges from multiple members |\\n| `bridge` | Connects two otherwise separate clusters |\\n\\n```\\nsociogram \\\"Role annotations\\\"\\n config: layout = circular\\n center [label: \\\"Maria\\\", role: star]\\n bridge_node [label: \\\"Sam\\\", role: bridge]\\n newcomer [label: \\\"New Kid\\\", role: neglectee]\\n loner [label: \\\"Alex\\\", role: isolate]\\n outcast [label: \\\"Pat\\\", role: rejected]\\n center <-> bridge_node\\n bridge_node -> outcast\\n newcomer -> center\\n newcomer -> bridge_node\\n center -x> outcast\\n bridge_node -x> outcast\\n```\\n\\n---\\n\\n## 7. Labels & comments\\n\\n- **Title:** `sociogram \\\"Study group\\\"` — first line only.\\n- **Node label:** `alice [label: \\\"Alice K.\\\"]`.\\n- **Group label:** `group boys [label: \\\"Boys\\\"]`.\\n- **Edge label:** `alice -> bob [label: \\\"lab partners\\\"]`.\\n- **Comments:** `#` at the start of a line (after leading whitespace).\\n\\n---\\n\\n## 8. Reserved words & escaping\\n\\n**Reserved at line start:** `sociogram` (header), `group`, `config:`.\\n\\n**Reserved operator tokens** — avoid these sequences inside IDs: `->`, `<-`, `<->`, `--`, `===`, `==>`, `<==`, `<===>`, `-x>`, `<x-`, `-x-`, `<x->`, `-.>`, `<.->`, `-.-`.\\n\\n**Strings with spaces** must be double-quoted in `label:` and `color:` values.\\n\\n---\\n\\n## 9. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `tom; jack; mike` on one group line | `tom;` fails the ID regex — silently ignored | One node per line, each indented ≥4 spaces |\\n| Group members indented 2 spaces | Not treated as group members (parser requires ≥4) | Use 4+ spaces indent |\\n| `alice <> bob` | No matching operator — not parsed as an edge | Use `<->` for mutual positive |\\n| `config: layout = grid` | Unknown value silently ignored; layout stays `circular` | Use `circular`, `force-directed`, or `concentric` |\\n| Node with a space in the ID: `dr park` | Parser takes `dr` as the ID and `park` as a stray token | Use underscore: `dr_park [label: \\\"Dr. Park\\\"]` |\\n\\n---\\n\\n## 10. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | config | group-block | edge | node)*\\n\\nheader = \\\"sociogram\\\" ( WS quoted-string )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nconfig = \\\"config:\\\" WS key WS \\\"=\\\" WS value NEWLINE\\nkey = \\\"layout\\\" | \\\"sizing\\\" | \\\"coloring\\\" | \\\"highlight\\\"\\n\\ngroup-block = \\\"group\\\" WS id ( \\\"[\\\" group-attrs \\\"]\\\" )? NEWLINE\\n ( INDENT≥4 member-line )*\\nmember-line = id ( \\\"[\\\" node-attrs \\\"]\\\" )? NEWLINE\\ngroup-attrs = group-attr (\\\",\\\" group-attr)*\\ngroup-attr = \\\"label:\\\" quoted-string | \\\"color:\\\" quoted-string\\n\\nnode = id ( \\\"[\\\" node-attrs \\\"]\\\" )? NEWLINE\\nnode-attrs = node-attr (\\\",\\\" node-attr)*\\nnode-attr = \\\"label:\\\" quoted-string\\n | \\\"group:\\\" id\\n | \\\"role:\\\" role\\n | \\\"size:\\\" (\\\"small\\\" | \\\"medium\\\" | \\\"large\\\")\\n\\nedge = id WS op WS id ( \\\"[\\\" edge-attrs \\\"]\\\" )? NEWLINE\\nedge-attrs = edge-attr (\\\",\\\" edge-attr)*\\nedge-attr = \\\"label:\\\" quoted-string | \\\"weight:\\\" number\\n\\nop = // positive\\n \\\"<===>\\\" | \\\"===>\\\" | \\\"<===\\\"\\n | \\\"<==>\\\"|\\\"==>\\\"|\\\"<==\\\"\\n | \\\"===\\\" | \\\"<->\\\" | \\\"->\\\" | \\\"<-\\\" | \\\"--\\\"\\n // negative\\n | \\\"<x->\\\" | \\\"-x>\\\" | \\\"<x-\\\" | \\\"-x-\\\"\\n // neutral\\n | \\\"<.->\\\" | \\\"-\\\\.>\\\" | \\\"-.-\\\"\\n\\nrole = \\\"star\\\" | \\\"isolate\\\" | \\\"bridge\\\" | \\\"neglectee\\\" | \\\"rejected\\\"\\nid = [a-zA-Z] [a-zA-Z0-9_-]*\\ncomment = \\\"#\\\" any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/sociogram/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"timing\": {\n \"title\": \"Timing diagram\",\n \"content\": \"## 1. Your first timing diagram\\n\\nThe smallest useful timing diagram: a clock and one data signal.\\n\\n```\\ntiming\\nCLK: pppppppp\\nDATA: 0011==00 data: [\\\"A\\\",\\\"B\\\"]\\n```\\n\\nThree rules cover 80% of usage:\\n\\n1. Start with the keyword `timing`, optionally followed by a quoted title and `[hscale: N]`.\\n2. Each signal is one line: `NAME: wavestring` — name, colon, then a string of wave characters (no spaces inside the wave).\\n3. Add `data: [\\\"val1\\\", \\\"val2\\\"]` after the wave string to label bus segments.\\n\\n> Comments must start with `#` on their own line.\\n\\n---\\n\\n## 2. Wave characters\\n\\nThe wave string is a sequence of characters, one per time period. The parser accepts these:\\n\\n| Character | State | Meaning |\\n|---|---|---|\\n| `0` | Logic low | Signal at GND / VSS |\\n| `1` | Logic high | Signal at VDD |\\n| `x` | Unknown | Don't-care, undefined, or uninitialized |\\n| `z` | High-Z | Tri-state / high-impedance |\\n| `p` | Clock pulse (positive) | Rising-edge-active clock; one `p` = one full period (low→high→low) |\\n| `P` | Clock pulse (positive, tall) | Same as `p`, visually taller |\\n| `n` | Clock pulse (negative) | Falling-edge-active; one `n` = one full period (high→low→high) |\\n| `N` | Clock pulse (negative, tall) | Same as `n`, visually taller |\\n| `=` | Bus data | Parallel-bus segment; add labels via `data: […]` |\\n| `2`–`9` | Named bus segment | Same as `=`, indexed into `data: […]` by position |\\n| `.` | Hold / continue | Extend the previous state for one more period |\\n| `h` / `H` | Hold high | Force-high for this period |\\n| `l` / `L` | Hold low | Force-low for this period |\\n| `u` | Rising edge | Diagonal from low to high (transition only) |\\n| `d` / `D` | Falling edge | Diagonal from high to low (transition only) |\\n\\n```\\ntiming \\\"Wave character reference\\\"\\nclk: pppppppppp\\nhigh: 1111111111\\nlow: 0000000000\\nunkn: xxxxxxxxxx\\nhiz: zzzzzzzzzz\\nbus: x========x data: [\\\"ADDR\\\",\\\"DATA\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\"]\\nhold: 0..1..0..1\\nrise: 0u1\\nfall: 1d0\\n```\\n\\n---\\n\\n## 3. Data labels\\n\\nWhen a signal carries a bus value, tag the wave with `data: [\\\"label1\\\", \\\"label2\\\", …]`. Each non-empty quoted string is placed inside the corresponding `=` (or `2`–`9`) segment.\\n\\n```\\nMOSI: x======= data: [\\\"0xAB\\\",\\\"0xCD\\\",\\\"0xEF\\\",\\\"0x01\\\",\\\"0x02\\\",\\\"0x03\\\",\\\"0x04\\\",\\\"0x05\\\"]\\n```\\n\\nEmpty strings `\\\"\\\"` leave a segment unlabeled (useful for segments that extend a previous value).\\n\\n```\\nMISO: zzzz==== data: [\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"0xFF\\\",\\\"0x12\\\",\\\"0x34\\\",\\\"0x56\\\"]\\n# first four z-periods have no label; four = segments get labels starting at 0xFF\\n```\\n\\n```\\ntiming \\\"I2C read burst\\\"\\nSCL: ppppppppppp\\nSDA: x1=======1x data: [\\\"ADDR+R\\\",\\\"ACK\\\",\\\"D0\\\",\\\"D1\\\",\\\"D2\\\",\\\"D3\\\",\\\"D4\\\",\\\"D5\\\",\\\"NACK\\\"]\\n```\\n\\n---\\n\\n## 4. Grouping signals\\n\\nWrap related signals in a `[GroupName]` block. A `---` line closes the group and also acts as a visual separator between groups.\\n\\n```\\n[Control]\\nCLK: pppppppp\\nCS_N: 10000001\\n---\\n[Data]\\nMOSI: x======= data: [\\\"0xAB\\\",\\\"0xCD\\\",\\\"0xEF\\\",\\\"0x01\\\",\\\"0x02\\\",\\\"0x03\\\",\\\"0x04\\\",\\\"0x05\\\"]\\nMISO: zzzz==== data: [\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"0xFF\\\",\\\"0x12\\\",\\\"0x34\\\",\\\"0x56\\\"]\\n```\\n\\nAlternative `group \\\"name\\\" { … }` syntax is also accepted (closing `}` closes the group).\\n\\n```\\ntiming \\\"UART frame\\\"\\n[Clock & control]\\nCLK: pppppppppppp\\nTX_EN: 0111111110\\n---\\n[Data lines]\\nTX: 1========== data: [\\\"START\\\",\\\"D0\\\",\\\"D1\\\",\\\"D2\\\",\\\"D3\\\",\\\"D4\\\",\\\"D5\\\",\\\"D6\\\",\\\"D7\\\",\\\"STOP\\\"]\\nRX: zz1=======1 data: [\\\"\\\",\\\"\\\",\\\"D0\\\",\\\"D1\\\",\\\"D2\\\",\\\"D3\\\",\\\"D4\\\",\\\"D5\\\",\\\"D6\\\",\\\"D7\\\"]\\n```\\n\\n---\\n\\n## 5. Title and hscale\\n\\n**Title:** `timing \\\"SPI Transaction\\\"` — appears at the top of the diagram.\\n\\n**hscale:** `timing \\\"title\\\" [hscale: 2]` — scales the width of each time period. Default is 1. Use 2 for wider periods when data labels need more room.\\n\\n```\\ntiming \\\"Wide bus\\\" [hscale: 2]\\nCLK: pppp\\nDATA: ==== data: [\\\"long label here\\\",\\\"another\\\",\\\"third\\\",\\\"fourth\\\"]\\n```\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Signal name:** anything before the first `:` on a signal line. Names with spaces are fine — the colon is the delimiter.\\n- **Data labels:** `data: [\\\"a\\\", \\\"b\\\"]` after the wave string.\\n- **Title:** first token after `timing` keyword, quoted.\\n- **Comments:** `#` at the start of a line (after leading whitespace).\\n\\n```\\ntiming \\\"Demo\\\"\\n# this is a comment\\nCLK: pppp # ← inline trailing comment is NOT supported\\n```\\n\\n---\\n\\n## 7. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `CLK: p p p p` (spaces in wave) | Wave string parsed as `p` only; the rest is treated as data clause | Remove spaces: `CLK: pppp` |\\n| `DATA: =====` with no `data:` | Segments render as unlabeled bus cells | Add `data: [\\\"A\\\",\\\"B\\\",\\\"C\\\",\\\"D\\\",\\\"E\\\"]` |\\n| Wave character `s` or `r` | `TimingParseError: Invalid wave string` | Only the characters listed in §2 are valid |\\n| `CLK pppp` (no colon) | Line does not match signal pattern; silently skipped | The colon after the signal name is required |\\n| `data: [A, B, C]` (unquoted) | Values not recognized — parser looks for `\\\"…\\\"` | Quote each value: `data: [\\\"A\\\",\\\"B\\\",\\\"C\\\"]` |\\n| `[Group Name with spaces]` | Group label is `Group Name with spaces` — parsed fine | Supported |\\n| `hscale: 2` on its own line | Not recognized (hscale goes on the header line) | `timing \\\"title\\\" [hscale: 2]` |\\n\\n---\\n\\n## 8. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | group-open | group-close | separator | signal)*\\n\\nheader = \\\"timing\\\" ( WS quoted-string )? ( WS \\\"[\\\" \\\"hscale:\\\" number \\\"]\\\" )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\ngroup-open = \\\"[\\\" label \\\"]\\\" NEWLINE\\n | \\\"group\\\" WS quoted-string WS \\\"{\\\"? NEWLINE\\ngroup-close = \\\"}\\\" NEWLINE\\nseparator = \\\"---\\\" NEWLINE\\n\\nsignal = name \\\":\\\" WS wave-string ( WS data-clause )? NEWLINE\\nname = any text before the first \\\":\\\"\\nwave-string = wave-char+\\nwave-char = \\\"0\\\"|\\\"1\\\"|\\\"x\\\"|\\\"z\\\"\\n | \\\"p\\\"|\\\"P\\\"|\\\"n\\\"|\\\"N\\\"\\n | \\\"h\\\"|\\\"H\\\"|\\\"l\\\"|\\\"L\\\"\\n | \\\"u\\\"|\\\"d\\\"|\\\"D\\\"\\n | \\\"=\\\"|\\\".\\\"|\\\"2\\\"|\\\"3\\\"|\\\"4\\\"|\\\"5\\\"|\\\"6\\\"|\\\"7\\\"|\\\"8\\\"|\\\"9\\\"\\n\\ndata-clause = \\\"data:\\\" WS ( \\\"[\\\" quoted-string (\\\",\\\" quoted-string)* \\\"]\\\"\\n | quoted-string+ )\\n\\ncomment = \\\"#\\\" any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/timing/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"logic\": {\n \"title\": \"Logic gate diagram\",\n \"content\": \"## 1. Your first logic gate diagram\\n\\nThe smallest useful diagram: two inputs, one gate, one output.\\n\\n```\\nlogic \\\"NAND check\\\"\\ninput A, B\\noutput F\\nF = NAND(A, B)\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with the keyword `logic`, optionally followed by a quoted title and `style: ansi` or `style: iec`.\\n2. Declare ports with `input` and `output` lines — comma-separated signal names.\\n3. Each gate is `id = GATE_TYPE(input1, input2, …)`. The `id` becomes a named signal wire.\\n4. An `output` name that matches a gate `id` is automatically wired; use `OUTPUT <- gate_id` when the names differ.\\n\\n> Comments must start with `#` or `--` on their own line (or after the last token on a gate line).\\n\\n---\\n\\n## 2. Gate types\\n\\n### 2.1 Combinational gates\\n\\n| DSL keyword | Function | ANSI shape | IEC symbol |\\n|---|---|---|---|\\n| `AND` | A · B | D-shaped body | Rectangle + `&` |\\n| `OR` | A + B | Curved body | Rectangle + `≥1` |\\n| `NOT` | Ā | Triangle + bubble | Rectangle + `1` + bubble |\\n| `NAND` | ¬(A · B) | AND + bubble | Rectangle + `&` + bubble |\\n| `NOR` | ¬(A + B) | OR + bubble | Rectangle + `≥1` + bubble |\\n| `XOR` | A ⊕ B | OR + extra arc | Rectangle + `=1` |\\n| `XNOR` | ¬(A ⊕ B) | XOR + bubble | Rectangle + `=1` + bubble |\\n| `BUF` | A (buffer) | Triangle, no bubble | Rectangle + `1` |\\n\\n```\\nlogic \\\"Gate gallery\\\" style: ansi\\ninput A, B, C\\noutput Y_and, Y_or, Y_xor, Y_nand, Y_not\\nY_and = AND(A, B)\\nY_or = OR(A, B)\\nY_xor = XOR(A, B)\\nY_nand = NAND(A, B)\\nY_not = NOT(C)\\n```\\n\\n### 2.2 Special-output buffers\\n\\n| DSL keyword | Function |\\n|---|---|\\n| `TRISTATE_BUF` | Three-state buffer — Z output when enable is low |\\n| `TRISTATE_INV` | Three-state inverting buffer |\\n| `OPEN_DRAIN` | Open-drain / open-collector output (external pull-up required) |\\n| `SCHMITT` | Schmitt trigger — hysteresis symbol inside body |\\n\\n```\\nlogic \\\"Special buffers\\\" style: ansi\\ninput A, EN\\noutput Y_tri, Y_od, Y_sch\\nY_tri = TRISTATE_BUF(A, EN)\\nY_od = OPEN_DRAIN(A)\\nY_sch = SCHMITT(A)\\n```\\n\\n### 2.3 Flip-flops and latches\\n\\n| DSL keyword | Type | Key pins |\\n|---|---|---|\\n| `DFF` | D flip-flop (edge-triggered) | D, CLK, Q, Q̄ |\\n| `JKFF` | JK flip-flop | J, K, CLK, Q, Q̄ |\\n| `SRFF` | SR flip-flop | S, R, CLK, Q, Q̄ |\\n| `TFF` | T (toggle) flip-flop | T, CLK, Q, Q̄ |\\n| `LATCH_SR` | SR latch (level-sensitive, no clock) | S, R, Q, Q̄ |\\n| `LATCH_D` | D latch (transparent when enable=1) | D, EN, Q, Q̄ |\\n\\n```\\nlogic \\\"Flip-flop gallery\\\" style: ansi\\ninput D, J, K, CLK, EN\\noutput Q_dff, Q_jk, Q_latch\\nQ_dff = DFF(D, CLK)\\nQ_jk = JKFF(J, K, CLK)\\nQ_latch = LATCH_D(D, EN)\\n```\\n\\n### 2.4 Complex combinational\\n\\n| DSL keyword | Function |\\n|---|---|\\n| `MUX` | Multiplexer |\\n| `DEMUX` | Demultiplexer |\\n| `DECODER` | Binary decoder |\\n| `ENCODER` | Priority encoder |\\n\\n```\\nlogic \\\"Combinational MSI\\\" style: ansi\\ninput A, B, S\\noutput Y_mux, Y_dec\\nY_mux = MUX(A, B, S)\\nY_dec = DECODER(A, B)\\n```\\n\\n### 2.5 Sequential complex\\n\\n| DSL keyword | Function |\\n|---|---|\\n| `COUNTER` | Generic binary counter (`CTR` label, CLK/RESET/Q0–Q3) |\\n| `SHIFT_REG` | Generic shift register (`SRG` label, CLK/SER/Q0–Q7) |\\n\\n```\\nlogic \\\"Sequential MSI\\\" style: ansi\\ninput DATA, CLK, RESET\\noutput Q_cnt, Q_sr\\nQ_cnt = COUNTER(CLK, RESET)\\nQ_sr = SHIFT_REG(DATA, CLK)\\n```\\n\\n---\\n\\n## 3. Inputs and outputs\\n\\n### 3.1 Declaring ports\\n\\n```\\ninput A, B, Cin # three input ports\\noutput Sum, Cout # two output ports\\n```\\n\\nEach name in an `input` or `output` list becomes a named signal wire available throughout the diagram.\\n\\n### 3.2 Active-low inputs\\n\\nPrefix a signal name with `~` in the input list to mark it as active-low. The renderer draws a bubble at the port symbol.\\n\\n```\\ninput ~nRESET, CLK, DATA\\n```\\n\\nActive-low notation also works inside gate input lists:\\n\\n```\\ng1 = AND(~nRESET, CLK)\\n```\\n\\n### 3.3 Wiring outputs to gates\\n\\nIf the output ID matches a gate ID, the connection is implicit:\\n\\n```\\noutput Sum # Sum is also a gate id → auto-wired\\nSum = XOR(s1, Cin)\\n```\\n\\nWhen the names differ, use the explicit assignment operator:\\n\\n```\\noutput F\\nq1 = NOR(A, B)\\nF <- q1 # F draws from q1's output\\n```\\n\\n```\\nlogic \\\"SR latch from NOR gates\\\"\\ninput S, R\\noutput Q, Qn\\nq_gate = NOR(R, Qn)\\nqn_gate = NOR(S, Q)\\nQ <- q_gate\\nQn <- qn_gate\\n```\\n\\n---\\n\\n## 4. Symbol style\\n\\nThe `style:` option on the header line selects the symbol standard. It applies to every gate in the diagram.\\n\\n| Value | Standard | Use when |\\n|---|---|---|\\n| `ansi` (default) | IEEE Std 91 — distinctive curved shapes | US education, hardware docs |\\n| `iec` | IEC 60617-12 — uniform rectangles + function label | International, European industry |\\n\\n```\\nlogic \\\"ALU slice\\\" style: iec\\n```\\n\\n```\\nlogic \\\"1-bit Full Adder\\\" style: iec\\ninput A, B, Cin\\noutput Sum, Cout\\n# XOR stage — sum bits\\ns1 = XOR(A, B)\\nSum = XOR(s1, Cin)\\n# AND/OR stage — carry\\nc1 = AND(A, B)\\nc2 = AND(s1, Cin)\\nCout = OR(c1, c2)\\n```\\n\\n```\\nlogic \\\"Gate gallery — IEC style\\\" style: iec\\ninput A, B, C\\noutput Y_and, Y_or, Y_xor, Y_nand, Y_not\\nY_and = AND(A, B)\\nY_or = OR(A, B)\\nY_xor = XOR(A, B)\\nY_nand = NAND(A, B)\\nY_not = NOT(C)\\n```\\n\\n---\\n\\n## 5. Module blocks\\n\\nUse `module` to group gates into a labeled sub-circuit box. Module blocks are useful for documenting hierarchical designs — each module renders as a named rectangle around its member gates.\\n\\n```\\nlogic \\\"Hierarchical adder\\\"\\ninput A, B, Cin\\noutput Sum, Cout\\n\\nmodule \\\"Half Adder\\\" {\\n s1 = XOR(A, B)\\n c1 = AND(A, B)\\n}\\n\\nSum = XOR(s1, Cin)\\nCout = OR(c1, AND(s1, Cin))\\n```\\n\\nModule syntax rules:\\n- `module \\\"Label\\\" {` — opens a module (quoted label or bare identifier). The `{` must be on the same line.\\n- `}` on its own line closes the most recently opened module.\\n- Modules can be nested.\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Diagram title:** `logic \\\"Full Adder\\\"` — first line only.\\n- **Gate signal names:** the `id` in `id = GATE(…)` is both the gate name and the output wire name.\\n- **Output labels:** `output Sum` — the output port label matches the signal name by default.\\n- **Active-low marker:** `~` prefix on a port or gate input.\\n- **Comments:** `#` or `--` at the start of a line, or after the last meaningful token on a line.\\n\\n---\\n\\n## 7. Reserved words & escaping\\n\\n**Reserved at line start:** `logic` (header), `input`, `output`, `module`, `}`.\\n\\n**Reserved operator tokens** — avoid these inside signal names: `=`, `(`, `)`, `,`, `<-`, `~`.\\n\\n**Signal name rules:** must match `[a-zA-Z_][a-zA-Z0-9_]*`. Lowercase and uppercase are both accepted; gate type keywords (`AND`, `OR`, etc.) are case-insensitive in the parser.\\n\\n---\\n\\n## 8. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `f = and(A, B)` (lowercase gate) | Accepted — gate types are case-insensitive | Both `AND` and `and` work |\\n| `output F` then `F <- q1` but `q1` not declared | `LogicParseError: Unknown signal \\\"q1\\\"` | Declare `q1` as a gate before referencing it |\\n| `input A B C` (spaces, no commas) | Parser takes `A` only; `B` and `C` are ignored | Use commas: `input A, B, C` |\\n| `F = BUFFER(A)` | `LogicParseError: Unknown gate type: BUFFER` | Use `BUF` |\\n| `module FullAdder {` (no `{` brace) | Line does not match module pattern — skipped silently | The opening `{` is required on the same line as `module` |\\n| `style: IEEE` on the header | Unknown style value — silently defaults to `ansi` | Use `style: ansi` or `style: iec` |\\n\\n---\\n\\n## 9. Grammar (EBNF)\\n\\n```text\\ndocument = header statement*\\n\\nheader = \\\"logic\\\" ( WS quoted-string )? ( WS \\\"style:\\\" WS style )? NEWLINE\\nstyle = \\\"ansi\\\" | \\\"iec\\\"\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nstatement = blank | comment | input-decl | output-decl | gate-def | assign | module-block\\n\\ncomment = ( \\\"#\\\" | \\\"--\\\" ) any NEWLINE\\n\\ninput-decl = \\\"input\\\" WS port-list NEWLINE\\noutput-decl = \\\"output\\\" WS port-list NEWLINE\\nport-list = port-id ( \\\",\\\" WS? port-id )*\\nport-id = \\\"~\\\"? id\\n\\ngate-def = id WS \\\"=\\\" WS gate-type \\\"(\\\" input-list \\\")\\\" NEWLINE\\ninput-list = ( \\\"~\\\"? id ) ( \\\",\\\" WS? ( \\\"~\\\"? id ) )*\\n\\nassign = id WS \\\"<-\\\" WS id NEWLINE\\n\\nmodule-block = module-open ( statement | module-block )* module-close\\nmodule-open = \\\"module\\\" WS ( quoted-string | id ) WS? \\\"{\\\" NEWLINE\\nmodule-close = \\\"}\\\" NEWLINE\\n\\ngate-type = \\\"AND\\\" | \\\"OR\\\" | \\\"NOT\\\" | \\\"NAND\\\" | \\\"NOR\\\" | \\\"XOR\\\" | \\\"XNOR\\\" | \\\"BUF\\\"\\n | \\\"TRISTATE_BUF\\\" | \\\"TRISTATE_INV\\\" | \\\"OPEN_DRAIN\\\" | \\\"SCHMITT\\\"\\n | \\\"DFF\\\" | \\\"JKFF\\\" | \\\"SRFF\\\" | \\\"TFF\\\"\\n | \\\"LATCH_SR\\\" | \\\"LATCH_D\\\"\\n | \\\"MUX\\\" | \\\"DEMUX\\\" | \\\"DECODER\\\" | \\\"ENCODER\\\"\\n | \\\"COUNTER\\\" | \\\"SHIFT_REG\\\"\\n // all case-insensitive\\n\\nid = [a-zA-Z_] [a-zA-Z0-9_]*\\n```\\n\\nAuthoritative source: `src/diagrams/logic/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"circuit\": {\n \"title\": \"Circuit schematic\",\n \"content\": \"## 1. A minimal circuit\\n\\nThe smallest useful positional circuit: a voltage source, a resistor, and a ground.\\n\\n```\\ncircuit \\\"Voltage divider\\\"\\nV1: voltage_source down value=\\\"5V\\\"\\nwire right\\nR1: resistor right value=\\\"10k\\\"\\nwire right\\nground\\n```\\n\\nFour rules cover 80% of positional-mode usage:\\n\\n1. Start with the keyword `circuit`, optionally followed by a quoted title.\\n2. Each component is `id: type direction` — or just `type direction` for anonymous components.\\n3. Each component's output end becomes the starting point for the next component (the \\\"cursor\\\").\\n4. `at: id.end` (or `at: id.start`) jumps the cursor to any named anchor — use this to branch.\\n\\n> Comments must start with `#` on their own line.\\n\\n---\\n\\n## 2. Components\\n\\n### 2.1 Positional mode syntax\\n\\nA named component line has the form:\\n\\n```\\nid: type direction [value=\\\"…\\\"] [label=\\\"…\\\"]\\n```\\n\\nAn anonymous component omits the `id:` prefix — the parser assigns an auto ID.\\n\\n```\\nR1: resistor right value=\\\"4.7k\\\" label=\\\"R1\\\"\\ncapacitor down value=\\\"100n\\\"\\n```\\n\\n**Direction** is one of `right` (default), `left`, `up`, `down`. It controls which way the component extends from the current cursor position.\\n\\n### 2.2 Passive components\\n\\n| DSL type | Description |\\n|---|---|\\n| `resistor` | Zigzag (ANSI) or rectangle (IEC) |\\n| `potentiometer` | Resistor + wiper arrow, 3-pin |\\n| `rheostat` | 2-pin variable resistor |\\n| `thermistor_ntc` | NTC thermistor (also: `therm`, `ntc`) |\\n| `thermistor_ptc` | PTC thermistor (also: `ptc`) |\\n| `ldr` | Light-dependent resistor |\\n| `varistor` | Voltage-dependent resistor |\\n| `fuse` | Standard fuse |\\n| `fuse_slow` | Slow-blow fuse (`T` designation) |\\n| `capacitor` | Non-polar capacitor |\\n| `electrolytic_cap` | Polar/electrolytic capacitor (also: `ecap`) |\\n| `variable_cap` | Variable capacitor |\\n| `inductor` | Air-core inductor |\\n| `inductor_iron` | Iron-core inductor |\\n| `inductor_ferrite` | Ferrite-core inductor |\\n| `variable_inductor` | Variable inductor |\\n| `ferrite_bead` | EMI ferrite bead |\\n| `crystal` | Quartz crystal oscillator (also: `xtal`) |\\n| `transformer` | Coupled coils (also: `xfmr`) |\\n\\n```\\ncircuit \\\"Passive components gallery\\\"\\n# Row 1: resistor → capacitor → inductor\\nR1: resistor right value=\\\"1k\\\"\\nwire right\\nC1: capacitor right value=\\\"100n\\\"\\nwire right\\nL1: inductor right value=\\\"10u\\\"\\n# Row 2: crystal and transformer, offset below\\nat: R1.start\\nwire down\\nwire down\\nX1: crystal right\\nwire right\\nwire right\\nT1: transformer right\\n```\\n\\n### 2.3 Sources and power\\n\\n| DSL type | Description |\\n|---|---|\\n| `voltage_source` | Circle + polarity (also: `vsource`) |\\n| `current_source` | Circle + arrow (also: `isource`) |\\n| `ac_source` | Circle + sine symbol (also: `acsource`) |\\n| `battery` | Alternating long/short terminal lines |\\n| `vcc` | Power rail arrow (pointing up) |\\n| `ground` | Earth ground — 3 decreasing lines (also: `gnd`) |\\n| `gnd_signal` | Signal ground — solid triangle |\\n| `gnd_chassis` | Chassis ground |\\n| `gnd_digital` | Digital ground |\\n\\n```\\ncircuit \\\"Sources and power gallery\\\"\\n# voltage source with ground\\nV1: voltage_source down value=\\\"5V\\\"\\nwire down\\nground\\nat: V1.start\\nwire right\\nwire right\\n# battery\\nB1: battery down value=\\\"9V\\\"\\nwire down\\nground\\nat: B1.start\\nwire right\\nwire right\\n# ac source\\nA1: ac_source down value=\\\"120V\\\"\\nwire down\\nground\\nat: A1.start\\nwire right\\nwire right\\n# vcc rail\\nvcc up\\nwire down\\ngnd_signal down\\n```\\n\\n### 2.4 Semiconductors — diodes\\n\\n| DSL type | Description |\\n|---|---|\\n| `diode` | Triangle + cathode bar |\\n| `zener` | Diode + bent cathode bar |\\n| `schottky` | Diode + S-bar |\\n| `led` | Diode + outward emission arrows |\\n| `photodiode` | Diode + inward light arrows |\\n| `varactor` | Diode + variable capacitor |\\n| `tvs_diode` | Bidirectional TVS (two bent bars) |\\n| `bridge_rectifier` | 4-diode bridge, 4-pin |\\n\\n```\\ncircuit \\\"Diode types gallery\\\"\\nD1: diode right\\nwire right\\nD2: zener right\\nwire right\\nD3: led right\\nwire right\\nD4: schottky right\\nwire right\\nD5: photodiode right\\nwire right\\nground\\nat: D1.start\\nwire left\\nground\\n```\\n\\n### 2.5 Semiconductors — transistors\\n\\n| DSL type | Description |\\n|---|---|\\n| `npn` | NPN BJT (also: `transistor`, `bjt_npn`) |\\n| `pnp` | PNP BJT (also: `bjt_pnp`) |\\n| `darlington_npn` | NPN Darlington pair |\\n| `darlington_pnp` | PNP Darlington pair |\\n| `nmos` | N-channel MOSFET enhancement (also: `mosfet_n`) |\\n| `pmos` | P-channel MOSFET enhancement (also: `mosfet_p`) |\\n| `nmos_depletion` | N-channel MOSFET depletion |\\n| `jfet_n` | N-channel JFET |\\n| `jfet_p` | P-channel JFET |\\n| `igbt` | IGBT |\\n| `scr` | SCR / thyristor |\\n| `triac` | TRIAC |\\n| `diac` | DIAC |\\n| `phototransistor` | NPN with light arrows |\\n| `optocoupler` | LED + phototransistor in isolation box |\\n\\n```\\ncircuit \\\"Transistor types gallery\\\"\\n# NPN BJT\\nQ1: npn right\\nwire right\\nwire right\\n# PNP BJT\\nQ2: pnp right\\nwire right\\nwire right\\n# N-channel MOSFET\\nQ3: nmos right\\nwire right\\nwire right\\n# P-channel MOSFET\\nQ4: pmos right\\n```\\n\\n### 2.6 Analog ICs and op-amps\\n\\n| DSL type | Description |\\n|---|---|\\n| `opamp` | Triangle: +/− inputs, output |\\n| `comparator` | Same shape, open-collector output |\\n| `schmitt_buffer` | Buffer + hysteresis symbol |\\n| `tri_state_buffer` | Buffer + enable pin |\\n| `instrumentation_amp` | Three-op-amp INA block |\\n| `generic_ic` | Configurable rect with labeled pins (also: `ic`) |\\n| `voltage_regulator` | 3-terminal block: IN/GND/OUT (also: `reg`) |\\n| `dc_dc_converter` | 2-port block with DC/DC label |\\n| `555_timer` | 8-pin 555 pinout block (also: `timer555`) |\\n\\n```\\ncircuit \\\"Analog IC gallery\\\"\\n# op-amp with input/output wires\\nwire right\\nU1: opamp right\\nwire right\\nwire right\\nwire right\\n# comparator\\nU2: comparator right\\nwire right\\nwire right\\nwire right\\n# generic IC block\\nU3: generic_ic right\\n```\\n\\n### 2.7 Switches and relays\\n\\n| DSL type | Description |\\n|---|---|\\n| `switch_spst` | Single-pole single-throw |\\n| `switch_spdt` | Single-pole double-throw |\\n| `switch_dpdt` | Double-pole double-throw |\\n| `push_no` | Push button normally-open |\\n| `push_nc` | Push button normally-closed |\\n| `relay_coil` | Relay coil (2-pin rect) |\\n| `relay_no` | Relay contact normally-open |\\n| `relay_nc` | Relay contact normally-closed |\\n\\n```\\ncircuit \\\"Switch and relay gallery\\\"\\n# SPST switch\\nS1: switch_spst right\\nwire right\\nwire right\\n# SPDT switch\\nS2: switch_spdt right\\nwire right\\nwire right\\n# normally-open push button\\nS3: push_no right\\nwire right\\nwire right\\n# relay coil + contact pair\\nK1: relay_coil right\\nwire right\\nK2: relay_no right\\n```\\n\\n### 2.8 Electromechanical and measurement\\n\\n| DSL type | Description |\\n|---|---|\\n| `motor` | Circle + M |\\n| `speaker` | Cone + box |\\n| `microphone` | Capsule symbol |\\n| `buzzer` | Piezo buzzer |\\n| `ammeter` | Circle + A |\\n| `voltmeter` | Circle + V |\\n| `wattmeter` | Circle + W |\\n| `oscilloscope` | Circle + waveform |\\n\\n### 2.9 Connectors and annotations\\n\\n| DSL type | Description |\\n|---|---|\\n| `wire` | Plain wire segment |\\n| `dot` | Junction dot (T-junction marker) |\\n| `label` | Net label / flag |\\n| `port` | Named port (hollow circle) |\\n| `test_point` | TP marker |\\n| `no_connect` | X — intentionally unconnected pin |\\n| `antenna` | Antenna stub |\\n\\n```\\ncircuit \\\"Passive components\\\"\\nR1: resistor right value=\\\"1k\\\" label=\\\"R1\\\"\\nwire right\\nC1: capacitor down value=\\\"100n\\\" label=\\\"C1\\\"\\nwire down\\nground\\nat: R1.start\\nwire up\\nbattery up label=\\\"9V\\\"\\n```\\n\\n---\\n\\n## 3. Wiring and branching\\n\\n### 3.1 Wire segments\\n\\n`wire direction [N]` draws a bare wire from the current cursor in the given direction. An optional number sets the length in pixels.\\n\\n```\\nwire right\\nwire down 40\\nwire left 20\\n```\\n\\n### 3.2 Jumping the cursor with `at:`\\n\\n`at: id.end` moves the cursor to a named anchor without drawing anything. Use it to branch from a previously placed component.\\n\\n```\\nR1: resistor right value=\\\"10k\\\"\\nat: R1.end\\nC1: capacitor down value=\\\"100n\\\"\\n```\\n\\nNamed anchor suffixes: `end`, `start`. Components retain their ID across the whole diagram, so you can jump back to any previously placed component.\\n\\n### 3.3 Junction dots\\n\\nPlace a `dot` (or use `net NAME: dot`) to mark a T-junction — a point where three or more wires meet. Without a dot, crossed wires are drawn as a crossover (no connection).\\n\\n```\\nR1: resistor right\\ndot\\nwire right # continues from R1.end\\nat: R1.end\\nC1: capacitor down # branches down from the same point\\n```\\n\\n### 3.4 Named nets\\n\\n`net NAME` declares a named net. `net NAME: dot` declares the net and places a junction dot at the current cursor, remembering that location. Later, `at: NAME` jumps back to that net's anchor.\\n\\n```\\nnet VOUT: dot\\nR2: resistor right value=\\\"10k\\\"\\nat: VOUT\\nC1: capacitor down value=\\\"470n\\\"\\n```\\n\\n### 3.5 Net labels\\n\\n`label \\\"text\\\" direction?` places a text label at the current cursor position. Labels do not advance the cursor. They are useful for naming power rails or inter-sheet connections.\\n\\n```\\nlabel \\\"VCC\\\" up\\nlabel \\\"GND\\\" down\\n```\\n\\n```\\ncircuit \\\"RC filter\\\"\\nV1: voltage_source down value=\\\"5V\\\"\\nwire right\\nR1: resistor right value=\\\"1k\\\" label=\\\"R1\\\"\\nnet OUT: dot\\nwire right\\nlabel \\\"Vout\\\" right\\nat: OUT\\nC1: capacitor down value=\\\"100n\\\" label=\\\"C1\\\"\\nwire down\\nground\\n```\\n\\n---\\n\\n## 4. Netlist mode\\n\\nAdd `netlist` after the title on the header line to switch to SPICE-style netlist parsing. The auto-layout engine computes component positions from the net connectivity.\\n\\n```\\ncircuit \\\"Low-pass filter\\\" netlist\\n```\\n\\n### 4.1 Netlist line format\\n\\nEach line is: `ID net1 net2 [net3…] [value] [key=value…]`\\n\\n- **ID** — component identifier. The first letter determines the default type (SPICE prefix convention).\\n- **net1, net2, …** — net names the pins connect to. Use `0`, `gnd`, or `GND` for the ground net.\\n- **value** (optional bare token) — component value or model name.\\n- **key=value** (optional) — `label=`, `value=`, `type=` overrides.\\n\\n### 4.2 SPICE prefix → component type\\n\\n| Prefix | Default type | Pin order |\\n|---|---|---|\\n| `R` | `resistor` | p1, p2 |\\n| `C` | `capacitor` | p1, p2 |\\n| `L` | `inductor` | p1, p2 |\\n| `D` | `diode` | anode (start), cathode (end) |\\n| `V` | `voltage_source` | plus, minus |\\n| `I` | `current_source` | plus, minus |\\n| `Q` | `npn` | c, b, e |\\n| `M` | `nmos` | d, g, s |\\n| `J` | `jfet_n` | d, g, s |\\n| `S` | `switch_spst` | p1, p2 |\\n| `F` | `fuse` | p1, p2 |\\n| `B` | `battery` | plus, minus |\\n| `K` | `relay_coil` | p1, p2 |\\n| `U`, `X` | `generic_ic` | custom via `pins=` |\\n\\n### 4.3 Transistor model override\\n\\nFor `Q` lines, a trailing model name overrides the type:\\n\\n```\\nQ1 c b e npn # NPN BJT\\nQ2 c b e pnp # PNP BJT\\nM1 d g s nmos # N-channel MOSFET\\nM2 d g s pmos # P-channel MOSFET\\n```\\n\\nFor `D` lines, similarly:\\n\\n```\\nD1 anode cathode zener\\nD2 anode cathode led\\nD3 anode cathode schottky\\nD4 anode cathode photodiode\\n```\\n\\n### 4.4 Netlist example\\n\\n```\\ncircuit \\\"CE Amp (netlist)\\\" netlist\\nV1 vcc 0 9V\\nRc vcc c 2.2k\\nRb vcc b 100k\\nQ1 c b e npn\\nRe e 0 1k\\n```\\n\\n---\\n\\n## 5. Attributes\\n\\nBoth positional and netlist modes accept these key=value attributes:\\n\\n| Attribute | Accepted by | Effect |\\n|---|---|---|\\n| `label=\\\"…\\\"` | all components | Display label (reference designator) |\\n| `value=\\\"…\\\"` | all components | Value annotation (1kΩ, 100nF, 5V) |\\n| `at=id.end` | positional components | Start this component at a named anchor |\\n| `length=N` | `wire`, some passives | Length in pixels |\\n\\nIn positional mode, `at=` inside the component line is equivalent to a preceding `at:` line:\\n\\n```\\nC1: capacitor down at=R1.end value=\\\"100n\\\"\\n```\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Diagram title:** `circuit \\\"RC Filter\\\"` — first line only.\\n- **Component label:** `label=\\\"R1\\\"` attribute — reference designator shown beside the symbol.\\n- **Value annotation:** `value=\\\"4.7k\\\"` — shown beside or below the component.\\n- **Net label:** `label \\\"VOUT\\\" right` — standalone net flag at the current cursor.\\n- **Comments:** `#` at the start of a line (after leading whitespace).\\n\\n---\\n\\n## 7. Reserved words & escaping\\n\\n**Reserved at line start (positional):** `circuit` (header), `at:`, `net`, `wire`, `label`.\\n\\n**Reserved in netlist mode:** same header rules apply; all other lines are SPICE component lines.\\n\\n**Ground net aliases (netlist only):** `0`, `gnd`, `GND`, `Gnd`, `ground`, `Ground` — all treated as the same node.\\n\\n**Component IDs** must match `[a-zA-Z_][a-zA-Z0-9_]*`. Spaces in values must be quoted: `value=\\\"10 kΩ\\\"`.\\n\\n---\\n\\n## 8. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `resistor right 1k` (bare value without `value=`) | `1k` is parsed as an unknown attribute flag and ignored | Use `value=\\\"1k\\\"`: `resistor right value=\\\"1k\\\"` |\\n| `at: R1.center` | `center` is not a recognized anchor suffix — cursor stays at current position | Use `at: R1.end` or `at: R1.start` |\\n| `wire 40` (no direction) | Direction defaults to `right`; length `40` is accepted | Explicit direction recommended: `wire right 40` |\\n| `R1 vcc out 10k` in positional mode | Line matches the bare-type pattern; `R1` is read as a type name, fails lookup | In positional mode, use `R1: resistor right value=\\\"10k\\\"` |\\n| `Q1 c b e` (netlist, no model) | Type defaults to `npn` from `Q` prefix — correct | OK; add `npn` explicitly for clarity |\\n| `net OUT` then `at: OUT` without `net OUT: dot` | `OUT` net exists but has no anchor; jump has no destination | Use `net OUT: dot` to register the cursor position |\\n| `label VCC up` (unquoted label) | `VCC` is parsed as a direction token, then `up` — the label text is lost | Quote the text: `label \\\"VCC\\\" up` |\\n\\n---\\n\\n## 9. Grammar (EBNF)\\n\\n```text\\ndocument = header statement*\\n\\n-- Positional mode --\\nheader = \\\"circuit\\\" ( WS quoted-string )? NEWLINE\\nstatement = blank | comment | component | wire | at | net-decl | label-stmt\\n\\ncomponent = ( id \\\":\\\" WS )? type WS direction? attrs* NEWLINE\\nwire = \\\"wire\\\" ( WS direction )? ( WS integer )? NEWLINE\\nat = \\\"at:\\\" WS anchor NEWLINE\\nanchor = id \\\".\\\" ( \\\"start\\\" | \\\"end\\\" )\\n | id // net name anchor\\n\\nnet-decl = \\\"net\\\" WS id NEWLINE // declare net only\\n | \\\"net\\\" WS id \\\":\\\" WS \\\"dot\\\" NEWLINE // declare + place dot\\n\\nlabel-stmt = \\\"label\\\" WS quoted-string ( WS direction )? NEWLINE\\n\\ncomponent-attr = \\\"value=\\\" quoted-string\\n | \\\"label=\\\" quoted-string\\n | \\\"at=\\\" anchor\\n | \\\"length=\\\" integer\\n\\ndirection = \\\"right\\\" | \\\"left\\\" | \\\"up\\\" | \\\"down\\\"\\ntype = // any value from §2 component tables\\n\\n-- Netlist mode --\\nnetlist-header = \\\"circuit\\\" ( WS quoted-string )? WS \\\"netlist\\\" NEWLINE\\nnetlist-stmt = id WS net-ref+ ( WS kv-pair )* NEWLINE\\n | comment\\nnet-ref = id | \\\"0\\\" // net name or ground alias\\nkv-pair = id \\\"=\\\" ( quoted-string | bare-value )\\n\\nid = [a-zA-Z_] [a-zA-Z0-9_]*\\ninteger = [0-9]+\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\ncomment = \\\"#\\\" any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/circuit/parser.ts` and `src/diagrams/circuit/netlist.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"block\": {\n \"title\": \"Block diagram\",\n \"content\": \"## 1. Your first block diagram\\n\\nThe smallest useful block diagram: one controller, one plant, one feedback loop.\\n\\n```\\nblockdiagram \\\"Temperature control\\\"\\nctrl = block(\\\"PID\\\") [role: controller]\\nplant = block(\\\"Heater\\\") [role: plant]\\nsensor = block(\\\"Thermocouple\\\") [role: sensor]\\nerr = sum(+ref, -measured)\\nref = signal(\\\"Setpoint\\\")\\nmeasured = signal(\\\"T_measured\\\")\\nin -> ref\\nref -> err\\nerr -> ctrl\\nctrl -> plant\\nplant -> measured\\nmeasured -> sensor\\nsensor -> err\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with `blockdiagram`, optionally followed by a quoted title.\\n2. Declare each component with `ID = block(\\\"label\\\")`, each summing junction with `ID = sum(+a, -b)`, and each named signal with `ID = signal(\\\"label\\\")`.\\n3. Connect components with `->`. Chain multiple hops on one line: `A -> B -> C`.\\n4. Optionally annotate connections with a trailing label: `A -> B [\\\"E(s)\\\"]`.\\n\\n> Comments must start with `#` on their own line.\\n\\n---\\n\\n## 2. Blocks\\n\\nA block represents any functional element — controller, plant, filter, actuator, sensor. The label is typically a transfer function or a descriptive name.\\n\\n**Syntax:** `ID = block(\\\"label\\\") [role: X]`\\n\\n| Attribute | Values | Effect |\\n|---|---|---|\\n| `role: plant` | `plant`, `controller`, `sensor`, `actuator`, `reference`, `disturbance`, `generic` | Visual color coding; `generic` is the default |\\n| `route: above` | `above`, `below` | Routing hint for feedback and feedforward blocks |\\n\\n**ID rules.** Must start with a letter or underscore, followed by letters, digits, or underscores: `[A-Za-z_]\\\\w*`.\\n\\n```\\nblockdiagram \\\"Block roles\\\"\\nref_block = block(\\\"r(t)\\\") [role: reference]\\nctrl = block(\\\"C(s)\\\") [role: controller]\\nact = block(\\\"Actuator\\\") [role: actuator]\\nplant = block(\\\"G(s)\\\") [role: plant]\\nsensor = block(\\\"H(s)\\\") [role: sensor]\\ndist = block(\\\"d(t)\\\") [role: disturbance]\\nref_block -> ctrl\\nctrl -> act\\nact -> plant\\nplant -> sensor\\ndist -> plant\\n```\\n\\n---\\n\\n## 3. Summing junctions\\n\\nA summing junction combines multiple signals into one, with explicit polarity for each input. It renders as a circle with `+`/`−` signs — the standard control-systems symbol.\\n\\n**Syntax:** `ID = sum(+a, -b, +c, …)`\\n\\n- Each input is a signed ID: `+x` adds signal `x`, `-y` subtracts signal `y`.\\n- An input without a sign is treated as positive.\\n- The summing junction ID is then used as the target of connection lines, just like a block ID.\\n\\n```\\nblockdiagram \\\"Error with disturbance rejection\\\"\\nctrl = block(\\\"PI C(s)\\\") [role: controller]\\nplant = block(\\\"G(s)\\\") [role: plant]\\nsensor = block(\\\"H(s)\\\") [role: sensor]\\n# Main error junction: add reference, subtract feedback\\nerr = sum(+r, -ym)\\n# Disturbance junction: add plant input, add disturbance\\ndisturb = sum(+ctrl_out, +d)\\nr = signal(\\\"r (setpoint)\\\")\\nym = signal(\\\"y_m\\\")\\nctrl_out = signal(\\\"u(t)\\\")\\nin -> r\\nr -> err\\nerr -> ctrl\\nctrl -> ctrl_out\\nctrl_out -> disturb\\ndisturb -> plant\\nplant -> ym\\nym -> sensor\\nsensor -> err\\n```\\n\\n---\\n\\n## 4. Signals\\n\\nA signal declaration creates a named signal node that the parser inlines as an edge label. Signals are pass-through: when the parser sees `A -> sig` and `sig -> B`, it merges them into a single edge from `A` to `B`, labeling it with the signal's display text.\\n\\n**Syntax:** `ID = signal(\\\"label\\\") [discrete]`\\n\\n- Omit `[discrete]` for continuous signals (solid line).\\n- Add `[discrete]` for sampled-data signals (dashed line).\\n\\n```\\ne_sig = signal(\\\"E(s)\\\")\\nu_sig = signal(\\\"U(s)\\\") [discrete]\\n```\\n\\nSignals are purely a labeling convenience — you can also label edges directly with a trailing attribute (see §5).\\n\\n---\\n\\n## 5. Connections\\n\\nA connection line is `from -> to`. The `->` operator always produces a directed, arrowed line.\\n\\n**Single hop:** `A -> B`\\n\\n**Chain:** `A -> B -> C` — equivalent to `A -> B` and `B -> C`. Both are written in one line.\\n\\n**With a signal label:** append `[\\\"label text\\\"]` at the end of the chain. The label applies to the last hop only.\\n\\n```\\nctrl -> plant [\\\"U(s)\\\"]\\n```\\n\\n**With a discrete flag:** append `[discrete]` to make the last-hop arrow dashed.\\n\\n```\\nplant -> adc [\\\"y\\\"] [discrete]\\n```\\n\\n**Both label and discrete:** use `[label: \\\"Y(s)\\\", discrete]` (comma-separated).\\n\\n```\\nadc -> ctrl [label: \\\"y[k]\\\", discrete]\\n```\\n\\n```\\nblockdiagram \\\"Mixed continuous/discrete\\\"\\nctrl = block(\\\"Digital PID\\\") [role: controller]\\ndac = block(\\\"DAC\\\") [role: actuator]\\nplant = block(\\\"G(s)\\\") [role: plant]\\nadc = block(\\\"ADC\\\") [role: sensor]\\nerr = sum(+r, -yk)\\nr = signal(\\\"r[k]\\\") [discrete]\\nyk = signal(\\\"y[k]\\\") [discrete]\\nin -> r\\nr -> err\\nerr -> ctrl\\nctrl -> dac [label: \\\"u[k]\\\", discrete]\\ndac -> plant [\\\"u(t)\\\"]\\nplant -> adc [\\\"y(t)\\\"]\\nadc -> yk [discrete]\\nyk -> err\\n```\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Title:** `blockdiagram \\\"My System\\\"` — first line, quoted.\\n- **Block label:** the quoted string inside `block(\\\"…\\\")` — appears inside the box.\\n- **Signal label:** the quoted string inside `signal(\\\"…\\\")` — appears on the merged edge.\\n- **Edge label:** trailing `[\\\"text\\\"]` or `[label: \\\"text\\\"]` on a connection line — appears on that arrow.\\n- **Comments:** `#` at the start of a line (after leading whitespace). Inline trailing comments are not supported.\\n\\n---\\n\\n## 7. Reserved words & escaping\\n\\n**Reserved at line start:** `blockdiagram` (header).\\n\\n**Structural keywords** (avoid as block/signal/sum IDs to prevent ambiguity): `block`, `signal`, `sum`.\\n\\n**`in` and `out`** are conventional IDs for the external boundary of a diagram — the parser treats them as ordinary identifiers, but the renderer uses them as implicit source/sink nodes. Using `in -> r` and `plant -> out` is idiomatic.\\n\\n**Strings with spaces** must be double-quoted in `block(\\\"…\\\")` and `signal(\\\"…\\\")` labels.\\n\\n---\\n\\n## 8. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `G = block(G(s))` (no quotes) | Parse fails — label must be quoted | `G = block(\\\"G(s)\\\")` |\\n| `err = sum(r, -ym)` (no `+`) | `r` treated as `+r` — works, but ambiguous | Write `sum(+r, -ym)` for clarity |\\n| `ctrl -> plant, plant -> out` (comma on one line) | `,` is not a separator — parse fails | One connection per line or use chain: `ctrl -> plant -> out` |\\n| `s1 = signal(\\\"E(s)\\\") [label: \\\"E\\\"]` | `label:` not valid on signal; use it on connections | Drop `label:` from signal declaration |\\n| `role: filter` | Unknown role — silently defaults to `generic` | Use `plant`, `controller`, `sensor`, `actuator`, `reference`, `disturbance`, or `generic` |\\n| `A -> B [discrete, label: \\\"e\\\"]` — label first fails | Order of attrs inside `[…]` doesn't matter, but bare `\\\"text\\\"` shorthand only works when it's the only item | Use `[label: \\\"e\\\", discrete]` |\\n\\n---\\n\\n## 9. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | block-def | sum-def | signal-def | connection)*\\n\\nheader = \\\"blockdiagram\\\" ( WS quoted-string )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nblock-def = id WS \\\"=\\\" WS \\\"block\\\" \\\"(\\\" quoted-string \\\")\\\" ( \\\"[\\\" block-attrs \\\"]\\\" )? NEWLINE\\nblock-attrs = block-attr (\\\",\\\" block-attr)*\\nblock-attr = \\\"role:\\\" role | \\\"route:\\\" (\\\"above\\\" | \\\"below\\\")\\nrole = \\\"plant\\\" | \\\"controller\\\" | \\\"sensor\\\" | \\\"actuator\\\"\\n | \\\"reference\\\" | \\\"disturbance\\\" | \\\"generic\\\"\\n\\nsum-def = id WS \\\"=\\\" WS \\\"sum\\\" \\\"(\\\" sum-inputs \\\")\\\" NEWLINE\\nsum-inputs = sum-input (\\\",\\\" sum-input)*\\nsum-input = (\\\"+\\\" | \\\"-\\\")? id\\n\\nsignal-def = id WS \\\"=\\\" WS \\\"signal\\\" \\\"(\\\" quoted-string \\\")\\\" ( \\\"[\\\" \\\"discrete\\\" \\\"]\\\" )? NEWLINE\\n\\nconnection = id (\\\"->\\\" id)+ ( \\\"[\\\" conn-attrs \\\"]\\\" )? NEWLINE\\nconn-attrs = quoted-string # shorthand: bare label only\\n | conn-attr (\\\",\\\" conn-attr)*\\nconn-attr = \\\"label:\\\" quoted-string | \\\"discrete\\\"\\n\\nid = [A-Za-z_] \\\\w*\\ncomment = \\\"#\\\" any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/blockdiagram/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"ladder\": {\n \"title\": \"Ladder logic\",\n \"content\": \"## 1. Your first ladder diagram\\n\\nThe smallest useful ladder program: one rung, two contacts, one coil.\\n\\n```\\nladder \\\"First Rung\\\"\\nrung 1 \\\"Start when button pressed, stop on fault\\\":\\n XIC(START_PB)\\n XIO(FAULT)\\n OTE(MOTOR_RUN)\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with `ladder`, optionally followed by a quoted title.\\n2. Each **rung** begins with `rung N \\\"optional comment\\\":` on its own line.\\n3. Elements are listed one per line, indented under the rung — left to right means series (AND logic).\\n4. A `parallel:` / `branch:` block introduces OR logic. Every branch holds its own element list.\\n\\n> Comments must start with `#` on their own line.\\n\\n---\\n\\n## 2. Contacts\\n\\nContacts represent input conditions — they pass power when the associated bit matches the contact type.\\n\\n| Type | Name | Passes power when… |\\n|---|---|---|\\n| `XIC` | Examine If Closed | Tag bit = 1 (normally open) |\\n| `XIO` | Examine If Open | Tag bit = 0 (normally closed) |\\n| `ONS` | One-Shot Rising | Tag transitions 0 → 1 (rising edge, one scan) |\\n| `OSF` | One-Shot Falling | Tag transitions 1 → 0 (falling edge, one scan) |\\n\\n**Syntax:**\\n\\n```\\nXIC(tag)\\nXIC(tag, \\\"address\\\")\\nXIC(tag, \\\"address\\\", name=\\\"Description\\\")\\nXIC(tag, address=\\\"address\\\", name=\\\"Description\\\")\\n```\\n\\n- `tag` — required. The PLC tag name (displayed below the contact symbol).\\n- `\\\"address\\\"` — optional positional second argument. The I/O address (e.g. `\\\"IN 1.0\\\"`, `\\\"BIT 3.1\\\"`).\\n- `name=\\\"…\\\"` — optional key-value. Human-readable description (displayed above the symbol).\\n\\n```\\nladder \\\"Contact types\\\"\\nrung 1 \\\"All four contact types\\\":\\n XIC(START_PB, \\\"IN 1.0\\\", name=\\\"Start Button\\\")\\n XIO(E_STOP, \\\"IN 1.5\\\", name=\\\"Emergency Stop NC\\\")\\n ONS(PULSE_IN, \\\"BIT 5.0\\\", name=\\\"One-Shot Rising\\\")\\n OSF(RESET_SIG, \\\"BIT 5.1\\\", name=\\\"One-Shot Falling\\\")\\n OTE(OUT_RLY, \\\"OUT 2.0\\\", name=\\\"Output Relay\\\")\\n```\\n\\n---\\n\\n## 3. Coils\\n\\nCoils represent output actions — they act on a tag bit when the rung has power flow.\\n\\n| Type | Name | Effect on tag bit |\\n|---|---|---|\\n| `OTE` | Output Energize | Sets bit = 1 while rung is true; clears to 0 when rung is false |\\n| `OTL` | Output Latch | Sets bit = 1; **retains** even after rung goes false (latches) |\\n| `OTU` | Output Unlatch | Clears bit = 0; retains even after rung goes false |\\n| `OTN` | Output Negate | Sets bit = 0 while rung is true; sets to 1 when rung is false |\\n\\n**Syntax:** identical to contacts — `OTE(tag)`, `OTE(tag, \\\"address\\\")`, `OTE(tag, \\\"address\\\", name=\\\"…\\\")`.\\n\\n`OTL` and `OTU` are used in pairs to build Set/Reset flip-flops. The last rung to write wins.\\n\\n```\\nladder \\\"Set-Reset latch\\\"\\nrung 1 \\\"Set on start\\\":\\n XIC(START_PB, \\\"IN 1.0\\\", name=\\\"Start\\\")\\n OTL(MOTOR_ON, \\\"BIT 3.0\\\", name=\\\"Motor Latch\\\")\\nrung 2 \\\"Reset on stop or fault\\\":\\n parallel:\\n branch:\\n XIC(STOP_PB, \\\"IN 1.1\\\", name=\\\"Stop\\\")\\n branch:\\n XIC(E_STOP, \\\"IN 1.5\\\", name=\\\"E-Stop\\\")\\n OTU(MOTOR_ON, \\\"BIT 3.0\\\", name=\\\"Motor Latch\\\")\\n```\\n\\n---\\n\\n## 4. Function blocks\\n\\nFunction blocks perform timer, counter, math, and comparison operations. They appear inline in a rung and have keyword parameters after the mandatory tag argument.\\n\\n### 4.1 Timers\\n\\n| Type | Name | Key parameters |\\n|---|---|---|\\n| `TON` | Timer On-Delay | `PT=` preset time in milliseconds |\\n| `TOFF` | Timer Off-Delay | `PT=` preset time in milliseconds |\\n| `TP` | Timer Pulse | `PT=` preset time in milliseconds |\\n\\n```\\nTON(timer_tag, PT=5000)\\n```\\n\\nThe timer tag stores elapsed time. The timer's `Q` bit (done output) is accessed by tag name in downstream contacts.\\n\\n### 4.2 Counters\\n\\n| Type | Name | Key parameters |\\n|---|---|---|\\n| `CTU` | Count Up | `PV=` preset value (integer) |\\n| `CTD` | Count Down | `PV=` preset value |\\n| `CTUD` | Count Up/Down | `PV=` preset value |\\n\\n```\\nCTU(cycle_counter, PV=100)\\n```\\n\\n### 4.3 Math\\n\\n| Type | Operation |\\n|---|---|\\n| `ADD` | Add |\\n| `SUB` | Subtract |\\n| `MUL` | Multiply |\\n| `DIV` | Divide |\\n| `MOV` | Move (copy) |\\n\\n```\\nADD(result_tag, IN1=setpoint, IN2=offset)\\nMOV(dest_tag, IN1=source_tag)\\n```\\n\\n### 4.4 Comparisons\\n\\n| Type | Meaning |\\n|---|---|\\n| `EQU` | Equal |\\n| `NEQ` | Not equal |\\n| `GRT` | Greater than |\\n| `LES` | Less than |\\n| `GEQ` | Greater than or equal |\\n| `LEQ` | Less than or equal |\\n\\n```\\nEQU(compare_tag, IN1=speed_actual, IN2=speed_setpoint)\\n```\\n\\n```\\nladder \\\"Timer and counter\\\"\\nrung 1 \\\"Start run timer\\\":\\n XIC(MOTOR_CMD, \\\"BIT 3.0\\\", name=\\\"Motor Running\\\")\\n TON(RUN_TIMER, PT=10000)\\nrung 2 \\\"Count completed cycles\\\":\\n XIC(CYCLE_SENSOR, \\\"IN 2.0\\\", name=\\\"Cycle Sensor\\\")\\n CTU(PART_COUNT, PV=500)\\nrung 3 \\\"Alarm when batch complete\\\":\\n XIC(PART_COUNT, name=\\\"Count Done\\\")\\n OTE(BATCH_ALARM, \\\"OUT 3.0\\\", name=\\\"Batch Complete Alarm\\\")\\n```\\n\\n---\\n\\n## 5. Parallel branches\\n\\nA `parallel:` block introduces OR logic — the rung has power if **any** branch conducts. Each branch is a `branch:` sub-block with its elements indented below it.\\n\\n```\\nparallel:\\n branch:\\n XIC(LOCAL_START)\\n branch:\\n XIC(REMOTE_START)\\n```\\n\\nBranches in a `parallel:` are evaluated simultaneously. The block closes when indentation returns to the level before `parallel:`.\\n\\n**Rules:**\\n- `parallel:` must appear inside a rung.\\n- `branch:` must appear inside a `parallel:` — using it alone throws `LadderParseError`.\\n- Each branch holds one or more elements.\\n- Elements after the `parallel:` block are series with it (AND logic).\\n\\n```\\nladder \\\"Parallel OR logic\\\"\\nrung 1 \\\"Start from either local or remote\\\":\\n parallel:\\n branch:\\n XIC(LOCAL_START, \\\"IN 1.0\\\", name=\\\"Local Start\\\")\\n branch:\\n XIC(REMOTE_START, \\\"BIT 5.2\\\", name=\\\"Remote Start\\\")\\n XIO(STOP_ALL, \\\"IN 1.5\\\", name=\\\"Stop All\\\")\\n OTE(CONVEYOR, \\\"OUT 2.0\\\", name=\\\"Conveyor Run\\\")\\n```\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Title:** `ladder \\\"Motor Control\\\"` — first line only, quoted string.\\n- **Rung number:** required integer after `rung`.\\n- **Rung comment:** optional quoted string after the rung number, before the colon: `rung 3 \\\"Run indicator\\\":`.\\n- **Tag:** first argument inside the parentheses — displayed below the symbol.\\n- **Address:** second positional argument (quoted): `XIC(START_PB, \\\"IN 1.0\\\")`.\\n- **Name:** `name=\\\"…\\\"` keyword argument — human-readable description displayed above the symbol.\\n- **Line comments:** `#` at the start of a line (after leading whitespace). Inline `# …` after an element line is also stripped.\\n\\n---\\n\\n## 7. Reserved words & escaping\\n\\n**Reserved at line start (case-insensitive):** `ladder`, `rung`, `parallel:`, `branch:`.\\n\\n**Element names** are all uppercase ASCII: `XIC`, `XIO`, `ONS`, `OSF`, `OTE`, `OTL`, `OTU`, `OTN`, `TON`, `TOFF`, `TP`, `CTU`, `CTD`, `CTUD`, `ADD`, `SUB`, `MUL`, `DIV`, `MOV`, `EQU`, `NEQ`, `GRT`, `LES`, `GEQ`, `LEQ`.\\n\\n**Tag IDs** — must match `[A-Z][A-Z0-9_]*` (the parser matches `[A-Z][A-Z0-9_]*` for the element name prefix). Lowercase tags are accepted inside the parentheses.\\n\\n**Quoted strings** in address or name arguments must use double quotes `\\\"…\\\"`.\\n\\n---\\n\\n## 8. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `rung 1` (no colon) | `LadderParseError: element outside of rung` | Rung header must end with `:` — `rung 1:` |\\n| `ONF(TAG)` | `LadderParseError: unknown element type \\\"ONF\\\"` | Falling-edge contact is `OSF`, not `ONF` |\\n| `parallel:` without `branch:` | Empty parallel block — rung has no element | Add at least one `branch:` inside the `parallel:` |\\n| `branch:` before `parallel:` | `LadderParseError: branch: without parallel:` | Always open `parallel:` first |\\n| `OTE()` — no tag | `LadderParseError: element missing tag` | Tag is required: `OTE(MY_TAG)` |\\n| `var StartBtn: bool` (variable declaration) | `LadderParseError: invalid element syntax` | No variable declarations — tags are used directly |\\n| Empty rung (no elements after `rung N:`) | `LadderParseError: Rung N: empty rung` | Add at least one element to each rung |\\n| `TON(T1, T#5s)` | `LadderParseError: invalid element syntax` (T# not a valid number) | Use milliseconds integer: `TON(T1, PT=5000)` |\\n\\n---\\n\\n## 9. Grammar (EBNF)\\n\\n```text\\ndocument = header NEWLINE rung+\\n\\nheader = \\\"ladder\\\" ( WS quoted-string )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nrung = \\\"rung\\\" WS integer ( WS quoted-string )? \\\":\\\" NEWLINE\\n element+\\n\\nelement = contact-line\\n | coil-line\\n | fb-line\\n | parallel-block\\n\\ncontact-line = contact-type \\\"(\\\" tag ( \\\",\\\" arg )* \\\")\\\" NEWLINE\\ncontact-type = \\\"XIC\\\" | \\\"XIO\\\" | \\\"ONS\\\" | \\\"OSF\\\"\\n\\ncoil-line = coil-type \\\"(\\\" tag ( \\\",\\\" arg )* \\\")\\\" NEWLINE\\ncoil-type = \\\"OTE\\\" | \\\"OTL\\\" | \\\"OTU\\\" | \\\"OTN\\\"\\n\\nfb-line = fb-type \\\"(\\\" tag ( \\\",\\\" arg )* \\\")\\\" NEWLINE\\nfb-type = \\\"TON\\\" | \\\"TOFF\\\" | \\\"TP\\\"\\n | \\\"CTU\\\" | \\\"CTD\\\" | \\\"CTUD\\\"\\n | \\\"ADD\\\" | \\\"SUB\\\" | \\\"MUL\\\" | \\\"DIV\\\" | \\\"MOV\\\"\\n | \\\"EQU\\\" | \\\"NEQ\\\" | \\\"GRT\\\" | \\\"LES\\\" | \\\"GEQ\\\" | \\\"LEQ\\\"\\n\\narg = quoted-string // positional (address)\\n | key \\\"=\\\" quoted-string // keyword (e.g. name=\\\"…\\\")\\n | key \\\"=\\\" number // keyword (e.g. PT=5000)\\n\\nparallel-block = INDENT≥2 \\\"parallel:\\\" NEWLINE\\n ( INDENT branch-block )+\\n\\nbranch-block = \\\"branch:\\\" NEWLINE\\n ( INDENT element )+\\n\\ntag = [A-Za-z][A-Za-z0-9_]*\\nkey = [A-Za-z][A-Za-z0-9_]*\\ninteger = [0-9]+\\nnumber = [0-9]+ ( \\\".\\\" [0-9]+ )?\\ncomment = \\\"#\\\" any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/ladder/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"sld\": {\n \"title\": \"Single-line diagram (SLD)\",\n \"content\": \"## 1. Your first single-line diagram\\n\\nThe simplest SLD: a utility source, a transformer, a breaker, and a load.\\n\\n```\\nsld \\\"Simple feeder\\\"\\nutil = utility [label: \\\"Utility 13.8kV\\\"]\\nxfmr = transformer [rating: \\\"500 kVA\\\", voltage: \\\"13.8kV/480V\\\"]\\nbus1 = bus [voltage: \\\"480V\\\", label: \\\"480V Bus\\\"]\\ncb1 = breaker [rating: \\\"200A\\\"]\\nload1 = load [label: \\\"Panel LP-1\\\"]\\nutil -> xfmr\\nxfmr -> bus1\\nbus1 -> cb1\\ncb1 -> load1\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with `sld`, optionally followed by a quoted title.\\n2. Declare each equipment item as `id = nodeType [attributes]` — one per line.\\n3. Connect items with `from -> to`, optionally adding `[cable: \\\"…\\\", label: \\\"…\\\"]`.\\n4. IDs may contain letters, digits, underscores, and hyphens — but must start with a letter.\\n\\n> Comments must start with `#` on their own line.\\n\\n---\\n\\n## 2. Node types\\n\\nA node line is `id = nodeType [attr: value, …]`. The node type determines the symbol drawn.\\n\\n### 2.1 Sources\\n\\n| Type | Symbol | Typical use |\\n|---|---|---|\\n| `utility` | Utility source arrow | Infinite bus / grid connection |\\n| `generator` | Circle with `G` | Diesel, gas, or hydro genset |\\n| `solar` | PV panel symbol | Photovoltaic array |\\n| `wind` | Turbine symbol | Wind turbine |\\n| `ups` | Block with battery | Uninterruptible power supply |\\n\\n```\\nsld \\\"Generation sources\\\"\\nutil = utility [label: \\\"Grid 115 kV\\\"]\\ngen = generator [rating: \\\"2 MW\\\", label: \\\"Diesel Gen\\\"]\\nsol = solar [rating: \\\"500 kW\\\", label: \\\"PV Array\\\"]\\nwnd = wind [rating: \\\"1 MW\\\", label: \\\"Wind Turbine\\\"]\\nups = ups [rating: \\\"100 kVA\\\", label: \\\"UPS System\\\"]\\nutil -> gen\\nutil -> sol\\nutil -> wnd\\nutil -> ups\\n```\\n\\n### 2.2 Transformers\\n\\n| Type | Winding configuration | Notes |\\n|---|---|---|\\n| `transformer` | Generic two-winding | No winding spec |\\n| `transformer_dy` | Delta → Wye grounded (Δ-Yg) | Most common distribution |\\n| `transformer_yd` | Wye grounded → Delta (Yg-Δ) | |\\n| `transformer_yy` | Wye-Wye (both grounded) | |\\n| `transformer_dd` | Delta-Delta | |\\n| `autotransformer` | Single-winding with tap | Zigzag coil symbol |\\n| `transformer_3winding` | Three-winding | HV / MV / LV taps |\\n\\n```\\nsld \\\"Transformer configurations\\\"\\nsrc = utility [label: \\\"138kV Grid\\\"]\\nt_dy = transformer_dy [rating: \\\"30 MVA\\\", voltage: \\\"138kV/13.8kV\\\", label: \\\"Δ-Yg (most common)\\\"]\\nt_yy = transformer_yy [rating: \\\"10 MVA\\\", voltage: \\\"138kV/13.8kV\\\", label: \\\"Yg-Yg\\\"]\\nt_auto = autotransformer [rating: \\\"50 MVA\\\", voltage: \\\"138kV/69kV\\\", label: \\\"Autotransformer\\\"]\\nt_3w = transformer_3winding [rating: \\\"40 MVA\\\", voltage: \\\"138/13.8/4.16kV\\\", label: \\\"3-Winding\\\"]\\nsrc -> t_dy\\nsrc -> t_yy\\nsrc -> t_auto\\nsrc -> t_3w\\n```\\n\\n### 2.3 Buses and nodes\\n\\n| Type | Symbol | Typical use |\\n|---|---|---|\\n| `bus` | Thick horizontal line | Main voltage bus bar |\\n| `bus_tie` | Bus-tie breaker | Links two parallel buses at the same voltage |\\n| `hub` | Wide rectangle | Multi-feeder combining point |\\n\\n### 2.4 Switching and protection\\n\\n| Type | Symbol | Device number |\\n|---|---|---|\\n| `breaker` | Diagonal + arc | 52 (AC circuit breaker) |\\n| `breaker_vacuum` | Diagonal + V-oval | 52 vacuum type |\\n| `switch` | Diagonal (no arc) | 89 (disconnect / isolator) |\\n| `switch_load` | Load interrupter switch | — |\\n| `ground_switch` | Diagonal + ground symbol | Grounding disconnect |\\n| `ats` | Transfer switch symbol | Automatic transfer switch |\\n| `recloser` | Diagonal + arc + arrow | Auto-reclosing breaker |\\n| `sectionalizer` | Diagonal + S | Distribution sectionalizer |\\n| `fuse` | Oval with diagonal | Expulsion fuse cutout |\\n| `fuse_cl` | Rectangle with diagonal | Current-limiting fuse |\\n\\n```\\nsld \\\"Switching and protection\\\"\\nsrc = utility [label: \\\"Source\\\"]\\nrclsr = recloser [label: \\\"Recloser\\\"]\\nsect = sectionalizer [label: \\\"Sectionalizer\\\"]\\nfuse1 = fuse [label: \\\"Fuse\\\"]\\nsw = switch [label: \\\"Disconnect\\\"]\\ngnd_sw = ground_switch [label: \\\"Ground SW\\\"]\\nsrc -> rclsr\\nrclsr -> sect\\nsect -> fuse1\\nsect -> sw\\nsw -> gnd_sw\\n```\\n\\n### 2.5 Protection and monitoring\\n\\n| Type | Symbol | Typical use |\\n|---|---|---|\\n| `ct` | Small circle with line through | Current transformer |\\n| `pt` | Small circle | Potential / voltage transformer |\\n| `relay` | Small circle with device number | Protection relay (ANSI number via `device:`) |\\n| `surge_arrester` | Arrow + ground | Lightning arrester |\\n| `ground_fault` | GFI symbol | Ground-fault detector |\\n\\n### 2.6 Loads and equipment\\n\\n| Type | Symbol | Typical use |\\n|---|---|---|\\n| `motor` | Circle with `M` | Three-phase motor |\\n| `load` | Rectangle | Generic load or feeder |\\n| `capacitor_bank` | Two plates + switch | Power factor correction |\\n| `harmonic_filter` | LC symbol | Passive harmonic filter |\\n| `vfd` | Rectangle with VFD | Variable-frequency drive |\\n\\n### 2.7 Metering\\n\\n| Type | Symbol | Typical use |\\n|---|---|---|\\n| `watthour_meter` | Circle with `Wh` | Energy meter |\\n| `demand_meter` | Circle with `D` | Demand meter |\\n\\n```\\nsld \\\"Equipment types\\\"\\nsrc = utility [label: \\\"Grid 13.8kV\\\"]\\ntx = transformer_dy [rating: \\\"1000 kVA\\\", voltage: \\\"13.8kV/480V\\\", label: \\\"Main TX\\\"]\\nbk = breaker [rating: \\\"2000A\\\", label: \\\"Main Breaker\\\"]\\nbus = bus [voltage: \\\"480V\\\", label: \\\"480V MV Bus\\\"]\\nct1 = ct [label: \\\"CT-1\\\"]\\nrly = relay [device: \\\"51\\\", label: \\\"Overcurrent Relay\\\"]\\ncap = capacitor_bank [rating: \\\"150 kVAR\\\", label: \\\"PF Cap\\\"]\\nmtr = motor [rating: \\\"100HP\\\", label: \\\"Pump Motor\\\"]\\ngen = generator [rating: \\\"500kW\\\", label: \\\"Emergency Gen\\\"]\\nats = ats [rating: \\\"800A\\\", label: \\\"ATS-1\\\"]\\nsrc -> tx\\ntx -> bk\\nbk -> bus\\nbus -> ct1\\nct1 -> rly\\nbus -> cap\\nbus -> mtr\\ngen -> ats\\nats -> bus\\n```\\n\\n---\\n\\n## 3. Node attributes\\n\\nAttributes are written inside `[…]` after the node type, comma-separated.\\n\\n| Attribute | Values | Effect |\\n|---|---|---|\\n| `label: \\\"…\\\"` | quoted string | Display name on the diagram |\\n| `voltage: \\\"…\\\"` | quoted string, e.g. `\\\"13.8kV\\\"`, `\\\"480V\\\"` | Voltage level annotation |\\n| `rating: \\\"…\\\"` | quoted string, e.g. `\\\"1000 kVA\\\"`, `\\\"200A\\\"` | Equipment rating annotation |\\n| `device: \\\"…\\\"` | ANSI device number, e.g. `\\\"51\\\"`, `\\\"87\\\"` | Used with `relay` nodes |\\n| any other key | quoted string | Stored as nameplate data (transformer kVA, %Z, etc.) |\\n\\n**Example with all common attributes:**\\n\\n```\\nxfmr = transformer_dy [\\n label: \\\"Main Transformer\\\",\\n voltage: \\\"13.8kV/480V\\\",\\n rating: \\\"1000 kVA\\\",\\n impedance: \\\"5.75%Z\\\"\\n]\\n```\\n\\nThe attribute block may span multiple lines — the parser joins lines until the `]` is balanced.\\n\\n---\\n\\n## 4. Connections\\n\\nA connection line is `fromId -> toId`, optionally followed by `[cable: \\\"…\\\", label: \\\"…\\\"]`.\\n\\n```\\nbus1 -> cb1\\nbus1 -> cb1 [cable: \\\"3#2/0 AWG\\\"]\\nbus1 -> cb1 [cable: \\\"3#2/0 AWG\\\", label: \\\"Feeder A\\\"]\\n```\\n\\n**Rules:**\\n- Both IDs must be declared before or after the connection — all connections are validated at end of parse.\\n- Only `->` (directed, source-to-load) is accepted. The connection direction is used for layout.\\n- An unknown node ID throws `SLDParseError: Connection references unknown node \\\"…\\\"`.\\n\\n```\\nsld \\\"ATS backup with cable labels\\\"\\nUTIL = utility [label: \\\"Utility 480V\\\"]\\nGEN = generator [rating: \\\"500 kW\\\", label: \\\"Emergency Gen\\\"]\\nATS1 = ats [rating: \\\"800A\\\", label: \\\"ATS-1\\\"]\\nBUS1 = bus [voltage: \\\"480V\\\", label: \\\"Critical Bus\\\"]\\nCB1 = breaker [rating: \\\"200A\\\", label: \\\"CB-1\\\"]\\nCB2 = breaker [rating: \\\"200A\\\", label: \\\"CB-2\\\"]\\nL1 = load [label: \\\"Server Room\\\"]\\nL2 = load [label: \\\"Life Safety\\\"]\\nUTIL -> ATS1 [label: \\\"Normal source\\\"]\\nGEN -> ATS1 [label: \\\"Emergency source\\\"]\\nATS1 -> BUS1 [cable: \\\"3#2/0 AWG\\\"]\\nBUS1 -> CB1\\nBUS1 -> CB2\\nCB1 -> L1 [cable: \\\"3#4 AWG\\\"]\\nCB2 -> L2 [cable: \\\"3#4 AWG\\\"]\\n```\\n\\n---\\n\\n## 5. Labels & comments\\n\\n- **Title:** `sld \\\"Substation One-Line\\\"` — first line only.\\n- **Node label:** `id = type [label: \\\"…\\\"]` — the display name.\\n- **Connection label:** `A -> B [label: \\\"…\\\"]` — appears alongside the connecting line.\\n- **Cable annotation:** `A -> B [cable: \\\"3#2/0 AWG, 200ft\\\"]` — conductor specification.\\n- **Comments:** `#` at the start of a line. Inline `#` on the same line as a node or connection is also stripped.\\n\\n---\\n\\n## 6. Reserved words & escaping\\n\\n**Reserved at line start:** `sld` (header).\\n\\n**Operator token** — avoid `->` inside node IDs. IDs may contain `[A-Za-z][A-Za-z0-9_-]*` — hyphens are valid (e.g. `CB-101` is a legal ID).\\n\\n**Attribute block** — `[…]` brackets may span multiple physical lines. The parser joins continuation lines until the bracket depth reaches zero.\\n\\n**Duplicate IDs** throw `SLDParseError: Duplicate node id \\\"…\\\"`.\\n\\n---\\n\\n## 7. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `xfmr1 [type: transformer]` | `SLDParseError: Cannot parse line` | Use `=` assignment: `xfmr1 = transformer [...]` |\\n| `id = battery [...]` | `SLDParseError: Unknown node type \\\"battery\\\"` | No `battery` type — use `ups` or `generator` |\\n| `A -- B` (bidirectional) | `SLDParseError: Cannot parse line` | Only `->` is accepted; use two `->` lines if needed |\\n| `A -> B -> C` (chained) | `SLDParseError: Cannot parse line` | Each connection is one `->` per line |\\n| `relay [label: \\\"OC\\\"]` (no device number) | Relay renders with blank number | Add `device: \\\"51\\\"` for the ANSI device number |\\n| `voltage: 480V` (unquoted) | Attribute value not recognized | Quote all values: `voltage: \\\"480V\\\"` |\\n| Node ID starting with digit: `2BUS` | `SLDParseError: Cannot parse line` | IDs must start with a letter: `BUS2` |\\n| Connection before node declared | `SLDParseError: Connection references unknown node \\\"…\\\"` | Declare nodes before or after connections — validated at end of parse, so order is flexible |\\n\\n---\\n\\n## 8. Grammar (EBNF)\\n\\n```text\\ndocument = header NEWLINE ( blank | comment | node-def | connection )*\\n\\nheader = \\\"sld\\\" ( WS quoted-string )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nnode-def = id WS \\\"=\\\" WS node-type ( WS \\\"[\\\" attr-list \\\"]\\\" )? NEWLINE\\nnode-type = \\\"utility\\\" | \\\"generator\\\" | \\\"solar\\\" | \\\"wind\\\" | \\\"ups\\\"\\n | \\\"transformer\\\" | \\\"transformer_dy\\\" | \\\"transformer_yd\\\"\\n | \\\"transformer_yy\\\" | \\\"transformer_dd\\\"\\n | \\\"autotransformer\\\" | \\\"transformer_3winding\\\"\\n | \\\"bus\\\" | \\\"bus_tie\\\" | \\\"hub\\\"\\n | \\\"breaker\\\" | \\\"breaker_vacuum\\\" | \\\"switch\\\" | \\\"switch_load\\\"\\n | \\\"ground_switch\\\" | \\\"ats\\\" | \\\"recloser\\\" | \\\"sectionalizer\\\"\\n | \\\"fuse\\\" | \\\"fuse_cl\\\"\\n | \\\"ct\\\" | \\\"pt\\\" | \\\"relay\\\" | \\\"surge_arrester\\\" | \\\"ground_fault\\\"\\n | \\\"motor\\\" | \\\"load\\\" | \\\"capacitor_bank\\\" | \\\"harmonic_filter\\\" | \\\"vfd\\\"\\n | \\\"watthour_meter\\\" | \\\"demand_meter\\\"\\n\\nattr-list = attr ( \\\",\\\" attr )*\\nattr = key \\\":\\\" WS quoted-string\\n\\nconnection = id WS \\\"->\\\" WS id ( WS \\\"[\\\" conn-attrs \\\"]\\\" )? NEWLINE\\nconn-attrs = conn-attr ( \\\",\\\" conn-attr )*\\nconn-attr = \\\"cable\\\" \\\":\\\" WS quoted-string\\n | \\\"label\\\" \\\":\\\" WS quoted-string\\n\\nid = [A-Za-z] [A-Za-z0-9_-]*\\nkey = [A-Za-z] [A-Za-z0-9_]*\\ncomment = \\\"#\\\" any NEWLINE\\n```\\n\\nThe attribute block `[…]` may span multiple physical lines — the parser joins continuation lines until the bracket depth returns to zero.\\n\\nAuthoritative source: `src/diagrams/sld/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"entity\": {\n \"title\": \"Entity structure diagram\",\n \"content\": \"## 1. Your first entity structure\\n\\nThe smallest useful entity structure: a parent owning two subsidiaries.\\n\\n```\\nentity-structure \\\"Simple holding\\\"\\nentity holdco \\\"Holdco LLC\\\" llc@DE\\nentity opco \\\"OpCo Inc.\\\" corp@DE\\nentity sub_uk \\\"UK Sub Ltd.\\\" llc@UK\\nholdco -> opco : 100%\\nholdco -> sub_uk : 100%\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with `entity-structure`, optionally followed by a quoted title.\\n2. Each legal entity is a node: `entity ID \\\"Display Name\\\" type` — the type determines the shape.\\n3. Connect entities with `->`. Append `: pct` to label ownership percentage: `parent -> child : 60%`.\\n4. Add `@jurisdiction` after the type to show a jurisdiction badge: `corp@DE`.\\n\\n> Comments must start with `#` on their own line.\\n\\n---\\n\\n## 2. Entity types\\n\\nThe entity type determines the shape rendered for that node. Schematex maps several common aliases to a canonical type.\\n\\n| Canonical type | Aliases accepted | Rendered shape | Typical use |\\n|---|---|---|---|\\n| `corp` | `corporation`, `inc` | Rectangle (sharp corners) | C-corp, S-corp, Ltd., SA, AG |\\n| `llc` | `llp`, `gmbh`, `bv` | Rounded rectangle | LLC, LLP, GmbH, BV |\\n| `lp` | `lllp`, `fund` | Notched rectangle | LP, LLLP, investment fund |\\n| `trust` | — | Ellipse | Family trust, statutory trust |\\n| `individual` | `person` | Circle | Founder, grantor, natural person |\\n| `foundation` | `npo` | Pentagon (shield) | Non-profit, charitable foundation |\\n| `disregarded` | `branch` | Dashed rectangle | Disregarded entity, foreign branch |\\n| `pool` | — | Dashed rounded rectangle | Option pool, ESOP, unissued shares |\\n| `placeholder` | `tbf` | Dashed rectangle (faded) | To-be-formed entity, acquisition target |\\n\\n**Entity syntax:**\\n\\n```\\nentity ID \\\"Display Name\\\" type\\nentity ID \\\"Display Name\\\" type@JURISDICTION\\nentity ID \\\"Display Name\\\" type@JURISDICTION [properties]\\n```\\n\\n**ID rules.** Must start with a letter, followed by letters, digits, underscores, or hyphens: `[A-Za-z][A-Za-z0-9_-]*`.\\n\\n```\\nentity-structure \\\"Entity type shapes\\\"\\nentity c1 \\\"Delaware C-Corp\\\" corp@DE\\nentity l1 \\\"California LLC\\\" llc@CA\\nentity p1 \\\"Cayman Fund LP\\\" lp@KY\\nentity t1 \\\"Family Trust\\\" trust@SD\\nentity i1 \\\"Jane Smith\\\" individual\\nentity f1 \\\"Acme Foundation\\\" foundation\\nentity d1 \\\"Irish Branch\\\" disregarded@IE\\nentity pool1 \\\"ESOP Pool\\\" pool\\nentity tbf1 \\\"Acquisition Target\\\" placeholder\\nc1 -> l1 : 100%\\nc1 -> p1 : 80%\\nc1 -> f1\\ni1 -> t1\\nt1 -> c1 : 100%\\n```\\n\\n---\\n\\n## 3. Node properties\\n\\nOptional properties inside `[…]` annotate the entity with additional context visible in the rendered node.\\n\\n| Property | Syntax | Effect |\\n|---|---|---|\\n| `status: new` | `new`, `eliminated`, `modified`, `normal` | Visual badge for transaction-step diagrams |\\n| `tax: ccorp` | quoted string | Tax classification label below entity name |\\n| `role: \\\"Grantor\\\"` | quoted string | Role label (for individuals and trustees) |\\n| `note: \\\"…\\\"` | quoted string | Small note displayed inside the node |\\n| `est: \\\"2024-03-15\\\"` | quoted string | Formation date |\\n\\nAny key not in the reserved set (`status`, `tax`, `role`, `note`, `est`) is stored as a custom property.\\n\\n```\\nentity alice \\\"Alice Chen\\\" individual [role: \\\"Founder & CEO\\\"]\\nentity trust1 \\\"Smith Irrevocable Trust\\\" trust@SD [est: \\\"2019-06-01\\\", note: \\\"Spendthrift provisions\\\"]\\nentity opco \\\"OpCo, Inc.\\\" corp@DE [status: new, tax: ccorp]\\n```\\n\\n---\\n\\n## 4. Jurisdiction\\n\\nAppend `@CODE` after the entity type to display a jurisdiction badge on the node. The code is a 2–3 letter string — ISO 3166-1 alpha-2 country codes and US state abbreviations are both accepted.\\n\\n```\\nentity parent \\\"Parent Corp\\\" corp@US\\nentity ie_sub \\\"Ireland Sub\\\" corp@IE\\nentity ky_fund \\\"Cayman Fund\\\" lp@KY\\nentity de_llc \\\"Delaware LLC\\\" llc@DE\\n```\\n\\nCommon codes: `US` United States · `DE` Delaware · `CA` California · `NY` New York · `UK` United Kingdom · `IE` Ireland · `NL` Netherlands · `KY` Cayman Islands · `SG` Singapore · `HK` Hong Kong · `JP` Japan · `BM` Bermuda · `VG` British Virgin Islands · `CH` Switzerland · `LU` Luxembourg · `SD` South Dakota.\\n\\n`@DE` resolves as Delaware by default (most common in US legal contexts).\\n\\n### Jurisdiction clusters\\n\\nDeclare a jurisdiction with `jurisdiction CODE \\\"Name\\\" [color: \\\"#hex\\\"]` to group all entities sharing that code into a labeled, dashed-border cluster region on the diagram.\\n\\n```\\njurisdiction US \\\"United States\\\" [color: \\\"#3b82f6\\\"]\\njurisdiction IE \\\"Ireland\\\" [color: \\\"#059669\\\"]\\n```\\n\\nWhen an entity's `@CODE` matches a declared `jurisdiction`, it is automatically placed inside that cluster. If `jurisdiction` is not declared, the badge still appears but no cluster region is drawn.\\n\\n```\\nentity-structure \\\"IP holding structure\\\"\\njurisdiction US \\\"United States\\\" [color: \\\"#3b82f6\\\"]\\njurisdiction IE \\\"Ireland\\\" [color: \\\"#059669\\\"]\\njurisdiction KY \\\"Cayman Islands\\\" [color: \\\"#d97706\\\"]\\nentity parent \\\"TopCo, Inc.\\\" corp@US\\nentity ie_hold \\\"Ireland Holdings\\\" corp@IE\\nentity ie_ip \\\"IP HoldCo\\\" corp@KY [note: \\\"Group IP\\\"]\\nentity ie_op \\\"EU Opco\\\" llc@IE\\nparent -> ie_hold : 100%\\nie_hold -> ie_ip : 100%\\nie_hold -> ie_op : 100%\\nie_ip -~-> ie_op [label: \\\"IP License\\\"]\\n```\\n\\n### Manual clusters\\n\\nUse `cluster \\\"Label\\\" [members: [id1, id2], color: \\\"#hex\\\"]` to group entities that don't share a single jurisdiction code.\\n\\n```\\ncluster \\\"Ireland / Cayman IP\\\" [members: [ie_ip, nl_bv], color: \\\"#059669\\\"]\\n```\\n\\n---\\n\\n## 5. Ownership edges\\n\\nAn edge line connects two entity IDs with an operator. The operator determines the line style and visual semantics.\\n\\n| Operator | Renders as | Meaning |\\n|---|---|---|\\n| `->` | Solid arrow | Equity ownership (default) |\\n| `==>` | Double line arrow | Voting-only control (no economic interest) |\\n| `-.->` | Dashed grey arrow | Option pool / conditional ownership |\\n| `-~->` | Dashed purple arrow | IP license, management agreement, intercompany service |\\n| `-->` | Dashed green arrow | Distribution (trust to beneficiaries) |\\n\\n**Ownership percentage:** append `: pct` after the target ID.\\n\\n```\\nparent -> subsidiary : 60%\\n```\\n\\n**Share class:** add `[class: \\\"Series A Pref\\\"]` to label the share class on the edge.\\n\\n```\\nvc -> startup : 22% [class: \\\"Series A Pref\\\"]\\n```\\n\\n**Non-equity label:** use `[label: \\\"…\\\"]` for descriptive edge text on license or distribution edges.\\n\\n```\\nip_co -~-> opco [label: \\\"IP License · royalty\\\"]\\ntrust --> beneficiary [label: \\\"Discretionary distributions\\\"]\\n```\\n\\n**Combining:** `class:` and `label:` can appear together.\\n\\n```\\nfund -> portfolio : 35% [class: \\\"Common\\\", label: \\\"Post-Series B\\\"]\\n```\\n\\n```\\nentity-structure \\\"Mixed edge types\\\"\\nentity holdco \\\"HoldCo LLC\\\" llc@DE\\nentity opco \\\"OpCo Inc.\\\" corp@DE\\nentity ip_co \\\"IP Ltd\\\" corp@KY\\nentity fund \\\"Growth Fund\\\" lp@DE\\nentity esop \\\"ESOP Pool\\\" pool\\nentity trust1 \\\"Family Trust\\\" trust@SD\\nentity founder \\\"J. Smith\\\" individual\\n# Equity ownership\\nholdco -> opco : 100%\\nholdco -> ip_co : 100%\\n# IP license (non-equity)\\nip_co -~-> opco [label: \\\"Exclusive license\\\"]\\n# Option pool (dashed)\\nesop -.-> opco : 10%\\n# Voting control\\nfund ==> holdco\\n# Trust distribution\\ntrust1 --> founder [label: \\\"Income distributions\\\"]\\nfounder -> trust1 : 100%\\n```\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Title:** `entity-structure \\\"Acme Holdings\\\"` — first line, quoted.\\n- **Entity display name:** the quoted string in `entity ID \\\"Name\\\" type` — appears inside the node.\\n- **Jurisdiction badge:** `@CODE` after type — 2–3 letter code shown in the node corner.\\n- **Node properties:** `[note: \\\"…\\\", role: \\\"…\\\", status: new, …]` — annotations inside the node.\\n- **Edge percentage:** `: pct` after the target ID — appears on the ownership arrow.\\n- **Edge class:** `[class: \\\"…\\\"]` — share class label on the arrow.\\n- **Edge label:** `[label: \\\"…\\\"]` — descriptive text on the arrow.\\n- **Comments:** `#` at the start of a line (after leading whitespace). Inline trailing `#` comments inside bracket blocks are also stripped.\\n\\n---\\n\\n## 7. Reserved words & escaping\\n\\n**Reserved at line start:** `entity-structure` (header), `entity`, `jurisdiction`, `cluster`.\\n\\n**Entity type keywords** — these strings are parsed as entity types and must not be used as entity IDs: `corp`, `corporation`, `inc`, `llc`, `llp`, `gmbh`, `bv`, `lp`, `lllp`, `fund`, `trust`, `individual`, `person`, `foundation`, `npo`, `disregarded`, `branch`, `pool`, `placeholder`, `tbf`.\\n\\n**Edge operator tokens** — avoid these sequences inside IDs: `->`, `==>`, `-.->`, `-~->`, `-->`.\\n\\n**Strings with spaces** must be double-quoted in display names, notes, role labels, and color values.\\n\\n**Jurisdiction codes** are case-insensitive in the source but normalized to uppercase: `@de` and `@DE` are equivalent.\\n\\n---\\n\\n## 8. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `entity acme Acme Inc. corp@DE` (name unquoted) | Parse fails — name must be quoted | `entity acme \\\"Acme Inc.\\\" corp@DE` |\\n| `acme -> sub [50%]` (percent inside brackets) | `50%` not recognized as a property key | `acme -> sub : 50%` (colon before percent, outside brackets) |\\n| `entity acme \\\"Acme\\\" Ltd@DE` | `Ltd` is not a recognized type keyword | Use `corp` or `llc`; `Ltd` is not in the alias table |\\n| `acme -> unknown_id : 100%` | `EntityParseError: Edge references unknown entity \\\"unknown_id\\\"` | Declare every entity before using it in an edge |\\n| `cluster \\\"US entities\\\" [ids: [a, b]]` | `ids:` not recognized; property is silently ignored | Use `members: [a, b]` |\\n| `jurisdiction DE \\\"Delaware\\\"` then `entity co \\\"Co\\\" corp@DE` | Cluster draws around `co` — correct. But `@DE` in a US context is Delaware, not Germany | Use `@DE` for Delaware, `@DEU` is not valid — there is no ambiguity workaround; document the intent in a `note:` |\\n| `entity x \\\"X\\\" corp [status: pending]` | `pending` is not a valid status — property silently stored as custom prop | Use `new`, `eliminated`, `modified`, or `normal` |\\n\\n---\\n\\n## 9. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | jurisdiction-def | cluster-def | entity-def | edge)*\\n\\nheader = \\\"entity-structure\\\" ( WS quoted-string )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\njurisdiction-def = \\\"jurisdiction\\\" CODE WS quoted-string ( \\\"[\\\" jur-attrs \\\"]\\\" )? NEWLINE\\njur-attrs = jur-attr (\\\",\\\" jur-attr)*\\njur-attr = \\\"color:\\\" quoted-string\\n\\ncluster-def = \\\"cluster\\\" WS quoted-string ( \\\"[\\\" cluster-attrs \\\"]\\\" )? NEWLINE\\ncluster-attrs = cluster-attr (\\\",\\\" cluster-attr)*\\ncluster-attr = \\\"members:\\\" \\\"[\\\" id (\\\",\\\" id)* \\\"]\\\"\\n | \\\"color:\\\" quoted-string\\n\\nentity-def = \\\"entity\\\" WS id WS quoted-string WS entity-type ( \\\"@\\\" CODE )?\\n ( \\\"[\\\" entity-attrs \\\"]\\\" )? NEWLINE\\nentity-attrs = entity-attr (\\\",\\\" entity-attr)*\\nentity-attr = \\\"status:\\\" status\\n | \\\"tax:\\\" quoted-string\\n | \\\"role:\\\" quoted-string\\n | \\\"note:\\\" quoted-string\\n | \\\"est:\\\" quoted-string\\n | key \\\":\\\" quoted-string # custom property\\n\\nentity-type = \\\"corp\\\" | \\\"corporation\\\" | \\\"inc\\\"\\n | \\\"llc\\\" | \\\"llp\\\" | \\\"gmbh\\\" | \\\"bv\\\"\\n | \\\"lp\\\" | \\\"lllp\\\" | \\\"fund\\\"\\n | \\\"trust\\\"\\n | \\\"individual\\\" | \\\"person\\\"\\n | \\\"foundation\\\" | \\\"npo\\\"\\n | \\\"disregarded\\\" | \\\"branch\\\"\\n | \\\"pool\\\"\\n | \\\"placeholder\\\" | \\\"tbf\\\"\\n\\nedge = id WS op WS id ( \\\":\\\" WS pct-text )? ( \\\"[\\\" edge-attrs \\\"]\\\" )? NEWLINE\\nop = \\\"->\\\" | \\\"==>\\\" | \\\"-.->\\\" | \\\"-~->\\\" | \\\"-->\\\"\\npct-text = any text up to \\\"[\\\" or end of line # e.g. \\\"100%\\\" or \\\"V 75% / E 50%\\\"\\n\\nedge-attrs = edge-attr (\\\",\\\" edge-attr)*\\nedge-attr = \\\"class:\\\" quoted-string\\n | \\\"label:\\\" quoted-string\\n\\nstatus = \\\"new\\\" | \\\"eliminated\\\" | \\\"modified\\\" | \\\"normal\\\"\\nCODE = [A-Za-z]{2,3}\\nid = [A-Za-z] [A-Za-z0-9_-]*\\ncomment = \\\"#\\\" any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/entity/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"fishbone\": {\n \"title\": \"Fishbone diagram\",\n \"content\": \"## 1. Your first fishbone\\n\\nThe smallest useful fishbone: three categories, one cause each, one with a sub-cause.\\n\\n```\\nfishbone \\\"API latency spike\\\"\\neffect \\\"P99 > 2 s after deploy\\\"\\ncategory code \\\"Code\\\"\\ncategory infra \\\"Infra\\\"\\ncategory data \\\"Data\\\"\\ncode : \\\"N+1 query in new endpoint\\\"\\n - \\\"Missing eager-load on orders\\\"\\ninfra : \\\"DB connection pool exhausted\\\"\\ndata : \\\"Index missing on accounts table\\\"\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with `fishbone`, optionally followed by a quoted title.\\n2. Declare each branch with `category id \\\"Label\\\"` — the `id` is a short internal key, `\\\"Label\\\"` is what prints on the diagram.\\n3. Add causes with `id : \\\"cause text\\\"` on their own lines.\\n4. Indent a line by at least 2 spaces and start it with `-` to create a sub-cause (second-order branch) under the preceding cause.\\n\\n> Comments must start with `#` on their own line.\\n\\n---\\n\\n## 2. Building blocks\\n\\n### The spine and effect\\n\\n`effect \\\"Problem statement\\\"` places text in the fish's head. If `effect` is omitted the parser falls back to the diagram title.\\n\\n```\\nfishbone \\\"Title\\\"\\neffect \\\"Specific problem statement\\\"\\n```\\n\\nThe head sits on the right by default (`config direction = right`). Use `config direction = left` to flip it.\\n\\n### Categories (major bones)\\n\\n`category id \\\"Label\\\"` declares a branch. The `id` is used internally to assign causes; the quoted `\\\"Label\\\"` appears on the diagram.\\n\\nCategories also accept optional properties in `[…]`:\\n\\n| Property | Values | Effect |\\n|---|---|---|\\n| `color: \\\"#hex\\\"` | hex color string | Branch and label color |\\n| `side: top` / `side: bottom` | `top`, `bottom` | Forces this branch to the top or bottom rail (default: alternating) |\\n| `order: N` | integer | Position within its rail — lower numbers sit closer to the tail |\\n\\n```\\ncategory rework \\\"Rework\\\" [color: \\\"#E53935\\\", side: top, order: 1]\\n```\\n\\n### Causes (minor bones)\\n\\nTwo styles are accepted and can be mixed in one diagram:\\n\\n**Style A — structured.** Declare categories first, then assign causes with `id : \\\"text\\\"`:\\n\\n```\\ncategory code \\\"Code\\\"\\ncategory infra \\\"Infra\\\"\\ncode : \\\"N+1 query in endpoint\\\"\\ncode : \\\"Missing cache layer\\\"\\ninfra : \\\"Auto-scaling lag\\\"\\n```\\n\\n**Style B — compact.** Category label and causes in one line, separated by `;` or `,`:\\n\\n```\\ncategory Code: N+1 query; Missing cache; Synchronous call\\ncategory Infra: Auto-scaling lag; CDN misconfigured\\n```\\n\\nIn compact style the `id` is auto-derived from the label text (lowercased, spaces → hyphens). Quotes are optional for cause text.\\n\\n```\\nfishbone \\\"Conversion rate drop\\\"\\neffect \\\"Checkout conversion -12% MoM\\\"\\n# Style A — structured\\ncategory ux \\\"UX\\\"\\ncategory trust \\\"Trust\\\"\\nux : \\\"Confusing multi-step form\\\"\\nux : \\\"Slow page on mobile\\\"\\ntrust : \\\"No payment security badge\\\"\\n# Style B — compact\\ncategory Pricing: Price-anchoring missing; No annual discount shown; Coupon field too prominent\\n```\\n\\n---\\n\\n## 3. Sub-causes (second-order branches)\\n\\nIndent a `-` line by at least 2 spaces after a Level-1 cause to attach a sub-cause to it. The `-` dash is part of the syntax; the text follows it.\\n\\n```\\nmethod : \\\"Stencil aperture undersized\\\"\\n - \\\"Tolerance spec from 2018 board revision\\\"\\n - \\\"No re-validation after material change\\\"\\nmethod : \\\"Pick-and-place speed too high\\\"\\n - \\\"Speed limit lifted during overtime run\\\"\\n```\\n\\nSub-causes appear as shorter, narrower twigs branching off their parent rib.\\n\\n```\\nfishbone \\\"Medication error increase\\\"\\neffect \\\"Errors up 18% in Q3\\\"\\ncategory process \\\"Process\\\"\\ncategory people \\\"People\\\"\\nprocess : \\\"CPOE alert fatigue\\\"\\n - \\\"47 non-critical alerts per shift\\\"\\n - \\\"Override too easy — one click\\\"\\nprocess : \\\"5-Rights verification skipped\\\"\\n - \\\"No barcode scanner at bedside\\\"\\npeople : \\\"Float staff unfamiliar with unit\\\"\\n - \\\"No unit-specific orientation checklist\\\"\\npeople : \\\"Handoff communication gaps\\\"\\n```\\n\\n---\\n\\n## 4. Config options\\n\\n`config key = value` lines can appear anywhere after the header. Unknown keys and values are silently ignored.\\n\\n| Config key | Values | Default | Effect |\\n|---|---|---|---|\\n| `direction` | `right` / `left` (also `ltr` / `rtl`) | `right` | Which side the effect head appears on |\\n| `sides` | `both`, `top`, `bottom` | `both` | Which half of the spine hosts branches |\\n| `density` | `compact`, `normal`, `spacious` | `normal` | Spacing between ribs — affects how many branches fit before overlap |\\n| `slope` (or `ribslope`) | `gentle`, `normal`, `steep`, or a number (0–3) | `normal` (0.6) | Rib angle — shallow vs. steep diagonal |\\n| `causeside` (or `cause-side`) | `head`, `tail`, `both` | `head` | Which side of a rib sub-causes branch off from |\\n| `width` | integer px | auto | Override canvas width |\\n| `height` | integer px | auto | Override canvas height |\\n\\n```\\nconfig direction = left\\nconfig density = compact\\nconfig slope = gentle\\nconfig sides = top\\n```\\n\\n---\\n\\n## 5. Labels & comments\\n\\n- **Diagram title:** `fishbone \\\"Website Traffic Drop\\\"` — first line, optional.\\n- **Effect label:** `effect \\\"30% organic traffic decline\\\"` — the problem at the fish's head.\\n- **Category label:** `category id \\\"Human-readable name\\\"` — printed on the branch.\\n- **Cause text:** quoted `\\\"like this\\\"` or unquoted (spaces allowed in compact style).\\n- **Sub-cause text:** after the leading `-`, quoted or unquoted.\\n- **Comments:** `#` at the start of a line (after optional leading whitespace). The `#` character inside a double-quoted string is not treated as a comment.\\n\\n---\\n\\n## 6. Reserved words & escaping\\n\\n**Reserved at line start:** `fishbone` (header), `effect`, `category`, `config`.\\n\\n**The `-` prefix** on an indented line is reserved as the sub-cause marker. To include a literal hyphen-dash at the start of cause text, quote it: `code : \\\"- old deprecated path\\\"`.\\n\\n**Strings with spaces** in structured-style cause text should be double-quoted: `code : \\\"N+1 query\\\"`. In compact style (`category Label: ...`) the text runs to the `;` or `,` separator and quoting is optional.\\n\\n**The `#` character** starts a comment unless inside a double-quoted string.\\n\\n| Reserved sequence | Context | Alternative |\\n|---|---|---|\\n| `#` at line start | Comment marker | Quote the text if `#` is part of content |\\n| `-` at start after ≥2-space indent | Sub-cause marker | Quote: `- \\\"- text with dash\\\"` |\\n| `category`, `effect`, `config`, `fishbone` | Line-start keywords | Cannot be used as category IDs |\\n\\n---\\n\\n## 7. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `cause1 : \\\"text\\\"` with no prior `category cause1` | `FishboneParseError: Unknown category \\\"cause1\\\"` | Declare `category cause1 \\\"Label\\\"` before assigning causes |\\n| `- \\\"sub-cause\\\"` at the start of the file (no preceding Level-1 cause) | `FishboneParseError: Sub-cause … has no preceding Level-1 cause` | Place the sub-cause line immediately after a `id : \\\"cause\\\"` line |\\n| `- \\\"sub-cause\\\"` with only 1-space indent | Treated as a cause line, not a sub-cause | Indent with at least 2 spaces |\\n| `category Code: cause one, cause two` | Parsed as compact style — `,` and `;` are both separators | Intended behavior; both separators work |\\n| `config direction = center` | Unknown value — silently ignored, stays `right` | Use `right` or `left` |\\n| `config slope = 45` | Out of range (must be 0–3 exclusive); silently ignored | Use a preset (`gentle`, `normal`, `steep`) or a value like `0.5` |\\n| `fishbone: \\\"Title\\\"` | Parsed correctly — colon after keyword is optional | Both `fishbone \\\"Title\\\"` and `fishbone: \\\"Title\\\"` work |\\n\\n---\\n\\n## 8. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | effect | category | config | cause | sub-cause)*\\n\\nheader = \\\"fishbone\\\" \\\":\\\"? ( WS quoted-string )? NEWLINE\\neffect = \\\"effect\\\" \\\":\\\"? WS quoted-string NEWLINE\\nconfig = \\\"config\\\" WS config-key WS \\\"=\\\" WS config-value NEWLINE\\nconfig-key = \\\"direction\\\" | \\\"width\\\" | \\\"height\\\" | \\\"sides\\\"\\n | \\\"slope\\\" | \\\"ribslope\\\" | \\\"density\\\" | \\\"causeside\\\" | \\\"cause-side\\\"\\nconfig-value = bare-word | number | quoted-string\\n\\ncategory = \\\"category\\\" WS id WS label-or-compact ( \\\"[\\\" category-attrs \\\"]\\\" )? NEWLINE\\n\\nlabel-or-compact\\n = quoted-string # structured form: category id \\\"Label\\\"\\n | id WS \\\":\\\" WS compact-causes # compact form: category Label: cause; cause\\n\\ncategory-attrs = category-attr (\\\",\\\" category-attr)*\\ncategory-attr = \\\"color:\\\" quoted-string\\n | \\\"side:\\\" ( \\\"top\\\" | \\\"bottom\\\" )\\n | \\\"order:\\\" integer\\n\\ncause = id WS \\\":\\\" WS cause-text NEWLINE # structured form\\ncause-text = quoted-string | bare-text\\n\\nsub-cause = INDENT≥2 \\\"-\\\" WS cause-text NEWLINE\\n\\ncompact-causes = compact-cause ( (\\\";\\\" | \\\",\\\") compact-cause )*\\ncompact-cause = quoted-string | bare-text\\n\\ncomment = \\\"#\\\" any NEWLINE\\nid = [a-zA-Z] [a-zA-Z0-9_-]*\\nquoted-string = '\\\"' any-char-but-unescaped-quote* '\\\"'\\n```\\n\\nAuthoritative source: `src/diagrams/fishbone/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"venn\": {\n \"title\": \"Venn / Euler diagram\",\n \"content\": \"## 1. Your first Venn diagram\\n\\nThe smallest useful diagram: two sets, one overlap, two exclusive regions.\\n\\n```\\nvenn \\\"Support channels\\\"\\nset A \\\"Email support\\\" [color: \\\"#1E88E5\\\"]\\nset B \\\"Live chat\\\" [color: \\\"#E53935\\\"]\\nA & B : 320\\nA only : 1450\\nB only : 890\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with `venn`, optionally followed by a quoted title.\\n2. Declare each **set** with `set ID \\\"Label\\\"` — the id is used internally, the label appears in the diagram.\\n3. Assign values to **regions** using `A & B : value` for intersections and `A only : value` for exclusive regions.\\n4. Configure appearance with `config:` lines; the diagram mode (`venn` vs `euler`) can be set explicitly or left as `auto`.\\n\\n> Comments must start with `#` on their own line.\\n\\n---\\n\\n## 2. Sets\\n\\nA set declaration creates one circle in the diagram.\\n\\n```\\nset ID \\\"Label\\\" [color: \\\"#hex\\\"]\\n```\\n\\n| Part | Required | Notes |\\n|---|---|---|\\n| `ID` | Yes | Must match `[A-Za-z][A-Za-z0-9_-]*` |\\n| `\\\"Label\\\"` | Yes | Quoted string displayed on the circle |\\n| `[color: \\\"#hex\\\"]` | No | Override fill color for this set |\\n\\nSets must be declared before they are referenced in region or relation lines.\\n\\n```\\nvenn \\\"Programming paradigms\\\"\\nset oop \\\"Object-Oriented\\\" [color: \\\"#1E88E5\\\"]\\nset fp \\\"Functional\\\" [color: \\\"#E53935\\\"]\\nset logic \\\"Logic\\\" [color: \\\"#43A047\\\"]\\noop & fp : 180\\noop & logic : 45\\nfp & logic : 90\\noop & fp & logic : 12\\noop only : 620\\nfp only : 340\\nlogic only : 95\\n```\\n\\n---\\n\\n## 3. Regions\\n\\nA region assigns a value to an intersection or exclusive area. The four DSL modes can be mixed in one diagram.\\n\\n### 3.1 Declarative mode — counts and percentages\\n\\nAssign a number or percentage to a named region. The region key is either an `&`-separated list of set ids (for intersections) or `ID only` (for the part of that set not covered by any other set).\\n\\n```\\nA & B : 320 # integer count\\nA & B & C : 45 # three-way intersection\\nA only : 1450 # A minus all other sets\\nA & B : 18.5% # percentage value\\n```\\n\\n```\\nvenn \\\"Market research\\\"\\nset aware \\\"Awareness\\\" [color: \\\"#7B1FA2\\\"]\\nset consider \\\"Consideration\\\" [color: \\\"#0288D1\\\"]\\nset convert \\\"Conversion\\\" [color: \\\"#388E3C\\\"]\\naware & consider : 3400\\nconsider & convert : 890\\naware & convert : 210\\naware & consider & convert : 150\\naware only : 18200\\nconsider only : 2100\\nconvert only : 540\\n```\\n\\n### 3.2 Region labels (text)\\n\\nUse the `region` keyword prefix and assign a quoted string instead of a number. The string is rendered inside the region.\\n\\n```\\nregion A & B : \\\"Nurture\\\"\\nregion B & C : \\\"Convert\\\"\\nregion A & B & C : \\\"Loyal customer\\\"\\n```\\n\\n```\\nvenn \\\"Go-to-market funnel\\\"\\nset A \\\"Awareness\\\" [color: \\\"#7B1FA2\\\"]\\nset B \\\"Consideration\\\" [color: \\\"#0288D1\\\"]\\nset C \\\"Purchase\\\" [color: \\\"#388E3C\\\"]\\nregion A & B : \\\"Nurture\\\"\\nregion B & C : \\\"Convert\\\"\\nregion A only : \\\"Cold audience\\\"\\nregion A & B & C : \\\"Loyal\\\"\\nregion C only : \\\"Direct buyers\\\"\\n```\\n\\n### 3.3 Enumeration mode — element lists\\n\\nList the actual elements of each set. Schematex computes all intersections automatically.\\n\\n```\\nID = { element1, element2, element3 }\\n```\\n\\nElements are comma-separated bare words or quoted strings. Enumeration sets do not need an explicit `set` declaration — the declaration is implied.\\n\\n```\\nvenn \\\"Full-stack team skills\\\"\\nFrontend = { React, TypeScript, CSS, Jest, Webpack }\\nBackend = { TypeScript, Node.js, PostgreSQL, Jest, Redis }\\nDevOps = { Docker, Kubernetes, PostgreSQL, Terraform, Redis }\\n```\\n\\n---\\n\\n## 4. Euler relations\\n\\nEuler relations express structural containment or separation — that one set is a subset of another, that two sets are completely disjoint, or that they merely overlap. They must reference set ids already declared with `set`.\\n\\n```\\nfrom subset to # from is fully inside to (also: \\\"in\\\")\\nfrom in to # alias for subset\\nfrom disjoint to # from and to do not overlap\\nfrom overlap to # from and to partially overlap (explicit — the default for unrelated sets)\\n```\\n\\n```\\nvenn \\\"Biology taxonomy\\\"\\nset animals \\\"Animals\\\"\\nset vertebrates \\\"Vertebrates\\\"\\nset mammals \\\"Mammals\\\"\\nset birds \\\"Birds\\\"\\nset fish \\\"Fish\\\"\\nvertebrates subset animals\\nmammals subset vertebrates\\nbirds subset vertebrates\\nfish subset vertebrates\\nmammals disjoint birds\\nmammals disjoint fish\\nbirds disjoint fish\\n```\\n\\n| Keyword | Alias | Meaning |\\n|---|---|---|\\n| `subset` | `in` | `from` is fully contained within `to` |\\n| `disjoint` | — | `from` and `to` do not intersect |\\n| `overlap` | — | `from` and `to` intersect but neither contains the other |\\n\\n---\\n\\n## 5. Configuration\\n\\n`config:` lines tune diagram behavior. Each goes on its own line.\\n\\n| Config key | Values | Default | Effect |\\n|---|---|---|---|\\n| `diagram` | `venn`, `euler`, `auto` | `auto` | Force Venn (all circles fixed) or Euler (subset nesting). `auto` infers from the presence of Euler relations. |\\n| `proportional` | `true`, `false` | `false` | Scale circle area proportional to region count values |\\n| `showCounts` | `true`, `false` | `auto` | Always / never show count labels. `auto` shows them when counts are provided. |\\n| `showPercent` | `true`, `false` | `false` | Show each region value as a percentage of the grand total |\\n| `palette` | `default`, `brand`, `monochrome` | `default` | Color palette for sets (overridden by per-set `color:`) |\\n| `blendMode` | `multiply`, `screen`, `none` | `multiply` | How overlapping fill colors blend |\\n\\nConfig can also be written inline on the header line: `venn \\\"Title\\\" [proportional: true, showPercent: true]`.\\n\\n```\\nvenn \\\"Segment overlap\\\"\\nconfig: proportional = true\\nconfig: showPercent = true\\nconfig: blendMode = screen\\n```\\n\\n**Layout selection:**\\n\\n- `layout venn` — alternative form of `config: diagram = venn` (parses identically).\\n- `layout euler` — alternative form of `config: diagram = euler`.\\n- `layout auto` — alternative form of `config: diagram = auto`.\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Title:** `venn \\\"My diagram\\\"` — first line only.\\n- **Set label:** `set A \\\"Email subscribers\\\"` — quoted string on the `set` line.\\n- **Region value:** integer, percentage, quoted string, or element list assigned after the `:`.\\n- **Comments:** `#` at the start of a line (after leading whitespace).\\n\\n---\\n\\n## 7. Reserved words & escaping\\n\\n**Reserved at line start:** `venn` (header), `set`, `config:`, `layout`, `region`.\\n\\n**Reserved operators in region keys:** `&` (intersection), `only` (exclusive region).\\n\\n**Euler relation keywords:** `subset`, `in`, `disjoint`, `overlap` — cannot be used as set ids.\\n\\n**ID rules:** must match `[A-Za-z][A-Za-z0-9_-]*`. Labels with spaces go in the quoted `\\\"Label\\\"` field.\\n\\n---\\n\\n## 8. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `A & B : 320` before `set A …` and `set B …` | `VennParseError: unknown set id \\\"A\\\" in region key` | Declare sets with `set` before referencing them in region lines |\\n| `dogs subset mammals` before `set dogs …` | `VennParseError: unknown set \\\"dogs\\\" in relation` | Declare sets first, then write Euler relations |\\n| `set A Email subscribers` (unquoted label with space) | Parser error — label is expected to be a quoted string | Quote it: `set A \\\"Email subscribers\\\"` |\\n| `A & B = 320` (equals instead of colon) | Line doesn't match region pattern; parse error | Use colon: `A & B : 320` |\\n| `Frontend = { React TypeScript }` (no commas) | `React TypeScript` treated as one element | Comma-separate: `Frontend = { React, TypeScript }` |\\n| `config: mode = venn` | `mode` is not a recognized key (key is `diagram`) | Use `config: diagram = venn` |\\n| Mixing enumeration sets with explicit `A & B :` regions | Enumeration auto-derive only runs when `regions.length === 0` | Use one style per diagram or add all regions explicitly |\\n\\n---\\n\\n## 9. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | config | layout-stmt | set-decl | enum-decl | euler-rel | region)*\\n\\nheader = \\\"venn\\\" ( \\\":\\\"? WS quoted-string )? ( WS \\\"[\\\" config-props \\\"]\\\" )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nconfig = \\\"config\\\" WS \\\":\\\" WS config-key WS \\\"=\\\" WS config-value NEWLINE\\nconfig-key = \\\"diagram\\\" | \\\"proportional\\\" | \\\"palette\\\" | \\\"blendMode\\\" | \\\"showCounts\\\" | \\\"showPercent\\\"\\n\\nlayout-stmt = \\\"layout\\\" WS ( \\\"venn\\\" | \\\"euler\\\" | \\\"auto\\\" ) NEWLINE\\n\\nset-decl = \\\"set\\\" WS id WS quoted-string ( WS \\\"[\\\" set-props \\\"]\\\" )? NEWLINE\\nset-props = \\\"color:\\\" quoted-hex | \\\"fill:\\\" quoted-hex\\n\\nenum-decl = id WS \\\"=\\\" WS \\\"{\\\" element-list \\\"}\\\" NEWLINE\\nelement-list = element ( \\\",\\\" element )*\\nelement = quoted-string | bare-word\\n\\neuler-rel = id WS euler-op WS id NEWLINE\\neuler-op = \\\"subset\\\" | \\\"in\\\" | \\\"disjoint\\\" | \\\"overlap\\\"\\n\\nregion = \\\"region\\\"? WS region-key WS \\\":\\\" WS region-value NEWLINE\\nregion-key = id WS \\\"only\\\"\\n | id ( WS \\\"&\\\" WS id )+\\nregion-value = integer | percent | quoted-string | \\\"[\\\" element-list \\\"]\\\"\\n\\npercent = number \\\"%\\\"\\nid = [A-Za-z] [A-Za-z0-9_-]*\\ncomment = \\\"#\\\" any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/venn/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"decisiontree\": {\n \"title\": \"Decision tree diagram\",\n \"content\": \"## 1. Your first decision tree\\n\\nThe smallest useful decision tree: a root question with two branches.\\n\\n```\\ndecisiontree \\\"Laptop troubleshoot\\\"\\n\\nquestion \\\"Does it power on?\\\"\\n yes: answer \\\"Check display — connect external monitor\\\"\\n no: question \\\"Is the charger light on?\\\"\\n yes: answer \\\"Hold power button 10 s — try again\\\"\\n no: answer \\\"Check outlet and charging cable\\\"\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with `decisiontree`, optionally with `:mode` and a quoted title.\\n2. Each question node uses `question \\\"text\\\"` (or shorthand `q \\\"text\\\"`).\\n3. Each answer/leaf uses `answer \\\"text\\\"` (or `a \\\"text\\\"` or `leaf \\\"text\\\"`).\\n4. Branch labels — `yes:`, `no:`, or a custom `label \\\"X\\\":` — prefix the child node on the same line.\\n\\nIndentation controls nesting: each level adds 2 spaces. The parser computes parent-child relationships from indent depth.\\n\\n> Comments must start with `#` or `//` on their own line.\\n\\n---\\n\\n## 2. Modes\\n\\nThe mode is set in the header line:\\n\\n| Header | Mode | Used for |\\n|---|---|---|\\n| `decisiontree` | taxonomy | Yes/no question flows, troubleshooting guides, clinical decision support |\\n| `decisiontree:decision` (or `decisiontree:da`) | decision analysis | Investment decisions, risk analysis, expected value calculation |\\n| `decisiontree:ml` | machine learning | Visualizing trained CART classifiers (scikit-learn, XGBoost, etc.) |\\n\\nDefault direction is `top-down` for taxonomy and ML, `left-right` for decision analysis.\\n\\n---\\n\\n## 3. Taxonomy mode\\n\\nBest for: troubleshooting guides, FAQs, clinical protocols, product recommendation flows.\\n\\n### Node keywords\\n\\n| Keyword | Aliases | Meaning |\\n|---|---|---|\\n| `question \\\"…\\\"` | `q \\\"…\\\"` | Internal node — a question with children |\\n| `answer \\\"…\\\"` | `a \\\"…\\\"`, `leaf \\\"…\\\"` | Leaf node — a terminal outcome |\\n\\n### Branch labels\\n\\n| Syntax | Meaning |\\n|---|---|\\n| `yes: question \\\"…\\\"` | Branch labeled \\\"yes\\\" |\\n| `no: answer \\\"…\\\"` | Branch labeled \\\"no\\\" |\\n| `label \\\"Custom text\\\": answer \\\"…\\\"` | Branch with any custom label |\\n\\nCustom labels let you go beyond yes/no for multi-way decisions from one question.\\n\\n```\\ndecisiontree \\\"Triage — chest pain onset\\\"\\n\\nq \\\"Onset sudden?\\\"\\n yes: q \\\"ECG changes present?\\\"\\n yes: a \\\"ACS protocol — cardiology consult\\\"\\n no: q \\\"D-dimer elevated?\\\"\\n yes: a \\\"PE workup — CT pulmonary angiography\\\"\\n no: a \\\"Aortic dissection — CT angiography\\\"\\n no: q \\\"Pain reproducible on palpation?\\\"\\n yes: a \\\"Musculoskeletal — NSAIDs, follow-up PCP\\\"\\n no: a \\\"GI / anxiety — further history\\\"\\n```\\n\\n```\\ndecisiontree \\\"Pain level triage\\\"\\n\\nquestion \\\"Reported pain level?\\\"\\n label \\\"Severe (8-10)\\\": answer \\\"Emergency — send to ER immediately\\\"\\n label \\\"Moderate (4-7)\\\": answer \\\"Urgent care — within 2 hours\\\"\\n label \\\"Mild (1-3)\\\": answer \\\"Schedule next available — OTC care\\\"\\n label \\\"None\\\": answer \\\"Monitor — patient may be post-medication\\\"\\n```\\n\\n---\\n\\n## 4. Decision analysis mode\\n\\nBest for: investment decisions, build-vs-buy analysis, risk-weighted strategy evaluation.\\n\\n### Node keywords\\n\\n| Keyword | Aliases | Meaning |\\n|---|---|---|\\n| `decision \\\"…\\\"` | — | Decision node — the actor chooses a branch |\\n| `chance \\\"…\\\"` | — | Chance node — an uncertain outcome |\\n| `end \\\"…\\\"` | `outcome \\\"…\\\"` | Terminal node — final payoff |\\n\\n### Branch keywords\\n\\n| Keyword | Meaning |\\n|---|---|\\n| `choice \\\"label\\\"` | Names the incoming branch from a decision node |\\n| `prob N` | Sets the probability (0–1) on the incoming branch from a chance node |\\n\\n### Payoff attribute\\n\\n`payoff=N` on any node sets the payoff value. On `end` / `outcome` nodes it defines the terminal value. The parser runs expected-value rollback automatically: each `chance` node's EV is the probability-weighted sum of its children's EVs; each `decision` node's EV is the maximum child EV, and the optimal branch is flagged.\\n\\n**Constraint:** probabilities on all direct children of a `chance` node must sum to 1.0 (±0.01). The parser throws a `DTreeParseError` if they do not.\\n\\n```\\ndecisiontree:decision \\\"Cloud vendor selection\\\"\\n\\ndecision \\\"Which vendor?\\\"\\n choice \\\"Build in-house\\\"\\n chance \\\"Project outcome\\\"\\n prob 0.6 end \\\"On-time delivery\\\" payoff=900000\\n prob 0.4 end \\\"Over budget / delayed\\\" payoff=150000\\n choice \\\"Managed SaaS vendor\\\"\\n end \\\"Predictable cost\\\" payoff=500000\\n choice \\\"Hybrid approach\\\"\\n chance \\\"Integration complexity\\\"\\n prob 0.5 end \\\"Smooth integration\\\" payoff=700000\\n prob 0.5 end \\\"Integration rework\\\" payoff=300000\\n```\\n\\n---\\n\\n## 5. Machine learning mode\\n\\nBest for: explaining trained CART classifiers, model transparency reports, feature importance analysis.\\n\\n### Node keywords\\n\\n| Keyword | Meaning |\\n|---|---|\\n| `split \\\"…\\\"` | Internal split node — contains a feature test |\\n| `leaf \\\"…\\\"` | Leaf node — class or regression value |\\n\\n### Branch prefixes\\n\\n`true` and `false` prefix child nodes to mark which branch each child represents.\\n\\n### Properties (key=value, no colon, no quotes around values)\\n\\n| Property | Applies to | Meaning |\\n|---|---|---|\\n| `feature=name` | split | Feature name used at the split |\\n| `op=\\\"<=\\\"` | split | Comparison operator (quote if contains special chars) |\\n| `threshold=5.9` | split | Split threshold value |\\n| `samples=150` | split, leaf | Sample count at this node |\\n| `gini=0.5` | split, leaf | Gini impurity |\\n| `entropy=0.5` | split, leaf | Entropy impurity |\\n| `mse=0.3` | split, leaf | Mean squared error (regression) |\\n| `gain=0.2` | split, leaf | Information gain |\\n| `class=name` | leaf | Predicted class name |\\n| `value=50` | leaf | Sample count; use `value=[50,30,20]` for class distribution |\\n\\n```\\ndecisiontree:ml \\\"Iris classification (CART)\\\"\\ndirection: top-down\\nimpurity: gini\\n\\nsplit \\\"Petal length ≤ 2.45\\\" feature=petal_length op=\\\"<=\\\" threshold=2.45 samples=150 gini=0.667\\n true leaf \\\"Setosa\\\" class=Iris-setosa value=50 gini=0.0\\n false split \\\"Petal width ≤ 1.75\\\" feature=petal_width op=\\\"<=\\\" threshold=1.75 samples=100 gini=0.5\\n true leaf \\\"Versicolor\\\" class=Iris-versicolor value=50 gini=0.0\\n false leaf \\\"Virginica\\\" class=Iris-virginica value=50 gini=0.0\\n```\\n\\n---\\n\\n## 6. Config options\\n\\nConfig lines appear between the header and the first node. Each is `key: value` (colon, no `config` keyword).\\n\\n### Shared config (all modes)\\n\\n| Key | Values | Default | Effect |\\n|---|---|---|---|\\n| `direction:` | `top-down`, `left-right` | `top-down` (taxonomy/ML), `left-right` (decision) | Layout direction |\\n| `edgeStyle:` (or `edge-style:`) | `diagonal`, `orthogonal`, `bracket` | mode-dependent | Edge drawing style |\\n\\n### Taxonomy config\\n\\n| Key | Values | Default | Effect |\\n|---|---|---|---|\\n| `branchLabels:` (or `branch-labels:`) | `boolean`, `relation` | `boolean` | Branch label style |\\n\\n### Decision analysis config\\n\\n| Key | Values | Default | Effect |\\n|---|---|---|---|\\n| `branchLength:` (or `branch-length:`) | `probability` | off | Scale branch length proportional to probability |\\n\\n### ML config\\n\\n| Key | Values | Default | Effect |\\n|---|---|---|---|\\n| `impurity:` | `gini`, `entropy`, `mse`, `gain` | `gini` | Impurity metric shown on nodes |\\n| `classes:` | comma-separated list | — | Class label names for display |\\n\\n```\\ndecisiontree:ml \\\"Loan classifier\\\"\\ndirection: top-down\\nimpurity: gini\\nclasses: Approved, Denied, Review\\n```\\n\\n---\\n\\n## 7. Labels & comments\\n\\n- **Diagram title:** `decisiontree \\\"Title\\\"` — the quoted string after the header keyword.\\n- **Node label:** the quoted string immediately after the node keyword — `question \\\"Is the fee waived?\\\"`.\\n- **Branch label:** `yes:`, `no:`, or `label \\\"Custom\\\":` before the child node — on the same line as the child.\\n- **Payoff:** `payoff=250000` at the end of a decision/end node line.\\n- **ML properties:** `key=value` tokens after the node's label string (no `[…]` brackets, no colons).\\n- **Comments:** `#` or `//` at the start of a line (after optional leading whitespace). Only full-line comments are supported — inline trailing comments are not.\\n\\n---\\n\\n## 8. Reserved words & escaping\\n\\n**Reserved node keywords:** `decision`, `chance`, `end`, `outcome`, `choice`, `prob`, `split`, `leaf`, `question`, `q`, `answer`, `a`.\\n\\n**Reserved branch prefixes:** `yes:`, `no:`, `true`, `false`, `label`.\\n\\n**Reserved header forms:** `decisiontree`, `decisiontree:decision`, `decisiontree:da`, `decisiontree:ml`.\\n\\n**Strings with spaces** must be double-quoted: `question \\\"Annual revenue > $1M?\\\"`. Node labels, branch labels from `label \\\"…\\\":` syntax, and the diagram title all require double quotes.\\n\\n| Reserved token | Context | Notes |\\n|---|---|---|\\n| `yes:` / `no:` | Line start in taxonomy | Cannot be used as a label — use `label \\\"yes\\\":` if you need literal text \\\"yes\\\" |\\n| `true` / `false` | Line start in ML mode | Cannot be a node label |\\n| `choice` | Line start in decision mode | Acts as branch wrapper, not a node |\\n| `prob` | Line start in decision mode | Must be followed by a number |\\n\\n---\\n\\n## 9. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `yes: \\\"Approve\\\"` (no node keyword) | `DTreeParseError: Missing taxonomy node kind` | `yes: answer \\\"Approve\\\"` |\\n| Probabilities on `chance` children summing to 0.8 | `DTreeParseError: probabilities do not sum to 1.0` | Adjust so all `prob` values sum to exactly 1.0 (±0.01) |\\n| `question \\\"text\\\"` with a child at the same indent level | Child not parsed as a child — becomes a sibling | Indent children by 2 more spaces than the parent |\\n| `config direction = top-down` (using `config` keyword) | `config` is a fishbone keyword — not recognized here | Use `direction: top-down` (no `config` prefix) |\\n| `feature = petal_length` (spaces around `=`) | Parsed as separate tokens; property not recognized | No spaces: `feature=petal_length` |\\n| `[payoff: 500000]` bracket syntax | Not recognized — parser ignores brackets for payoff | Use `payoff=500000` (no brackets, no spaces around `=`) |\\n| `decisiontree:taxonomy` | `DTreeParseError: Invalid header` | Use `decisiontree` (no mode suffix for taxonomy) |\\n\\n---\\n\\n## 10. Grammar (EBNF)\\n\\n```text\\ndocument = header ( config-line )* node\\n\\nheader = \\\"decisiontree\\\" ( \\\":\\\" mode )? ( WS quoted-string )? NEWLINE\\nmode = \\\"decision\\\" | \\\"da\\\" | \\\"ml\\\"\\n // omitted → taxonomy\\n\\nconfig-line = config-key \\\":\\\" WS config-value NEWLINE\\nconfig-key = \\\"direction\\\" | \\\"edgeStyle\\\" | \\\"edge-style\\\"\\n | \\\"branchLabels\\\" | \\\"branch-labels\\\"\\n | \\\"branchLength\\\" | \\\"branch-length\\\"\\n | \\\"impurity\\\" | \\\"classes\\\"\\n\\n// ── Taxonomy mode ──────────────────────────────\\nnode = ( branch-prefix WS )? tax-node ( WS \\\"[\\\" tax-attrs \\\"]\\\" )? NEWLINE\\n INDENT child-node*\\ntax-node = ( \\\"question\\\" | \\\"q\\\" ) WS quoted-string\\n | ( \\\"answer\\\" | \\\"a\\\" | \\\"leaf\\\" ) WS quoted-string\\nbranch-prefix = \\\"yes:\\\" | \\\"no:\\\" | \\\"label\\\" WS quoted-string \\\":\\\"\\n\\n// ── Decision-analysis mode ─────────────────────\\nda-node = \\\"decision\\\" WS quoted-string NEWLINE INDENT da-child+\\n | \\\"chance\\\" WS quoted-string NEWLINE INDENT da-prob-child+\\n | ( \\\"end\\\" | \\\"outcome\\\" ) WS quoted-string ( WS \\\"payoff=\\\" number )? NEWLINE\\nda-child = \\\"choice\\\" WS quoted-string NEWLINE INDENT da-node\\nda-prob-child = \\\"prob\\\" WS number WS da-node // prob, value, and child all on one line\\n\\n// ── ML mode ───────────────────────────────────\\nml-node = ( \\\"true\\\" | \\\"false\\\" )? ml-kind WS quoted-string ml-prop* NEWLINE\\n INDENT ml-child*\\n // \\\"true\\\"/\\\"false\\\" and ml-kind must be on the same line\\nml-kind = \\\"split\\\" | \\\"leaf\\\"\\nml-prop = WS key \\\"=\\\" value // no spaces around \\\"=\\\"\\n\\ncomment = ( \\\"#\\\" | \\\"//\\\" ) any NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n```\\n\\nAuthoritative source: `src/diagrams/decisiontree/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"flowchart\": {\n \"title\": \"Flowchart\",\n \"content\": \"## 1. Your first flowchart\\n\\nThe smallest useful flowchart: a decision with two outcomes.\\n\\n```\\nflowchart TD\\n A([Start]) --> B{File exists?}\\n B -->|Yes| C[Read file]\\n B -->|No| D[Return error]\\n C --> E([Done])\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with `flowchart` followed by a direction: `TD`, `LR`, `BT`, or `RL`.\\n2. Each node is `ID[Label]` — the shape brackets determine the node type (see §2).\\n3. Connect nodes with `-->`. Add a label between pipe characters: `-->|Yes|`.\\n4. Nodes are created automatically when first referenced in an edge — but explicit declarations let you set shapes and labels independently.\\n\\n> Comments start with `%%` on their own line.\\n\\n---\\n\\n## 2. Node shapes\\n\\nEach node shape is written as `ID<brackets>Label<brackets>`. The ID must start with a letter and may contain letters, digits, `_`, and `-`.\\n\\n| Syntax | Shape | Typical use |\\n|---|---|---|\\n| `A[Label]` | Rectangle | Process step, operation |\\n| `A(Label)` | Rounded rectangle | Subprocess, soft step |\\n| `A([Label])` | Stadium (pill) | Start / end terminal |\\n| `A{Label}` | Diamond | Decision / condition |\\n| `A{{Label}}` | Hexagon | Preparation, configuration |\\n| `A[[Label]]` | Subroutine | Predefined process |\\n| `A[(Label)]` | Cylinder | Database, storage |\\n| `A((Label))` | Circle | Connector, junction |\\n| `A(((Label)))` | Double circle | End state |\\n| `A[/Label/]` | Parallelogram | Input / output |\\n| `A[\\\\Label\\\\]` | Parallelogram (alt) | Manual operation |\\n| `A[/Label\\\\]` | Trapezoid | Manual input |\\n| `A[\\\\Label/]` | Trapezoid (alt) | Off-page connector |\\n| `A>Label]` | Asymmetric | Tag, annotation |\\n\\n```\\nflowchart TD\\n t([Terminal / stadium])\\n r[Rectangle process]\\n d{Diamond decision}\\n p[/Parallelogram input/]\\n db[(Cylinder database)]\\n sub[[Subroutine]]\\n t --> r --> d\\n d -->|branch A| p\\n d -->|branch B| db\\n p --> sub\\n db --> sub\\n```\\n\\n---\\n\\n## 3. Edges\\n\\nAn edge connects two nodes. The connector symbol determines the visual style and whether a label or arrowhead is present.\\n\\n### 3.1 Edge types\\n\\n```\\nflowchart TD\\n A --> B\\n C --- D\\n E -.-> F\\n G ==> H\\n I <--> J\\n K --x L\\n M --o N\\n```\\n\\n| Syntax | Style | Arrow | Typical use |\\n|---|---|---|---|\\n| `A --> B` | Solid | Arrow | Normal flow |\\n| `A --- B` | Solid | None | Association, undirected link |\\n| `A -.-> B` | Dotted | Arrow | Optional / async path |\\n| `A ==> B` | Thick | Arrow | Critical / primary path |\\n| `A <--> B` | Solid | Both ends | Bidirectional flow |\\n| `A --x B` | Solid | Cross | Blocked / rejected path |\\n| `A --o B` | Solid | Circle | Aggregation / composition |\\n\\n### 3.2 Edge labels\\n\\nTwo syntaxes attach a label to an edge:\\n\\n**Pipe label** — placed between `|` characters directly after the arrow:\\n```\\nA -->|Yes| B\\nA -.->|optional| B\\nA ==>|critical| B\\n```\\n\\n**Inline label** — text placed between the dashes, before the arrow character:\\n```\\nA -- success --> B\\nA -- error --x C\\n```\\n\\nBoth produce identical results. Pipe label is more common when sharing a diagram with Mermaid tools.\\n\\n```\\nflowchart TD\\n req[Request received]\\n req -->|valid| proc[Process]\\n req -->|invalid| err[Return 400]\\n proc -- success --> ok([Done])\\n proc -.->|timeout| retry[Retry queue]\\n retry ==>|max retries| dead[(Dead letter)]\\n```\\n\\n### 3.3 Chains\\n\\nConnect three or more nodes in a single line:\\n\\n```\\nA --> B --> C --> D\\n```\\n\\nThis is equivalent to three separate edge statements.\\n\\n### 3.4 Fan-out with `&`\\n\\nUse `&` to include multiple nodes on either side of an arrow. The parser generates the full cross-product of edges:\\n\\n```\\nA & B --> C %% A→C and B→C\\nA --> B & C %% A→B and A→C\\nA & B --> C & D %% four edges: A→C, A→D, B→C, B→D\\n```\\n\\n```\\nflowchart LR\\n deploy[Deploy service]\\n smoke[Smoke test]\\n health[Health check]\\n notify_slack[Slack alert]\\n notify_email[Email alert]\\n deploy --> smoke & health\\n smoke & health -->|fail| notify_slack & notify_email\\n```\\n\\n---\\n\\n## 4. Subgraphs\\n\\nA `subgraph` groups related nodes into a labeled cluster with a visible border.\\n\\n```\\nsubgraph \\\"Title\\\"\\n A --> B\\nend\\n```\\n\\nThree subgraph header forms are accepted:\\n\\n| Form | ID | Label |\\n|---|---|---|\\n| `subgraph \\\"My Group\\\"` | auto-generated | `My Group` |\\n| `subgraph sg1 \\\"My Group\\\"` | `sg1` | `My Group` |\\n| `subgraph sg1 [My Group]` | `sg1` | `My Group` |\\n\\nSubgraphs can have their own `direction` override:\\n\\n```\\nsubgraph sg1 \\\"Frontend\\\"\\n direction LR\\n ui[React App] --> api[API Client]\\nend\\n```\\n\\n```\\nflowchart TB\\n subgraph ingestion [Ingestion]\\n raw[/Raw events/] --> parse[Parse & validate]\\n parse --> enrich[Enrich]\\n end\\n subgraph storage [Storage]\\n dw[(Data warehouse)]\\n cache[(Redis cache)]\\n end\\n enrich --> dw\\n enrich --> cache\\n dw --> report[Generate report]\\n```\\n\\n---\\n\\n## 5. Styling\\n\\n### 5.1 Semantic classes\\n\\nAssign CSS class names to nodes for theme-level visual grouping. Classes are defined with `classDef` and applied with `class`.\\n\\n```\\nclassDef danger fill:#f9c,stroke:#c00\\nclassDef safe fill:#cfc,stroke:#090\\nclass errorNode danger\\nclass successNode safe\\n```\\n\\n### 5.2 Per-node style overrides\\n\\n```\\nstyle nodeId fill:#f9f,stroke:#333,stroke-width:4px\\n```\\n\\nAccepts standard CSS property names. Multiple properties are comma-separated.\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Direction:** `flowchart TD` — first token after `flowchart` or `graph`. `TD` and `TB` are equivalent.\\n- **Title:** `flowchart LR \\\"My diagram\\\"` — optional quoted string after the direction.\\n- **Edge labels:** pipe syntax `-->|label|` or inline `-- label -->`.\\n- **Comments:** `%%` at the start of a line (after leading whitespace).\\n\\n```\\nflowchart LR\\n%% This is a comment — ignored by the parser\\nA[Step 1] --> B[Step 2] %% inline %% is NOT supported — only line-start %%\\n```\\n\\n---\\n\\n## 7. Reserved words & escaping\\n\\n**Reserved at line start:** `flowchart`, `graph` (header), `subgraph`, `end`, `direction`, `class`, `classDef`, `style`, `linkStyle`.\\n\\n**Reserved ID characters:** IDs match `[A-Za-z0-9_-]` starting with a letter. Do not use spaces or operator characters in node IDs.\\n\\n**Operator tokens to avoid inside IDs:** `-->`, `---`, `-.->`, `==>`, `<-->`, `--x`, `--o`, `|`, `&`.\\n\\n**Labels with special characters:** The label is everything inside the shape brackets. Special characters are supported inside labels as-is — brackets/braces that would be ambiguous are closed by the matching closing token.\\n\\n---\\n\\n## 8. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `flowchart` with no direction | Direction defaults to `TB` | Add a direction: `flowchart TD` |\\n| `A --> B` before declaring shapes | Works — nodes created as rectangles with the ID as label | Declare explicitly when you need a non-rect shape: `A([Start])` |\\n| `A[Label with [brackets]]` | Inner `]` closes the shape early | Avoid nested brackets in labels |\\n| `subgraph My Group` (unquoted, with space) | Parser takes `My` as subgraph id, `Group` as unknown token | Quote: `subgraph \\\"My Group\\\"` |\\n| `%% comment` mid-line after code | Inline comments are not supported; `%%` must be at line start | Move comments to their own line |\\n| `A --> B --> C` mixed with `A --> B` | Chains are additive — duplicate edges may appear | Use chains OR separate lines, not both for the same pair |\\n| `direction LR` outside a subgraph | Silently ignored — `direction` override only applies inside `subgraph … end` | Set direction on the `flowchart` header line |\\n\\n---\\n\\n## 9. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | subgraph-block | direction-stmt\\n | class-stmt | classdef-stmt | style-stmt\\n | linkstyle-stmt | chain-stmt)*\\n\\nheader = (\\\"flowchart\\\" | \\\"graph\\\") ( WS direction )? ( WS title )? NEWLINE\\ndirection = \\\"TD\\\" | \\\"TB\\\" | \\\"BT\\\" | \\\"LR\\\" | \\\"RL\\\"\\ntitle = '\\\"' any-char-but-quote* '\\\"' | bare-word\\n\\nsubgraph-block = \\\"subgraph\\\" ( WS subgraph-header )? NEWLINE\\n ( WS? \\\"direction\\\" WS direction NEWLINE )?\\n statement*\\n \\\"end\\\" NEWLINE\\nsubgraph-header = id WS \\\"[\\\" label \\\"]\\\"\\n | id WS quoted-string\\n | quoted-string\\n | id\\n\\nchain-stmt = node-group ( WS edge-op WS pipe-label? WS node-group )* NEWLINE\\nnode-group = node-ref ( WS \\\"&\\\" WS node-ref )*\\nnode-ref = id shape-suffix?\\nshape-suffix = \\\"[\\\" label \\\"]\\\" %% rect\\n | \\\"(\\\" label \\\")\\\" %% round\\n | \\\"([\\\" label \\\"])\\\" %% stadium\\n | \\\"{\\\" label \\\"}\\\" %% diamond\\n | \\\"{{\\\" label \\\"}}\\\" %% hexagon\\n | \\\"[[\\\" label \\\"]]\\\" %% subroutine\\n | \\\"[(\\\" label \\\")]\\\" %% cylinder\\n | \\\"((\\\" label \\\"))\\\" %% circle\\n | \\\"(((\\\" label \\\")))\\\" %% double-circle\\n | \\\"[/\\\" label \\\"/]\\\" %% parallelogram\\n | \\\"[\\\\\\\" label \\\"\\\\]\\\" %% parallelogram-alt\\n | \\\"[/\\\" label \\\"\\\\]\\\" %% trapezoid\\n | \\\"[\\\\\\\" label \\\"/]\\\" %% trapezoid-alt\\n | \\\">\\\" label \\\"]\\\" %% asymmetric\\n\\nedge-op = \\\"-->\\\" | \\\"---\\\" | \\\"-.\\\"-\\\".->\\\" | \\\"==>\\\" | \\\"<-->\\\" | \\\"--x\\\" | \\\"--o\\\"\\n | inline-label variants of the above\\npipe-label = \\\"|\\\" text \\\"|\\\"\\n\\nclass-stmt = \\\"class\\\" WS id-list WS class-name NEWLINE\\nclassdef-stmt = \\\"classDef\\\" WS class-name WS css-props NEWLINE\\nstyle-stmt = \\\"style\\\" WS id WS css-props NEWLINE\\nlinkstyle-stmt = \\\"linkStyle\\\" WS ... %% parsed but not yet rendered\\n\\ncomment = \\\"%%\\\" any NEWLINE\\nid = [A-Za-z] [A-Za-z0-9_-]*\\n```\\n\\nAuthoritative source: `src/diagrams/flowchart/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"matrix\": {\n \"title\": \"Matrix / Quadrant diagram\",\n \"content\": \"## 1. Your first matrix\\n\\nThe smallest useful matrix: a custom 2×2 with two labeled axes and three points.\\n\\n```\\nmatrix \\\"Feature Prioritization\\\"\\nx-axis: Low Effort → High Effort\\ny-axis: Low Value → High Value\\n\\n\\\"Add search\\\" at (0.3, 0.8)\\n\\\"Rebuild pipeline\\\" at (0.85, 0.7)\\n\\\"Update footer\\\" at (0.2, 0.2)\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with the keyword `matrix`, optionally followed by a template name or a quoted title.\\n2. Set the axes with `x-axis:` and `y-axis:` — or use a built-in template and skip this step entirely.\\n3. Each point is `\\\"Label\\\" at (x, y)` where `x` and `y` are decimal fractions from 0.0 (low/left/bottom) to 1.0 (high/right/top).\\n4. Add optional properties — `size:`, `category:`, `color:`, `shape:`, `highlight:` — after the coordinates.\\n\\n> Comments must start with `#` anywhere on a line (outside quoted strings).\\n\\n---\\n\\n## 2. Built-in templates\\n\\nA template pre-configures axes, quadrant labels, and grid size. Just use the template name as the second token on the header line.\\n\\n| Template | Grid | Use case |\\n|---|---|---|\\n| `eisenhower` | 2×2 | Urgency / Importance task prioritization |\\n| `impact-effort` | 2×2 | Feature prioritization by impact vs. effort |\\n| `rice` | 2×2 | RICE scoring — Reach × Impact vs. Effort |\\n| `bcg` | 2×2 | Portfolio — Market Share vs. Growth rate |\\n| `ansoff` | 2×2 | Product/market growth strategy |\\n| `johari` | 2×2 | Self-awareness — known-to-self vs. known-to-others |\\n| `9-box` | 3×3 | HR talent review — Performance vs. Potential |\\n| `risk-matrix` | 5×5 | Risk assessment — Likelihood vs. Severity (heatmap) |\\n\\n```\\nmatrix eisenhower \\\"This Week\\\"\\n\\\"Ship hotfix\\\" at (0.1, 0.9) size: 5 highlight: true\\n\\\"Team 1:1s\\\" at (0.1, 0.7) size: 3\\n\\\"Write Q3 OKRs\\\" at (0.8, 0.85) size: 4\\n\\\"Inbox zero\\\" at (0.1, 0.3) size: 2\\n\\\"Refactor auth\\\" at (0.75, 0.4) size: 3\\n```\\n\\nAxes and quadrant labels from a template can be overridden with explicit `x-axis:` / `y-axis:` / `quadrant` directives.\\n\\n---\\n\\n## 3. Axes\\n\\nAxis lines declare the semantic poles of each dimension.\\n\\n```\\nx-axis: Low Effort → High Effort\\ny-axis: Low Value → High Value\\n```\\n\\nThe arrow separates the low label (left / bottom) from the high label (right / top). All of these separators are equivalent:\\n\\n| Separator | Example |\\n|---|---|\\n| `→` (Unicode) | `x-axis: Rare → Certain` |\\n| `->` (ASCII) | `x-axis: Rare -> Certain` |\\n| `↑` | `y-axis: Cheap ↑ Expensive` |\\n| `←` / `<-` / `<` | Reversed axis — high label is on the left |\\n\\nA **reversed axis** is for conventions where the \\\"high\\\" value sits at the left or bottom:\\n\\n```\\nx-axis: High Market Share ← Low Market Share\\n```\\n\\n```\\nmatrix \\\"Product Portfolio\\\"\\nx-axis: High Market Share ← Low Market Share\\ny-axis: Low Growth → High Growth\\n\\nquadrant Q1 \\\"Question Marks\\\"\\nquadrant Q2 \\\"Stars\\\"\\nquadrant Q3 \\\"Cash Cows\\\"\\nquadrant Q4 \\\"Dogs\\\"\\n\\n\\\"Analytics Suite\\\" at (0.25, 0.35) size: 5\\n\\\"ChatBot Pro\\\" at (0.2, 0.8) size: 4 highlight: true\\n\\\"Legacy CRM\\\" at (0.75, 0.25) size: 6\\n\\\"Mobile App\\\" at (0.65, 0.75) size: 3\\n```\\n\\n---\\n\\n## 4. Points\\n\\nEach point is a bubble positioned by a normalized (x, y) coordinate pair.\\n\\n```\\n\\\"Label\\\" at (x, y)\\n\\\"Label\\\" at (x, y) size: 4 category: design color: #7B1FA2 highlight: true note: \\\"clarify spec\\\"\\n```\\n\\n| Property | Values | Meaning |\\n|---|---|---|\\n| `size:` | positive number | Bubble area weight (default: 3) |\\n| `category:` | bareword | Color group; drives the legend |\\n| `color:` | hex string | Override bubble color for this point |\\n| `shape:` | `circle` \\\\| `square` \\\\| `triangle` \\\\| `diamond` | Bubble shape (default: `circle`) |\\n| `highlight:` | `true` | Draws an emphasis ring around the bubble |\\n| `note:` | quoted string | Tooltip annotation |\\n| `label:` | quoted string | Replaces the display label (different from the ID) |\\n\\nCoordinates outside `[0, 1]` are clamped to the chart boundary and flagged with a badge — the original value is stored for tooltip display.\\n\\n```\\nmatrix \\\"Risk Register\\\"\\nx-axis: Low Impact → High Impact\\ny-axis: Rare → Certain\\n\\n\\\"Vendor delay\\\" at (0.45, 0.7) size: 4 category: schedule highlight: true\\n\\\"Security breach\\\" at (0.9, 0.3) size: 5 category: security shape: diamond\\n\\\"Budget overrun\\\" at (0.5, 0.65) size: 3 category: finance\\n\\\"Key hire falls through\\\" at (0.6, 0.55) size: 3 category: people\\n\\\"Scope creep\\\" at (0.4, 0.8) size: 4 category: schedule\\n```\\n\\n---\\n\\n## 5. Quadrant labels\\n\\nLabel each quadrant with a name and an optional subtitle.\\n\\n```\\nquadrant Q1 \\\"Do First\\\"\\nquadrant Q2 \\\"Schedule\\\"\\nquadrant Q3 \\\"Delete\\\"\\nquadrant Q4 \\\"Delegate\\\"\\n\\n# With an optional subtitle:\\nquadrant Q1 \\\"Do First\\\" description: \\\"High urgency, high importance\\\"\\n```\\n\\nQuadrant numbering follows the standard mathematical convention: **Q1 = top-right, Q2 = top-left, Q3 = bottom-left, Q4 = bottom-right**. The `Q` prefix is optional — `quadrant 1 \\\"Label\\\"` is equally valid.\\n\\n---\\n\\n## 6. Heatmap mode\\n\\nHeatmap mode fills N×M cells with color intensity instead of plotting bubble positions.\\n\\n```\\nmatrix heatmap 4x3 \\\"Skill Matrix\\\"\\nrows: [Strategy, Execution, Communication, Technical]\\ncols: [Junior, Mid, Senior]\\n\\ncell (0,0) level: weak\\ncell (1,0) level: medium\\ncell (2,0) level: strong\\ncell (0,1) value: 7\\ncell (1,2) label: \\\"Top 10%\\\"\\n```\\n\\n- `matrix heatmap COLxROW` — header sets the grid dimensions.\\n- `rows:` and `cols:` — comma-separated or bracket-list of axis labels.\\n- `cell (col, row)` — zero-indexed, column first, row second (row 0 = bottom).\\n- `level:` — `strong` (3), `medium` (2), or `weak` (1) — shorthand for heat intensity.\\n- `value:` — explicit numeric value (overrides `level:`).\\n- `label:` — quoted text placed inside the cell.\\n\\n```\\nmatrix heatmap 4x4 \\\"Competency Heat Map\\\"\\nrows: [Leadership, Execution, Communication, Technical]\\ncols: [Junior, Mid, Senior, Staff]\\n\\ncell (0,0) level: weak\\ncell (1,0) level: medium\\ncell (2,0) level: strong\\ncell (3,0) level: strong\\ncell (0,1) level: medium\\ncell (1,1) level: medium\\ncell (2,1) level: strong\\ncell (3,1) level: strong\\ncell (0,2) level: weak\\ncell (1,2) level: medium\\ncell (2,2) level: medium\\ncell (3,2) level: strong\\ncell (0,3) level: weak\\ncell (1,3) level: weak\\ncell (2,3) level: medium\\ncell (3,3) level: strong\\n```\\n\\n---\\n\\n## 7. Correlation mode\\n\\nCorrelation mode renders an N×M dot matrix where intensity represents the relationship strength between row and column variables.\\n\\n```\\nmatrix correlation 4x4 \\\"Product Metrics\\\"\\nrows: [DAU, Retention, Revenue, NPS]\\ncols: [DAU, Retention, Revenue, NPS]\\n\\ncell (0,0) value: 1\\ncell (1,0) value: 0.82\\ncell (2,0) value: 0.54\\ncell (3,0) value: 0.71\\n```\\n\\nThe same `cell` syntax applies. `level: strong | medium | weak` is also accepted in correlation mode.\\n\\n---\\n\\n## 8. Config options\\n\\nA `config:` block tunes visual rendering. Each option goes on its own indented line below the `config:` header.\\n\\n```\\nconfig:\\n quadrantBg: true\\n gridLines: true\\n axisArrows: true\\n bubbleScale: area\\n legendPosition: bottom-right\\n```\\n\\n| Key | Values | Default | Effect |\\n|---|---|---|---|\\n| `quadrantBg` | `true` \\\\| `false` | `true` | Colored quadrant background fills |\\n| `gridLines` | `true` \\\\| `false` | `true` | Grid lines overlay |\\n| `axisArrows` | `true` \\\\| `false` | `true` | Arrows at axis ends |\\n| `bubbleScale` | `area` \\\\| `radius` | `area` | Whether `size:` scales bubble area or radius |\\n| `quadrantAnnotations` | `true` \\\\| `false` | `true` | Show quadrant label text in corners |\\n| `legendPosition` | `bottom-right` \\\\| `right` \\\\| `bottom-center` \\\\| `none` | `bottom-right` | Category legend placement |\\n| `labelCollision` | `auto` \\\\| `offset-only` \\\\| `leader-only` \\\\| `off` | `auto` | Overlap avoidance strategy for point labels |\\n| `offChartPolicy` | `clamp-badge` \\\\| `drop` | `clamp-badge` | What to do with points outside [0,1] |\\n\\nTwo shorthand directives also work at the top level (not inside the `config:` block):\\n\\n```\\naxis: off # off | on | auto — show or hide the axis lines\\nmargins: true # true | false — show Score + Rank margins (correlation mode)\\n```\\n\\n---\\n\\n## 9. Labels & comments\\n\\n- **Title:** `matrix \\\"My Title\\\"` or `title: My Title` as a standalone line.\\n- **Point label:** the quoted string before `at (…)`.\\n- **Axis labels:** `x-axis:` and `y-axis:` directives.\\n- **Quadrant labels:** `quadrant Q1 \\\"Name\\\"` directive.\\n- **Comments:** `#` anywhere on a line, outside quoted strings.\\n\\n```\\nmatrix \\\"Prioritization\\\"\\n# This is a comment\\nx-axis: Low Cost → High Cost # inline comment after a directive\\n\\\"Fix bug\\\" at (0.1, 0.9) size: 3 # comment after a point\\n```\\n\\n---\\n\\n## 10. Reserved words & escaping\\n\\n**Reserved at line start:** `matrix` (header), `x-axis:`, `y-axis:`, `quadrant`, `config:`, `title:`, `rows:`, `cols:`, `grid:`, `axis:`, `margins:`, `cell`.\\n\\n**Point lines must start with a quote character** (`\\\"` or `'`). A line that does not start with a quote is not treated as a point.\\n\\n**Strings with spaces** in axis labels do not need quoting — the text after the colon (and after the arrow) is taken verbatim. In `note:` and `label:` point properties, use double quotes.\\n\\n---\\n\\n## 11. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `\\\"Fix bug\\\" at (1, 2)` | Point parsed; x=1 clamped, y=1 clamped; off-chart badge shown | Keep coordinates in [0.0, 1.0] or accept the clamp-badge |\\n| `quadrant 1 \\\"Quick Wins\\\"` (no Q prefix) | Accepted — `Q` prefix is optional | Both `quadrant 1` and `quadrant Q1` work |\\n| `config: gridLines: false` (on same line) | Only `config:` keyword recognized; `gridLines: false` silently ignored | Put options on their own indented lines below `config:` |\\n| `x-axis: \\\"Low\\\" → \\\"High\\\"` (quoted labels) | Arrow not found inside quotes — treated as plain text | Remove quotes: `x-axis: Low → High` |\\n| `matrix heatmap` without dimensions | Defaults to 2×2; rows/cols directives set actual size | Specify dimensions on the header: `matrix heatmap 4x4` |\\n| `cell (0, 0) level: Strong` (capital S) | `level` match is case-insensitive — accepted | Both `strong` and `Strong` work |\\n| `shape: oval` | Unknown shape value — silently ignored | Use `circle`, `square`, `triangle`, or `diamond` |\\n\\n---\\n\\n## 12. Grammar (EBNF)\\n\\n```text\\ndocument = header directive*\\n\\nheader = \\\"matrix\\\" ( template-name | mode-header | title )? NEWLINE\\ntemplate-name = \\\"eisenhower\\\"|\\\"impact-effort\\\"|\\\"rice\\\"|\\\"bcg\\\"|\\\"ansoff\\\"|\\\"johari\\\"|\\\"9-box\\\"|\\\"risk-matrix\\\"\\nmode-header = ( \\\"heatmap\\\" | \\\"correlation\\\" ) ( number \\\"x\\\" number )? title?\\ntitle = quoted-string | bare-text\\n\\ndirective = x-axis | y-axis | quadrant-dir | config-block\\n | point | cell | rows-dir | cols-dir | grid-dir\\n | title-dir | axis-dir | margins-dir | comment | blank\\n\\nx-axis = \\\"x-axis:\\\" WS axis-spec NEWLINE\\ny-axis = \\\"y-axis:\\\" WS axis-spec NEWLINE\\naxis-spec = text arrow text | text # plain text → high label only\\narrow = \\\"→\\\" | \\\"->\\\" | \\\"↑\\\" | \\\"←\\\" | \\\"<-\\\" | \\\"<\\\" | \\\"↓\\\"\\n\\nquadrant-dir = \\\"quadrant\\\" WS \\\"Q\\\"? digit WS quoted-string ( WS \\\"description:\\\" quoted-string )? NEWLINE\\n\\nconfig-block = \\\"config:\\\" NEWLINE ( INDENT key \\\":\\\" WS value NEWLINE )*\\n\\npoint = quoted-string WS \\\"at\\\" WS \\\"(\\\" number \\\",\\\" number \\\")\\\" ( WS point-prop )* NEWLINE\\npoint-prop = \\\"size:\\\" number\\n | \\\"category:\\\" bareword\\n | \\\"color:\\\" hex-color\\n | \\\"shape:\\\" ( \\\"circle\\\"|\\\"square\\\"|\\\"triangle\\\"|\\\"diamond\\\" )\\n | \\\"highlight:\\\" \\\"true\\\"\\n | \\\"note:\\\" quoted-string\\n | \\\"label:\\\" quoted-string\\n\\ncell = \\\"cell\\\" WS \\\"(\\\" digit \\\",\\\" digit \\\")\\\" ( WS cell-prop )* NEWLINE\\ncell-prop = \\\"value:\\\" number\\n | \\\"label:\\\" quoted-string\\n | \\\"level:\\\" ( \\\"strong\\\" | \\\"medium\\\" | \\\"weak\\\" )\\n\\nrows-dir = \\\"rows:\\\" WS label-list NEWLINE\\ncols-dir = \\\"cols:\\\" WS label-list NEWLINE\\ngrid-dir = \\\"grid:\\\" WS number \\\"x\\\" number NEWLINE\\naxis-dir = \\\"axis:\\\" WS ( \\\"off\\\" | \\\"on\\\" | \\\"auto\\\" ) NEWLINE\\nmargins-dir = \\\"margins:\\\" WS ( \\\"true\\\" | \\\"false\\\" | \\\"on\\\" | \\\"1\\\" ) NEWLINE\\n\\nlabel-list = \\\"[\\\" text (\\\",\\\" text)* \\\"]\\\" | text (\\\",\\\" text)*\\nquoted-string = '\\\"' any-char-but-quote* '\\\"' | \\\"'\\\" any-char-but-quote* \\\"'\\\"\\ncomment = \\\"#\\\" any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/matrix/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"orgchart\": {\n \"title\": \"Org chart\",\n \"content\": \"## 1. Your first org chart\\n\\nThe smallest useful org chart: a three-level hierarchy with one open role.\\n\\n```\\norgchart \\\"Engineering Team\\\"\\ncto: \\\"Wei Zhang\\\" | CTO [role: cto]\\n lead: \\\"Sam Obi\\\" | Engineering Lead [role: engineer]\\n eng: \\\"Ana Rossi\\\" | Engineer [role: engineer]\\n open1: open \\\"TBH\\\" | Engineer [role: engineer]\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with the keyword `orgchart`, optionally followed by a quoted title.\\n2. Each person is a **node** — `id: \\\"Name\\\" | \\\"Title\\\" | \\\"Department\\\" [props]`. The `|` separates the name, title, and department fields.\\n3. **Indentation determines hierarchy** — each extra two (or more) spaces moves a node one level deeper under the nearest less-indented node above it.\\n4. Declare open roles with `open id:` or `draft id:`, and external advisors with `advisor id:`.\\n\\n> Comments must start with `#` on their own line (inline trailing `//` is also stripped).\\n\\n---\\n\\n## 2. Nodes\\n\\nA node line has the form `[kind] id: fields [props]`. The `id` must match `[A-Za-z][A-Za-z0-9_-]*`.\\n\\n### 2.1 Fields (pipe-separated)\\n\\nThe part after the `:` and before the optional `[props]` block is split on `|`:\\n\\n```\\nalice: \\\"Alice Zhang\\\" # name only\\nalice: \\\"Alice Zhang\\\" | \\\"VP Engineering\\\" # name + title\\nalice: \\\"Alice Zhang\\\" | \\\"VP Eng\\\" | \\\"Platform\\\" # name + title + department\\nalice: \\\"Alice Zhang\\\" | \\\"VP Eng\\\" | \\\"Platform\\\" | \\\"x@co.com\\\" # + info line\\n```\\n\\n| Position | Content | Notes |\\n|---|---|---|\\n| 1st | Name | Quoted or unquoted |\\n| 2nd | Title / job level | Optional |\\n| 3rd | Department | Optional |\\n| 4th | Info line | Optional; also settable via `note:`, `email:`, `phone:`, `location:` props |\\n\\n```\\norgchart \\\"Field count demo\\\"\\nalice: \\\"Alice\\\"\\n bob: \\\"Bob\\\" | \\\"CTO\\\"\\n carol: \\\"Carol\\\" | \\\"Engineer\\\" | \\\"Engineering\\\"\\n dave: \\\"Dave\\\" | \\\"VP Sales\\\" | \\\"Sales\\\" | \\\"dave@co.com\\\"\\n```\\n\\n### 2.2 Node kinds\\n\\nThe optional **kind keyword** before the id changes how the node is rendered:\\n\\n| Keyword(s) | Kind | Meaning |\\n|---|---|---|\\n| *(none)* | `person` | Regular person |\\n| `role`, `open` | `role` | Open / unfilled position |\\n| `draft`, `tbh` | `draft` | Planned position, not actively recruiting |\\n| `advisor`, `external` | `advisor` | External advisor, board member, or contractor |\\n\\n```\\norgchart \\\"Node kinds\\\"\\nceo: \\\"Jordan Kim\\\" | CEO [role: ceo]\\n eng_lead: \\\"Priya Nair\\\" | Engineering Lead [role: engineer]\\n open eng_open: \\\"TBH\\\" | Senior Engineer [role: engineer]\\n draft eng_draft: \\\"TBH\\\" | Staff Engineer [role: engineer]\\n advisor adv1: \\\"Dr. Lee\\\" | Board Advisor [role: advisor]\\n```\\n\\n### 2.3 Node properties\\n\\nProperties go in `[key: value, …]` at the end of a node line.\\n\\n| Property | Values | Effect |\\n|---|---|---|\\n| `role:` | see table below | Role icon displayed in the avatar |\\n| `icon:` | same as `role:` | Alias for `role:` |\\n| `department:` | text | Overrides the department field |\\n| `status:` | `new` \\\\| `leaving` \\\\| `on-leave` | Status pill on the card |\\n| `avatar-color:` | hex color (e.g. `\\\"#7B1FA2\\\"`) | Avatar background color |\\n| `gender:` | `male` \\\\| `female` | Avatar silhouette (used when no role icon is set) |\\n| `note:` | text | Info line (first one wins) |\\n| `email:` | text | Info line |\\n| `phone:` | text | Info line |\\n| `location:` | text | Info line |\\n| `assistant-of:` | node id | Renders this node as an assistant to the named node |\\n| `matrix:` | space- or comma-separated node ids | Adds dotted matrix lines from those nodes to this one |\\n| `reports:` | node id | Explicit parent override (instead of indentation) |\\n| `open` | *(bare flag)* | Marks node as open; equivalent to using `role` kind keyword |\\n| `draft` or `tbh` | *(bare flag)* | Marks node as draft; equivalent to `draft` kind keyword |\\n| `external` | *(bare flag)* | Marks node as external; equivalent to `advisor` kind keyword |\\n\\n**Role icons** — the `role:` value resolves to a display icon. Accepted keywords (case-insensitive):\\n\\n| Keywords | Icon |\\n|---|---|\\n| `ceo` | CEO |\\n| `cto` | CTO |\\n| `cfo` | CFO |\\n| `coo` | COO |\\n| `cmo` | CMO |\\n| `cpo` | CPO |\\n| `vp` | VP |\\n| `engineer`, `engineering` | Engineer |\\n| `designer`, `design` | Designer |\\n| `sales` | Sales |\\n| `hr` | HR |\\n| `legal` | Legal |\\n| `ops`, `operations` | Ops |\\n| `marketing` | Marketing |\\n| `product` | Product |\\n| `data` | Data |\\n| `advisor` | Advisor |\\n| `intern` | Intern |\\n| `vacant` | Vacant |\\n\\n```\\norgchart \\\"People directory\\\"\\nceo: \\\"Jamie Torres\\\" | CEO [role: ceo, avatar-color: \\\"#1E88E5\\\"]\\n cto: \\\"Raj Patel\\\" | CTO [role: cto]\\n eng1: \\\"Priya Nair\\\" | Staff Engineer [role: engineer, status: new]\\n eng2: \\\"Jordan Lee\\\" | Senior Engineer [role: engineer]\\n cfo: \\\"Maria Santos\\\" | CFO [role: cfo]\\n fin1: \\\"Nour Ahmed\\\" | Finance Manager [role: ops, status: on-leave]\\n advisor adv1: \\\"Dr. Alan Ford\\\" | Board Advisor [role: advisor]\\n draft draft1: \\\"TBH\\\" | General Counsel [role: legal]\\n```\\n\\n---\\n\\n## 3. Hierarchy\\n\\nHierarchy is expressed by **indentation** — the most common pattern. Each node becomes a child of the nearest node above it that has a smaller indent. Tabs are treated as two spaces.\\n\\n```\\nceo: \\\"CEO\\\"\\n cto: \\\"CTO\\\" # child of ceo (indent 2)\\n eng: \\\"Engineer\\\" # child of cto (indent 4)\\n cfo: \\\"CFO\\\" # child of ceo (indent 2, same level as cto)\\n```\\n\\nAlternatively, use the `reports:` property to set a parent explicitly regardless of indentation:\\n\\n```\\norgchart \\\"Flat file\\\"\\nceo: \\\"CEO\\\"\\ncto: \\\"CTO\\\" [reports: ceo]\\neng: \\\"Engineer\\\" [reports: cto]\\n```\\n\\n```\\norgchart \\\"Series A Startup\\\"\\nceo: \\\"Lena Brandt\\\" | CEO [role: ceo]\\n cto: \\\"James Osei\\\" | CTO [role: cto]\\n be: \\\"Platform Lead\\\" | Engineering [role: engineer]\\n be1: \\\"Fatima Al-Rashid\\\" | Backend Engineer [role: engineer]\\n be2: \\\"Marco Ricci\\\" | Backend Engineer [role: engineer]\\n fe: \\\"Frontend Lead\\\" | Engineering [role: engineer]\\n fe1: \\\"Yumi Tanaka\\\" | Frontend Engineer [role: engineer, status: new]\\n open_fe: open \\\"TBH\\\" | Frontend Engineer [role: engineer]\\n cpo: \\\"Diana Russo\\\" | CPO [role: cpo]\\n pm1: \\\"Chris Obi\\\" | Product Manager [role: product]\\n cmo: \\\"Kai Nakamura\\\" | CMO [role: cmo]\\n mkt1: draft \\\"TBH\\\" | Growth Marketer [role: marketing]\\n```\\n\\n---\\n\\n## 4. Edges\\n\\nTwo kinds of edges exist. Most reporting lines come from the indentation hierarchy automatically. You can also write them explicitly — or add matrix (dotted) lines.\\n\\n| Operator | Edge kind | Meaning |\\n|---|---|---|\\n| `from -> to` | `report` | Solid reporting line |\\n| `from -.-> to` | `matrix` | Dotted matrix (indirect) reporting line |\\n\\nExplicit edges require that both node ids are already declared. A `report` edge created explicitly is not duplicated if the same relationship is already implied by indentation.\\n\\nEdge labels are supported:\\n\\n```\\npm1 -.-> design [label: \\\"product partnership\\\"]\\n```\\n\\n```\\norgchart \\\"Matrix org\\\"\\nconfig: direction = LR\\ncpo: \\\"Ellen Wu\\\" | CPO [role: cpo]\\n pm_core: \\\"Core PM\\\" | Product Manager [role: product]\\n pm_growth: \\\"Growth PM\\\" | Product Manager [role: product]\\n design: \\\"Suki Ito\\\" | Design Lead [role: designer]\\n data_lead: \\\"Ben Park\\\" | Data Lead [role: data]\\npm_core -.-> design [label: \\\"design partner\\\"]\\npm_growth -.-> design\\npm_growth -.-> data_lead\\n```\\n\\n---\\n\\n## 5. Configuration\\n\\n`config:` lines adjust layout and orientation. Each goes on its own line.\\n\\n| Config key | Values | Default | Effect |\\n|---|---|---|---|\\n| `direction` | `TD`, `LR` | `TD` | Top-down or left-right flow |\\n| `layout` | `tree`, `list`, `directory`, `compact` | `tree` | Visual layout mode |\\n\\n**Layout notes:**\\n- `tree` — standard hierarchical tree with branching connectors. Best for most org charts.\\n- `list` / `directory` / `compact` — compact indented directory view. Good for large headcount lists where tree branching becomes unwieldy.\\n\\n**Tree layout** (default) — avatar cards with branching connectors and department color-coding. Best for teams up to ~30 people.\\n\\n```\\norgchart \\\"Acme Engineering — tree\\\"\\ncto: \\\"Wei Zhang\\\" | CTO [role: cto]\\n platform: \\\"Platform Lead\\\" | Engineering [role: engineer]\\n p1: \\\"Amara Diallo\\\" | Staff Engineer [role: engineer]\\n p2: \\\"Ben Novak\\\" | Senior Engineer [role: engineer]\\n p3: draft \\\"TBH\\\" | Engineer [role: engineer]\\n growth: \\\"Growth Lead\\\" | Engineering [role: engineer, status: new]\\n g1: \\\"Fatima Al-Rashid\\\" | Senior Engineer [role: engineer]\\n g2: \\\"Marco Ricci\\\" | Engineer [role: engineer]\\n```\\n\\n**List layout** — compact directory rows with indent guides. Best for large teams where tree branching becomes unwieldy.\\n\\n```\\norgchart \\\"Engineering Directory — list\\\"\\nconfig: layout = list\\ncto: \\\"Wei Zhang\\\" | CTO [role: cto]\\n platform: \\\"Platform Team\\\"\\n p1: \\\"Amara Diallo\\\" | Staff Eng [role: engineer]\\n p2: \\\"Ben Novak\\\" | Senior Eng [role: engineer]\\n p3: draft \\\"TBH\\\" | Engineer [role: engineer]\\n growth: \\\"Growth Team\\\"\\n g1: \\\"Fatima Al-Rashid\\\" | Senior Eng [role: engineer, status: new]\\n g2: \\\"Marco Ricci\\\" | Engineer [role: engineer, status: leaving]\\n```\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Title:** `orgchart \\\"Acme Corp\\\"` — first line only.\\n- **Name field:** first pipe-delimited field after the colon; may be quoted (`\\\"Alice Zhang\\\"`) or unquoted (`Alice`).\\n- **Title/department fields:** second and third pipe-delimited fields.\\n- **Info line:** fourth pipe-delimited field, or set via `note:`, `email:`, `phone:`, or `location:` props. First one encountered wins.\\n- **Comments:** `#` at the start of a line (after leading whitespace). Inline `//` is also stripped.\\n\\n---\\n\\n## 7. Reserved words & escaping\\n\\n**Reserved at line start:** `orgchart` (header), `config:`, `role`, `open`, `draft`, `tbh`, `advisor`, `external`.\\n\\n**Reserved operator tokens** — avoid these sequences inside ids: `->`, `-.->`\\n\\n**ID rules:** must match `[A-Za-z][A-Za-z0-9_-]*`. Names with spaces go in the quoted name field, not the id.\\n\\n**Strings with spaces** in props values (e.g. `department: \\\"Platform Eng\\\"`) must be double-quoted.\\n\\n---\\n\\n## 8. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `alice: Alice Zhang` (unquoted name with space) | Parses `Alice` as the name, `Zhang` is lost or misread | Quote the name: `alice: \\\"Alice Zhang\\\"` |\\n| `open1 open: \\\"TBH\\\"` | `open` after the id is not a kind keyword; fails id regex | Kind keyword comes first: `open open1: \\\"TBH\\\"` |\\n| `alice -> bob` before either node is declared | `OrgchartParseError: Edge references unknown node` | Declare nodes first, then write edges at the end |\\n| `config: direction = top-down` | Unknown value ignored; direction stays `TD` | Use `TD` or `LR` |\\n| `config: layout = compact` | Accepted — maps to `list` layout | Correct; `compact`, `directory`, and `list` all work |\\n| `alice [matrix: bob charlie]` | Space-separated ids in `matrix:` — both are added | Also works with commas: `matrix: \\\"bob, charlie\\\"` |\\n| Node id with a space: `fe lead` | Parser takes `fe` as the id; `lead` fails | Use underscore: `fe_lead` |\\n| Duplicate id | `OrgchartParseError: Duplicate node id` | Each node needs a unique id |\\n\\n---\\n\\n## 9. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | config | edge | node)*\\n\\nheader = \\\"orgchart\\\" ( WS quoted-string )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nconfig = \\\"config\\\" WS \\\":\\\" WS key WS \\\"=\\\" WS value NEWLINE\\nkey = \\\"direction\\\" | \\\"layout\\\"\\n\\nnode = INDENT* kind? id \\\":\\\" WS fields ( \\\"[\\\" node-attrs \\\"]\\\" )? NEWLINE\\nkind = \\\"role\\\" | \\\"open\\\" | \\\"draft\\\" | \\\"tbh\\\" | \\\"advisor\\\" | \\\"external\\\" | \\\"person\\\"\\nfields = field ( \\\"|\\\" field )*\\nfield = quoted-string | unquoted-text\\nnode-attrs = node-attr (\\\",\\\" node-attr)*\\nnode-attr = \\\"role:\\\" role-keyword\\n | \\\"icon:\\\" role-keyword\\n | \\\"department:\\\" text\\n | \\\"status:\\\" ( \\\"new\\\" | \\\"leaving\\\" | \\\"on-leave\\\" )\\n | \\\"avatar-color:\\\" quoted-hex\\n | \\\"gender:\\\" ( \\\"male\\\" | \\\"female\\\" )\\n | \\\"note:\\\" text\\n | \\\"email:\\\" text\\n | \\\"phone:\\\" text\\n | \\\"location:\\\" text\\n | \\\"assistant-of:\\\" id\\n | \\\"matrix:\\\" id-list\\n | \\\"reports:\\\" id\\n | bare-flag\\n\\nbare-flag = \\\"open\\\" | \\\"draft\\\" | \\\"tbh\\\" | \\\"external\\\"\\n\\nedge = id WS edge-op WS id ( \\\"[\\\" edge-attrs \\\"]\\\" )? NEWLINE\\nedge-op = \\\"->\\\" | \\\".->\\\" // -.-> for matrix\\nedge-attrs = \\\"label:\\\" quoted-string\\n\\nid = [A-Za-z] [A-Za-z0-9_-]*\\ncomment = ( \\\"#\\\" | \\\"//\\\" ) any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/orgchart/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"mindmap\": {\n \"title\": \"Mind map\",\n \"content\": \"## 1. Your first mind map\\n\\nThe smallest useful mind map: a central topic with two branches, one with a sub-item.\\n\\n```\\nmindmap\\n\\n# Team retrospective\\n\\n## What went well\\n- Clear sprint goals\\n- Good test coverage\\n\\n## What to improve\\n- Slower PR reviews\\n - Add a review SLA\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with an optional `mindmap` keyword on its own line, then a blank line.\\n2. The root is the single `#` heading — exactly one is allowed.\\n3. Use `##`, `###`, and deeper headings to set branch depth. Heading level equals tree depth.\\n4. Use `-`, `*`, or `+` bullets to add sub-items under any heading. Each 2-space indent adds one more depth level.\\n\\n> Comments are not supported. Use `%%` directives (before the `#` root) for configuration only.\\n\\n---\\n\\n## 2. Headings and depth\\n\\nHeading level maps directly to tree depth. `#` is always the root (depth 0). `##` is depth 1. `###` is depth 2, and so on up to `######` (depth 5).\\n\\n```\\nmindmap\\n\\n# Root\\n## Branch A ← depth 1\\n### Sub-branch ← depth 2\\n#### Leaf ← depth 3\\n## Branch B\\n```\\n\\nHeadings can jump levels — `####` after `##` is valid and produces a node at depth 3. The tree depth is relative to the root, not to the previous heading.\\n\\n---\\n\\n## 3. Bullets\\n\\nBullets extend a heading branch with further detail. Any of `-`, `*`, or `+` is accepted as the bullet marker. Each **2 spaces** of indentation adds one level of depth relative to the enclosing heading.\\n\\n```\\n## Risks\\n- Technical complexity ← depth 2 (one level under ## Risks)\\n - Legacy integrations ← depth 3 (2 spaces indent)\\n - Auth service ← depth 4 (4 spaces indent)\\n- Team availability ← depth 2 again\\n```\\n\\n```\\nmindmap\\n\\n# Book outline\\n\\n## Chapter 1 — Introduction\\n- Why this matters\\n - Historical context\\n - Current state\\n- What you will learn\\n\\n## Chapter 2 — Core concepts\\n- Concept A\\n - Definition\\n - Examples\\n- Concept B\\n - Definition\\n - Worked example\\n - Step-by-step walkthrough\\n```\\n\\n---\\n\\n## 4. Inline formatting\\n\\nNode labels support a subset of Markdown inline formatting. The parser tokenizes labels at parse time; the renderer uses the tokens to emit styled text.\\n\\n| Syntax | Effect | Example |\\n|---|---|---|\\n| `**text**` | Bold | `**Critical path**` |\\n| `*text*` | Italic | `*optional*` |\\n| `` `code` `` | Monospace code | `` `npm install` `` |\\n| `[text](url)` | Link | `[RFC 7519](https://tools.ietf.org/html/rfc7519)` |\\n| `[ ] item` | Unchecked task | `[ ] Write tests` |\\n| `[x] item` | Checked task | `[x] Design review` |\\n\\nThe checkbox must be at the very start of the label (before any other text). Inline formatting can be nested: `**[bold link](url)**`.\\n\\n```\\nmindmap\\n\\n# Sprint 24 review\\n\\n## Completed\\n- [x] **Auth redesign** — JWT + refresh tokens\\n- [x] API rate limiting \\\\`per-user\\\\`\\n- [x] [Error budget dashboard](https://metrics.example.com)\\n\\n## In progress\\n- [ ] *Mobile push notifications*\\n - [ ] iOS APNs integration\\n - [ ] Android FCM setup\\n\\n## Blocked\\n- [ ] **Payment webhook** — waiting on Stripe team\\n - *Escalated to account manager*\\n```\\n\\n---\\n\\n## 5. Layout styles\\n\\nThe `%% style:` directive selects the layout algorithm. Place it before the `#` root heading.\\n\\n| Style | Layout | Best for |\\n|---|---|---|\\n| `map` (default) | Radial — branches spread in all directions from the center | Brainstorming, concept maps, free-form exploration |\\n| `logic-right` | Horizontal tree — all branches extend to the right | Structured outlines, hierarchies, sequential breakdowns |\\n\\n```\\n%% style: map\\n%% style: logic-right\\n```\\n\\n**`map`** (default) — radial layout, branches spread in all directions from the center. Best for brainstorming and concept maps.\\n\\n```\\nmindmap\\n\\n# Machine learning\\n\\n## Supervised\\n### Classification\\n- Decision tree\\n- SVM\\n- Neural net\\n### Regression\\n- Linear\\n- Gradient boosting\\n\\n## Unsupervised\\n### Clustering\\n- K-means\\n- DBSCAN\\n### Reduction\\n- PCA\\n- t-SNE\\n\\n## Reinforcement\\n- Q-learning\\n- Policy gradient\\n```\\n\\n**`logic-right`** — horizontal tree, all branches extend to the right. Best for structured outlines and sequential hierarchies.\\n\\n```\\nmindmap\\n%% style: logic-right\\n\\n# Machine learning\\n\\n## Supervised\\n### Classification\\n- Decision tree\\n- SVM\\n- Neural net\\n### Regression\\n- Linear\\n- Gradient boosting\\n\\n## Unsupervised\\n### Clustering\\n- K-means\\n- DBSCAN\\n### Reduction\\n- PCA\\n- t-SNE\\n\\n## Reinforcement\\n- Q-learning\\n- Policy gradient\\n```\\n\\n---\\n\\n## 6. Directives\\n\\nDirectives are `%%` lines placed **before** the `#` root heading. They configure the diagram globally.\\n\\n| Directive | Values | Default | Effect |\\n|---|---|---|---|\\n| `%% style: …` | `map`, `logic-right` | `map` | Layout algorithm |\\n| `%% theme: …` | any string | (none) | Theme override passed to renderer |\\n| `%% maxLabelWidth: …` | integer 80–1000 | `240` | Max pixel width before label wraps |\\n\\n```\\nmindmap\\n%% style: logic-right\\n%% maxLabelWidth: 320\\n\\n# Wide label root\\n```\\n\\n```\\nmindmap\\n%% style: logic-right\\n%% maxLabelWidth: 200\\n\\n# Schematex features\\n\\n## DSL-first design\\n- One keyword per diagram\\n- AI-friendly syntax\\n- CJK support\\n\\n## Zero dependencies\\n- Hand-written parser\\n- No D3, no dagre\\n- ~KB-level bundle\\n\\n## Standards-compliant\\n- IEEE for logic gates\\n- IEC for circuits\\n- McGoldrick for genograms\\n```\\n\\n---\\n\\n## 7. Labels & comments\\n\\n- **Root title:** the text after `#` on the root heading line.\\n- **Branch labels:** the text after `##`, `###`, etc.\\n- **Bullet labels:** the text after the `- ` / `* ` / `+ ` marker.\\n- **Inline formatting:** `**bold**`, `*italic*`, `` `code` ``, `[text](url)`, `[ ]` / `[x]`.\\n- **Comments:** not supported in the body. Use `%%` directives before the `#` root for configuration; `%%` lines in the body are treated as directives (silently ignored if unrecognized).\\n\\n---\\n\\n## 8. Reserved words & escaping\\n\\n**Reserved at document start:** `mindmap` (optional keyword) and `%%` (directive prefix).\\n\\n**Reserved as root:** exactly one `#` heading; a second `#` heading throws a parse error.\\n\\n**Bullet markers:** `-`, `*`, `+` followed by a space. A `*` that is not followed by a space is treated as an italic marker if it appears inside label text.\\n\\n**Inline conflicts:** a label beginning with `[ ]` or `[x] ` is parsed as a checkbox, not a Markdown link. If you need a label that literally starts with `[`, write `\\\\[` — the backslash escapes the bracket.\\n\\n---\\n\\n## 9. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| Two `#` headings | `Error: multiple # center nodes not allowed` | Use exactly one `#` heading as the root |\\n| `##Branch` (no space after `##`) | Line is not recognized as a heading; silently skipped | Always put a space: `## Branch` |\\n| Bullet indented 3 spaces | Depth = `lastHeadingDepth + 1 + floor(3/2) = lastHeadingDepth + 2` — may create an unexpected level | Use multiples of 2 spaces: 0, 2, 4, 6… |\\n| `%% style: radial` | Unknown value silently ignored; layout stays `map` | Use `map` or `logic-right` |\\n| `mindmap` keyword mid-document | Treated as a plain text line (the keyword is only recognized on the very first line) | Place `mindmap` on line 1, before any content |\\n| `[ ]text` (no space after bracket) | Checkbox not recognized; rendered as literal `[ ]text` | `[ ] text` — space required after the closing bracket |\\n\\n---\\n\\n## 10. Grammar (EBNF)\\n\\n```text\\ndocument = (\\\"mindmap\\\" NEWLINE)? (blank | directive)* node*\\n\\ndirective = \\\"%%\\\" WS key \\\":\\\" WS value NEWLINE\\nkey = \\\"style\\\" | \\\"theme\\\" | \\\"maxlabelwidth\\\"\\n\\nnode = heading | bullet\\nheading = INDENT? \\\"#\\\"+ SPACE label NEWLINE\\nbullet = SPACE* bullet-marker SPACE label NEWLINE\\nbullet-marker = \\\"-\\\" | \\\"*\\\" | \\\"+\\\"\\n\\nlabel = inline-token*\\ninline-token = checkbox\\n | \\\"**\\\" inline-token* \\\"**\\\"\\n | \\\"*\\\" inline-token* \\\"*\\\"\\n | \\\"`\\\" code-text \\\"`\\\"\\n | \\\"[\\\" inline-token* \\\"]\\\" \\\"(\\\" url \\\")\\\"\\n | plain-text\\n\\ncheckbox = \\\"[ ]\\\" SPACE | \\\"[x]\\\" SPACE | \\\"[X]\\\" SPACE\\n\\nINDENT = WS* %% headings may have leading whitespace (ignored)\\nSPACE = \\\" \\\" | \\\"\\\\t\\\"\\n```\\n\\n**Depth rules:**\\n- Heading `#` → depth 0 (root)\\n- Heading `##` → depth 1, `###` → depth 2, etc.\\n- Bullet at `n` leading spaces → depth = `lastHeadingDepth + 1 + floor(n / 2)`\\n\\nAuthoritative source: `src/diagrams/mindmap/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"timeline\": {\n \"title\": \"Timeline diagram\",\n \"content\": \"## 1. Your first timeline\\n\\nThe smallest useful timeline: a title, two ordinary events, and a milestone.\\n\\n```\\ntimeline \\\"Product Launch\\\"\\n2024-06-01: \\\"Development complete\\\"\\n2024-08-15: \\\"Closed beta\\\"\\n2024-09-01: milestone \\\"Public launch\\\"\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with the keyword `timeline`, optionally followed by a quoted title.\\n2. Each event is `DATE: \\\"Label\\\"` — a date, a colon, then a quoted label. A `milestone` keyword before the label marks the event as a milestone.\\n3. Date ranges use `DATE - DATE:` or `DATE .. DATE:` (both are equivalent).\\n4. Add `[key: value]` properties after the quoted label to set color, side placement, shape, or category.\\n\\n> Comments must start with `#` or `//` on their own line.\\n\\n---\\n\\n## 2. Events\\n\\nAn event line places a labeled marker at a point in time (or a bar across a span).\\n\\n### 2.1 Point events\\n\\n```\\n2024-09-15: \\\"Conference keynote\\\"\\n```\\n\\n### 2.2 Range events\\n\\n```\\n2024-06-01 - 2024-08-31: \\\"Q3 development sprint\\\"\\n2024-06-01 .. 2024-08-31: \\\"Q3 development sprint\\\"\\n```\\n\\nBoth separators (`-` surrounded by spaces, or `..`) are equivalent.\\n\\n### 2.3 Milestones\\n\\n```\\n2024-09-01: milestone \\\"Public launch\\\"\\n```\\n\\nThe `milestone` keyword before the quoted label switches the marker to a diamond shape.\\n\\n### 2.4 Event properties\\n\\nProperties go in `[key: value, …]` after the quoted label, before the newline.\\n\\n| Property | Values | Meaning |\\n|---|---|---|\\n| `color:` | hex string | Custom color for this marker or bar |\\n| `side:` | `above` \\\\| `below` | Force placement above or below the axis (swimlane / lollipop) |\\n| `icon:` | any text (e.g. emoji) | Icon displayed with the event |\\n| `shape:` | `circle` \\\\| `square` \\\\| `diamond` \\\\| `star` \\\\| `flag` | Point marker shape |\\n| `category:` | quoted string | Group label — drives color in gantt legend |\\n\\n```\\n2024-09-01: milestone \\\"Launch day\\\" [color: #E53935, side: above]\\n2024-07-15: \\\"Beta ships\\\" [icon: 🚀, category: \\\"product\\\"]\\n```\\n\\n```\\ntimeline \\\"Engineering Milestones\\\"\\nconfig: style = lollipop\\n\\n2024-01-08: \\\"Sprint planning complete\\\"\\n2024-02-14: milestone \\\"Design system shipped\\\" [side: above, color: #1565C0]\\n2024-03-22: \\\"Incident — 4h outage\\\" [icon: ⚠️]\\n2024-04-01 - 2024-06-30: \\\"API v2 build\\\"\\n2024-07-01: milestone \\\"API v2 GA\\\" [side: above, color: #2E7D32]\\n```\\n\\n---\\n\\n## 3. Date formats\\n\\nAll date formats can appear anywhere a date is expected — in events, eras, and ranges.\\n\\n| Format | Example | Notes |\\n|---|---|---|\\n| Full date | `2024-09-15` | Day-precision; `YYYY-MM-DD` |\\n| Year-month | `2024-09` | Month-precision |\\n| Year | `2024` | Year-precision |\\n| Quarter | `2024-Q3` | Maps to start of that quarter |\\n| BC year (negative) | `-753` | 753 BC |\\n| BC year (suffix) | `753BC` or `753BCE` | Same as `-753` |\\n| Geological | `65Ma`, `4.6Ga`, `12ka` | Million / billion / thousand years ago |\\n\\n```\\ntimeline \\\"Age of Earth\\\"\\nconfig: style = lollipop\\nconfig: scale = log\\n\\n4600Ma: \\\"Earth forms\\\"\\n540Ma: \\\"Cambrian explosion\\\"\\n252Ma: milestone \\\"Permian extinction — 96% of species lost\\\"\\n66Ma: milestone \\\"K-Pg extinction — end of non-avian dinosaurs\\\"\\n3Ma: \\\"Earliest stone tools (Lomekwi)\\\"\\n0: \\\"Present day\\\"\\n```\\n\\n---\\n\\n## 4. Eras (background spans)\\n\\nAn `era` line draws a shaded background band across the time axis. It always requires a date range.\\n\\n```\\nera 2020 - 2022: \\\"Foundation Phase\\\"\\nera 2022 .. 2025: \\\"Growth Phase\\\" [color: #E8F5E9]\\n```\\n\\nThe `[color: …]` property is optional. Overlapping eras stack into separate bands automatically.\\n\\n```\\ntimeline \\\"Company History\\\"\\nconfig: style = swimlane\\n\\nera 2019..2021: \\\"Early Stage\\\" [color: #FFF8E1]\\nera 2021..2024: \\\"Series A & B\\\" [color: #E8F5E9]\\n\\n2019-03-01: \\\"Founded\\\"\\n2019-11-15: \\\"First paying customer\\\"\\n2020-05-01: milestone \\\"Product-market fit\\\" [side: above]\\n2021-02-10: \\\"Series A — $8M\\\"\\n2022-09-01: \\\"Series B — $30M\\\"\\n2023-06-15: milestone \\\"Profitability\\\" [color: #1B5E20]\\n```\\n\\n---\\n\\n## 5. Tracks (swimlane grouping)\\n\\nA `track` block groups related events into a named swimlane. Indented event lines (2 spaces) belong to the track.\\n\\n```\\ntrack \\\"Engineering\\\":\\n 2024-01-15 - 2024-03-31: \\\"API design\\\"\\n 2024-04-01 - 2024-07-31: \\\"Implementation\\\"\\n\\ntrack \\\"Marketing\\\":\\n 2024-06-01: \\\"Campaign kick-off\\\"\\n 2024-09-01: milestone \\\"Launch campaign\\\" [color: #1B5E20]\\n```\\n\\nTracks are most useful in `gantt` style, where each track becomes its own labeled row.\\n\\n```\\ntimeline \\\"Q3 Project Plan\\\"\\nconfig: style = gantt\\n\\ntrack \\\"Backend\\\":\\n 2024-07-01 - 2024-08-15: \\\"Database migration\\\"\\n 2024-08-16 - 2024-09-30: \\\"API hardening\\\"\\n\\ntrack \\\"Frontend\\\":\\n 2024-07-01 - 2024-08-31: \\\"New design system\\\"\\n 2024-09-01 - 2024-09-30: \\\"Integration & QA\\\"\\n\\ntrack \\\"DevOps\\\":\\n 2024-07-15 - 2024-08-01: \\\"Staging environment\\\"\\n 2024-09-30: milestone \\\"Go-live\\\" [color: #2E7D32]\\n```\\n\\n---\\n\\n## 6. Notes\\n\\nA `note:` line indented under an event attaches a tooltip annotation to it.\\n\\n```\\n2024-06-01: \\\"Beta launch\\\"\\n note: \\\"First external users; NPS target 40+\\\"\\n```\\n\\nNotes work both for flat events and for events inside tracks.\\n\\n---\\n\\n## 7. Configuration\\n\\n`config:` lines tune the layout and visual style. Each is its own line in the form `config: key = value`.\\n\\n| Key | Values | Default | Effect |\\n|---|---|---|---|\\n| `style` | `swimlane` \\\\| `gantt` \\\\| `lollipop` | `swimlane` | Visual rendering mode |\\n| `orientation` | `horizontal` \\\\| `vertical` | `horizontal` | Axis direction |\\n| `scale` | `proportional` \\\\| `equidistant` \\\\| `log` | `proportional` | How time maps to screen space |\\n| `axis` | `bottom` \\\\| `center` | `bottom` | Where the time axis is drawn |\\n\\n**Style notes:**\\n- `swimlane` — events alternate above and below a horizontal axis; `side:` controls per-event placement. Eras add colored background bands. Best for roadmaps and biographies.\\n- `gantt` — each named track becomes a horizontal bar lane; milestones become pins above the bars. `gantt-project` is an alias. Best for project scheduling.\\n- `lollipop` — a center axis with labeled cards on alternating stems. Best for historical retrospectives with sparse, memorable events.\\n\\n**Swimlane** — roadmaps, product timelines, biographies. Eras add color bands; events alternate above and below.\\n\\n```\\ntimeline \\\"SaaS Platform Roadmap\\\"\\nconfig: style = swimlane\\n\\nera 2024-Q1..2024-Q2: \\\"Foundation\\\" [color: #E3F2FD]\\nera 2024-Q3..2024-Q4: \\\"Growth\\\" [color: #E8F5E9]\\n\\n2024-01-15: \\\"Kick-off & team onboarding\\\"\\n2024-02-01: milestone \\\"Architecture sign-off\\\" [side: above]\\n2024-03-01 - 2024-05-31: \\\"API v2 build\\\"\\n2024-04-10: \\\"Security audit\\\" [color: #F9A825]\\n2024-06-30: milestone \\\"Beta launch\\\" [side: above]\\n2024-07-01 - 2024-09-30: \\\"Performance & scaling\\\"\\n2024-10-15: milestone \\\"General availability\\\" [color: #2E7D32]\\n```\\n\\n**Gantt** — parallel tracks with horizontal duration bars and milestone pins. Best for project scheduling.\\n\\n```\\ntimeline \\\"Product Launch — Q4\\\"\\nconfig: style = gantt\\n\\ntrack \\\"Engineering\\\":\\n 2024-10-01 - 2024-11-15: \\\"Feature freeze & hardening\\\"\\n 2024-11-16 - 2024-11-30: \\\"Load testing\\\"\\n\\ntrack \\\"Design\\\":\\n 2024-10-01 - 2024-10-31: \\\"Final UI polish\\\"\\n 2024-11-01 - 2024-11-14: \\\"Asset delivery\\\"\\n\\ntrack \\\"Marketing\\\":\\n 2024-10-15 - 2024-11-14: \\\"Campaign prep\\\"\\n 2024-11-15 - 2024-11-30: \\\"Launch campaign\\\"\\n 2024-12-01: milestone \\\"Launch day\\\" [color: #2E7D32]\\n```\\n\\n**Lollipop** — sparse milestones on a centered axis with alternating cards. Best for historical retrospectives and brand stories.\\n\\n```\\ntimeline \\\"History of Computing\\\"\\nconfig: style = lollipop\\nconfig: scale = equidistant\\n\\n1936: \\\"Turing — On Computable Numbers\\\"\\n1945: \\\"ENIAC — first general-purpose computer\\\"\\n1969: \\\"ARPANET\\\"\\n1971: milestone \\\"Intel 4004 microprocessor\\\" [side: above]\\n1976: \\\"Apple I\\\"\\n1991: \\\"World Wide Web (Berners-Lee)\\\"\\n2007: milestone \\\"iPhone\\\" [color: #1565C0, side: above]\\n```\\n\\n---\\n\\n## 8. Labels & comments\\n\\n- **Title:** `timeline \\\"My Title\\\"` — first line only.\\n- **Event label:** quoted string after the colon: `DATE: \\\"Label\\\"`.\\n- **Milestone label:** `DATE: milestone \\\"Label\\\"`.\\n- **Era label:** `era DATE - DATE: \\\"Label\\\"`.\\n- **Track name:** `track \\\"Name\\\":`.\\n- **Note:** `note: \\\"text\\\"` indented under an event.\\n- **Comments:** `#` or `//` at the start of a line (after leading whitespace).\\n\\n---\\n\\n## 9. Reserved words & escaping\\n\\n**Reserved at line start:** `timeline` (header), `config:`, `era`, `track`, `note:`.\\n\\n**Date range separators:** ` - ` (space-hyphen-space) and `..` — avoid these sequences inside label text that appears before the colon.\\n\\n**BC years as negative integers:** `-753` is the year 753 BC. The parser distinguishes the negative sign from a range separator by checking for surrounding whitespace — ` - ` (with spaces) is a range; `-753` (no leading space after a colon) is a BC year.\\n\\n**Property blocks:** `[key: value]` must appear *after* the quoted label on the same line. The closing `]` must be present; unclosed brackets produce a parse error.\\n\\n---\\n\\n## 10. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `2024-06-01: Launch day` (unquoted label) | Line not recognized as an event — `TimelineParseError` | Quote the label: `2024-06-01: \\\"Launch day\\\"` |\\n| `2024-06 - 2024-09: \\\"Q3\\\"` (year-month range) | Parsed correctly | This works — all date formats are valid in ranges |\\n| `era 2024: \\\"Whole year\\\"` (no range) | `TimelineParseError: era requires a date range` | Use a range: `era 2024 - 2024: \\\"Whole year\\\"` |\\n| `track \\\"Backend\\\"` (no colon) | `TimelineParseError: Expected ':' after track name` | Add the colon: `track \\\"Backend\\\":` |\\n| `2024-01-01: \\\"Event\\\" [side: left]` | `side: left` silently ignored; only `above` and `below` are valid | Use `side: above` or `side: below` |\\n| `config: style = Gantt` (capital G) | `TimelineParseError: Invalid style: Gantt` | Use lowercase: `config: style = gantt` |\\n| `2024-01-01-2024-03-31: \\\"Q1\\\"` (no spaces around `-`) | Parser reads `2024-01-01-2024` as a date — fails | Use spaces: `2024-01-01 - 2024-03-31:` or `..`: `2024-01-01..2024-03-31:` |\\n| Indented event without a track | Indented lines under the timeline header that aren't inside a `track` block — parsed as flat events | Only indent events that are inside a `track \\\"Name\\\":` block |\\n\\n---\\n\\n## 11. Grammar (EBNF)\\n\\n```text\\ndocument = header ( blank | comment | config | era | track | event )*\\n\\nheader = \\\"timeline\\\" ( WS quoted-string )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nconfig = \\\"config:\\\" WS key WS \\\"=\\\" WS value NEWLINE\\nkey = \\\"style\\\" | \\\"orientation\\\" | \\\"scale\\\" | \\\"axis\\\"\\n\\nera = \\\"era\\\" WS date-range \\\":\\\" WS quoted-string ( WS props )? NEWLINE\\ntrack = \\\"track\\\" WS quoted-string \\\":\\\" NEWLINE\\n ( INDENT≥2 event | INDENT≥2 note )*\\n\\nevent = date-spec \\\":\\\" WS event-body ( WS props )? NEWLINE\\n ( INDENT note )?\\nevent-body = ( \\\"milestone\\\" WS )? quoted-string\\ndate-spec = date ( ( \\\" - \\\" | \\\"..\\\" ) date )?\\n\\nnote = \\\"note:\\\" WS quoted-string NEWLINE\\n\\nprops = \\\"[\\\" prop-list \\\"]\\\"\\nprop-list = prop ( \\\",\\\" prop )*\\nprop = key \\\":\\\" WS value\\n | key \\\":\\\" WS quoted-string\\n\\ndate = iso-date | year-month | year | quarter | bc-year | geological\\niso-date = digit{4} \\\"-\\\" digit{2} \\\"-\\\" digit{2}\\nyear-month = digit{4} \\\"-\\\" digit{2}\\nyear = \\\"-\\\"? digit{1,5}\\nquarter = digit{4} \\\"-\\\"? \\\"Q\\\" [1-4]\\nbc-year = digit+ ( \\\"BC\\\" | \\\"BCE\\\" )\\ngeological = number ( \\\"Ma\\\" | \\\"Ga\\\" | \\\"ka\\\" )\\n\\ncomment = ( \\\"#\\\" | \\\"//\\\" ) any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/timeline/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"state\": {\n \"title\": \"State diagram\",\n \"content\": \"## 1. Your first state diagram\\n\\nThe smallest useful state diagram has an initial state, a final state, and one transition.\\n\\n```\\nstateDiagram-v2\\n[*] --> Running\\nRunning --> [*] : done\\n```\\n\\nThree rules cover 80% of usage:\\n\\n1. Start with `stateDiagram-v2` (Mermaid-style) or `state` (Schematex-style) header.\\n2. Use `[*]` for the implicit start node (when on the left of `-->`) or end node (when on the right).\\n3. Connect states with `-->`. Add a label after `:` — full UML form is `trigger [guard] / action`.\\n\\nThe default direction is **TB** (top-to-bottom) to match Mermaid. Override with `direction LR` on its own line, or `[direction: LR]` on the header.\\n\\n> Comments use `%%` (Mermaid), `#`, or `//`.\\n\\n---\\n\\n## 2. State declarations\\n\\nStates are auto-created when first referenced in a transition, but explicit declarations let you control the label and shape.\\n\\n```\\nstate Authenticating\\nstate \\\"Awaiting Approval\\\" as Approval\\nIdle: Waiting for input\\n```\\n\\n| Form | Effect |\\n|---|---|\\n| `Idle` | Bare ID — created as a simple state with `Idle` as both id and label |\\n| `state Idle` | Explicit declaration; same effect |\\n| `state \\\"Awaiting Approval\\\" as Approval` | Aliased — display `Awaiting Approval`, refer to it by `Approval` in transitions |\\n| `Idle: Waiting for input` | Inline label — `Idle` is the id, `Waiting for input` is the visible label |\\n\\n---\\n\\n## 3. Pseudo-states\\n\\nPseudo-states control the flow of a state machine without representing a stable resting state.\\n\\n| Mermaid | Schematex | Symbol | Purpose |\\n|---|---|---|---|\\n| `[*]` (source) | `initial id` | Filled black circle | Entry point of a region |\\n| `[*]` (target) | `final id` | Filled circle inside outer ring | Successful exit |\\n| `state X <<choice>>` | `choice X` | Diamond | Dynamic branch (guards evaluated at runtime) |\\n| `state X <<fork>>` | `fork X` | Thick black bar | One-input → N parallel outputs |\\n| `state X <<join>>` | `join X` | Thick black bar | N inputs → one output |\\n| — | `junction X` | Small filled circle | Static merge point |\\n| — | `history X` | Circle with `H` | Re-enter at last visited sub-state |\\n| — | `dhistory X` | Circle with `H*` | Deep history (recursive) |\\n| — | `terminate X` | `×` mark | Abnormal termination (no cleanup) |\\n| — | `entry_point X` / `exit_point X` | Hollow circle on composite border | Named entry / exit points |\\n\\n`[*]` is the Mermaid alias and is always direction-resolved: on the **source** side of `-->` it's an `initial`, on the **target** side it's a `final`. Each composite scope gets its own pair.\\n\\n```\\nstateDiagram-v2\\n[*] --> Idle\\nstate Decide <<choice>>\\nstate Joiner <<join>>\\nIdle --> Decide : evaluate\\nDecide --> Authorized : [role == \\\"admin\\\"]\\nDecide --> Joiner : [role == \\\"user\\\"]\\nAuthorized --> Joiner\\nJoiner --> [*]\\n```\\n\\n---\\n\\n## 4. Transitions\\n\\nThe full UML 2.5 transition label has three optional parts:\\n\\n```\\ntrigger [guard] / action\\n```\\n\\n| Field | Meaning | Example |\\n|---|---|---|\\n| `trigger` | Event that fires the transition | `submit`, `tick`, `timeout(30s)` |\\n| `[guard]` | Boolean expression evaluated at trigger time | `[count > 0]`, `[role == \\\"admin\\\"]` |\\n| `/ action` | Action(s) executed when transition is taken | `/ log(); increment()` |\\n\\nAll three are optional — an unlabeled transition is anonymous (completion transition).\\n\\n```\\nA --> B %% anonymous completion\\nA --> B : tick %% trigger only\\nA --> B : [count > 0] %% guard only\\nA --> B : / clearErrors() %% action only\\nA --> B : tick [count > 0] / log() %% all three\\nA --> B : tick, tock [enabled] / handle() %% multi-trigger\\n```\\n\\nLong labels wrap automatically when they exceed the lane width.\\n\\n---\\n\\n## 5. Composite states\\n\\nA composite state contains a nested sub-statechart. The outer state acts as a container with its own initial / final pseudo-states.\\n\\n```\\nstate Playing {\\n [*] --> Buffering\\n Buffering --> Streaming : buffer_full\\n Streaming --> Buffering : underflow\\n}\\n```\\n\\nBoth Mermaid syntax (`state X { … }`) and the Schematex form (`composite X { … }`) are accepted. Activities can be declared inside the composite block:\\n\\n```\\nstate Playing {\\n entry / startBuffer()\\n exit / stopBuffer()\\n do / decodeFrames()\\n\\n [*] --> Buffering\\n Buffering --> Streaming : buffer_full\\n Streaming --> Buffering : underflow\\n}\\n```\\n\\nThe renderer draws composites as a styled cluster with title bar + activity compartment.\\n\\n```\\nstateDiagram-v2\\n\\n[*] --> Stopped\\n\\nStopped --> Playing : play / loadSource()\\nPlaying --> Paused : pause\\nPaused --> Playing : play\\nPlaying --> Stopped : stop / releaseSource()\\n\\nstate Playing {\\n entry / startBuffer()\\n exit / stopBuffer()\\n do / decodeFrames()\\n\\n [*] --> Buffering\\n Buffering --> Streaming : buffer_full\\n Streaming --> Buffering : underflow\\n}\\n```\\n\\nCross-boundary transitions (`Outside --> Inside`) are routed automatically — the Sugiyama layout pulls the source/target through the composite border.\\n\\n---\\n\\n## 6. Concurrent regions\\n\\nInside a composite, the `--` separator (Mermaid) or `---` (Schematex) splits the body into orthogonal regions that run concurrently.\\n\\n```\\nstate Active {\\n [*] --> r1_idle\\n r1_idle --> Connected : connect\\n --\\n [*] --> r2_idle\\n r2_idle --> Working : start\\n}\\n```\\n\\nUse `fork` and `join` to spawn / synchronize across regions:\\n\\n```\\nstateDiagram-v2 [direction: LR]\\n\\n[*] --> F\\nstate F <<fork>>\\nstate J <<join>>\\nF --> A\\nF --> B\\nA --> J : done_a\\nB --> J : done_b\\nJ --> [*]\\n```\\n\\n---\\n\\n## 7. Notes\\n\\nA short annotation can be attached to either side of any state.\\n\\n```\\nnote right of Checking : Calls /api/verify synchronously.\\nnote left of Idle : Anonymous landing state\\n```\\n\\nMulti-line notes use the Mermaid `end note` block form, or the Schematex `{ … }` form:\\n\\n```\\nnote right of Authenticating\\n Stores the JWT in localStorage\\n on success.\\nend note\\n\\nnote left_of Idle {\\n Anonymous landing state.\\n Returns here on 401.\\n}\\n```\\n\\n---\\n\\n## 8. Self-transitions\\n\\nA transition `A --> A` renders as a curved arc on the right side of the node.\\n\\n```\\nIdle --> Idle : poll / refresh()\\n```\\n\\nThe label is placed beside the arc, outside the bounding box.\\n\\n---\\n\\n## 9. Layout direction\\n\\nSchematex defaults to **`TB`** (top-to-bottom), matching Mermaid. Override on the header:\\n\\n```\\nstateDiagram-v2\\ndirection LR\\n\\n[*] --> Loading\\nLoading --> Ready\\n```\\n\\nOr with the Schematex bracket-attr form:\\n\\n```\\nstate \\\"Order Lifecycle\\\" [direction: TB]\\n\\n[*] --> Pending\\nPending --> Paid\\n```\\n\\n`BT` and `RL` are accepted by the parser but normalized to `TB` and `LR` respectively (the layout engine doesn't yet flip the visual order).\\n\\n---\\n\\n## 10. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `[*] -> [*]` | Treated as both initial alias and final alias on the same line | Always have at least one named state between `[*]` aliases |\\n| `state X <<branch>>` | `branch` is not a stereotype | Use `<<choice>>` (dynamic) or `<<fork>>` / `<<join>>` |\\n| `note right of`<br/>`text` | Multi-line note must end with `end note` | Add `end note` on its own line |\\n| `composite X` (no braces) | Treated as a bare state declaration | Open the block: `composite X {` |\\n| `direction LR` inside composite | Per-region direction not yet supported | Set direction on the header line |\\n\\n---\\n\\n## 11. Grammar (EBNF)\\n\\n```text\\ndocument = header statement*\\nheader = (\\\"stateDiagram-v2\\\" | \\\"stateDiagram\\\" | \\\"state\\\")\\n ( title )? ( \\\"[\\\" attrs \\\"]\\\" )? NEWLINE\\nattrs = attr (\\\",\\\" attr)*\\nattr = \\\"direction:\\\" (\\\"TB\\\" | \\\"LR\\\")\\n\\nstatement = comment\\n | direction-stmt %% direction LR / TB / BT / RL\\n | state-decl\\n | alias-decl %% state \\\"Long\\\" as ID\\n | stereotype-decl %% state ID <<choice|fork|join|end>>\\n | pseudo-decl %% initial / final / choice / ... ID\\n | composite-block %% (state | composite) ID { ... }\\n | label-stmt %% ID : description\\n | transition\\n | note-stmt\\n | region-sep %% -- or ---\\n\\ntransition = (ID | \\\"[*]\\\") \\\"-->\\\" (ID | \\\"[*]\\\") ( \\\":\\\" trans-label )? NEWLINE\\ntrans-label = trigger? ( \\\"[\\\" guard \\\"]\\\" )? ( \\\"/\\\" action )?\\n\\nnote-stmt = \\\"note\\\" side ID \\\":\\\" inline-text NEWLINE\\n | \\\"note\\\" side ID NEWLINE text-line+ (\\\"end note\\\" | \\\"}\\\") NEWLINE\\nside = \\\"left of\\\" | \\\"right of\\\" | \\\"left_of\\\" | \\\"right_of\\\"\\n\\ncomment = \\\"%%\\\" any | \\\"#\\\" any | \\\"//\\\" any\\n\\nID = [A-Za-z_] [A-Za-z0-9_]*\\n```\\n\\nAuthoritative source: `src/diagrams/state/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"pid\": {\n \"title\": \"P&ID (Piping & Instrumentation Diagram)\",\n \"content\": \"## 1. Your first P&ID\\n\\nA minimal P&ID has at least one piece of equipment and one process line.\\n\\n```\\npid\\n\\nequip T-1 : tank_atm\\nequip P-1 : pump_centrifugal\\nequip V-1 : vessel_v\\n\\nline L1 from T-1.bottom to P-1.in [size: \\\"2\\\\\\\\\\\"\\\", type: \\\"process\\\"]\\nline L2 from P-1.out to V-1.in [size: \\\"2\\\\\\\\\\\"\\\", type: \\\"process\\\"]\\n```\\n\\nThree rules cover 80% of usage:\\n\\n1. Start the document with `pid` (optional title and `[direction: LR]` attrs).\\n2. Declare each piece of equipment: `equip : <type> [tag: \\\"label\\\"]`.\\n3. Connect them with `line from <equip>.<port> to <equip>.<port> [type: \\\"process\\\", size: \\\"4\\\\\\\"\\\"]`.\\n\\nInstrumentation is added separately with `inst : <category>` plus indented `measures` / `controls` clauses.\\n\\n> Comments use `#` at the start of a line.\\n\\n---\\n\\n## 2. Equipment\\n\\nThe `equip` statement declares process equipment. The catalog follows ISO 10628 / ISA-5.1 conventions.\\n\\n```\\nequip T-101 : tank_atm [tag: \\\"Feed Tank\\\"]\\nequip P-101 : pump_centrifugal\\nequip E-201 : hx_shell_tube [tag: \\\"Overhead Cond\\\"]\\nequip T-201 : column_tray [tag: \\\"Stripper\\\"]\\n```\\n\\n### 2.1 Equipment catalog\\n\\n| Type | Symbol | Purpose |\\n|---|---|---|\\n| `tank_atm` | Cylinder + dome top | Atmospheric storage tank |\\n| `tank_cone_roof` | Cylinder + cone roof | Cone-roof storage tank |\\n| `vessel_v` | Vertical capsule | Vertical pressure vessel |\\n| `vessel_h` | Horizontal capsule | Horizontal pressure vessel |\\n| `sphere` | Filled circle | LPG / ammonia sphere |\\n| `column_tray` | Tall capsule + horizontal tray lines | Distillation tray column |\\n| `column_packed` | Tall capsule + cross-hatch | Packed absorption column |\\n| `hx_shell_tube` | Horizontal capsule + tube bundle | Shell-and-tube heat exchanger |\\n| `hx_air_cooled` | Rectangle + fan circle | Air-cooled (fin-fan) cooler |\\n| `reboiler` | Capsule + parallel tube lines | Kettle reboiler |\\n| `condenser` | Horizontal capsule + tubes | Overhead condenser |\\n| `pump_centrifugal` | Circle + right-side triangle outlet | Centrifugal pump |\\n| `pump_pd` | Circle + internal gears | Positive-displacement pump |\\n| `compressor` | Trapezoid (narrow on right) | Centrifugal compressor |\\n| `blower` | Circle + 3-blade fan | Blower / fan |\\n| `reactor_cstr` | Vertical capsule + agitator | Stirred tank reactor (CSTR) |\\n| `reactor_pfr` | Horizontal capsule + packed bed dots | Plug-flow / fixed-bed reactor |\\n| `filter` | Rectangle + diagonal hatch | Filter |\\n| `cyclone` | Cylinder + cone bottom | Cyclone separator |\\n| `flare` | Tall stack + flame | Flare stack |\\n| `cooling_tower` | Hourglass | Induced-draft cooling tower |\\n\\n### 2.2 Valve catalog\\n\\nValves are equipment that sit on the piping line. Render in `bowtie` style with type-specific actuator decoration.\\n\\n| Type | Decoration | Purpose |\\n|---|---|---|\\n| `valve_gate` | Plain bowtie | Manual on/off (full-port) |\\n| `valve_ball` | Bowtie + filled center circle | Manual on/off (quarter-turn) |\\n| `valve_globe` | Bowtie + small top circle | Manual flow control |\\n| `valve_butterfly` | Bowtie + center vertical line | Quarter-turn throttle |\\n| `valve_check` | Bowtie + arc | Non-return check valve |\\n| `valve_control` | Bowtie + diaphragm actuator | Pneumatic control valve (paired with FIC) |\\n| `valve_psv` | Bowtie + 45° outlet + spring stack | Pressure safety relief valve |\\n\\n```\\nequip V-101 : valve_control [tag: \\\"V-101 (FC)\\\"]\\nequip V-303 : valve_psv [tag: \\\"V-303 · 150 psig\\\"]\\n```\\n\\n---\\n\\n## 3. Piping & signal lines\\n\\nThe `line` statement connects two anchor points (equipment ports or instrument tags).\\n\\n```\\nline L1 from T-101.bottom to P-101.in [size: \\\"4\\\\\\\"\\\", service: \\\"water\\\", type: \\\"process\\\"]\\nline s1 from FT-101 to FIC-101 [type: \\\"electric\\\"]\\nline s2 from FIC-101 to V-101 [type: \\\"pneumatic\\\"]\\n```\\n\\n### 3.1 Anchor syntax\\n\\nEach end of a line is either:\\n- `<equip-id>.<port>` — port name from §2.2 (`in`, `out`, `top`, `bottom`, `feed`, `shell_in`, `tube_out`, `reflux`, etc.)\\n- `<equip-id>` — port omitted; defaults to `in` (target) / `out` (source) per equipment family\\n- `<inst-tag>` — instrument bubble center (signal lines)\\n\\n### 3.2 Line types (ISA-5.1 §5)\\n\\n| `type:` | Stroke | Use |\\n|---|---|---|\\n| `process` | Solid, thick | Major process line (default) |\\n| `process_minor` | Solid, thin | Auxiliary / utility |\\n| `pneumatic` | Solid + diagonal tick marks | Air-actuator signal |\\n| `electric` | Long-dash | Electric / 4–20 mA signal |\\n| `hydraulic` | Long-dash + pause | Hydraulic actuator |\\n| `capillary` | Dotted (round caps) | Filled-system temperature |\\n| `software` | Short-dash, light | DCS / PLC internal data link |\\n| `mechanical` | Mixed dash | Mechanical linkage |\\n\\n### 3.3 Line tags\\n\\nThe standard PIP PIC001 tag format is `<size>\\\"-<service>-<sequence>-<spec>`. Pass it via the `tag:` attribute and the renderer places a small white tag rectangle at the line midpoint.\\n\\n```\\nline L1 from T-101.bottom to P-101.in [size: \\\"4\\\\\\\"\\\", service: \\\"PG\\\", tag: \\\"4\\\\\\\"-PG-101-A1B\\\"]\\n```\\n\\n---\\n\\n## 4. Instrumentation (ISA-5.1 §4)\\n\\nThe `inst` statement declares an instrument bubble. The **tag** uses the ISA letter-code convention: first letter is the *measured variable*, subsequent letters are *modifiers* and *function*.\\n\\n```\\ninst FT-101 : field_discrete %% Flow Transmitter, loop 101\\ninst FIC-101 : cr_shared %% Flow Indicating Controller (DCS)\\ninst PSHH-301: cr_plc %% Pressure Switch High-High (PLC)\\ninst LIC-201 : cr_shared\\n measures D-201\\n controls V-202\\n```\\n\\n### 4.1 Letter codes (first letter)\\n\\nMost-used: `F` flow · `L` level · `P` pressure · `T` temperature · `A` analysis · `S` speed · `H` hand · `Y` event/state. Full list in ISA-5.1 Table 1.\\n\\n### 4.2 Function modifiers\\n\\n`I` indicator · `R` recorder · `C` controller · `T` transmitter · `E` element · `V` valve · `S` switch · `A` alarm · `H`/`L` high/low. Combine into the multi-letter tag: `FIC` = Flow Indicating Controller; `PSHH` = Pressure Switch High-High.\\n\\n### 4.3 Bubble categories\\n\\nISA-5.1 distinguishes **location** (where the instrument lives) and **type** (analog vs. shared vs. computer vs. PLC). Schematex implements the four most common combinations:\\n\\n| Category | Bubble shape | Use |\\n|---|---|---|\\n| `field_discrete` | Plain circle | Field-mounted analog instrument (FT, PT) |\\n| `cr_shared` | Circle + horizontal line + inscribed hexagon | DCS-controlled HMI display |\\n| `cr_computer` | Circle + horizontal line + inscribed diamond | Computer function (FY, calculation) |\\n| `cr_plc` | Circle + horizontal line + inscribed square | PLC-driven logic |\\n\\n`field_*` variants omit the horizontal centerline; `local_*` variants use a dashed centerline; `cr_*` variants use a solid centerline indicating \\\"main control panel — front.\\\"\\n\\n### 4.4 measures / controls\\n\\nIndented under an `inst` declaration:\\n\\n| Clause | Effect |\\n|---|---|\\n| `measures <equip-id>` | Auto-routed dashed-electric signal line from the equipment to the bubble |\\n| `controls <equip-id>` | Auto-routed pneumatic signal line from the bubble to the equipment (typically a `valve_control`) |\\n\\n```\\ninst FT-101 : field_discrete\\n measures P-101\\ninst FIC-101 : cr_shared\\n controls V-101\\n```\\n\\nThese auto-signals are independent of the explicit `line` statements — they get rendered with the appropriate signal-line style based on the relation type.\\n\\n---\\n\\n## 5. Layout direction\\n\\nThe default direction is **`LR`** (left-to-right) — process feed enters on the left, product exits on the right. Override on the header:\\n\\n```\\npid \\\"Distillation Tower\\\" [direction: TB]\\n\\nequip T-201 : column_tray\\n…\\n```\\n\\nThe MVP layout places equipment in declaration order along the primary direction with Manhattan signal-line routing. **Multi-row / parallel-flow layouts and tee junctions are roadmap items** — see §9.\\n\\n---\\n\\n## 6. Worked example: Distillation column\\n\\nA real overhead-condenser loop with reboiler, reflux drum, and instrumentation:\\n\\n```\\npid \\\"Distillation T-201\\\"\\n\\nequip T-201 : column_tray [tag: \\\"T-201\\\"]\\nequip E-201 : condenser [tag: \\\"Overhead Cond\\\"]\\nequip D-201 : vessel_h [tag: \\\"Reflux Drum\\\"]\\nequip P-201 : pump_centrifugal [tag: \\\"Reflux Pump\\\"]\\nequip E-202 : reboiler [tag: \\\"Reboiler\\\"]\\n\\nline L1 from T-201.top to E-201.shell_in [size: \\\"8\\\\\\\\\\\"\\\", service: \\\"vapor\\\", type: \\\"process\\\"]\\nline L2 from E-201.shell_out to D-201.in [size: \\\"8\\\\\\\\\\\"\\\", type: \\\"process\\\"]\\nline L3 from D-201.bottom to P-201.in [size: \\\"3\\\\\\\\\\\"\\\", type: \\\"process\\\"]\\nline L4 from P-201.out to T-201.reflux [size: \\\"3\\\\\\\\\\\"\\\", type: \\\"process\\\"]\\nline L5 from T-201.bottom to E-202.in [size: \\\"6\\\\\\\\\\\"\\\", type: \\\"process\\\"]\\n\\ninst PT-201 : field_discrete\\n measures T-201\\ninst LIC-201 : cr_shared\\n measures D-201\\ninst TIC-201 : cr_shared\\n measures T-201\\n```\\n\\n---\\n\\n## 7. Grammar (EBNF)\\n\\n```text\\ndocument = header statement*\\nheader = \\\"pid\\\" ( title )? ( \\\"[\\\" attrs \\\"]\\\" )? NEWLINE\\nattrs = attr (\\\",\\\" attr)*\\nattr = \\\"direction:\\\" (\\\"LR\\\" | \\\"TB\\\")\\n | \\\"units:\\\" (\\\"imperial\\\" | \\\"metric\\\")\\n\\nstatement = comment\\n | equipment-decl\\n | line-decl\\n | instrument-decl\\n\\nequipment-decl = \\\"equip\\\" ID \\\":\\\" equip-type ( \\\"[\\\" attr-list \\\"]\\\" )? NEWLINE\\nequip-type = \\\"tank_atm\\\" | \\\"tank_cone_roof\\\"\\n | \\\"vessel_v\\\" | \\\"vessel_h\\\" | \\\"sphere\\\"\\n | \\\"column_tray\\\" | \\\"column_packed\\\"\\n | \\\"hx_shell_tube\\\" | \\\"hx_air_cooled\\\" | \\\"reboiler\\\" | \\\"condenser\\\"\\n | \\\"pump_centrifugal\\\" | \\\"pump_pd\\\"\\n | \\\"compressor\\\" | \\\"blower\\\"\\n | \\\"reactor_cstr\\\" | \\\"reactor_pfr\\\"\\n | \\\"filter\\\" | \\\"cyclone\\\" | \\\"flare\\\" | \\\"cooling_tower\\\"\\n | \\\"valve_gate\\\" | \\\"valve_ball\\\" | \\\"valve_globe\\\" | \\\"valve_butterfly\\\"\\n | \\\"valve_check\\\" | \\\"valve_control\\\" | \\\"valve_psv\\\"\\n\\nline-decl = \\\"line\\\" ID \\\"from\\\" anchor \\\"to\\\" anchor ( \\\"[\\\" attr-list \\\"]\\\" )? NEWLINE\\nanchor = ID ( \\\".\\\" port )?\\nport = \\\"in\\\" | \\\"out\\\" | \\\"top\\\" | \\\"bottom\\\" | \\\"left\\\" | \\\"right\\\"\\n | \\\"feed\\\" | \\\"reflux\\\" | \\\"shell_in\\\" | \\\"shell_out\\\"\\n | \\\"tube_in\\\" | \\\"tube_out\\\" | \\\"vapor_out\\\" | \\\"liquid_out\\\"\\n | \\\"bottom_return\\\"\\n\\ninstrument-decl = \\\"inst\\\" tag \\\":\\\" inst-category ( \\\"[\\\" attr-list \\\"]\\\" )? NEWLINE\\n ( indented \\\"measures\\\" anchor NEWLINE )*\\n ( indented \\\"controls\\\" ID NEWLINE )*\\ntag = letter-code \\\"-\\\" loop-num %% e.g., \\\"FIC-101\\\"\\ninst-category = \\\"field_discrete\\\" | \\\"field_shared\\\" | \\\"field_computer\\\" | \\\"field_plc\\\"\\n | \\\"cr_discrete\\\" | \\\"cr_shared\\\" | \\\"cr_computer\\\" | \\\"cr_plc\\\"\\n | \\\"local_discrete\\\" | \\\"local_shared\\\"\\n\\nattr-list = attr (\\\",\\\" attr)*\\nattr = key \\\":\\\" value\\nkey = \\\"tag\\\" | \\\"size\\\" | \\\"service\\\" | \\\"type\\\" | \\\"set_pressure\\\"\\n | \\\"actuator\\\" | \\\"fail\\\" | \\\"trays\\\" | …\\nvalue = quoted-string | bare-word\\n\\nID = [A-Za-z] [A-Za-z0-9_-]*\\n```\\n\\nAuthoritative source: `src/diagrams/pid/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n }\n};\n","/**\n * Example library — runtime lookup over the bundled MDX examples.\n */\nimport { EXAMPLES, type GeneratedExample } from \"./_generated\";\n\nexport type Example = GeneratedExample;\n\nexport interface GetExamplesOptions {\n /** Maximum number of examples to return. Default 5. */\n limit?: number;\n /** Prefer examples marked `featured: true` when set. */\n preferFeatured?: boolean;\n /** Maximum complexity (1–5). */\n maxComplexity?: number;\n}\n\n/**\n * Normalise a diagram-registry type to the diagram key used in example\n * frontmatter. Most are identical; a few legacy keys differ.\n */\nfunction normaliseDiagramKey(type: string): string[] {\n // The frontmatter uses short keys like \"block\" while the plugin type is\n // \"blockdiagram\". Return all aliases to match on.\n switch (type) {\n case \"blockdiagram\":\n return [\"block\", \"blockdiagram\"];\n default:\n return [type];\n }\n}\n\nexport function getExamplesForType(\n type: string,\n opts: GetExamplesOptions = {}\n): Example[] {\n const keys = normaliseDiagramKey(type);\n const all = EXAMPLES.filter((e) => keys.includes(e.diagram));\n let filtered = all;\n const maxComplexity = opts.maxComplexity;\n if (typeof maxComplexity === \"number\") {\n filtered = filtered.filter((e) => e.complexity <= maxComplexity);\n }\n // Featured first when requested, then by complexity ascending.\n const sorted = [...filtered].sort((a, b) => {\n if (opts.preferFeatured) {\n if (a.featured !== b.featured) return a.featured ? -1 : 1;\n }\n return a.complexity - b.complexity;\n });\n const limit = opts.limit ?? 5;\n return sorted.slice(0, limit);\n}\n\nexport function listAllExampleSlugs(): string[] {\n return EXAMPLES.map((e) => e.slug);\n}\n","/**\n * Syntax lookup — LLM-facing per-diagram grammar reference.\n *\n * v1: returns the stripped-MDX content from `website/content/docs/*.mdx`\n * (JSX components replaced with fenced DSL code blocks).\n *\n * Later: may be replaced with curated compact summaries per diagram if\n * the stripped docs prove too long for good LLM performance.\n */\nimport { SYNTAX, type GeneratedSyntax } from \"./_generated\";\n\nexport type SyntaxDoc = GeneratedSyntax & { key: string };\n\nexport function getSyntaxForType(syntaxKey: string): SyntaxDoc | undefined {\n const s = SYNTAX[syntaxKey];\n if (!s) return undefined;\n return { key: syntaxKey, ...s };\n}\n\nexport function listSyntaxKeys(): string[] {\n return Object.keys(SYNTAX);\n}\n","/**\n * AI-facing tool functions — the five tools an LLM uses to work with Schematex.\n *\n * Pure TypeScript, zero framework deps. Both the Vercel AI SDK adapter\n * (ai-sdk.ts) and the MCP server wrap these functions.\n */\nimport { parse, render, type SchematexConfig } from \"../core/api\";\nimport {\n DIAGRAM_REGISTRY,\n getDiagramMeta,\n type DiagramMeta,\n} from \"./registry\";\nimport { extractError, type SchematexValidationError } from \"./errors\";\nimport { getExamplesForType, type Example, type GetExamplesOptions } from \"./examples\";\nimport { getSyntaxForType, type SyntaxDoc } from \"./syntax\";\n\n// ─── listDiagrams ───────────────────────────────────────────────\n\nexport interface DiagramListItem {\n type: string;\n name: string;\n tagline: string;\n useWhen: string;\n cluster: DiagramMeta[\"cluster\"];\n standard: string;\n}\n\nexport function listDiagrams(): DiagramListItem[] {\n return DIAGRAM_REGISTRY.map((d) => ({\n type: d.type,\n name: d.name,\n tagline: d.tagline,\n useWhen: d.useWhen,\n cluster: d.cluster,\n standard: d.standard,\n }));\n}\n\n// ─── getSyntax ──────────────────────────────────────────────────\n\nexport interface GetSyntaxResult {\n type: string;\n name: string;\n standard: string;\n syntax: SyntaxDoc;\n}\n\nexport function getSyntax(type: string): GetSyntaxResult {\n const meta = getDiagramMeta(type);\n if (!meta) {\n throw new Error(\n `Unknown diagram type '${type}'. Call listDiagrams() for valid types.`\n );\n }\n const syntax = getSyntaxForType(meta.syntaxKey);\n if (!syntax) {\n throw new Error(`No syntax doc available for '${type}' (key: ${meta.syntaxKey}).`);\n }\n return {\n type: meta.type,\n name: meta.name,\n standard: meta.standard,\n syntax,\n };\n}\n\n// ─── getExamples ────────────────────────────────────────────────\n\nexport interface GetExamplesResult {\n type: string;\n count: number;\n examples: Example[];\n}\n\nexport function getExamples(\n type: string,\n opts: GetExamplesOptions = {}\n): GetExamplesResult {\n const meta = getDiagramMeta(type);\n if (!meta) {\n throw new Error(\n `Unknown diagram type '${type}'. Call listDiagrams() for valid types.`\n );\n }\n const examples = getExamplesForType(meta.type, opts);\n return { type: meta.type, count: examples.length, examples };\n}\n\n// ─── validateDsl ────────────────────────────────────────────────\n\nexport type ValidateDslResult =\n | { ok: true; type: string | null }\n | { ok: false; type: string | null; errors: SchematexValidationError[] };\n\nexport function validateDsl(type: string | undefined, dsl: string): ValidateDslResult {\n const config: SchematexConfig | undefined = type\n ? { type: type as SchematexConfig[\"type\"] }\n : undefined;\n try {\n parse(dsl, config);\n return { ok: true, type: type ?? resolveTypeFromText(dsl) };\n } catch (err) {\n return {\n ok: false,\n type: type ?? resolveTypeFromText(dsl),\n errors: [extractError(err)],\n };\n }\n}\n\n// ─── renderDsl ──────────────────────────────────────────────────\n\nexport type RenderDslResult =\n | { ok: true; type: string | null; svg: string }\n | { ok: false; type: string | null; errors: SchematexValidationError[] };\n\nexport function renderDsl(\n type: string | undefined,\n dsl: string,\n options: Omit<SchematexConfig, \"type\"> = {}\n): RenderDslResult {\n const config: SchematexConfig = {\n ...options,\n ...(type ? { type: type as SchematexConfig[\"type\"] } : {}),\n };\n try {\n const svg = render(dsl, config);\n return { ok: true, type: type ?? resolveTypeFromText(dsl), svg };\n } catch (err) {\n return {\n ok: false,\n type: type ?? resolveTypeFromText(dsl),\n errors: [extractError(err)],\n };\n }\n}\n\n// ─── helpers ────────────────────────────────────────────────────\n\nfunction resolveTypeFromText(text: string): string | null {\n const first = text.trim().split(/\\s+|\\n/)[0]?.toLowerCase() ?? \"\";\n const meta = DIAGRAM_REGISTRY.find((d) => d.type === first);\n return meta?.type ?? null;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/ai/registry.ts","../src/ai/errors.ts","../src/ai/_generated.ts","../src/ai/examples.ts","../src/ai/syntax.ts","../src/ai/tools.ts"],"names":["parse","render"],"mappings":"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","file":"chunk-J3Y2QDJR.cjs","sourcesContent":["/**\n * Diagram registry — metadata for every diagram type Schematex supports.\n *\n * This is the index an LLM sees when calling `listDiagrams()`. Descriptions\n * are tuned to help the model pick the right type for a user request.\n */\n\nimport type { DiagramType } from \"../core/types\";\n\nexport type DiagramCluster =\n | \"relationships\"\n | \"electrical-industrial\"\n | \"corporate-legal\"\n | \"causality-analysis\"\n | \"generic\"\n | \"strategy\"\n | \"knowledge\"\n | \"behavior-modeling\";\n\nexport interface DiagramMeta {\n /** Canonical type id — matches `DiagramType` and plugin keys. */\n type: DiagramType;\n /** Human-readable name. */\n name: string;\n /** One-sentence tagline. */\n tagline: string;\n /** When should an LLM pick this diagram? Written in \"use X when …\" form. */\n useWhen: string;\n /** Domain cluster for grouping. */\n cluster: DiagramCluster;\n /** Published standard the parser and layout follow. */\n standard: string;\n /** Path to the syntax doc key in the generated content bundle. */\n syntaxKey: string;\n}\n\nexport const DIAGRAM_REGISTRY: readonly DiagramMeta[] = [\n // ── Relationships ────────────────────────────────────────────\n {\n type: \"genogram\",\n name: \"Genogram\",\n tagline: \"Family diagram with emotional, medical, and generational notation.\",\n useWhen:\n \"Use for family therapy, social-work case notes, or medical family history. Handles 3+ generations with deaths, cutoffs, hostility, closeness, and the proband marker.\",\n cluster: \"relationships\",\n standard: \"McGoldrick, Gerson & Petry (2020) + GenoPro emotional taxonomy\",\n syntaxKey: \"genogram\",\n },\n {\n type: \"ecomap\",\n name: \"Ecomap\",\n tagline: \"Radial diagram of a client's connections to external systems.\",\n useWhen:\n \"Use for social work intake to visualise a client's support network — school, work, healthcare, faith, extended family — with strong/weak/stressful tie variants.\",\n cluster: \"relationships\",\n standard: \"Hartman (1978) + NSGC\",\n syntaxKey: \"ecomap\",\n },\n {\n type: \"pedigree\",\n name: \"Pedigree chart\",\n tagline: \"Clinical genetic-counselling pedigree with affected/carrier states.\",\n useWhen:\n \"Use for genetic counselling or medical genetics education — mendelian inheritance, carrier status, consanguinity, deceased generations. Follows NSGC conventions.\",\n cluster: \"relationships\",\n standard: \"NSGC Pedigree Standardization (Bennett 2008)\",\n syntaxKey: \"pedigree\",\n },\n {\n type: \"phylo\",\n name: \"Phylogenetic tree\",\n tagline: \"Rectangular cladogram from a Newick/NHX tree string.\",\n useWhen:\n \"Use for evolutionary biology, taxonomy, or species relationships. Accepts standard Newick input with optional branch lengths and clade highlighting.\",\n cluster: \"relationships\",\n standard: \"Newick + NHX extensions\",\n syntaxKey: \"phylo\",\n },\n {\n type: \"sociogram\",\n name: \"Sociogram\",\n tagline: \"Force-directed social network graph with edge types and weights.\",\n useWhen:\n \"Use for classroom sociometry, team influence mapping, or organisational network analysis. Edges can be directed, weighted, positive, negative, or reciprocal.\",\n cluster: \"relationships\",\n standard: \"Moreno (1934) sociometry\",\n syntaxKey: \"sociogram\",\n },\n // ── Electrical & Industrial ──────────────────────────────────\n {\n type: \"timing\",\n name: \"Timing / waveform diagram\",\n tagline: \"Digital signal timing diagram with clocks, buses, and annotations.\",\n useWhen:\n \"Use for digital-logic or bus-protocol documentation (SPI/I²C/AXI). Supports clock, data, bus, and gap signals with transition annotations.\",\n cluster: \"electrical-industrial\",\n standard: \"WaveDrom-compatible signal description\",\n syntaxKey: \"timing\",\n },\n {\n type: \"logic\",\n name: \"Logic gate netlist\",\n tagline: \"IEEE 91 logic-gate diagram from a gate-list DSL.\",\n useWhen:\n \"Use for combinational / sequential logic design — AND/OR/XOR/NAND/NOR/NOT/MUX/latches. Auto-routes via DAG topological sort.\",\n cluster: \"electrical-industrial\",\n standard: \"IEEE Std 91/91a-1991\",\n syntaxKey: \"logic\",\n },\n {\n type: \"circuit\",\n name: \"Circuit schematic\",\n tagline: \"Positional circuit schematic with resistors, sources, transistors.\",\n useWhen:\n \"Use for analogue/mixed-signal schematics — voltage/current sources, passives, diodes, BJT/MOSFET, op-amps. Uses an explicit positional DSL, not auto-layout.\",\n cluster: \"electrical-industrial\",\n standard: \"IEEE Std 315 / ANSI Y32.2\",\n syntaxKey: \"circuit\",\n },\n {\n type: \"blockdiagram\",\n name: \"Control-systems block diagram\",\n tagline: \"Transfer-function block diagram with summing junctions and feedback.\",\n useWhen:\n \"Use for classical control theory — plants, controllers, sensors, summing junctions, feedback loops. Nested feedback supported.\",\n cluster: \"electrical-industrial\",\n standard: \"Ogata / standard controls textbook convention\",\n syntaxKey: \"block\",\n },\n {\n type: \"ladder\",\n name: \"Ladder logic\",\n tagline: \"IEC 61131-3 ladder-logic program with rungs, contacts, coils.\",\n useWhen:\n \"Use for PLC / industrial-automation programs — normally-open/closed contacts, output coils, timers, counters. Renders with fixed power-rail layout.\",\n cluster: \"electrical-industrial\",\n standard: \"IEC 61131-3 Ladder Diagram\",\n syntaxKey: \"ladder\",\n },\n {\n type: \"sld\",\n name: \"Single-line diagram\",\n tagline: \"Electrical power distribution single-line (one-line) diagram.\",\n useWhen:\n \"Use for facility / industrial / utility power systems — utility, generator, transformer, ATS, bus, breaker, load. Top-to-bottom power flow.\",\n cluster: \"electrical-industrial\",\n standard: \"IEEE Std 315 + ANSI device numbering\",\n syntaxKey: \"sld\",\n },\n {\n type: \"pid\",\n name: \"P&ID (Piping & Instrumentation)\",\n tagline: \"ISA-5.1 process equipment, valves, and instrument bubbles.\",\n useWhen:\n \"Use for chemical / petrochemical / pharmaceutical / water-treatment process diagrams — vessels, columns, heat exchangers, pumps, valves, and instrument loops with ISA tag codes (FT/FIC/PT/etc.). Equipment + piping + instrumentation in one diagram.\",\n cluster: \"electrical-industrial\",\n standard: \"ANSI/ISA-5.1-2009 + ISO 10628-1:2014\",\n syntaxKey: \"pid\",\n },\n // ── Corporate / Legal ────────────────────────────────────────\n {\n type: \"entity\",\n name: \"Entity structure\",\n tagline: \"Corporate ownership hierarchy with percentage rollup.\",\n useWhen:\n \"Use for legal entity structures, holdco/opco charts, international tax charts, Series-A cap-table snapshots. Tiered layout with ownership percentages.\",\n cluster: \"corporate-legal\",\n standard: \"Tier-based ownership hierarchy\",\n syntaxKey: \"entity\",\n },\n // ── Causality / Analysis ─────────────────────────────────────\n {\n type: \"fishbone\",\n name: \"Fishbone (Ishikawa)\",\n tagline: \"Ishikawa cause-and-effect diagram with categorised root causes.\",\n useWhen:\n \"Use for root-cause analysis, post-mortems, quality investigations. Categories branch off the spine; each cause is a bone.\",\n cluster: \"causality-analysis\",\n standard: \"Ishikawa (1968) cause-and-effect\",\n syntaxKey: \"fishbone\",\n },\n {\n type: \"venn\",\n name: \"Venn / Euler\",\n tagline: \"Set-theoretic Venn / Euler diagram with 2, 3, or 4 sets.\",\n useWhen:\n \"Use to visualise set overlaps, commonalities, or category intersections. Supports 2/3-set Venn and Euler (non-overlapping) arrangements.\",\n cluster: \"causality-analysis\",\n standard: \"Venn (1880) / Euler diagrams\",\n syntaxKey: \"venn\",\n },\n {\n type: \"decisiontree\",\n name: \"Decision tree\",\n tagline: \"Decision/classification tree with splits, probabilities, leaves.\",\n useWhen:\n \"Use for decision analysis (Howard-Raiffa EV rollback), ML decision trees, or taxonomy classification. Binary or multi-way splits.\",\n cluster: \"causality-analysis\",\n standard: \"Howard-Raiffa / CART-sklearn / taxonomy\",\n syntaxKey: \"decisiontree\",\n },\n // ── Behavior modeling ────────────────────────────────────────\n {\n type: \"state\",\n name: \"State diagram\",\n tagline: \"UML 2.5 / Harel statechart with composite states and pseudo-states.\",\n useWhen:\n \"Use for modeling reactive system behavior — finite state machines, lifecycle states, controller modes, UI workflows. Supports simple states, composite (nested) states, fork/join, choice, history, and full Mermaid `stateDiagram-v2` syntax.\",\n cluster: \"behavior-modeling\",\n standard: \"OMG UML 2.5.1 §14 + Harel (1987) statechart\",\n syntaxKey: \"state\",\n },\n // ── Generic process / flow ───────────────────────────────────\n {\n type: \"flowchart\",\n name: \"Flowchart\",\n tagline: \"Generic flowchart with start/end/decision/process nodes.\",\n useWhen:\n \"Use for process flows, decision flows, or algorithms when no more specific diagram fits. Sugiyama layered layout with orthogonal routing.\",\n cluster: \"generic\",\n standard: \"Sugiyama layered DAG + orthogonal routing\",\n syntaxKey: \"flowchart\",\n },\n // ── Strategy / analysis ──────────────────────────────────────\n {\n type: \"matrix\",\n name: \"Matrix / quadrant\",\n tagline: \"2×2 / 3×3 / N×M matrix diagrams (Eisenhower, BCG, heatmap).\",\n useWhen:\n \"Use for prioritisation (Eisenhower urgent/important), portfolio (BCG growth/share), or any 2-axis categorisation.\",\n cluster: \"strategy\",\n standard: \"2×2 / N×M quadrant convention\",\n syntaxKey: \"matrix\",\n },\n {\n type: \"orgchart\",\n name: \"Organisation chart\",\n tagline: \"Corporate or team reporting-line hierarchy.\",\n useWhen:\n \"Use for reporting lines, team structure, or organisational design. Tidy-tree layout (not to be confused with legal `entity` ownership).\",\n cluster: \"corporate-legal\",\n standard: \"Reingold-Tilford tidy tree\",\n syntaxKey: \"orgchart\",\n },\n // ── Knowledge / brainstorming ────────────────────────────────\n {\n type: \"mindmap\",\n name: \"Mindmap\",\n tagline: \"Radial or markmap-style mindmap from markdown headings.\",\n useWhen:\n \"Use for brainstorming, note structures, concept maps, or outline visualisation. Accepts markdown-headings input.\",\n cluster: \"knowledge\",\n standard: \"Buzan radial + markmap-compat tree\",\n syntaxKey: \"mindmap\",\n },\n {\n type: \"timeline\",\n name: \"Timeline\",\n tagline: \"Horizontal or vertical timeline with events, eras, milestones.\",\n useWhen:\n \"Use for historical sequences, project milestones, product roadmaps. Horizontal or vertical orientation.\",\n cluster: \"generic\",\n standard: \"Timeline convention with era bands\",\n syntaxKey: \"timeline\",\n },\n] as const;\n\nexport function getDiagramMeta(type: string): DiagramMeta | undefined {\n return DIAGRAM_REGISTRY.find((d) => d.type === type);\n}\n\nexport function getAllDiagramTypes(): DiagramType[] {\n return DIAGRAM_REGISTRY.map((d) => d.type);\n}\n","/**\n * Structured error type returned by the AI tool layer.\n *\n * The underlying per-diagram parsers each throw their own error class\n * (genogram.ParseError, SLDParseError, PedigreeParseError, ...). Some\n * carry line/column, some don't. {@link extractError} normalises any\n * thrown value into this shape via structural extraction — no parser\n * refactor required.\n */\nexport interface SchematexValidationError {\n /** 1-based line number where the error occurred, if the parser reported it. */\n line?: number;\n /** 1-based column, if reported. */\n column?: number;\n /** Source snippet from the offending line, if the parser captured it. */\n source?: string;\n /** Human-readable error message. */\n message: string;\n /** Optional remediation hint. */\n hint?: string;\n}\n\n/**\n * Extract a {@link SchematexValidationError} from any thrown value.\n *\n * Works across all per-diagram parser error classes because it reads\n * `.line`, `.column`, `.source` structurally when present. Unknown\n * throws (non-Error values) are coerced to a message-only error.\n */\nexport function extractError(err: unknown): SchematexValidationError {\n if (err instanceof Error) {\n const anyErr = err as Error & {\n line?: number;\n column?: number;\n source?: string;\n hint?: string;\n };\n return {\n line: typeof anyErr.line === \"number\" ? anyErr.line : undefined,\n column: typeof anyErr.column === \"number\" ? anyErr.column : undefined,\n source: typeof anyErr.source === \"string\" ? anyErr.source : undefined,\n message: err.message,\n hint: typeof anyErr.hint === \"string\" ? anyErr.hint : undefined,\n };\n }\n return { message: String(err) };\n}\n","/**\n * AUTO-GENERATED by scripts/build-ai-content.mjs — do not edit by hand.\n * Regenerate with: npm run build:ai\n *\n * Compiled content bundle for the AI tool layer. Keeps MDX content\n * available to the published npm package without runtime fs access.\n */\n\nexport interface GeneratedExample {\n slug: string;\n diagram: string;\n title: string;\n description: string;\n standard: string;\n tags: readonly string[];\n complexity: number;\n featured: boolean;\n dsl: string;\n notes: string;\n}\n\nexport interface GeneratedSyntax {\n title: string;\n content: string;\n}\n\nexport const EXAMPLES: readonly GeneratedExample[] = [\n {\n \"slug\": \"block-pid-loop\",\n \"diagram\": \"block\",\n \"title\": \"PID control loop\",\n \"description\": \"Closed-loop PID block diagram with summing junction, controller, and plant — rendered from a signal-flow description without manual layout.\",\n \"standard\": \"Ogata control systems\",\n \"tags\": [\n \"PID\",\n \"closed-loop\",\n \"feedback\",\n \"summing-junction\",\n \"signal-flow\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"blockdiagram \\\"PID control loop\\\"\\nC = block(\\\"PID C(s)\\\") [role: controller]\\nG = block(\\\"Plant G(s)\\\") [role: plant]\\nerr = sum(+r, -y)\\nr = signal(\\\"r (setpoint)\\\")\\ny = signal(\\\"y (output)\\\")\\nin -> r\\nr -> err\\nerr -> C\\nC -> G\\nG -> y\\nG -> err\",\n \"notes\": \"## Scenario\\n\\nThe standard closed-loop PID block diagram appears in every control systems textbook (Ogata, Franklin, Åström) and every control system design spec sheet. Schematex renders it from a signal-flow description — not a generic flowchart — using proper summing junction symbols and automatic feedback routing.\\n\\n## Annotation key\\n\\n- `block(\\\"label\\\") [role: ...]` — transfer function block; `role: controller` and `role: plant` affect visual styling\\n- `sum(+r, -y)` — summing junction: adds the `+r` (reference) signal and subtracts the `-y` (output feedback)\\n- `signal(\\\"label\\\")` — named signal node\\n- `G -> err` — the feedback path: plant output `y` routes back to the summing junction\\n\\n## How to read\\n\\nThe setpoint `r` enters the summing junction `err`, which subtracts the plant output `y` to compute the error signal. The PID controller `C(s)` processes the error and drives the plant `G(s)`. The plant output `y` is both the system output and the feedback signal. The loop is closed when `G -> err` feeds `y` back to the summing junction.\"\n },\n {\n \"slug\": \"circuit-ce-amplifier\",\n \"diagram\": \"circuit\",\n \"title\": \"NPN common-emitter amplifier\",\n \"description\": \"NPN common-emitter amplifier rendered from a five-line SPICE netlist with automatic component placement per IEEE 315.\",\n \"standard\": \"IEEE 315\",\n \"tags\": [\n \"SPICE\",\n \"netlist\",\n \"BJT\",\n \"amplifier\",\n \"auto-layout\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"circuit \\\"CE Amp (netlist)\\\" netlist\\nV1 vcc 0 9V\\nRc vcc c 2.2k\\nRb vcc b 100k\\nQ1 c b e npn\\nRe e 0 1k\",\n \"notes\": \"## Scenario\\n\\nThe NPN common-emitter amplifier is the first transistor circuit every electronics student builds. Schematex renders it from a five-line SPICE netlist — the same format used in LTspice, ngspice, and Cadence — with automatic component placement and rail routing, so the diagram matches the hand-drawn textbook version without any manual layout.\\n\\n## Annotation key\\n\\n- `circuit \\\"...\\\" netlist` — enables netlist parsing mode (SPICE syntax)\\n- `V1 vcc 0 9V` — voltage source named V1, positive terminal at node `vcc`, negative at `0` (ground), value 9V\\n- `Rc vcc c 2.2k` — resistor Rc between nodes `vcc` and `c` with value 2.2 kΩ\\n- `Rb vcc b 100k` — base bias resistor between `vcc` and node `b`\\n- `Q1 c b e npn` — NPN BJT transistor: collector=c, base=b, emitter=e\\n- `Re e 0 1k` — emitter degeneration resistor between node `e` and ground\\n\\n## How to read\\n\\nThe supply rail (Vcc = 9 V) connects to the top of both resistors. Rc is the collector load; the output signal is taken across it. Rb biases the base into the active region. Re provides emitter degeneration for stability. Q1 amplifies a small base current into a large collector-to-emitter current flow. The auto-layout positions Vcc at top, ground at bottom, and Q1 in the center.\"\n },\n {\n \"slug\": \"decisiontree-investment-analysis\",\n \"diagram\": \"decisiontree\",\n \"title\": \"Investment decision analysis\",\n \"description\": \"Decision-analysis tree evaluating build-vs-buy vs hybrid for a platform choice — chance nodes with probabilities, automatic expected-value rollback.\",\n \"standard\": \"Raiffa & Schlaifer (1961)\",\n \"tags\": [\n \"decisiontree\",\n \"decision-analysis\",\n \"expected-value\",\n \"investment\",\n \"buildvbuy\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"decisiontree:decision \\\"Platform Vendor Choice\\\"\\n\\ndecision \\\"Which vendor?\\\"\\n choice \\\"Build in-house\\\"\\n chance \\\"Project outcome\\\"\\n prob 0.6 end \\\"On-time delivery\\\" payoff=900000\\n prob 0.4 end \\\"Over budget / delayed\\\" payoff=150000\\n choice \\\"Managed SaaS vendor\\\"\\n end \\\"Predictable cost\\\" payoff=500000\\n choice \\\"Hybrid approach\\\"\\n chance \\\"Integration complexity\\\"\\n prob 0.5 end \\\"Smooth integration\\\" payoff=700000\\n prob 0.5 end \\\"Integration rework\\\" payoff=300000\",\n \"notes\": \"## Scenario\\n\\nA CTO is deciding how to stand up a new data-platform layer: build internally, buy managed SaaS, or take a hybrid. Each path has outcomes with probabilities and net-value estimates. The decision-analysis tree rolls the expected value back to the decision node so the optimal branch is identified automatically.\\n\\n## Annotation key\\n\\n- `decision \\\"…\\\"` — actor chooses a branch\\n- `chance \\\"…\\\"` — nature chooses; `prob N` on each child (must sum to 1)\\n- `end \\\"…\\\" payoff=N` — terminal payoff\\n- `choice \\\"label\\\"` — label on an outgoing decision branch\\n- EV rollback: chance = probability-weighted sum; decision = max child EV\\n\\n## How to read\\n\\nEvaluate each branch's expected value. Build in-house: 0.6 × 900k + 0.4 × 150k = **600k**. Managed SaaS: flat **500k**. Hybrid: 0.5 × 700k + 0.5 × 300k = **500k**. Under these estimates the optimal branch is *Build in-house* — the parser flags it on render. The chart's real value is in forcing stakeholders to state probabilities explicitly; sensitivity to them is where the interesting argument happens.\"\n },\n {\n \"slug\": \"decisiontree-support-triage\",\n \"diagram\": \"decisiontree\",\n \"title\": \"Support ticket triage\",\n \"description\": \"Taxonomy-mode decision tree for front-line support — routes an incoming ticket through outage, billing, and reproducibility gates to the right team.\",\n \"standard\": \"Clinical/support decision flow\",\n \"tags\": [\n \"decisiontree\",\n \"support\",\n \"triage\",\n \"routing\",\n \"runbook\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"decisiontree \\\"Customer Support Triage\\\"\\ndirection: top-down\\n\\nquestion \\\"Is the service completely down?\\\"\\n yes: question \\\"Outage confirmed on status page?\\\"\\n yes: answer \\\"Follow incident protocol — page on-call\\\"\\n no: answer \\\"Check monitoring — open severity-1 ticket\\\"\\n no: question \\\"Is the issue affecting billing?\\\"\\n yes: answer \\\"Escalate to billing team — SLA breach risk\\\"\\n no: question \\\"Can user reproduce consistently?\\\"\\n yes: answer \\\"Collect HAR trace — file bug report\\\"\\n no: answer \\\"Ask for screenshot — watch for recurrence\\\"\",\n \"notes\": \"## Scenario\\n\\nThe support lead bakes this tree into the agent runbook so new hires can triage in week one without a buddy. Three gates — outage, billing, reproducibility — route 90% of tickets deterministically. The remaining 10% (edge cases, multi-category) get flagged for human escalation instead of guessed at.\\n\\n## Annotation key\\n\\n- `question \\\"text\\\"` — internal decision node\\n- `answer \\\"text\\\"` — terminal outcome\\n- `yes:` / `no:` — branch label\\n- Indentation (2 spaces) — parent-child nesting\\n\\n## How to read\\n\\nStart at the root. At each `question` node the agent answers yes or no and follows the matching branch. Every path terminates in an `answer` — a concrete next action, not another decision. The tree is deliberately shallow (max depth 3) so agents can hold it in their head; deeper branches would push into a runbook rather than a decision tree.\"\n },\n {\n \"slug\": \"ecomap-refugee-resettlement\",\n \"diagram\": \"ecomap\",\n \"title\": \"Refugee family resettlement\",\n \"description\": \"Ecomap of a refugee family's support network — IRC office, school, clinic, and sponsor family — categorized by resource type per Hartman 1978.\",\n \"standard\": \"Hartman 1978\",\n \"tags\": [\n \"resettlement\",\n \"cultural\",\n \"category\",\n \"arrow-direction\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"ecomap \\\"Nguyen Family Resettlement\\\"\\n center: family [label: \\\"Nguyen Family\\\"]\\n resettlement [label: \\\"IRC Office\\\", category: government]\\n school [label: \\\"Lincoln Elementary\\\", category: education]\\n esl [label: \\\"Adult ESL Class\\\", category: education]\\n clinic [label: \\\"Community Clinic\\\", category: health]\\n caseworker [label: \\\"Ms. Patel\\\", category: mental-health]\\n temple [label: \\\"Vietnamese Temple\\\", category: cultural]\\n neighbors [label: \\\"Sponsor Family\\\", category: community]\\n employer [label: \\\"Warehouse Job\\\", category: work]\\n cousins [label: \\\"Cousins (CA)\\\", category: family]\\n family === resettlement [label: \\\"active case\\\"]\\n family === school\\n family --- esl [label: \\\"twice weekly\\\"]\\n clinic --> family [label: \\\"vaccinations\\\"]\\n caseworker <-> family [label: \\\"weekly\\\"]\\n family === temple [label: \\\"anchor\\\"]\\n neighbors === family [label: \\\"housing host\\\"]\\n family --- employer [label: \\\"new, part-time\\\"]\\n cousins == family [label: \\\"phone support\\\"]\",\n \"notes\": \"## Scenario\\n\\nA resettlement caseworker at the IRC maps the Nguyen family's ecological support network during an initial home visit. The diagram surfaces which systems are strong anchors (temple, sponsor family) and which are fragile (new job, ESL class), guiding case prioritization and resource allocation.\\n\\n## Annotation key\\n\\n- `center: family [...]` — designates the central node (the family unit)\\n- `category: government / education / health / cultural / community / work / family` — color-codes each system node by domain\\n- `===` — strong, supportive tie; `==` — moderately strong; `---` — tenuous\\n- `-->` — unidirectional support (clinic delivers vaccinations to family)\\n- `<->` — reciprocal relationship (caseworker and family both invest in the relationship)\\n\\n## How to read\\n\\nThe family sits at the center with nine surrounding systems. The IRC and temple are the two strongest connections (===). The employer and ESL class are tenuous (---), representing early-stage relationships. The cousins in California provide moderate phone support (==) but no in-person resources. The caseworker's bidirectional arrow marks the primary professional support relationship.\"\n },\n {\n \"slug\": \"ecomap-substance-recovery\",\n \"diagram\": \"ecomap\",\n \"title\": \"Substance abuse recovery\",\n \"description\": \"Ecomap charting a client's recovery support network — AA group, family, probation, and therapist — with relationship strength and directional connections.\",\n \"standard\": \"Hartman 1978\",\n \"tags\": [\n \"recovery\",\n \"relationships\",\n \"line-types\",\n \"arrow-direction\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"ecomap \\\"Substance Abuse Recovery\\\"\\n center: client [male, age: 28, label: \\\"James\\\"]\\n aa [label: \\\"AA Group\\\", category: substance, importance: major]\\n sponsor [label: \\\"Bill (Sponsor)\\\", category: substance]\\n employer [label: \\\"Warehouse Job\\\", category: work]\\n mother [label: \\\"Mom\\\", category: family]\\n exwife [label: \\\"Ex-wife\\\", category: family]\\n kids [label: \\\"Children (2)\\\", category: family]\\n dealer [label: \\\"Old Friends\\\", category: substance]\\n probation [label: \\\"P.O. Johnson\\\", category: legal]\\n therapist [label: \\\"CBT Therapist\\\", category: mental-health]\\n client === aa\\n sponsor --> client\\n client --- employer [label: \\\"new, probationary\\\"]\\n client == mother [label: \\\"supportive\\\"]\\n client ~~~ exwife [label: \\\"custody conflict\\\"]\\n client - - kids [label: \\\"supervised visits\\\"]\\n client -/- dealer [label: \\\"trying to cut off\\\"]\\n probation --> client\\n therapist <-> client [label: \\\"weekly\\\"]\",\n \"notes\": \"## Scenario\\n\\nA case manager at a substance abuse recovery center creates this ecomap during an initial biopsychosocial assessment. The diagram externalises the client's full ecological field — every system pressing on his recovery — and immediately surfaces the imbalance: most energy flows inward (legal supervision, sponsor), almost nothing flows outward. The visual becomes the conversation starter for the treatment plan.\\n\\n## Annotation key\\n\\n- `===` — strong, supportive connection\\n- `==` — moderately strong connection\\n- `---` — tenuous or fragile connection\\n- `~~~` — stressful, conflicted connection\\n- `-/-` — severing or cut-off relationship\\n- `- - ` — currently restricted (supervised visits shown with spaces)\\n- `-->` — one-way energy or support flow (giver → receiver)\\n- `<->` — reciprocal, mutual relationship\\n- `category: mental-health / family / legal / work / substance` — colors the node by domain\\n- `importance: major` — increases node prominence in the layout\\n\\n## How to read\\n\\nThe client sits at the center. Arrow directions show who gives energy to whom: the probation officer and sponsor both point *toward* James (external pressure / support), while James gives energy *toward* the AA group. The therapist is the only `<->` reciprocal relationship — the treatment alliance. The `-/-` (severing) line to the dealer marks the highest-risk edge in early recovery. Supervised visits (`- -`) with the children shows the family system is strained but not severed.\"\n },\n {\n \"slug\": \"ecomap-teen-client\",\n \"diagram\": \"ecomap\",\n \"title\": \"Teen client intake\",\n \"description\": \"Quick intake ecomap for a 15-year-old showing family, school, soccer peers, and therapist — drawn in under five minutes during a counseling session.\",\n \"standard\": \"Hartman 1978\",\n \"tags\": [\n \"adolescent\",\n \"intake\",\n \"minimal\",\n \"school\",\n \"peers\"\n ],\n \"complexity\": 1,\n \"featured\": false,\n \"dsl\": \"ecomap \\\"Marcus, age 15\\\"\\n center: client [label: \\\"Marcus\\\"]\\n mom [label: \\\"Mother\\\", category: family]\\n dad [label: \\\"Father (divorced)\\\", category: family]\\n school [label: \\\"East High School\\\", category: education]\\n coach [label: \\\"Soccer Coach\\\", category: community]\\n peers [label: \\\"Soccer team\\\", category: community]\\n therapist [label: \\\"Ms. Chen\\\", category: mental-health]\\n mom === client [label: \\\"primary caregiver\\\"]\\n dad --- client [label: \\\"EOW weekends\\\"]\\n school === client\\n coach --> client [label: \\\"mentor\\\"]\\n peers === client\\n therapist <-> client [label: \\\"weekly\\\"]\",\n \"notes\": \"## Scenario\\n\\nA school-based counselor draws this ecomap during an initial intake session with Marcus, a 15-year-old referred for behavioral issues. The diagram takes under five minutes and immediately shows where Marcus's support is concentrated (peers, soccer team) and where it's fragile (divorced father, every-other-weekend contact).\\n\\n## Annotation key\\n\\n- `center: client` — the identified young person\\n- `===` — strong connection; `---` — tenuous connection\\n- `-->` — mentor/support flows toward client\\n- `<->` — reciprocal therapeutic alliance\\n- `EOW weekends` — edge label capturing the visitation schedule\\n\\n## How to read\\n\\nMarcus's strongest systems are his mother (primary caregiver), school, and soccer peers — all strong ties (===). His father is a tenuous connection (---). The therapist and soccer coach have supportive directional relationships pointing toward Marcus. The soccer team appears to be the central protective factor in this adolescent's life.\"\n },\n {\n \"slug\": \"entity-holding-company\",\n \"diagram\": \"entity\",\n \"title\": \"Multi-jurisdiction holding company\",\n \"description\": \"Multi-jurisdiction holding structure with a Delaware corp, UK subsidiary, and Cayman growth fund — the first document in any M&A due diligence package.\",\n \"standard\": \"Tier ownership\",\n \"tags\": [\n \"holding\",\n \"multi-jurisdiction\",\n \"trust\",\n \"fund\",\n \"Delaware\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"entity-structure \\\"Acme Holdings\\\"\\nentity trust_a \\\"Founder Trust\\\" trust@SD\\nentity acme_inc \\\"Acme Inc.\\\" corp@DE\\nentity acme_uk \\\"Acme UK Ltd.\\\" llc@UK\\nentity acme_fund \\\"Acme Growth Fund LP\\\" fund@KY\\ntrust_a -> acme_inc : 100%\\nacme_inc -> acme_uk : 100%\\nacme_inc -> acme_fund : 60%\",\n \"notes\": \"## Scenario\\n\\nAn M&A attorney or corporate counsel draws this structure for a client engagement letter, board minutes, or due diligence data room. The entity diagram is the first document requested in any M&A transaction, debt financing, or regulatory review — it maps the corporate family tree with jurisdiction and ownership percentages.\\n\\n## Annotation key\\n\\n- `entity id \\\"Display Name\\\" type@jurisdiction` — creates an entity node\\n- `corp` — C-corporation or equivalent; `llc` — limited liability company; `trust` — trust entity; `fund` — investment fund/LP\\n- `@SD / @DE / @UK / @KY` — state or country jurisdiction\\n- `->` — equity ownership relationship\\n- `: 100% / 60%` — percentage label on the ownership line\\n\\n## How to read\\n\\nThe South Dakota founder trust owns 100% of the Delaware C-corp (the operating parent). The Delaware entity owns 100% of the UK subsidiary and 60% of the Cayman Islands fund (implying 40% external LP interest). This is a typical private equity or founder-led holding structure for multi-market operations.\"\n },\n {\n \"slug\": \"entity-international-tax\",\n \"diagram\": \"entity\",\n \"title\": \"International tax holding structure\",\n \"description\": \"Cross-border tax holding structure with Irish IP company, Dutch distribution, and APAC entity — per OECD BEPS transfer-pricing documentation requirements.\",\n \"standard\": \"Tier ownership\",\n \"tags\": [\n \"cross-border\",\n \"IP-license\",\n \"royalty\",\n \"BEPS\",\n \"holding\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"entity-structure \\\"Acme Global Holdings\\\"\\nentity parent \\\"Acme Global, Inc.\\\" corp@US [note: \\\"Ultimate Parent\\\"]\\nentity ie-holdco \\\"Acme Ireland Holdings\\\" corp@IE\\nentity ie-ip \\\"Acme IP Ltd\\\" corp@KY [note: \\\"Holds group IP\\\"]\\nentity nl-bv \\\"Acme EU Distribution\\\" corp@NL\\nentity sg-apac \\\"Acme APAC Trading\\\" corp@SG\\nparent -> ie-holdco : 100%\\nie-holdco -> ie-ip : 100%\\nie-holdco -> nl-bv : 100%\\nie-ip -~-> nl-bv [label: \\\"IP License · royalty\\\"]\\nparent -> sg-apac : 100%\",\n \"notes\": \"## Scenario\\n\\nCorporate counsel or a Big Four tax team draws this structure during cross-border M&A due diligence or transfer-pricing documentation. The OECD BEPS framework requires taxpayers to document the substance and economic rationale of every intercompany flow — this diagram is the first attachment to the TP master file and the tax opinion memo.\\n\\n## Annotation key\\n\\n- `entity name \\\"Display Name\\\" corp@US` — creates an entity node; `corp` is the entity type, `US` is the jurisdiction\\n- `corp / llc / fund / trust` — entity type; determines the symbol rendered\\n- `@US / @IE / @KY / @NL / @SG` — ISO country code for jurisdiction labeling\\n- `[note: \\\"...\\\"]` — adds a subsidiary note below the entity name\\n- `->` — solid directed line; represents an equity ownership relationship\\n- `-~->` — dashed directed line; represents a contractual (non-equity) relationship such as an IP license or intercompany loan\\n- `: 100%` — label on a directed edge; shows the ownership percentage\\n\\n## How to read\\n\\nThe US parent owns 100% of the Irish holdco, which in turn holds both the Cayman IP entity and the Dutch distribution subsidiary. The dashed arrow from the IP entity to the Dutch entity represents the IP license — royalties flow from the Netherlands up to Cayman, shifting taxable income to a low-rate jurisdiction. Singapore APAC is a parallel branch for Asia-Pacific operations, owned directly by the US parent.\"\n },\n {\n \"slug\": \"entity-series-a-cap-table\",\n \"diagram\": \"entity\",\n \"title\": \"Series A cap table\",\n \"description\": \"Post-Series A cap table showing founders, seed fund, lead VC, angel group, and ESOP pool with ownership percentages — for 409A valuations and board consents.\",\n \"standard\": \"Tier ownership\",\n \"tags\": [\n \"cap-table\",\n \"Series-A\",\n \"ESOP\",\n \"VC\",\n \"founders\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"entity-structure \\\"Acme Inc. — post Series A\\\"\\nentity acme \\\"Acme Inc.\\\" corp@DE\\nentity founders \\\"Founders (2)\\\" individual\\nentity seed \\\"Seed Fund I\\\" lp@DE\\nentity lead \\\"Sequoia Series A\\\" lp@DE\\nentity angels \\\"Angel group\\\" individual\\nentity esop \\\"Employee Option Pool\\\" trust@DE\\nfounders -> acme : 45%\\nseed -> acme : 12%\\nlead -> acme : 22%\\nangels -> acme : 6%\\nesop -> acme : 15%\",\n \"notes\": \"## Scenario\\n\\nA startup attorney or CFO documents the post-Series A ownership table for a 409A valuation, board consent, or investor report. The cap table diagram makes the dilution story visual — founders can immediately see their post-money percentage, and the VC can verify their ownership stake before signing the term sheet.\\n\\n## Annotation key\\n\\n- `-> acme : 45%` — ownership arrow with percentage label; all percentages should sum to 100%\\n- `individual` — natural person (founder, angel)\\n- `lp` — institutional investor entity (fund/LP)\\n- `trust` — the ESOP/option pool (typically a Delaware trust or reserved pool)\\n- `corp@DE` — the issuer (Delaware C-corp)\\n\\n## How to read\\n\\nAcme Inc. (Delaware C-corp) sits at the bottom as the issuer. Five shareholder classes flow down with their ownership arrows: founders at 45%, the lead Series A investor at 22%, the employee option pool at 15%, the seed fund at 12%, and angels at 6%. Percentages sum to 100%, representing a clean fully-diluted cap table on the day of Series A close.\"\n },\n {\n \"slug\": \"fishbone-website-traffic\",\n \"diagram\": \"fishbone\",\n \"title\": \"Website traffic drop root-cause analysis\",\n \"description\": \"Ishikawa fishbone for a website traffic drop — six causal categories covering content, technical SEO, backlinks, UX, competition, and algorithm changes.\",\n \"standard\": \"Ishikawa 1968\",\n \"tags\": [\n \"root-cause\",\n \"Ishikawa\",\n \"six-categories\",\n \"growth\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"fishbone \\\"Fishbone diagram — Website traffic drop\\\"\\n\\neffect \\\"Traffic decline\\\"\\n\\ncategory content \\\"Content\\\"\\ncategory tech \\\"Technical\\\"\\ncategory links \\\"Backlinks\\\"\\ncategory ux \\\"UX\\\"\\ncategory competition \\\"Competition\\\"\\ncategory algo \\\"Algorithm\\\"\\n\\ncontent : \\\"Publishing frequency down\\\"\\ncontent : \\\"Content too generic\\\"\\ncontent : \\\"Keyword gaps\\\"\\ncontent : \\\"Low-quality AI content\\\"\\n\\ntech : \\\"Core Web Vitals failing\\\"\\ntech : \\\"Crawl coverage drop\\\"\\ntech : \\\"Crawler blocked by WAF\\\"\\ntech : \\\"Missing structured data\\\"\\n\\nlinks : \\\"High-quality backlinks lost\\\"\\nlinks : \\\"High ratio of low-quality links\\\"\\nlinks : \\\"Referring domain growth stalled\\\"\\nlinks : \\\"Low anchor text diversity\\\"\\n\\nux : \\\"Bounce rate rising\\\"\\nux : \\\"Poor mobile experience\\\"\\nux : \\\"Slow above-fold load\\\"\\nux : \\\"Excessive popup ads\\\"\\n\\ncompetition : \\\"New competitors entering\\\"\\ncompetition : \\\"AI tools replacing search\\\"\\ncompetition : \\\"Weakening brand recall\\\"\\ncompetition : \\\"Competitors publishing faster\\\"\\n\\nalgo : \\\"Core Update penalty\\\"\\nalgo : \\\"Weak E-E-A-T signals\\\"\\nalgo : \\\"AI Overviews / SGE cutoff\\\"\\nalgo : \\\"Search intent drift\\\"\",\n \"notes\": \"## Scenario\\n\\nAn ops lead runs a growth post-mortem after a 30% organic traffic drop. The Ishikawa (fishbone) diagram structures the team's brainstorm into six standard causal categories, preventing the meeting from fixating on the most vocal hypothesis while ignoring systemic causes. The completed diagram becomes the project brief for the remediation sprint.\\n\\n## Annotation key\\n\\n- `effect \\\"...\\\"` — the problem statement, placed at the fish's head (right side)\\n- `category id \\\"Label\\\"` — defines a major causal branch (a \\\"bone\\\"); use a short `id` to assign causes\\n- `id : \\\"cause text\\\"` — assigns a cause string to the named category branch\\n- Each category renders as a diagonal rib pointing toward the effect\\n- Sub-causes (second-order) can be added by nesting if the DSL supports it\\n\\n## How to read\\n\\nThe effect (traffic decline) sits at the right. Six causal ribs branch from the spine: Content, Technical, Backlinks, UX, Competition, and Algorithm. Each rib lists four specific hypotheses. In a workshop, the team votes on each cause, color-codes high-confidence ones, and converts the highest-priority items into action items. The diagram serves as both a brainstorming artifact and a living post-mortem document.\"\n },\n {\n \"slug\": \"flowchart-cicd-pipeline\",\n \"diagram\": \"flowchart\",\n \"title\": \"CI/CD pipeline with gated deploy\",\n \"description\": \"Flowchart of a trunk-based CI/CD pipeline — build, test, security scan, staging gate, and production deploy with automatic rollback on failed smoke tests.\",\n \"standard\": \"ISO 5807:1985\",\n \"tags\": [\n \"cicd\",\n \"devops\",\n \"pipeline\",\n \"deployment\",\n \"rollback\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"flowchart TD\\n commit([Push to main]) --> build[Build artifact]\\n build --> unit{Unit tests pass?}\\n unit -->|No| fail([Fail build])\\n unit -->|Yes| scan[Security scan]\\n scan --> vuln{High-severity CVEs?}\\n vuln -->|Yes| fail\\n vuln -->|No| stage[Deploy to staging]\\n stage --> smoke{Smoke tests green?}\\n smoke -->|No| fail\\n smoke -->|Yes| approve{Manual approval?}\\n approve -->|No| wait([Await approver])\\n approve -->|Yes| prod[Deploy to production]\\n prod --> health{Post-deploy health check?}\\n health -->|Yes| done([Release complete])\\n health -->|No| rollback[Automatic rollback]\\n rollback --> done\",\n \"notes\": \"## Scenario\\n\\nA platform engineer is documenting the team's trunk-based pipeline for a new-hire runbook. The diagram makes the four automated gates (tests → scan → smoke → post-deploy health) and the single human gate (manual approval) obvious at a glance, and shows that every failure path terminates the pipeline rather than silently continuing.\\n\\n## Annotation key\\n\\n- `([…])` — stadium; start and terminal nodes\\n- `{…}` — diamond; automated or manual gate\\n- `[…]` — rectangle; build / deploy / scan step\\n- `-->|Yes/No|` — branch labels on each gate\\n\\n## How to read\\n\\nStart at *Push to main*. Every diamond is a gate — a *No* on any of unit tests, CVE scan, or smoke tests terminates at *Fail build*. Manual approval is the only human gate; it can park the pipeline at *Await approver* without failing. The post-deploy health check guards production: a failure triggers automatic rollback, which still completes at *Release complete* because the rollback itself is a successful outcome.\"\n },\n {\n \"slug\": \"flowchart-order-fulfillment\",\n \"diagram\": \"flowchart\",\n \"title\": \"E-commerce order fulfillment\",\n \"description\": \"Flowchart mapping the full order-to-delivery path with inventory and payment decision gates, exception handling, and a single End terminal.\",\n \"standard\": \"ISO 5807:1985\",\n \"tags\": [\n \"process\",\n \"decision\",\n \"e-commerce\",\n \"operations\",\n \"exception-handling\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"flowchart LR\\n start([New order received])\\n start --> validate{Inventory available?}\\n validate -->|Yes| reserve[Reserve items]\\n validate -->|No| notify[Notify customer]\\n notify --> done([End])\\n reserve --> payment{Payment authorized?}\\n payment -->|Yes| ship[Ship order]\\n payment -->|No| cancel[Cancel & release]\\n ship --> confirm[Send confirmation email]\\n confirm --> done\\n cancel --> done\",\n \"notes\": \"## Scenario\\n\\nA product ops lead circulates this flowchart during an ops-review meeting to align engineering, customer support, and fulfillment on the single source of truth for what happens when a new order comes in. It surfaces the two decision gates (inventory, payment) and the three exception paths (out-of-stock notification, payment failure with released hold, successful ship with confirmation).\\n\\n## Annotation key\\n\\n- `([…])` — stadium / terminal; used for Start and End\\n- `{…}` — diamond; decision node\\n- `[…]` — rectangle; process step\\n- `-->|label|` — edge with a branch label (`Yes` / `No`)\\n\\n## How to read\\n\\nStart at the top-left terminal. Inventory check gates the first branch — a \\\"No\\\" routes straight to the End after notification. A \\\"Yes\\\" reserves stock then hits the payment gate. Payment failure releases the reservation and goes to End; success ships and emails the customer. Every path terminates at the same End node, so nothing dangles.\"\n },\n {\n \"slug\": \"genogram-brca-cancer\",\n \"diagram\": \"genogram\",\n \"title\": \"Hereditary cancer (BRCA1) family\",\n \"description\": \"Three-generation BRCA1 family genogram with hereditary breast/ovarian cancer conditions — captured at intake before formal clinical pedigree analysis.\",\n \"standard\": \"McGoldrick 2020\",\n \"tags\": [\n \"hereditary-cancer\",\n \"four-generation\",\n \"deceased\",\n \"BRCA\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"genogram \\\"BRCA1 Family\\\"\\n I_1 [male, 1930, 1995, deceased]\\n I_2 [female, 1932, 1990, deceased, conditions: ovarian_cancer(full, #B388FF)]\\n I_1 -- I_2\\n II_1 [female, 1955, conditions: breast_cancer(full, #EC407A)]\\n II_2 [male, 1958]\\n II_3 [female, 1960]\\n II_1 -- II_4 [male, 1954]\\n III_1 [female, 1985, index, conditions: breast_cancer(full, #EC407A)]\\n III_2 [male, 1988]\",\n \"notes\": \"## Scenario\\n\\nA family history genogram for hereditary breast/ovarian cancer, documented at the initial genetic counseling intake before formal pedigree analysis. For standardized clinical pedigree notation (NSGC), use the Pedigree diagram type instead.\\n\\n## Annotation key\\n\\n- `conditions: ovarian_cancer(full)` / `conditions: breast_cancer(full)` — medical conditions filling the symbol; color is optional hex\\n- `deceased` with birth and death years — marks individuals with a slash and date range\\n- `index` — marks the proband who triggered the clinical referral\\n\\n## How to read\\n\\nThe maternal grandmother (I_2) had ovarian cancer and is deceased. Her daughter (II_1) developed breast cancer. The proband (III_1, index) is a third-generation female with breast cancer — the inheritance pattern spanning three generations justifies BRCA genetic testing.\"\n },\n {\n \"slug\": \"genogram-foster-care\",\n \"diagram\": \"genogram\",\n \"title\": \"Foster care / child protection\",\n \"description\": \"Foster-care genogram for a real LATAM child-protection case — biological parents (cohabitation ended), abuse, current foster placement (dotted secondary link), unknown-count siblings, and a maternal uncle as known-relative-with-unknown-ancestry.\",\n \"standard\": \"McGoldrick 2020 + Bennett 2022 (adopted-out / dual-parent convention)\",\n \"tags\": [\n \"foster-care\",\n \"dual-parent\",\n \"abuse\",\n \"sibling-of\",\n \"unknown-siblings\",\n \"latam\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"genogram \\\"Familia Isaías\\\"\\n victor [male, label: \\\"Víctor Seguel\\\"]\\n monica [female, label: \\\"Mónica Barrientos\\\"]\\n victor ~/~ monica\\n ?\\n isaias [male, 2020, age: 6, label: \\\"Isaías\\\", index]\\n pablo_sr [male, label: \\\"Don Pablo\\\"]\\n priscila [female, label: \\\"Doña Priscila\\\"]\\n pablo_sr -- priscila\\n pablo_jr [male, label: \\\"Pablo (jr)\\\"]\\n alanis [female, label: \\\"Alanis\\\"]\\n isaias [foster]\\n tio_materno [male, label: \\\"Tío materno\\\", sibling-of: monica]\\n victor -physical-abuse-> isaias\\n monica -physical-abuse-> isaias\\n tio_materno -nevermet- isaias\",\n \"notes\": \"## Scenario\\n\\nA foster-care social worker in Chile is preparing the case file for Isaías, a 6-year-old boy removed from his biological parents (Víctor and Mónica) due to physical abuse from both. He currently lives with foster parents Don Pablo and Doña Priscila, who have two biological children of their own. The case file mentions Isaías has siblings whose names and ages are unknown, and a maternal uncle who is a potential reunification resource but currently has no contact.\\n\\nA judge or psychologist receiving this diagram must, at a glance, correctly conclude:\\n\\n1. Isaías is the **biological son** of Víctor and Mónica — solid parent-child line down from the bio couple.\\n2. He **currently lives** with Don Pablo and Doña Priscila as a foster child — *secondary dotted link* from the foster couple, drawn without pulling Isaías away from his bio-parent position.\\n3. He was **removed due to physical abuse** from both bio parents — directional red zigzag arrows.\\n4. The **maternal uncle** is Mónica's brother (`sibling-of: monica`) with **no current relationship** to Isaías — dashed bracket between Mónica and Tío + `nevermet` line.\\n5. Isaías has **unknown-count siblings** still with the bio parents — single `?` diamond placeholder.\\n6. **Isaías is the index person** — concentric outer border highlight.\\n\\n## Annotation key\\n\\n- `~/~` — cohabitation ended (never-married); standard for LATAM caseloads where bio parents lived together unmarried and the relationship has broken. Distinct from `-x-` divorce (no marriage) and `-/-` separation (still married).\\n- Re-declaring `isaias [foster]` under the foster couple after declaring him under the bio couple → engine treats the second declaration as a **secondary \\\"current caregiver\\\" link** (dotted), preserving all attributes from the first declaration.\\n- `?` on a child line → a single diamond with `?` glyph meaning \\\"≥1 siblings, count and identities unknown\\\" (standard pedigree convention).\\n- `[sibling-of: monica]` → places Tío materno on Mónica's generation with a dashed bracket between them, **without** synthesizing phantom maternal grandparents.\\n- `-physical-abuse->` → directional red arrow; the `>` indicates the perpetrator (left side) and victim (right side).\\n\\n## Why this matters\\n\\nA genogram engine that quietly rendered Isaías as a third biological child of Don Pablo + Doña Priscila — or dropped his sex and label when the `[foster]` redeclaration overwrote the original — would invert the case story. This example exercises every fix from the 2026-04 foster-care brief: dual-parent rendering, same-id merge, sibling-of, cohabiting-ended, and the unknown-siblings placeholder.\"\n },\n {\n \"slug\": \"genogram-medical-history\",\n \"diagram\": \"genogram\",\n \"title\": \"Multi-generation medical history\",\n \"description\": \"Three-generation family medical history genogram with multi-condition color annotations using fill zones — heart disease, diabetes, cancer, hypertension.\",\n \"standard\": \"McGoldrick 2020\",\n \"tags\": [\n \"conditions\",\n \"multicolor\",\n \"three-generation\",\n \"inheritance\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"genogram \\\"Medical History\\\"\\n grandfather [male, 1930, 1990, deceased, conditions: heart-disease(full, #e74c3c) + diabetes(half-left, #ff9800)]\\n grandmother [female, 1935, conditions: cancer(half-right, #9c27b0)]\\n grandfather -- grandmother\\n father [male, 1960, conditions: heart-disease(quad-tl, #e74c3c) + hypertension(quad-tr, #2196f3)]\\n uncle [male, 1963, conditions: diabetes(full, #ff9800)]\\n mother [female, 1962]\\n father -- mother\\n patient [male, 1988, index, conditions: hypertension(half-left, #2196f3)]\\n sister [female, 1991]\",\n \"notes\": \"## Scenario\\n\\nA clinical social worker or genetic counselor captures three generations of family medical history at intake. The `conditions()` annotation lets each person carry multiple diagnoses simultaneously — and the fill geometry (full, half, quadrant) encodes severity or inheritance proportion at a glance, without cluttering the diagram with text labels.\\n\\n## Annotation key\\n\\n- `conditions: X(fill, color)` — paints a shape inside the genogram symbol using the named fill zone and hex color\\n- `full` — entire symbol filled; indicates fully affected\\n- `half-left` / `half-right` — left or right half filled; often used for one of two conditions side-by-side\\n- `quad-tl` / `quad-tr` — top-left or top-right quadrant; allows up to four distinct conditions per person\\n- `+ diabetes(...)` — chain multiple conditions on the same person with `+`\\n- `deceased` — draws a diagonal slash through the symbol\\n- `index` — marks the proband with an arrow\\n\\n## How to read\\n\\nThe grandfather's full red fill (heart disease) and half-orange fill (diabetes) are visually inherited by the father, who carries both — encoded as top-left and top-right quadrant fills. The patient (index) shows only hypertension in the left half, indicating partial inheritance. Tracing any color across generations immediately reveals the inheritance chain.\"\n },\n {\n \"slug\": \"genogram-nuclear-family\",\n \"diagram\": \"genogram\",\n \"title\": \"Nuclear family (minimal template)\",\n \"description\": \"Minimal nuclear family genogram — married couple, one child, marriage date — the clinical intake starting template per McGoldrick 2020 notation.\",\n \"standard\": \"McGoldrick 2020\",\n \"tags\": [\n \"starter\",\n \"minimal\",\n \"marriage-date\",\n \"index\"\n ],\n \"complexity\": 1,\n \"featured\": false,\n \"dsl\": \"genogram \\\"Smith Family\\\"\\n john [male, 1975]\\n mary [female, 1977]\\n john -- mary \\\"m. 2002\\\"\\n alice [female, 2005, index]\",\n \"notes\": \"## Scenario\\n\\nThe simplest genogram that is clinically useful — a married couple with one child. Used as a session intake template and as the starting point when teaching genogram notation to new practitioners.\\n\\n## Annotation key\\n\\n- `--` — standard marriage/union line\\n- `\\\"m. 2002\\\"` — marriage year label\\n- `index` — marks Alice as the identified patient\\n\\n## How to read\\n\\nTwo parents connected by a union line with a marriage date; their child Alice (marked as the index person) hangs below. Extend this template by adding siblings, grandparents, or emotional relationship lines.\"\n },\n {\n \"slug\": \"genogram-potter-family\",\n \"diagram\": \"genogram\",\n \"title\": \"The Potter family\",\n \"description\": \"Three-generation Potter family genogram with emotional relationship lines — cutoff, hostile, and close — illustrating McGoldrick relational notation.\",\n \"standard\": \"McGoldrick 2020\",\n \"tags\": [\n \"emotional-relationships\",\n \"three-generation\",\n \"deceased\",\n \"cutoff\",\n \"hostile\",\n \"close\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"genogram \\\"The Potter Family\\\"\\n fleamont [male, 1909, 1979, deceased]\\n euphemia [female, 1920, 1979, deceased]\\n fleamont -- euphemia\\n james [male, 1960, 1981, deceased]\\n mr_evans [male, 1925, deceased]\\n mrs_evans [female, 1928, deceased]\\n mr_evans -- mrs_evans\\n lily [female, 1960, 1981, deceased]\\n petunia [female, 1958]\\n james -- lily \\\"m. 1978\\\"\\n harry [male, 1980, index]\\n petunia -- vernon [male, 1951]\\n dudley [male, 1980]\\n harry -cutoff- petunia\\n harry -hostile- dudley\\n harry -close- lily\",\n \"notes\": \"## Scenario\\n\\nA teaching example for social work students learning genogram notation. The Potter family is fictional but emotionally rich — death years, a marriage date, cross-family emotional relationships, and three distinct relational patterns (cutoff, hostile, close) all in one diagram.\\n\\n## Annotation key\\n\\n- `[male/female, birth_year, death_year, deceased]` — person with death marker\\n- `\\\"m. 1978\\\"` — marriage date label on the union line\\n- `index` — marks Harry as the identified patient (proband)\\n- `-cutoff-` — estrangement; drawn as two parallel bars across the relationship line\\n- `-hostile-` — conflict; drawn as zigzag line\\n- `-close-` — enmeshment/closeness; drawn as double parallel line\\n\\n## How to read\\n\\nRead each indented block as a family unit. James and Lily (index generation) both died in 1981. Harry's emotional world is defined by three relational lines: cutoff from Aunt Petunia, hostility toward cousin Dudley, and closeness to his deceased mother.\"\n },\n {\n \"slug\": \"ladder-mode-selection\",\n \"diagram\": \"ladder\",\n \"title\": \"System mode selection (Set/Reset)\",\n \"description\": \"IEC 61131-3 ladder logic for HMI-driven Auto/Manual mode selection using Set/Reset (OTL/OTU) coils with system fault interlocks.\",\n \"standard\": \"IEC 61131-3\",\n \"tags\": [\n \"OTL\",\n \"OTU\",\n \"Set-Reset\",\n \"parallel-outputs\",\n \"interlocks\",\n \"Allen-Bradley\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"ladder \\\"System Mode Selection\\\"\\n\\nrung 1 \\\"Set system Auto mode, reset Manual\\\":\\n XIC(AUTO_HMIPB, \\\"BIT 5.10\\\", name=\\\"Auto Mode HMI Pushbutton\\\")\\n XIO(MANL_HMIPB, \\\"BIT 5.11\\\", name=\\\"Manual Mode HMI Pushbutton\\\")\\n XIO(SYS_FAULT, \\\"BIT 3.0\\\", name=\\\"System Fault\\\")\\n parallel:\\n branch:\\n OTL(SYS_AUTO, \\\"BIT 3.1\\\", name=\\\"System Auto Mode\\\")\\n branch:\\n OTU(SYS_MANUAL, \\\"BIT 3.2\\\", name=\\\"System Manual Mode\\\")\\n\\nrung 2 \\\"Set Manual, reset Auto (with Home seal-in)\\\":\\n parallel:\\n branch:\\n XIC(MANL_HMIPB, \\\"BIT 5.11\\\", name=\\\"Manual Mode HMI Pushbutton\\\")\\n branch:\\n XIC(SYS_HOMECMD, \\\"BIT 3.5\\\", name=\\\"System Home Command\\\")\\n XIO(AUTO_HMIPB, \\\"BIT 5.10\\\", name=\\\"Auto Mode HMI Pushbutton\\\")\\n XIO(SYS_FAULT, \\\"BIT 3.0\\\", name=\\\"System Fault\\\")\\n parallel:\\n branch:\\n OTL(SYS_MANUAL, \\\"BIT 3.2\\\", name=\\\"System Manual Mode\\\")\\n branch:\\n OTU(SYS_AUTO, \\\"BIT 3.1\\\", name=\\\"System Auto Mode\\\")\",\n \"notes\": \"## Scenario\\n\\nAn Allen-Bradley PLC program for a machine that requires mutually exclusive Auto and Manual operating modes, with a Home command that can trigger a manual mode entry as a safety fallback. The latched Set/Reset coil pattern is standard for retained-state mode selection that survives a power cycle.\\n\\n## Annotation key\\n\\n- `OTL(tag, addr, name=...)` — Output Latch (Set): energizes and *latches* the bit high; bit stays high even when the rung loses power\\n- `OTU(tag, addr, name=...)` — Output Unlatch (Reset): clears a latched bit back to 0\\n- `parallel: branch:` — output-side parallel branches execute simultaneously when the rung is true\\n- Rung 1 sets Auto and simultaneously resets Manual; Rung 2 does the inverse\\n- The `SYS_FAULT` XIO contact appears in both rungs as a master interlock — no mode change is allowed during a fault\\n\\n## How to read\\n\\nRung 1 fires when the operator presses the Auto HMI button AND Manual is not pressed AND no fault exists. It simultaneously latches `SYS_AUTO` ON and unlatches `SYS_MANUAL`. Rung 2 is the mirror: Manual button OR Home command, guarded by Auto-not-pressed and no-fault, sets Manual and resets Auto. The latched coils mean the last-pressed mode persists through PLC power cycles.\"\n },\n {\n \"slug\": \"ladder-motor-start-stop\",\n \"diagram\": \"ladder\",\n \"title\": \"Motor start/stop seal-in circuit\",\n \"description\": \"Classic three-wire motor start/stop seal-in circuit in IEC 61131-3 ladder logic — the foundational pattern taught in every PLC certification course.\",\n \"standard\": \"IEC 61131-3\",\n \"tags\": [\n \"seal-in\",\n \"motor\",\n \"XIC\",\n \"XIO\",\n \"OTE\",\n \"parallel\"\n ],\n \"complexity\": 1,\n \"featured\": true,\n \"dsl\": \"ladder \\\"Motor Start/Stop\\\"\\nrung 1 \\\"Seal-in circuit\\\":\\n parallel:\\n branch:\\n XIC(START_PB, \\\"IN 1.0\\\", name=\\\"Start Button\\\")\\n branch:\\n XIC(MOTOR_AUX, \\\"BIT 3.0\\\", name=\\\"Aux Contact\\\")\\n XIO(STOP_PB, \\\"IN 1.1\\\", name=\\\"Stop Button\\\")\\n OTE(MOTOR_CMD, \\\"OUT 2.0\\\", name=\\\"Motor Command\\\")\",\n \"notes\": \"## Scenario\\n\\nEvery controls engineer learns the three-wire motor start/stop circuit before writing their first PLC program. It appears verbatim in IEC 61131-3 training materials, Allen-Bradley certification exams, and factory acceptance tests worldwide. The seal-in contact latches the motor ON after the momentary start pushbutton is released — the fundamental pattern for any maintained-output logic.\\n\\n## Annotation key\\n\\n- `XIC(tag, address, name=...)` — Examine If Closed: contact passes power when the referenced bit is `1` (true)\\n- `XIO(tag, address, name=...)` — Examine If Open: contact passes power when the referenced bit is `0` (false); normal for stop buttons wired N.C.\\n- `OTE(tag, address, name=...)` — Output Energize: coil energizes the referenced bit when rung has power\\n- `parallel: branch:` — models a parallel contact branch (logical OR)\\n- The `MOTOR_AUX` contact in the parallel branch is the seal-in: once the motor output energizes, the aux contact closes and holds the rung true even after the START_PB releases\\n\\n## How to read\\n\\nThe rung reads left to right. Power flows if *either* the start button (XIC START_PB) *or* the aux contact (XIC MOTOR_AUX) is closed, *and* the stop button (XIO STOP_PB) is not pressed. When the output coil (OTE MOTOR_CMD) energizes the motor, it also drives the aux contact bit — latching the rung high. Pressing STOP breaks the series path and de-energizes the rung, dropping the motor and the seal-in simultaneously.\"\n },\n {\n \"slug\": \"logic-full-adder\",\n \"diagram\": \"logic\",\n \"title\": \"1-bit full adder\",\n \"description\": \"1-bit full adder built from XOR, AND, and OR gates — the foundational building block of every arithmetic logic unit, from a functional description.\",\n \"standard\": \"IEEE 91\",\n \"tags\": [\n \"XOR\",\n \"AND\",\n \"OR\",\n \"combinational\",\n \"ALU\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"logic \\\"1-bit Full Adder\\\"\\ninput A, B, Cin\\noutput Sum, Cout\\ns1 = XOR(A, B)\\nSum = XOR(s1, Cin)\\nc1 = AND(A, B)\\nc2 = AND(s1, Cin)\\nCout = OR(c1, c2)\",\n \"notes\": \"## Scenario\\n\\nThe 1-bit full adder is the foundational building block of every arithmetic logic unit. Digital logic students derive it in lecture; FPGA engineers instantiate it in RTL. Schematex renders it from a purely functional description — no manual gate placement, no wire routing — making it easy to embed in textbooks, datasheets, or AI-generated hardware documentation.\\n\\n## Annotation key\\n\\n- `input A, B, Cin` — declare named input ports\\n- `output Sum, Cout` — declare named output ports\\n- `s1 = XOR(A, B)` — intermediate signal `s1` is the XOR of inputs A and B\\n- `Sum = XOR(s1, Cin)` — the sum bit is the XOR of the partial sum and carry-in\\n- `c1 = AND(A, B)` — carry generated when both A and B are 1\\n- `c2 = AND(s1, Cin)` — carry propagated when partial sum is 1 and Cin is 1\\n- `Cout = OR(c1, c2)` — carry-out is 1 if either generate or propagate carry is active\\n\\n## How to read\\n\\nThe diagram renders two XOR gates for the sum path (A⊕B, then ⊕Cin) and two AND gates feeding an OR for the carry-out (the standard generate/propagate structure). The layout is automatically ranked so data flows left to right, inputs on the left edge, outputs on the right. Every 4-bit or 8-bit ripple-carry adder in textbooks is just this circuit chained together.\"\n },\n {\n \"slug\": \"matrix-bcg-portfolio\",\n \"diagram\": \"matrix\",\n \"title\": \"BCG product portfolio\",\n \"description\": \"BCG matrix plotting five product lines by market share and growth rate — stars, cash cows, question marks, and one dog — for annual investment planning.\",\n \"standard\": \"BCG Growth-Share (1970)\",\n \"tags\": [\n \"matrix\",\n \"bcg\",\n \"portfolio\",\n \"strategy\",\n \"investment\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"matrix bcg \\\"Product Portfolio — FY26\\\"\\n\\\"Platform SDK\\\" at (0.8, 0.8) size: 5 highlight: true category: star\\n\\\"Legacy API\\\" at (0.85, 0.15) size: 4 category: cashcow\\n\\\"Mobile SDK\\\" at (0.25, 0.85) size: 3 category: question\\n\\\"Self-serve billing\\\" at (0.35, 0.75) size: 2 category: question\\n\\\"On-prem installer\\\" at (0.2, 0.15) size: 1 category: dog\",\n \"notes\": \"## Scenario\\n\\nA VP of product strategy presents this at the annual planning offsite. The Platform SDK is the clear star — keep investing. The Legacy API is a cash cow that funds new bets. Two question marks (Mobile SDK, Self-serve billing) get the hard conversation: which one earns the next round of engineering spend? The on-prem installer is a dog — sunset candidate.\\n\\n## Annotation key\\n\\n- `matrix bcg` — preset axes (market share ← → low; low → high growth)\\n- `\\\"Label\\\" at (x, y)` — share (0–1) × growth (0–1)\\n- `size:` — relative revenue contribution\\n- `category:` — BCG quadrant tag; drives colour\\n\\n## How to read\\n\\nBCG uses a *reversed* x-axis: high market share is on the left, low on the right. That quirk puts cash cows in the bottom-left (high share, low growth) and stars in the top-left (high share, high growth). The right half holds low-share products: top-right = question marks (decide: invest or kill), bottom-right = dogs (usually kill). Bubble size shows current revenue — don't prematurely kill a cow that funds a star.\"\n },\n {\n \"slug\": \"matrix-eisenhower-week\",\n \"diagram\": \"matrix\",\n \"title\": \"Eisenhower week prioritization\",\n \"description\": \"2×2 Eisenhower matrix plotting a week's to-dos by urgency and importance, with size encoding estimated hours and a highlighted must-do.\",\n \"standard\": \"Eisenhower (1954)\",\n \"tags\": [\n \"matrix\",\n \"eisenhower\",\n \"prioritization\",\n \"productivity\",\n \"planning\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"matrix eisenhower \\\"This Week\\\"\\n\\\"Ship hotfix\\\" at (0.1, 0.9) size: 5 highlight: true\\n\\\"Team 1:1s\\\" at (0.1, 0.7) size: 3\\n\\\"Write Q3 OKRs\\\" at (0.8, 0.85) size: 4\\n\\\"Customer demo prep\\\" at (0.2, 0.75) size: 3\\n\\\"Refactor auth layer\\\" at (0.75, 0.45) size: 4\\n\\\"Inbox zero\\\" at (0.15, 0.25) size: 2\\n\\\"LinkedIn updates\\\" at (0.85, 0.15) size: 1\",\n \"notes\": \"## Scenario\\n\\nAn engineering manager uses this chart at Monday planning to triage her week. The highlighted \\\"Ship hotfix\\\" is the unambiguous top item (urgent + important). \\\"Write Q3 OKRs\\\" sits in the important-but-not-urgent quadrant — the trap quadrant Eisenhower called out: it silently slips until it becomes urgent, at which point it's badly done.\\n\\n## Annotation key\\n\\n- `matrix eisenhower` — template with preset axes (urgency × importance) and quadrant labels\\n- `\\\"Label\\\" at (x, y)` — x is urgency (0=low, 1=high), y is importance\\n- `size:` — estimated effort (scales the bubble)\\n- `highlight: true` — red ring around the must-do\\n\\n## How to read\\n\\nTop-right is urgent + important — do first. Top-left (important, not urgent) is where careful planning lives; the Q3 OKRs and refactor belong here. Bottom-right (urgent, not important) is the delegation zone. Bottom-left (neither) is the \\\"delete\\\" zone — anything here is a candidate for saying no. Bubble size shows time cost; use it to sanity-check that your top-right doesn't exceed the week's available hours.\"\n },\n {\n \"slug\": \"mindmap-product-launch\",\n \"diagram\": \"mindmap\",\n \"title\": \"Product launch plan mindmap\",\n \"description\": \"Radial mind map for a product launch — market readiness, engineering, go-to-market, and success metrics — used as a kickoff whiteboard.\",\n \"standard\": \"Buzan (1970s)\",\n \"tags\": [\n \"mindmap\",\n \"product-launch\",\n \"planning\",\n \"gtm\",\n \"brainstorming\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"mindmap\\n\\n# Product Launch Plan\\n\\n## Market readiness\\n### Competitive analysis\\n- Direct competitors\\n- Pricing benchmarks\\n### Target segments\\n- SMB customers\\n- Enterprise pilot\\n\\n## Engineering\\n### Feature freeze\\n- Core API complete\\n- Edge cases resolved\\n### Infrastructure\\n- Load testing\\n- CDN configuration\\n - Cache rules\\n - Geo routing\\n\\n## Go-to-market\\n- Landing page live\\n- Email campaign\\n- Press outreach\\n - TechCrunch pitch\\n - Newsletter sponsors\\n\\n## Success metrics\\n- Week 1 signups\\n- Activation rate\\n- NPS at day 30\",\n \"notes\": \"## Scenario\\n\\nThe launch lead opens the kickoff meeting with this mindmap on a shared whiteboard. Four branches name the four owners (product, engineering, marketing, analytics) and every leaf is a checkable deliverable. The radial layout gives each owner roughly equal visual real estate — no function's work feels like an afterthought.\\n\\n## Annotation key\\n\\n- `#` — root (exactly one)\\n- `##`, `###` — branch depth; each extra `#` nests one level deeper\\n- `-` bullets — leaf items; 2-space indent adds another level\\n\\n## How to read\\n\\nStart at the centre. Each `##` heading is a top-level workstream with its own owner. `###` headings group sub-areas; bullet lists capture concrete deliverables. Indented bullets (e.g. *Cache rules* under *CDN configuration*) are sub-tasks owned by the same person who owns the parent. Anything without a bullet-or-heading is not tracked — the mindmap is the source of truth.\"\n },\n {\n \"slug\": \"mindmap-quarterly-okrs\",\n \"diagram\": \"mindmap\",\n \"title\": \"Quarterly OKRs mindmap\",\n \"description\": \"Company OKRs organized as a mindmap — three objectives, each with measurable key results — suited for the all-hands kickoff of a new quarter.\",\n \"standard\": \"Buzan (1970s)\",\n \"tags\": [\n \"mindmap\",\n \"okrs\",\n \"planning\",\n \"company\",\n \"quarterly\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"mindmap\\n\\n# Q4 Company OKRs\\n\\n## Grow ARR 30%\\n### Expand enterprise pipeline\\n- 10 new qualified logos\\n- Win rate ≥ 25%\\n### Increase expansion\\n- Net revenue retention ≥ 120%\\n- Seat adoption +40%\\n\\n## Ship Platform v2\\n### Core migration\\n- 100% API coverage\\n- Zero-downtime cutover\\n### Developer experience\\n- Sub-5-min quickstart\\n- 95% doc satisfaction\\n\\n## Strengthen team\\n### Hiring\\n- 8 senior engineers\\n- 2 staff PMs\\n### Retention\\n- Voluntary attrition < 5%\\n- eNPS ≥ 40\",\n \"notes\": \"## Scenario\\n\\nThe chief of staff projects this during the Q4 all-hands. Three objectives radiate from the centre; every key result is a leaf with a specific number. The mindmap format reads fast — every person in the company can find their team's objective within three seconds — and it tolerates mid-quarter edits without disturbing other branches.\\n\\n## Annotation key\\n\\n- `#` — root; company-level frame\\n- `##` — objective (qualitative direction)\\n- `###` — key-result cluster\\n- `-` bullets — specific measurable KRs\\n\\n## How to read\\n\\nThe root names the quarter. The three `##` branches are the objectives — the things that will be judged at the end of the quarter. Each `###` groups key results by theme; the bullets are the actual measurable targets. If a KR can't be reduced to a number, it probably belongs in a planning doc rather than on this mindmap.\"\n },\n {\n \"slug\": \"orgchart-matrix-reporting\",\n \"diagram\": \"orgchart\",\n \"title\": \"Scale-up with matrix reporting\",\n \"description\": \"Org chart for a 60-person scale-up with two product lines — solid-line functional reporting plus dotted-line product-manager reporting into each engineering lead.\",\n \"standard\": \"HR convention\",\n \"tags\": [\n \"orgchart\",\n \"matrix-reporting\",\n \"scale-up\",\n \"engineering\",\n \"organization\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"orgchart \\\"Scaleup — Matrixed Product Lines\\\"\\nceo: \\\"Jamie Torres\\\" | CEO [role: ceo]\\n cto: \\\"Raj Patel\\\" | CTO [role: cto]\\n lead_core: \\\"Priya Nair\\\" | Eng Lead | Core [role: engineer]\\n eng1: \\\"Alex Kim\\\" | Senior Engineer [role: engineer]\\n eng2: \\\"Jordan Lee\\\" | Engineer [role: engineer]\\n lead_growth: \\\"Omar Hassan\\\" | Eng Lead | Growth [role: engineer]\\n eng3: \\\"Yuki Tanaka\\\" | Staff Engineer [role: engineer]\\n eng4: \\\"Maya Patel\\\" | Engineer [role: engineer]\\n cpo: \\\"Ellen Wu\\\" | CPO [role: cpo]\\n pm_core: \\\"Tyler Brooks\\\" | PM | Core [role: product]\\n pm_growth: \\\"Suki Ito\\\" | PM | Growth [role: product]\\n cdo: \\\"Liu Wei\\\" | CDO [role: design]\\n des_core: \\\"Ana Rossi\\\" | Designer | Core [role: design]\\n des_growth: \\\"Kai Park\\\" | Designer | Growth [role: design]\\npm_core -.-> lead_core\\npm_growth -.-> lead_growth\\ndes_core -.-> lead_core\\ndes_growth -.-> lead_growth\",\n \"notes\": \"## Scenario\\n\\nThe head of engineering is explaining the matrix structure to a new eng lead. Functional managers (CTO, CPO, CDO) own career growth; product-line leads coordinate day-to-day work. The dotted lines from PMs and designers into the two eng leads make this split visible without implying a change in reporting chain.\\n\\n## Annotation key\\n\\n- Solid line (indentation) — functional / HR reporting\\n- `A -.-> B` — dotted line; secondary / product reporting\\n- `[role: …]` — colour-coded by function\\n\\n## How to read\\n\\nSolid lines (from indentation) answer \\\"who owns my performance review and career.\\\" Dotted lines answer \\\"whose roadmap am I aligned to this quarter.\\\" PMs and designers both report functionally into their chiefs but are dotted-lined into the engineering lead whose product line they're embedded in — a typical scale-up pattern that balances functional excellence with product-team velocity.\"\n },\n {\n \"slug\": \"orgchart-tech-startup\",\n \"diagram\": \"orgchart\",\n \"title\": \"Series-A tech startup org\",\n \"description\": \"Three-level org chart for a ~30-person Series-A startup — CEO with engineering, product, and ops directs, showing open roles and a board advisor.\",\n \"standard\": \"HR convention\",\n \"tags\": [\n \"orgchart\",\n \"startup\",\n \"hiring\",\n \"headcount\",\n \"series-a\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"orgchart \\\"Acme — Series A Team\\\"\\nceo: \\\"Jamie Torres\\\" | CEO [role: ceo]\\n cto: \\\"Raj Patel\\\" | CTO [role: cto]\\n lead_fe: \\\"Priya Nair\\\" | Eng Lead | Frontend [role: engineer]\\n eng1: \\\"Alex Kim\\\" | Senior Engineer [role: engineer]\\n eng2: \\\"Jordan Lee\\\" | Engineer [role: engineer, status: new]\\n open1: open \\\"TBH\\\" | Frontend Engineer [role: engineer]\\n lead_be: \\\"Omar Hassan\\\" | Eng Lead | Backend [role: engineer]\\n eng3: \\\"Yuki Tanaka\\\" | Staff Engineer [role: engineer]\\n draft1: draft \\\"TBH\\\" | Senior Engineer [role: engineer]\\n cpo: \\\"Ellen Wu\\\" | CPO [role: cpo]\\n pm1: \\\"Tyler Brooks\\\" | Product Lead | Core [role: product]\\n pm2: \\\"Suki Ito\\\" | Product Lead | Growth [role: product]\\n coo: \\\"Maria Santos\\\" | COO [role: ops]\\n fin1: \\\"Nour Ahmed\\\" | Finance Manager [role: ops]\\nadvisor adv1: \\\"Dr. Alan Ford\\\" | Board Advisor [role: advisor]\",\n \"notes\": \"## Scenario\\n\\nThe founder is preparing a hiring plan for the next two quarters and uses this chart in a board update. It shows the current team, the one confirmed open req (Frontend Engineer), one planned-but-not-recruiting slot (Staff Engineer backend), and the board advisor relationship. Indentation communicates reporting lines without drawing edges.\\n\\n## Annotation key\\n\\n- `id: \\\"Name\\\" | \\\"Title\\\" | \\\"Department\\\"` — a person node\\n- Indentation (2 spaces) — reporting hierarchy\\n- `open …` / `draft …` — unfilled / planned roles\\n- `advisor …` — external board or advisor relationship\\n- `[role: …]` — colour-coded by function\\n\\n## How to read\\n\\nThe single root is the CEO. Each two-space indent step moves one level down the reporting tree. Two kinds of \\\"ghost\\\" slots appear: `open` nodes (Frontend Engineer) are reqs you are actively hiring for; `draft` nodes (Staff Backend) are next-quarter plans. The advisor sits outside the tree — not in the reporting chain but formally associated with the org.\"\n },\n {\n \"slug\": \"pedigree-brca1\",\n \"diagram\": \"pedigree\",\n \"title\": \"BRCA1 hereditary cancer (four-generation)\",\n \"description\": \"Four-generation BRCA1 pedigree distinguishing affected, carrier, and presymptomatic individuals — per NSGC standard for cascade testing and insurance pre-authorization.\",\n \"standard\": \"NSGC\",\n \"tags\": [\n \"BRCA\",\n \"four-generation\",\n \"carrier\",\n \"presymptomatic\",\n \"proband\",\n \"NSGC\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"pedigree \\\"BRCA1 Family — Hereditary Breast/Ovarian Cancer\\\"\\n I-1 [male, unaffected]\\n I-2 [female, affected, deceased]\\n I-1 -- I-2\\n II-1 [female, affected]\\n II-2 [male, unaffected]\\n II-3 [female, carrier]\\n II-1 -- II-4 [male, unaffected]\\n III-1 [female, affected, proband]\\n III-2 [male, unaffected]\\n III-3 [female, presymptomatic]\\n II-3 -- II-6 [male, unaffected]\\n III-6 [female, carrier]\\n III-7 [male, unaffected]\",\n \"notes\": \"## Scenario\\n\\nA genetic counselor documents a four-generation BRCA1 pedigree for a patient referred after a personal diagnosis of breast cancer at age 35. The NSGC-standard pedigree distinguishes affected, carrier, and presymptomatic individuals — critical for insurance pre-authorization and cascade testing recommendations for at-risk relatives.\\n\\n## Annotation key\\n\\n- `affected` — full fill; individual has been diagnosed with the condition\\n- `carrier` — half fill; individual carries the mutation but is currently asymptomatic\\n- `presymptomatic` — quarter fill or dot; positive genetic test but no clinical diagnosis yet\\n- `proband` — triangle marker; the individual who triggered clinical investigation\\n- `deceased` — diagonal slash through the symbol\\n\\n## How to read\\n\\nThe pattern of affected females across three generations (I-2, II-1, III-1) is the red flag for hereditary BRCA1. The proband (III-1) is the entry point. Her aunt (II-3) is a carrier who has already passed the mutation to III-6. The presymptomatic sibling (III-3) has tested positive but is not yet diagnosed — she receives enhanced surveillance recommendations.\"\n },\n {\n \"slug\": \"pedigree-cystic-fibrosis\",\n \"diagram\": \"pedigree\",\n \"title\": \"Cystic fibrosis (autosomal recessive)\",\n \"description\": \"Classic autosomal recessive cystic fibrosis pedigree with two carrier parents — illustrates the 25% recurrence risk for genetic counseling and patient education.\",\n \"standard\": \"NSGC\",\n \"tags\": [\n \"autosomal-recessive\",\n \"carrier\",\n \"Mendelian\",\n \"proband\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"pedigree \\\"CF family — autosomal recessive\\\"\\n I-1 [male, carrier]\\n I-2 [female, carrier]\\n I-1 -- I-2\\n II-1 [male, affected, proband]\\n II-2 [female, carrier]\\n II-3 [male, unaffected]\\n II-2 -- II-4 [male, carrier]\\n III-1 [female, affected]\\n III-2 [male, unaffected]\",\n \"notes\": \"## Scenario\\n\\nThe classic textbook pedigree for autosomal recessive inheritance — used in genetics courses, patient education, and clinical counseling to illustrate the 25% recurrence risk when both parents are carriers.\\n\\n## Annotation key\\n\\n- `carrier` — half-filled symbol; one normal allele and one mutant allele (Aa)\\n- `affected` — fully filled symbol; two mutant alleles (aa)\\n- `unaffected` — open symbol; either homozygous normal (AA) or carrier (Aa) — cannot distinguish clinically without testing\\n- `proband` — triangle arrow; the first affected individual identified in the family\\n\\n## How to read\\n\\nBoth Generation I parents are carriers. Their Generation II children follow the expected 1:2:1 Mendelian ratio: one affected son (proband), one carrier daughter, one unaffected son. In Generation III, carrier daughter II-2 married a carrier II-4 — producing another affected granddaughter (III-1), reinforcing the recessive inheritance pattern.\"\n },\n {\n \"slug\": \"pedigree-hemophilia\",\n \"diagram\": \"pedigree\",\n \"title\": \"Hemophilia A (X-linked recessive)\",\n \"description\": \"Three-generation hemophilia A pedigree showing X-linked recessive inheritance with carrier females and affected males per NSGC clinical notation.\",\n \"standard\": \"NSGC\",\n \"tags\": [\n \"x-linked\",\n \"carrier\",\n \"three-generation\",\n \"NSGC\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"pedigree \\\"Hemophilia A\\\"\\n I-1 [male, unaffected]\\n I-2 [female, carrier-x]\\n I-1 -- I-2\\n II-1 [male, affected]\\n II-2 [female, carrier-x]\\n II-3 [male, unaffected]\\n II-4 [female, unaffected]\\n II-2 -- II-5 [male, unaffected]\\n III-1 [male, affected]\\n III-2 [female, carrier-x]\\n III-3 [male, unaffected]\",\n \"notes\": \"## Scenario\\n\\nA genetic counselor documents a three-generation hemophilia A pedigree during a prenatal consultation. The X-linked recessive pattern — carrier females who show no symptoms but pass the mutated allele — must be clearly distinguished from affected males. NSGC standard notation is required for clinical records and insurance pre-authorization.\\n\\n## Annotation key\\n\\n- `carrier-x` — female carrier of an X-linked recessive allele; rendered as a circle with a centre dot per NSGC convention\\n- `affected` — fully filled symbol; individual expresses the condition\\n- `unaffected` — open (unfilled) symbol; no clinical presentation\\n- `proband` — the index case who prompted clinical referral (not used here, but add `proband` to any individual)\\n- `I-1 -- I-2` followed by indented children — defines a mating pair and their offspring\\n\\n## How to read\\n\\nGeneration I: unaffected father, carrier mother. Generation II: one affected son (II-1), one carrier daughter (II-2), two unaffected children. Generation III: carrier daughter II-2 married an unaffected man; they produced another affected son (III-1) and another carrier daughter (III-2) — demonstrating the classic X-linked skip-generation pattern where the trait disappears in daughters only to re-emerge in grandsons.\"\n },\n {\n \"slug\": \"phylo-bacterial-diversity\",\n \"diagram\": \"phylo\",\n \"title\": \"Bacterial diversity (ten-taxon tree)\",\n \"description\": \"Ten-taxon bacterial phylogenetic tree from a Newick/NHX string with bootstrap support values, three colored clade arcs, and a branch-length scale bar.\",\n \"standard\": \"Newick/NHX\",\n \"tags\": [\n \"Newick\",\n \"NHX\",\n \"bootstrap\",\n \"clade\",\n \"color-coding\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"phylo \\\"Bacterial Diversity\\\"\\n newick: \\\"((((Ecoli:0.1,Salmonella:0.12):0.05[&&NHX:B=98],Vibrio:0.2):0.08[&&NHX:B=85],((Bacillus:0.15,Staph:0.18):0.06[&&NHX:B=92],Listeria:0.22):0.1):0.15,((Myco_tb:0.3,Myco_leprae:0.28):0.12[&&NHX:B=100],(Strepto:0.25,Lactobacillus:0.2):0.08[&&NHX:B=78]):0.2);\\\"\\n clade Gamma = (Ecoli, Salmonella, Vibrio) [color: \\\"#1E88E5\\\", label: \\\"γ-Proteobacteria\\\"]\\n clade Firmi = (Bacillus, Staph, Listeria, Strepto, Lactobacillus) [color: \\\"#E53935\\\", label: \\\"Firmicutes\\\"]\\n clade Actino = (Myco_tb, Myco_leprae) [color: \\\"#43A047\\\", label: \\\"Actinobacteria\\\"]\\n scale \\\"substitutions/site\\\"\",\n \"notes\": \"## Scenario\\n\\nA microbiologist or bioinformatician pastes a Newick tree string exported from RAxML, IQ-TREE, or MEGA and immediately gets a publication-ready SVG with clade highlights and a branch-length scale bar — no manual layout required.\\n\\n## Annotation key\\n\\n- `newick: \\\"...\\\"` — standard Newick format tree string; branch lengths follow `:` after each taxon name\\n- `[&&NHX:B=98]` — NHX annotation; `B=` is the bootstrap support value (0–100), rendered on internal nodes\\n- `clade id = (taxon, ...)` — defines a named clade by listing its leaf members\\n- `[color: \\\"#hex\\\", label: \\\"...\\\"]` — colors the clade's subtree and adds a labeled arc\\n- `scale \\\"...\\\"` — draws a calibrated scale bar with the given unit label\\n\\n## How to read\\n\\nThe tree shows three major bacterial clades. Blue (γ-Proteobacteria): *E. coli*, *Salmonella*, and *Vibrio* cluster with 98% bootstrap support. Red (Firmicutes): *Bacillus*, *Staph*, *Listeria*, *Streptococcus*, and *Lactobacillus*. Green (Actinobacteria): the two *Mycobacterium* species form a highly supported clade (bootstrap 100). Branch lengths represent substitutions per site — longer branches indicate faster evolutionary rates.\"\n },\n {\n \"slug\": \"sld-generator-ats\",\n \"diagram\": \"sld\",\n \"title\": \"Generator + ATS backup power\",\n \"description\": \"Single-line diagram for a utility + emergency generator ATS transfer system feeding critical loads on a 480 V bus — per IEEE 315 for facility design review.\",\n \"standard\": \"IEEE 315\",\n \"tags\": [\n \"ATS\",\n \"generator\",\n \"backup-power\",\n \"bus\",\n \"breaker\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"sld \\\"Utility + Generator Backup\\\"\\nUTIL = utility [voltage: \\\"480V\\\", label: \\\"Utility\\\"]\\nGEN = generator [rating: \\\"500 kW\\\", voltage: \\\"480V\\\", label: \\\"Emergency Gen\\\"]\\nATS1 = ats [rating: \\\"800A\\\", label: \\\"ATS-1\\\"]\\nBUS1 = bus [voltage: \\\"480V\\\", label: \\\"Critical Load Bus\\\"]\\nCB1 = breaker [rating: \\\"200A\\\"]\\nCB2 = breaker [rating: \\\"200A\\\"]\\nL1 = load [rating: \\\"100A\\\", label: \\\"Critical Load 1\\\"]\\nL2 = load [rating: \\\"100A\\\", label: \\\"Critical Load 2\\\"]\\nUTIL -> ATS1\\nGEN -> ATS1\\nATS1 -> BUS1\\nBUS1 -> CB1\\nBUS1 -> CB2\\nCB1 -> L1\\nCB2 -> L2\",\n \"notes\": \"## Scenario\\n\\nA facility engineer draws this one-line during the design review of a data-center UPS bypass or hospital emergency power system. The single-line diagram (SLD) is the first document a utility inspector or commissioning engineer asks for — it must show every source, switching device, bus, and load path in a single horizontal view without wiring details.\\n\\n## Annotation key\\n\\n- `utility` — mains supply; drawn as the IEEE 315 utility symbol (three-line source)\\n- `generator` — diesel or gas genset; drawn as rotating-machine circle with winding symbol\\n- `ats` — Automatic Transfer Switch; drawn as the NEMA/IEEE transfer-switch symbol\\n- `bus` — horizontal bus bar; all connected devices share the same voltage rail\\n- `breaker` — molded-case or air circuit breaker; drawn as the IEEE 315 breaker symbol\\n- `load` — end-consumer device or feeder\\n- `UTIL -> ATS1` — directed line representing the power path from source to device\\n\\n## How to read\\n\\nTwo sources (utility and generator) feed into the ATS. The ATS selects the live source and connects it to the 480 V critical load bus. From the bus, two independently-fused circuit breakers (CB1, CB2) feed their respective critical loads. If utility power fails, the ATS senses the loss, the generator starts, and the ATS transfers within seconds — all without interrupting the bus downstream.\"\n },\n {\n \"slug\": \"sld-substation-13kv\",\n \"diagram\": \"sld\",\n \"title\": \"13.8 kV utility substation\",\n \"description\": \"13.8 kV distribution substation single-line diagram with 138 kV grid input, 15 MVA step-down transformer, and three feeder breakers per IEEE 315.\",\n \"standard\": \"IEEE 315\",\n \"tags\": [\n \"substation\",\n \"transformer\",\n \"bus\",\n \"feeder\",\n \"HV\",\n \"MV\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"sld \\\"13.8 kV Substation\\\"\\nutility = utility [label: \\\"Grid 138 kV\\\"]\\nxfmr1 = transformer [kva: 15000, primary: 138, secondary: 13.8]\\nbus_hv = bus [voltage: 138]\\nbus_mv = bus [voltage: 13.8]\\nbrk1 = breaker [amps: 1200]\\nbrk2 = breaker [amps: 1200]\\nbrk3 = breaker [amps: 1200]\\nfeeder1 = load [label: \\\"Feeder 1\\\"]\\nfeeder2 = load [label: \\\"Feeder 2\\\"]\\nfeeder3 = load [label: \\\"Feeder 3\\\"]\\nutility -> bus_hv\\nbus_hv -> xfmr1\\nxfmr1 -> bus_mv\\nbus_mv -> brk1\\nbrk1 -> feeder1\\nbus_mv -> brk2\\nbrk2 -> feeder2\\nbus_mv -> brk3\\nbrk3 -> feeder3\",\n \"notes\": \"## Scenario\\n\\nA power systems engineer documents a distribution substation design for a utility interconnection application or a facility's electrical permit drawings. The single-line diagram is the first deliverable in any power system project — required by IEEE, NFPA 70E, and utility interconnection standards before detailed engineering begins.\\n\\n## Annotation key\\n\\n- `utility = utility [label: \\\"...\\\"]` — utility supply source (three-phase symbol)\\n- `[type: transformer, kva:..., primary:..., secondary:...]` — step-down transformer with rated kVA and voltage levels\\n- `[type: bus, voltage:...]` — horizontal bus bar at the specified voltage level\\n- `[type: breaker, amps:...]` — rated circuit breaker\\n- `[type: load, label:...]` — load or feeder destination\\n- `->` — directed power path from source to load\\n\\n## How to read\\n\\nThe 138 kV grid source feeds the high-voltage bus, which connects to the primary of the 15 MVA step-down transformer. The transformer secondary feeds the 13.8 kV medium-voltage bus. Three 1200 A circuit breakers fan out from the MV bus to three distribution feeders — each breaker isolates its feeder independently.\"\n },\n {\n \"slug\": \"sociogram-playground-dynamics\",\n \"diagram\": \"sociogram\",\n \"title\": \"Playground dynamics\",\n \"description\": \"Moreno sociogram of classroom playground dynamics — mutual friendships, one-way choices, and peer conflicts mapped by gender group using force-directed layout.\",\n \"standard\": \"Moreno 1934\",\n \"tags\": [\n \"groups\",\n \"force-directed\",\n \"mutual\",\n \"conflict\",\n \"classroom\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"sociogram \\\"Playground Dynamics\\\"\\n config: layout = force-directed\\n config: coloring = group\\n\\n group boys [label: \\\"Boys\\\", color: \\\"#42A5F5\\\"]\\n tom; jack; mike; leo\\n\\n group girls [label: \\\"Girls\\\", color: \\\"#EF5350\\\"]\\n anna; beth; chloe; diana\\n\\n tom <-> jack\\n tom -> mike\\n jack -> leo\\n mike -x> leo [label: \\\"conflict\\\"]\\n anna <-> beth\\n anna <-> chloe\\n beth <-> chloe\\n anna -> diana\\n diana -.- tom\\n leo -.- anna\",\n \"notes\": \"## Scenario\\n\\nA school counselor administers a sociometric survey to a class and maps the results to identify social stars, isolates, and conflict pairs. The force-directed layout naturally clusters tight friendship groups and surfaces bridging individuals — the counselor can immediately see who is at social risk and which cross-group connections are worth nurturing.\\n\\n## Annotation key\\n\\n- `group id [label:..., color:...]` — defines a named group; members listed below with `;` separator\\n- `<->` — mutual/reciprocal choice (both children named each other)\\n- `->` — one-way positive nomination (A chose B, B did not choose A)\\n- `-x>` — conflict or rejection edge; rendered with an X marker\\n- `-.-` — neutral / weak tie; neither positive nor negative nomination\\n- `config: layout = force-directed` — uses physics simulation to position nodes; tightly connected nodes cluster naturally\\n- `config: coloring = group` — colors each node by its assigned group\\n\\n## How to read\\n\\nThe diagram shows two tight cliques: the blue boys' group (Tom–Jack mutual friendship, with Leo drifting at the edge) and the red girls' group (Anna–Beth–Chloe triangle). Leo and Mike have a conflict edge — an immediate flag for the counselor. Diana sits between groups with only a weak tie to Tom, suggesting social isolation risk. Anna has the most outward nominations, making her a social star worth engaging as a peer ally.\"\n },\n {\n \"slug\": \"sociogram-team-influence\",\n \"diagram\": \"sociogram\",\n \"title\": \"Engineering team influence mapping\",\n \"description\": \"Informal influence map of an engineering team showing tech leads, senior ICs, and junior members — reveals bridging nodes and isolated individuals.\",\n \"standard\": \"Moreno 1934\",\n \"tags\": [\n \"force-directed\",\n \"groups\",\n \"bridging\",\n \"stars\",\n \"isolates\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"sociogram \\\"Engineering team — informal influence\\\"\\n config: layout = force-directed\\n group leads [label: \\\"Tech leads\\\", color: \\\"#1976D2\\\"]\\n alex; sam\\n group sr [label: \\\"Senior ICs\\\", color: \\\"#66BB6A\\\"]\\n priya; jordan; kim; tao\\n group jr [label: \\\"Junior\\\", color: \\\"#FFA726\\\"]\\n lee; ravi; nina; dev\\n alex <-> sam\\n alex -> priya\\n sam -> jordan\\n priya <-> kim\\n jordan <-> tao\\n kim -> lee\\n priya -> ravi\\n tao -> nina\\n dev -.- lee\\n nina -.- priya\",\n \"notes\": \"## Scenario\\n\\nAn engineering manager runs an informal network analysis survey (\\\"Who do you go to when you're stuck?\\\") and maps the results to identify knowledge hubs, bridging individuals between seniority tiers, and team members who are drifting toward isolation before performance reviews surface the issue.\\n\\n## Annotation key\\n\\n- `group id [label:..., color:...]` — assigns individuals to organizational tiers, color-coded\\n- `<->` — mutual influence; both nominated each other\\n- `->` — one-way influence nomination\\n- `-.-` — weak tie; neither party nominated the other in the survey\\n- The force-directed layout clusters mutual-nomination groups and separates isolates\\n\\n## How to read\\n\\nAlex and Sam (tech leads) are mutually influential. Alex bridges down to Priya, Sam to Jordan — healthy knowledge flow across tiers. Priya and Kim form a strong senior IC hub. Dev and Nina have only weak ties (--. to the network), suggesting integration risk. Dev's only connection is a weak tie to Lee — a coaching opportunity before the next performance cycle.\"\n },\n {\n \"slug\": \"timeline-company-milestones\",\n \"diagram\": \"timeline\",\n \"title\": \"Company milestone history\",\n \"description\": \"Lollipop timeline of a company's first five years — fundraising rounds, key hires, product GAs — suited for an investor deck or anniversary blog post.\",\n \"standard\": \"Timeline convention\",\n \"tags\": [\n \"timeline\",\n \"milestones\",\n \"fundraising\",\n \"company-history\",\n \"pitch\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"timeline \\\"Acme — First Five Years\\\"\\nconfig: style = lollipop\\n\\n2020-06: \\\"Founders meet at Y Combinator\\\" [side: below]\\n2020-11: milestone \\\"Incorporation + pre-seed $1M\\\" [side: above, color: #1565C0]\\n2021-04: \\\"First engineer hired\\\" [side: below]\\n2021-09: milestone \\\"Product beta — 50 design partners\\\" [side: above, color: #2E7D32]\\n2022-03: milestone \\\"Seed round — $6M\\\" [side: above, color: #1565C0]\\n2022-11: \\\"Team reaches 20 people\\\" [side: below]\\n2023-05: milestone \\\"Platform v1 GA\\\" [side: above, color: #6A1B9A]\\n2023-10: milestone \\\"Series A — $22M\\\" [side: above, color: #1565C0]\\n2024-06: \\\"100th enterprise customer\\\" [side: below]\\n2025-01: milestone \\\"Platform v2 launched\\\" [side: above, color: #6A1B9A]\",\n \"notes\": \"## Scenario\\n\\nThe founder drops this into the first page of the fundraising deck. Funding rounds, product GAs, and growth markers alternate above/below the axis, which makes the parallel story — \\\"we raised capital and shipped on time\\\" — visible in one glance. Reviewers who only read the top of the page still get the two-line story.\\n\\n## Annotation key\\n\\n- `style = lollipop` — dot-on-stick markers alternating above/below axis\\n- `milestone` — diamond marker for headline events\\n- `[side: above|below]` — explicit placement\\n- `[color: #hex]` — colour-code category (fundraising / product / team)\\n\\n## How to read\\n\\nTime runs left to right. Each marker is a single dated event; *milestone* markers are the diamond-shaped headline items (fundraising, GAs). Colour carries category: blue = fundraising, purple = product, green = early commercial traction. Events below the axis are supporting context (people, growth stats); events above are the announceable headlines.\"\n },\n {\n \"slug\": \"timeline-product-launch\",\n \"diagram\": \"timeline\",\n \"title\": \"Product launch timeline\",\n \"description\": \"Gantt-style timeline for a three-month product launch — overlapping workstreams, two milestones, and a freeze window, used for exec status updates.\",\n \"standard\": \"Timeline / Gantt convention\",\n \"tags\": [\n \"timeline\",\n \"gantt\",\n \"product-launch\",\n \"scheduling\",\n \"program\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"timeline \\\"Platform v2 Launch\\\"\\nconfig: style = gantt\\n\\n2025-07-01 - 2025-08-15: \\\"Engineering build\\\" [category: \\\"engineering\\\"]\\n2025-07-15 - 2025-08-31: \\\"Design polish\\\" [category: \\\"design\\\"]\\n2025-08-01 - 2025-09-10: \\\"Marketing collateral\\\" [category: \\\"marketing\\\"]\\n2025-08-20: milestone \\\"Feature freeze\\\" [color: #E53935]\\n2025-08-20 - 2025-09-05: \\\"QA hardening\\\" [category: \\\"engineering\\\"]\\n2025-09-01 - 2025-09-12: \\\"Press embargo outreach\\\" [category: \\\"marketing\\\"]\\n2025-09-15: milestone \\\"Public launch\\\" [color: #2E7D32]\",\n \"notes\": \"## Scenario\\n\\nThe launch PM shares this in weekly exec status. Overlapping bars show where workstreams parallelize (design polishing while engineering still builds) and the feature-freeze diamond makes the handoff between build and QA unmissable. The second milestone (public launch) anchors the entire timeline and is the reason every other bar exists.\\n\\n## Annotation key\\n\\n- `DATE - DATE: \\\"Label\\\"` — range (bar) event\\n- `DATE: milestone \\\"Label\\\"` — point milestone (diamond)\\n- `[category: …]` — group colour in the gantt legend\\n- `[color: #hex]` — explicit marker colour\\n\\n## How to read\\n\\nTime flows left to right. Horizontal bars are continuous work; diamonds are instantaneous events. Overlapping bars mean two teams are working simultaneously — fine, so long as they coordinate. The red *Feature freeze* marks the transition from net-new work to hardening; any engineering bar extending past it needs an exception. The green *Public launch* is the terminal milestone every other bar is serving.\"\n },\n {\n \"slug\": \"timing-spi-transaction\",\n \"diagram\": \"timing\",\n \"title\": \"SPI transaction timing diagram\",\n \"description\": \"WaveDrom-compatible SPI timing diagram for an 8-byte master-to-slave transaction with clock, chip-select, MOSI, and MISO signals for firmware documentation.\",\n \"standard\": \"WaveDrom / IEEE 1497\",\n \"tags\": [\n \"SPI\",\n \"digital\",\n \"clock\",\n \"chip-select\",\n \"MOSI\",\n \"MISO\",\n \"WaveDrom\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"timing \\\"SPI Transaction\\\"\\nCLK: pppppppp\\nCS_N: 10000001\\nMOSI: x======= data: [\\\"0xAB\\\",\\\"0xCD\\\",\\\"0xEF\\\",\\\"0x01\\\",\\\"0x02\\\",\\\"0x03\\\",\\\"0x04\\\",\\\"0x05\\\"]\\nMISO: zzzz==== data: [\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"0xFF\\\",\\\"0x12\\\",\\\"0x34\\\",\\\"0x56\\\"]\",\n \"notes\": \"## Scenario\\n\\nA firmware engineer or hardware designer documents an 8-byte SPI master-to-slave transaction for a device driver review or datasheet. The WaveDrom-compatible syntax means the same DSL can be pasted directly into WaveDrom's online editor or embedded in documentation pipelines.\\n\\n## Annotation key\\n\\n- `p` — clock pulse (high period followed by low); each `p` is one clock cycle\\n- `1` / `0` — logic high / logic low\\n- `=` — data bus: stable data (value unchanged from previous cycle)\\n- `x` — don't-care or undefined state (transition state)\\n- `z` — high-impedance (floating / tri-state)\\n- `data: [...]` — optional data labels for each stable segment, rendered inside the bus bar\\n- `CS_N` — active-low chip select; `1` = deselected, `0` = selected\\n\\n## How to read\\n\\nThe clock runs for 8 cycles. CS_N goes low at cycle 1 and returns high at cycle 8, framing the transaction. MOSI (master out) sends 8 bytes starting at cycle 1. MISO (slave in) is high-Z for the first 4 cycles (slave preparing the response) then transitions to stable data bytes 5–8. The transaction completes when CS_N de-asserts.\"\n },\n {\n \"slug\": \"venn-customer-segments\",\n \"diagram\": \"venn\",\n \"title\": \"Customer segment overlap\",\n \"description\": \"Three-set Venn showing email subscriber, paid-user, and mobile-app-user overlap with counts for every region — useful for lifecycle marketing planning.\",\n \"standard\": \"Venn (1880)\",\n \"tags\": [\n \"segmentation\",\n \"marketing\",\n \"analytics\",\n \"venn\",\n \"audience\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"venn \\\"Customer Segments — Q3 2025\\\"\\nset email \\\"Email subscribers\\\" [color: \\\"#1E88E5\\\"]\\nset paid \\\"Paid users\\\" [color: \\\"#E53935\\\"]\\nset mobile \\\"Mobile app users\\\" [color: \\\"#43A047\\\"]\\nemail & paid : 1840\\nemail & mobile : 920\\npaid & mobile : 2100\\nemail & paid & mobile : 650\\nemail only : 12400\\npaid only : 3200\\nmobile only : 8700\",\n \"notes\": \"## Scenario\\n\\nA lifecycle marketer is planning Q4 campaigns and needs to see which audiences overlap before deciding where to spend budget. The 650-strong triple intersection is the highest-LTV segment; the 12.4k email-only group is the biggest conversion opportunity. Putting the numbers on one Venn makes the gaps and overlaps argue for themselves in a 30-second leadership review.\\n\\n## Annotation key\\n\\n- `set ID \\\"Label\\\"` — declare a circle\\n- `A & B : n` — count in the intersection of A and B\\n- `A only : n` — count exclusive to A\\n\\n## How to read\\n\\nEach circle is a total audience; each overlap is people who belong to multiple audiences. The triple intersection (email ∩ paid ∩ mobile, 650 users) is your most engaged cohort — the obvious group to upsell. The *email only* and *mobile only* exclusive regions are your largest activation opportunities because each represents users who have not yet crossed into the other channels.\"\n },\n {\n \"slug\": \"venn-programming-paradigms\",\n \"diagram\": \"venn\",\n \"title\": \"Programming paradigm overlap\",\n \"description\": \"Venn diagram showing the intersection of object-oriented, functional, and logic programming paradigms with language counts — a teaching aid for CS curricula.\",\n \"standard\": \"Venn (1880)\",\n \"tags\": [\n \"education\",\n \"programming\",\n \"paradigms\",\n \"venn\",\n \"teaching\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"venn \\\"Programming Paradigms\\\"\\nset oop \\\"Object-Oriented\\\" [color: \\\"#1E88E5\\\"]\\nset fp \\\"Functional\\\" [color: \\\"#E53935\\\"]\\nset logic \\\"Logic\\\" [color: \\\"#43A047\\\"]\\noop & fp : 180\\noop & logic : 45\\nfp & logic : 90\\noop & fp & logic : 12\\noop only : 620\\nfp only : 340\\nlogic only : 95\",\n \"notes\": \"## Scenario\\n\\nA CS instructor opens a lecture on programming paradigms with this Venn. Counts come from a language survey — how many languages offer strong OOP, FP, and logic support. The tiny triple-overlap (12) tells students that languages supporting all three well are rare; the large OOP-only region shows industry gravity; the FP ∩ logic overlap (90) is where languages like Prolog-with-lambdas sit.\\n\\n## Annotation key\\n\\n- `set ID \\\"Label\\\"` — paradigm circle\\n- `A & B : n` — number of languages supporting both paradigms strongly\\n- `A only : n` — languages committed to a single paradigm\\n\\n## How to read\\n\\nEach circle represents a paradigm. Overlapping regions count multi-paradigm languages. The `oop & fp` intersection (180) is where modern mainstream languages — Scala, Kotlin, Swift, TypeScript — sit. The tiny triple intersection (12) is a reminder that truly multi-paradigm language design is expensive; the larger exclusive regions show how few languages commit to logic programming at all.\"\n }\n];\n\nexport const SYNTAX: Readonly<Record<string, GeneratedSyntax>> = {\n \"genogram\": {\n \"title\": \"Genogram\",\n \"content\": \"## 1. Your first genogram\\n\\nThe smallest clinically useful genogram: two parents, one child.\\n\\n```\\ngenogram\\n alice [female, 1980]\\n bob [male, 1978]\\n alice -- bob \\\"m. 2005\\\"\\n carol [female, 2008]\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with the keyword `genogram`, optionally followed by a quoted title.\\n2. Declare each person on their own line: `id [attributes]`. Attributes go in square brackets, comma-separated.\\n3. Connect two people with a **couple operator** — `--` (marriage) here; see §4.1 for all six. A trailing quoted string is the relationship label.\\n4. **Indent under the couple line** to add their children.\\n\\n> Comments must be on their own line, starting with `#`. Trailing inline comments (`bob [male, 1978] # ...`) are not supported and will break the parser — see §8.\\n\\n---\\n\\n## 2. Individuals\\n\\nAn individual line is `id [attr1, attr2, …]`. Attributes are comma-separated, order-independent, all optional.\\n\\n**ID rules.** Must match `[a-zA-Z][a-zA-Z0-9_-]*`. IDs are case-insensitive internally but preserve their original casing as the display label (override with `label:\\\"…\\\"`).\\n\\n**Attributes accepted by the parser today:**\\n\\n| Attribute | Values | Effect |\\n|---|---|---|\\n| Sex | `male`, `female`, `unknown`, `other` | Shape: square, circle, diamond, diamond |\\n| Status | `deceased`, `stillborn`, `miscarriage`, `abortion` | Visual modifier (X-out, scaled shape, etc.) |\\n| Birth year | 4-digit number, e.g. `1980` | First 4-digit token = birth year |\\n| Death year | 4-digit number after birth, e.g. `1980, 2055` | Second 4-digit token = death year |\\n| `index` | flag | Concentric shape = identified patient |\\n| `unknown-siblings` | flag | Diamond with `?` — placeholder for ≥1 siblings of unknown count |\\n| `age:N` | e.g. `age:42` | Age shown inside shape |\\n| `death:YYYY` | e.g. `death:2020` | Explicit death year |\\n| `label:\\\"…\\\"` | e.g. `label:\\\"Dr. Smith\\\"` | Display label override |\\n| `sibling-of:<id>` | e.g. `sibling-of:monica` | Pins same generation as the referenced sibling, draws a dashed bracket — for known relatives with unknown ancestry. |\\n| `conditions:…` | see §5 | Medical/psychological conditions |\\n| `key:value` | any custom | Stored as metadata |\\n\\n```\\ngenogram\\n grandma [female, 1920, 2002, deceased]\\n dad [male, 1950, age:74]\\n me [male, 1985, index]\\n daughter [female, 2012, label:\\\"Em\\\"]\\n```\\n\\n---\\n\\n## 3. Shapes\\n\\n| Visual | Sex value | Meaning |\\n|---|---|---|\\n| ☐ Square | `male` | Male |\\n| ○ Circle | `female` | Female |\\n| ◇ Diamond | `unknown`, `other`, *or* attribute omitted | Unknown / unspecified |\\n\\nStatus modifiers layer on top of the base shape:\\n\\n```\\ngenogram\\n alive [male, 1960]\\n passed [male, 1930, 2010, deceased]\\n stillborn_child [unknown, stillborn]\\n lost [unknown, miscarriage]\\n```\\n\\n---\\n\\n## 4. Connections\\n\\n### 4.1 Couple operators\\n\\nThe parser tries these in order. The first one that matches wins — so `-x-` beats `--`.\\n\\n| Operator | Type | Example | Meaning |\\n|---|---|---|---|\\n| `-x-` | divorced | `a -x- b` | Divorce |\\n| `-/-` | separated | `a -/- b` | Separation (married) |\\n| `-//` | separated | `a -// b` | Separation (alias for `-/-`) |\\n| `-o-` | engaged | `a -o- b` | Engagement |\\n| `==` | consanguineous | `a == b` | Blood-related couple |\\n| `--` | married | `a -- b` | Marriage |\\n| `~` | cohabiting | `a ~ b` | Cohabiting / LTR (current) |\\n| `~/~` | cohabiting-ended | `a ~/~ b` | Cohabitation has ended (never-married). Common in LATAM child-protection caseloads where biological parents lived together unmarried and the relationship has since broken — distinct from `-x-` divorce (no marriage) and `-/-` separation (still married). |\\n\\nA trailing quoted string becomes the relationship label (`a -- b \\\"m. 2005\\\"`).\\n\\n### 4.2 Inline individual on the right side\\n\\nIf the right-hand person hasn't been declared yet, you can declare them in-place:\\n\\n```\\ngenogram\\n ann [female, 1970]\\n ann -- ben [male, 1968] \\\"m. 1995\\\"\\n kim [female, 1997]\\n```\\n\\n### 4.3 Children (indented under a couple)\\n\\nIndentation under a couple line = \\\"these are the children of this couple.\\\" Any indent greater than the couple's indent works; by convention use 2 more spaces. Children are rendered in order of declaration (render also sorts by birth year when present).\\n\\n```\\ngenogram\\n dad [male, 1950]\\n mom [female, 1952]\\n dad -- mom\\n eldest [male, 1975]\\n middle [female, 1978, adopted]\\n twin_a [male, 1985, twin-identical]\\n twin_b [male, 1985, twin-identical]\\n```\\n\\n**Special child attributes:**\\n\\n| Attribute | Effect |\\n|---|---|\\n| `adopted` | Adoption line style |\\n| `foster` | Foster relationship |\\n| `guardian` | Guardianship by a non-parent relative (e.g. grandparent custody). Same primitive as `foster` — drawn as a secondary \\\"current caregiver\\\" link when biological parents are also declared. |\\n| `twin-identical` | Grouped with other `twin-identical` children of the same couple |\\n| `twin-fraternal` | Grouped with other `twin-fraternal` children |\\n| `unknown-siblings` | Single diamond with `?` glyph — \\\"≥1 siblings, count and identities unknown\\\" (pedigree convention). |\\n\\n### 4.3.1 Dual-parent families (foster, adoption, guardianship)\\n\\nChildren placed with a non-biological caregiver while biological parents are still part of the case can be declared under both couples. **Declare the child with full attributes the first time** (under the biological couple), then **redeclare with just `[foster]` / `[adopted]` / `[guardian]`** under the current caregiver. The first declaration wins layout; the second is drawn as a secondary dotted \\\"current caregiver\\\" link that does not pull the child away from their biological position.\\n\\n```\\ngenogram \\\"Foster placement\\\"\\n bp1 [male, label: \\\"Bio dad\\\"]\\n bp2 [female, label: \\\"Bio mom\\\"]\\n bp1 ~/~ bp2\\n child [male, 2018, index]\\n fp1 [male, label: \\\"Foster dad\\\"]\\n fp2 [female, label: \\\"Foster mom\\\"]\\n fp1 -- fp2\\n own [male, 2010]\\n child [foster]\\n```\\n\\nThe same primitive serves adoption (closed/open), foster placement, and guardianship by a relative — only the keyword differs. Re-declaration **merges** non-conflicting attributes (sex, birth year, label, `index` marker) into the original; declaring a conflicting `male` vs `female` raises a parse error rather than silently overwriting.\\n\\n### 4.3.2 Unknown-count siblings\\n\\nWhen a case file mentions \\\"the child has siblings\\\" without naming them, use either the `?` shorthand on its own line, or `[unknown-siblings]` on a regular id. Both render as a single diamond with a \\\"?\\\" glyph — the standard pedigree marker for \\\"one or more siblings, identities unknown.\\\"\\n\\n```\\ngenogram\\n dad [male]\\n mom [female]\\n dad -- mom\\n ?\\n known_kid [male, 2018]\\n```\\n\\n### 4.3.3 Sibling-of (known relative, unknown ancestry)\\n\\nTo express \\\"X is a sibling of Y\\\" without inventing parents, use the `sibling-of: <id>` property. The renderer pins X to Y's generation and draws a dashed bracket above the two — the standard pedigree convention for a known relative whose ancestry is not part of the case.\\n\\n```\\ngenogram\\n monica [female, 1990]\\n uncle [male, label: \\\"Tío materno\\\", sibling-of: monica]\\n```\\n\\n### 4.4 Emotional relationships\\n\\nSeparate line, parser pattern `A -TYPE- B` (non-directional) or `A -TYPE-> B` (directional). An optional quoted label goes at the end. **Both individuals must already be declared** before the emotional line.\\n\\n```\\nharry -cutoff- petunia # non-directional\\nharry -hostile- dudley \\\"since 1991\\\"\\nuncle -abuse-> nephew # directional (arrow)\\n```\\n\\nAll 32 types the parser accepts today:\\n\\n| Category | Types |\\n|---|---|\\n| Positive / close | `harmony`, `close`, `bestfriends`, `love`, `inlove`, `friendship` |\\n| Negative / hostile | `hostile`, `conflict`, `enmity`, `distant-hostile`, `cutoff` |\\n| Ambivalent | `close-hostile`, `fused`, `fused-hostile` |\\n| Distance | `distant`, `normal`, `nevermet` |\\n| Abuse *(directional)* | `abuse`, `physical-abuse`, `emotional-abuse`, `sexual-abuse`, `neglect` |\\n| Control *(directional)* | `manipulative`, `controlling`, `jealous` |\\n| Special | `focused`, `focused-neg`, `distrust`, `admirer`, `limerence` |\\n\\n```\\ngenogram\\n dad [male, 1950]\\n son [male, 1985]\\n daughter [female, 1988]\\n dad -close- daughter\\n dad -conflict- son\\n son -cutoff- dad \\\"since 2010\\\"\\n```\\n\\n---\\n\\n## 5. Medical conditions\\n\\nSyntax: `conditions: name(fill) [+ name(fill, #color)]…`\\n\\n```\\nfather [male, 1945, conditions: heart(full, #E53935)]\\nmother [female, 1948, conditions: diabetes(half-left) + anxiety(half-right, #26A69A)]\\n```\\n\\n- **`name`** — any identifier you choose (displayed in legend/tooltip).\\n- **`fill`** — required, controls which region of the shape is colored. See table below.\\n- **`color`** — optional hex. Default depends on the renderer theme.\\n- Multiple conditions are joined with `+`. Each needs its own `(fill)`.\\n\\n**Fill positions:**\\n\\n| `fill` value | Region |\\n|---|---|\\n| `full` | Entire shape |\\n| `half-left` / `half-right` | Left / right half |\\n| `half-top` / `half-bottom` | Top / bottom half |\\n| `quad-tl` / `quad-tr` / `quad-bl` / `quad-br` | One quadrant |\\n| `striped` | Diagonal stripe pattern (asymptomatic carrier) |\\n| `dotted` | Dot pattern |\\n\\n```\\ngenogram\\n dad [male, 1950, conditions: heart(full, #E53935)]\\n mom [female, 1952, conditions: diabetes(half-left) + depression(half-right, #5C6BC0)]\\n dad -- mom\\n son [male, 1980, conditions: carrier(striped)]\\n```\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Title:** `genogram \\\"Smith Family\\\"` — first line only.\\n- **Person label override:** `alice [female, label:\\\"Dr. Alice Smith\\\"]`.\\n- **Relationship label:** trailing quoted string on a couple or emotional line — `alice -- bob \\\"m. 2005\\\"`.\\n- **Comments:** `#` at the start of a line (after leading whitespace). Inline comments are **not** supported.\\n\\n```\\ngenogram \\\"Smith Family\\\"\\n # this line is a comment — fine\\n alice [female, 1980] # ← THIS trailing comment breaks the parser\\n```\\n\\n---\\n\\n## 7. Reserved words & escaping\\n\\n**Reserved at line start:** `genogram` (header keyword).\\n\\n**Reserved operator tokens** inside a line — avoid using these sequences in IDs:\\n`--`, `~`, `~/~`, `==`, `-x-`, `-/-`, `-//`, `-o-`, and any `-<type>-` / `-<type>->` matching an emotional-relationship type.\\n\\n**Reserved id `?`** — bare `?` on a child line auto-generates a synthetic placeholder with the `unknown-siblings` marker. Do not use `?` as a real id.\\n\\n**Strings with spaces** must be double-quoted: titles, labels, `label:\\\"…\\\"`. Single quotes and backticks are not recognized.\\n\\n---\\n\\n## 8. Common mistakes\\n\\nReal parser errors, what triggers them, and how to fix.\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `alex [nonbinary, 1995]` | `Unknown property 'nonbinary'` | Use `unknown` or `other` (nonbinary is §13 Roadmap) |\\n| `alice [female, transgender]` | `Unknown property 'transgender'` | Not yet parseable (§13 Roadmap) |\\n| `dad -- mom` ← followed by `child [male, 2010]` at the **same indent** | Child parsed as a new top-level individual, not as their child | Indent the child line deeper than the couple line (2 spaces is enough) |\\n| `A -- B` where `A` was never declared | `Unknown individual 'A'` | Declare `A [sex, year]` on a line above |\\n| `father -- mother \\\"married\\\"` on line 1 (no `genogram` header) | `Expected \\\"genogram\\\" header` | Start the file with `genogram` or `genogram \\\"Title\\\"` |\\n| `conditions: diabetes + cancer` (no parens) | `Invalid condition format 'diabetes'` | Add fill: `conditions: diabetes(half-left) + cancer(half-right)` |\\n| `[triplet-identical]` | `Unknown property 'triplet-identical'` | Triplets not yet parseable (§13 Roadmap) |\\n| `dad -- mom # first marriage` | Trailing inline `#` comment is treated as part of the label / errors | Move the comment to its own line |\\n| Same id declared twice with different sex (`x [male]` then `x [female]`) | `Conflicting sex for 'x': previously 'male', now 'female'` | Pick one or rename one of the ids |\\n| `child [foster]` redeclared but biological parents never declared | `child` becomes the foster couple's regular child (no secondary link drawn) | This is intentional — secondary links require an existing primary parent-child rel from a prior declaration |\\n\\n---\\n\\n## 9. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | individual | couple-block | emotional)*\\n\\nheader = \\\"genogram\\\" ( WS quoted-string )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nindividual = INDENT id ( \\\"[\\\" attrs \\\"]\\\" )? NEWLINE\\ncouple-block = INDENT id WS coupleOp WS right-side ( WS quoted-string )? NEWLINE\\n ( deeper-indent child )*\\nchild = INDENT id ( \\\"[\\\" attrs \\\"]\\\" )? NEWLINE\\n | INDENT \\\"?\\\" NEWLINE // unknown-count sibling shorthand\\nright-side = id ( \\\"[\\\" attrs \\\"]\\\" )?\\n\\nemotional = INDENT id WS \\\"-\\\" type \\\"-\\\" id ( WS quoted-string )? NEWLINE\\n | INDENT id WS \\\"-\\\" type \\\"->\\\" id ( WS quoted-string )? NEWLINE\\n\\ncoupleOp = \\\"~/~\\\" | \\\"-//\\\" | \\\"-x-\\\" | \\\"-/-\\\" | \\\"-o-\\\" | \\\"==\\\" | \\\"--\\\" | \\\"~\\\"\\ntype = \\\"harmony\\\" | \\\"close\\\" | \\\"bestfriends\\\" | \\\"love\\\" | \\\"inlove\\\"\\n | \\\"friendship\\\" | \\\"hostile\\\" | \\\"conflict\\\" | \\\"enmity\\\"\\n | \\\"distant-hostile\\\" | \\\"cutoff\\\" | \\\"close-hostile\\\" | \\\"fused\\\"\\n | \\\"fused-hostile\\\" | \\\"distant\\\" | \\\"normal\\\" | \\\"nevermet\\\"\\n | \\\"abuse\\\" | \\\"physical-abuse\\\" | \\\"emotional-abuse\\\"\\n | \\\"sexual-abuse\\\" | \\\"neglect\\\" | \\\"manipulative\\\" | \\\"controlling\\\"\\n | \\\"jealous\\\" | \\\"focused\\\" | \\\"focused-neg\\\" | \\\"distrust\\\"\\n | \\\"admirer\\\" | \\\"limerence\\\"\\n\\nid = [a-zA-Z] [a-zA-Z0-9_-]*\\nattrs = attr (\\\",\\\" attr)*\\nattr = \\\"male\\\" | \\\"female\\\" | \\\"unknown\\\" | \\\"other\\\"\\n | \\\"deceased\\\" | \\\"stillborn\\\" | \\\"miscarriage\\\" | \\\"abortion\\\"\\n | \\\"adopted\\\" | \\\"foster\\\" | \\\"guardian\\\"\\n | \\\"twin-identical\\\" | \\\"twin-fraternal\\\"\\n | \\\"index\\\" | \\\"unknown-siblings\\\"\\n | digit digit digit digit // year\\n | \\\"age\\\" \\\":\\\" digits\\n | \\\"death\\\" \\\":\\\" digit digit digit digit\\n | \\\"label\\\" \\\":\\\" quoted-string\\n | \\\"sibling-of\\\" \\\":\\\" id\\n | \\\"conditions\\\" \\\":\\\" condition (\\\"+\\\" condition)*\\n | key \\\":\\\" value // custom\\ncondition = name \\\"(\\\" fill (\\\",\\\" \\\"#\\\" hex)? \\\")\\\"\\nfill = \\\"full\\\" | \\\"half-left\\\" | \\\"half-right\\\" | \\\"half-top\\\" | \\\"half-bottom\\\"\\n | \\\"quad-tl\\\" | \\\"quad-tr\\\" | \\\"quad-bl\\\" | \\\"quad-br\\\"\\n | \\\"striped\\\" | \\\"dotted\\\"\\n\\ncomment = INDENT \\\"#\\\" any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/genogram/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"ecomap\": {\n \"title\": \"Ecomap\",\n \"content\": \"## 1. Your first ecomap\\n\\nThe smallest useful ecomap: one center and three outside systems.\\n\\n```\\necomap\\n center: client [label: \\\"Maria\\\"]\\n mom [label: \\\"Mother\\\", category: family]\\n work [label: \\\"Tech Corp\\\", category: work]\\n therapist [label: \\\"Dr. Patel\\\", category: mental-health]\\n mom === client\\n work --- client\\n therapist <-> client [label: \\\"weekly\\\"]\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with the keyword `ecomap`, optionally followed by a quoted title.\\n2. Declare the center on its own line: `center: id [label: \\\"…\\\"]`. Exactly one center per diagram.\\n3. Declare each outside system on its own line: `id [label: \\\"…\\\", category: …]`.\\n4. Connect any two declared IDs with a **connection operator** — `===` (strong), `---` (normal), `<->` (reciprocal), etc. See §3 for the full table. A trailing `[label: \\\"…\\\"]` adds an edge label.\\n\\n> Comments must be on their own line, starting with `#`. Inline trailing comments will break the parser.\\n\\n---\\n\\n## 2. Center and outside systems\\n\\nEvery ecomap has one **center** and any number of **outside systems**. Both use the same `id [attrs]` syntax; the only difference is the `center:` prefix.\\n\\n**ID rules.** Must match `[a-zA-Z][a-zA-Z0-9_-]*`. IDs are case-insensitive; the original token is kept as the default label.\\n\\n**Attributes accepted by the parser today:**\\n\\n| Attribute | Values | Effect |\\n|---|---|---|\\n| `label:\\\"…\\\"` | any quoted string | Display label override |\\n| `category:…` | see §2.1 | Color / grouping of a system node |\\n| `size:…` | `small`, `medium`, `large` | Node size |\\n| `importance:…` | `major`, `moderate`, `minor` | Visual weight |\\n| `sector:…` | `top`, `right`, `bottom`, `left` | Hint for which side of the center the system sits on |\\n| `age:N` | e.g. `age:34` | Age shown inside a person-typed center |\\n| `male` / `female` / `unknown` | flag | Sex for a person-typed center |\\n\\n### 2.1 System categories\\n\\nCategories color-code outside systems by the life domain they belong to. The parser accepts any string — these are the values the renderer has themed palettes for:\\n\\n| Category | Typical examples |\\n|---|---|\\n| `family` | Extended family, in-laws, cousins |\\n| `friends` | Friends, neighbors |\\n| `work` | Employer, coworkers |\\n| `education` | School, college, training program |\\n| `health` | Primary care, specialist, hospital |\\n| `mental-health` | Therapist, psychiatrist, support group |\\n| `religion` | Church, temple, spiritual community |\\n| `recreation` | Sports, hobbies, clubs |\\n| `legal` | Lawyer, probation, court |\\n| `government` | Social services, housing, immigration |\\n| `financial` | Bank, benefits, financial aid |\\n| `community` | Neighborhood groups, sponsors |\\n| `cultural` | Cultural/ethnic organizations |\\n| `substance` | Recovery programs or, if negative, active-use sources |\\n| `technology` | Online community, support forum |\\n| `pet` | Pets, service animals |\\n\\n```\\necomap\\n center: client [label: \\\"Marcus, age 15\\\"]\\n mom [label: \\\"Mother\\\", category: family]\\n dad [label: \\\"Father (divorced)\\\", category: family]\\n school [label: \\\"East High School\\\", category: education]\\n coach [label: \\\"Soccer Coach\\\", category: community]\\n therapist [label: \\\"Ms. Chen\\\", category: mental-health]\\n mom === client\\n dad --- client [label: \\\"EOW weekends\\\"]\\n school === client\\n coach --> client [label: \\\"mentor\\\"]\\n therapist <-> client [label: \\\"weekly\\\"]\\n```\\n\\n---\\n\\n## 3. Connections\\n\\nA connection is one line: `fromId OP toId` optionally followed by `[label: \\\"…\\\"]`. Both IDs must already be declared (center counts).\\n\\n### 3.1 Relationship-quality operators\\n\\n| Operator | Type | Meaning |\\n|---|---|---|\\n| `===` | strong | Close, supportive, high-frequency |\\n| `==` | moderate | Positive, moderate involvement |\\n| `---` | normal | Neutral / average |\\n| `- -` | weak | Tenuous, fragile, early-stage |\\n| `~~~` | stressful | Stressful relationship |\\n| `~=~` | stressful-strong | Close *and* stressful |\\n| `~x~` | conflictual | Active conflict |\\n| `-/-` | broken | Severed, estranged, cutoff |\\n\\n### 3.2 Energy-flow operators\\n\\nLayer arrow direction onto strong or normal lines:\\n\\n| Operator | Meaning |\\n|---|---|\\n| `-->` | One-way: energy flows from center to system |\\n| `<--` | One-way: energy flows from system to center |\\n| `<->` | Reciprocal / bidirectional |\\n| `===>` | Strong one-way outflow (draining) |\\n| `<===` | Strong one-way inflow (nourishing) |\\n| `<=>` | Strong reciprocal |\\n| `==>` | Moderate one-way outflow |\\n| `<==` | Moderate one-way inflow |\\n\\nThe parser normalizes direction so arrows read relative to the center. Writing `family === resettlement` and `resettlement === family` produces the same diagram; writing `clinic --> family` vs `family <-- clinic` likewise produces the same arrow pointing *from* the clinic *to* the family.\\n\\n```\\necomap\\n center: client [label: \\\"Rosa\\\"]\\n mom [category: family]\\n ex [category: family]\\n aa [label: \\\"AA Group\\\", category: substance]\\n job [label: \\\"Warehouse\\\", category: work]\\n mom === client [label: \\\"daily calls\\\"]\\n ex ~x~ client [label: \\\"custody disputes\\\"]\\n aa <== client [label: \\\"sponsor support\\\"]\\n job - - client [label: \\\"unstable hours\\\"]\\n```\\n\\n### 3.3 Edge labels\\n\\nA trailing `[label: \\\"…\\\"]` is the only attribute a connection line accepts. Put schedule, nature, or a short note here:\\n\\n```\\nfamily === temple [label: \\\"weekly service\\\"]\\nclinic --> family [label: \\\"vaccinations\\\"]\\ncaseworker <-> family [label: \\\"every Tuesday\\\"]\\n```\\n\\n---\\n\\n## 4. Labels & comments\\n\\n- **Title:** `ecomap \\\"Nguyen Family\\\"` — first line only.\\n- **Node label override:** `family [label: \\\"The Nguyens\\\"]`.\\n- **Edge label:** trailing `[label: \\\"…\\\"]` on a connection line.\\n- **Comments:** `#` or `//` at the start of a line (after leading whitespace). Inline trailing comments are **not** supported.\\n\\n```\\necomap \\\"Marcus Intake\\\"\\n # caseworker's notes — fine\\n // also fine\\n mom [category: family] # ← THIS trailing comment breaks the parser\\n```\\n\\n---\\n\\n## 5. Reserved words & escaping\\n\\n**Reserved at line start:** `ecomap`, `center:`.\\n\\n**Reserved operator tokens** inside a line — avoid using these sequences in IDs:\\n`===`, `==`, `---`, `- -`, `~~~`, `~=~`, `~x~`, `-/-`, and the directional variants listed in §3.2.\\n\\n**Strings with spaces** must be double-quoted: titles and any label. Single quotes and backticks are not recognized.\\n\\n---\\n\\n## 6. Common mistakes\\n\\nReal parser errors, what triggers them, and how to fix.\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `family -- school` | `Unexpected: family -- school` | Ecomap uses `===`/`---`/`<->` etc. `--` is a genogram/pedigree operator |\\n| `family === school` where `school` was never declared | Silently creates an empty `school` node with no label/category | Declare systems above their connection lines |\\n| No `center:` anywhere | Renders but with no visual center anchor | Every ecomap needs exactly one `center:` line |\\n| Two `center:` lines | Only the first is treated as center; the second becomes a regular system | Pick one |\\n| `family==school` (no spaces) | `Unexpected: family==school` | Operators require a space on each side |\\n| `family === school [weekly]` | Bare token `weekly` is parsed as a property flag, no label shown | Use `[label: \\\"weekly\\\"]` |\\n| `family === school # daily` | Trailing `#` is consumed as part of the line | Move the comment above |\\n\\n---\\n\\n## 7. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | center | system | connection)*\\n\\nheader = \\\"ecomap\\\" ( WS quoted-string )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\ncenter = \\\"center:\\\" WS id ( \\\"[\\\" attrs \\\"]\\\" )? NEWLINE\\nsystem = id ( \\\"[\\\" attrs \\\"]\\\" )? NEWLINE\\nconnection = id WS op WS id ( WS \\\"[\\\" \\\"label:\\\" quoted-string \\\"]\\\" )? NEWLINE\\n\\nop = \\\"===\\\" | \\\"==\\\" | \\\"---\\\" | \\\"- -\\\" | \\\"~~~\\\" | \\\"~=~\\\" | \\\"~x~\\\" | \\\"-/-\\\"\\n | \\\"===>\\\" | \\\"<===\\\" | \\\"<=>\\\" | \\\"==>\\\" | \\\"<==\\\"\\n | \\\"-->\\\" | \\\"<--\\\" | \\\"<->\\\"\\n\\nid = [a-zA-Z] [a-zA-Z0-9_-]*\\nattrs = attr (\\\",\\\" attr)*\\nattr = \\\"label\\\" \\\":\\\" quoted-string\\n | \\\"category\\\" \\\":\\\" category\\n | \\\"size\\\" \\\":\\\" (\\\"small\\\" | \\\"medium\\\" | \\\"large\\\")\\n | \\\"importance\\\" \\\":\\\" (\\\"major\\\" | \\\"moderate\\\" | \\\"minor\\\")\\n | \\\"sector\\\" \\\":\\\" (\\\"top\\\" | \\\"right\\\" | \\\"bottom\\\" | \\\"left\\\")\\n | \\\"age\\\" \\\":\\\" digits\\n | \\\"male\\\" | \\\"female\\\" | \\\"unknown\\\"\\n | key \\\":\\\" value // custom, stored as metadata\\n\\ncategory = \\\"family\\\" | \\\"friends\\\" | \\\"work\\\" | \\\"education\\\" | \\\"health\\\"\\n | \\\"mental-health\\\" | \\\"religion\\\" | \\\"recreation\\\" | \\\"legal\\\"\\n | \\\"government\\\" | \\\"financial\\\" | \\\"community\\\" | \\\"cultural\\\"\\n | \\\"substance\\\" | \\\"technology\\\" | \\\"pet\\\" | \\\"other\\\"\\n\\ncomment = ( \\\"#\\\" | \\\"//\\\" ) any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/ecomap/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"pedigree\": {\n \"title\": \"Pedigree\",\n \"content\": \"## 1. Your first pedigree\\n\\nThe smallest clinically useful pedigree: two parents and their affected child.\\n\\n```\\npedigree\\n I-1 [male, carrier]\\n I-2 [female, carrier]\\n I-1 -- I-2\\n II-1 [male, affected, proband]\\n II-2 [female, unaffected]\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with the keyword `pedigree`, optionally followed by a quoted title.\\n2. Declare each individual on their own line: `id [attributes]`. Conventionally IDs are `I-1`, `II-3`, etc. — Roman-numeral generation, dash, position within the generation.\\n3. Connect two individuals with a **couple operator** — `--` (mated), `==` (consanguineous), `-/-` (separated), `~` (no offspring). See §4.\\n4. **Indent under the couple line** to add their children. Any deeper indent works; two spaces is conventional.\\n\\n> Comments must be on their own line, starting with `#`. Inline trailing comments will break the parser.\\n\\n---\\n\\n## 2. Individuals\\n\\nAn individual line is `id [attr1, attr2, …]`. Attributes are comma-separated, order-independent, all optional.\\n\\n**ID rules.** Must match `[a-zA-Z][a-zA-Z0-9_-]*`. IDs are case-insensitive internally but preserve their original casing as the display label (override with `label:\\\"…\\\"`).\\n\\n**Attributes accepted by the parser today:**\\n\\n| Attribute | Values | Effect |\\n|---|---|---|\\n| Sex | `male`, `female`, `unknown`, `amab`, `afab`, `uaab` | Shape: square, circle, diamond (see §3) |\\n| Genetic status | `unaffected`, `affected`, `carrier`, `carrier-x`, `obligate-carrier`, `presymptomatic` | Fill / inner marker (see §3) |\\n| Marker | `proband`, `consultand`, `evaluated` | Arrow + letter annotation (see §3.3) |\\n| Life status | `deceased`, `stillborn`, `pregnancy`, `sab`, `tab`, `ectopic` | Visual modifier |\\n| Birth year | 4-digit number, e.g. `1958` | Shown below shape |\\n| `label:\\\"…\\\"` | any quoted string | Display label override |\\n| `affected: trait1+trait2` | see §5 | Multi-trait quadrant fill |\\n\\n```\\npedigree\\n I-1 [male, 1942, deceased]\\n I-2 [female, 1945, affected, deceased]\\n II-1 [female, affected, proband, label: \\\"Jane (42)\\\"]\\n II-2 [male, evaluated]\\n II-3 [female, presymptomatic]\\n```\\n\\n---\\n\\n## 3. Shapes, status, markers\\n\\n### 3.1 Shapes (Bennett 2022)\\n\\n| Visual | Sex value | Meaning |\\n|---|---|---|\\n| ☐ Square | `male` or `amab` | Assigned male at birth |\\n| ○ Circle | `female` or `afab` | Assigned female at birth |\\n| ◇ Diamond | `unknown`, `uaab`, or omitted | Unknown / DSD / not disclosed / in utero |\\n\\nBennett 2022 formalized that square and circle represent **assigned sex at birth**, not gender identity. If gender identity differs, record it in the label (`[female, label: \\\"Trans man (AFAB)\\\"]`) — do not change the shape.\\n\\n### 3.2 Genetic status (fill)\\n\\n| Status | Meaning |\\n|---|---|\\n| (default, no status token) | Unaffected — empty shape |\\n| `unaffected` | Explicit unaffected |\\n| `affected` | Fully filled shape |\\n| `carrier` | Half-filled — autosomal carrier |\\n| `carrier-x` | Center dot — X-linked carrier female |\\n| `obligate-carrier` | Center dot — inferred from pedigree structure |\\n| `presymptomatic` | Vertical line through shape — tested positive, no clinical signs yet |\\n\\n```\\npedigree\\n I-1 [male, unaffected]\\n I-2 [female, affected]\\n I-3 [female, carrier]\\n I-4 [female, carrier-x]\\n I-5 [male, obligate-carrier]\\n I-6 [female, presymptomatic]\\n```\\n\\n### 3.3 Markers\\n\\n| Marker | Meaning |\\n|---|---|\\n| `proband` | Arrow + \\\"P\\\" — the index case who triggered the referral |\\n| `consultand` | Arrow + \\\"C\\\" — the person who sought genetic counseling |\\n| `evaluated` | \\\"E\\\" — evaluated but no positive finding recorded |\\n\\n### 3.4 Life status\\n\\n| Value | Meaning |\\n|---|---|\\n| `deceased` | Diagonal slash across the shape |\\n| `stillborn` | Small shape + \\\"SB\\\" label |\\n| `pregnancy` | Shape + \\\"P\\\" label, or diamond if sex unknown |\\n| `sab` | Small triangle — spontaneous abortion |\\n| `tab` | Small triangle with bar — terminated pregnancy |\\n| `ectopic` | Small triangle + \\\"ECT\\\" label |\\n\\nMultiple tokens combine: `[female, affected, deceased]`, `[male, sab]`, `[unknown, pregnancy, presymptomatic]`.\\n\\n---\\n\\n## 4. Couples and children\\n\\n### 4.1 Couple operators\\n\\nThe parser tries these in order. The first match wins — so `-/-` beats `--`.\\n\\n| Operator | Type | Example | Meaning |\\n|---|---|---|---|\\n| `-/-` | separated | `a -/- b` | Mated pair, no longer together |\\n| `==` | consanguineous | `a == b` | Blood-related union (clinically critical) |\\n| `--` | married | `a -- b` | Mated pair with offspring |\\n| `~` | cohabiting | `a ~ b` | Partners without offspring |\\n\\n### 4.2 Inline individual on the right side\\n\\nIf the right-hand individual has not been declared yet, declare them in place:\\n\\n```\\npedigree\\n I-1 [female, carrier]\\n I-1 -- I-2 [male, unaffected]\\n II-1 [female, affected, proband]\\n II-2 [male, carrier]\\n```\\n\\n### 4.3 Children (indented under a couple)\\n\\nIndentation under a couple line = \\\"these are the children of this couple.\\\" Any indent greater than the couple's indent works; two spaces is conventional.\\n\\n```\\npedigree \\\"Cystic Fibrosis — autosomal recessive\\\"\\n I-1 [male, carrier]\\n I-2 [female, carrier]\\n I-1 -- I-2\\n II-1 [male, affected, proband]\\n II-2 [female, carrier]\\n II-3 [male, unaffected]\\n```\\n\\n### 4.4 Consanguineous unions\\n\\nConsanguinity is rendered as a double line and must be made visible — it is the single most load-bearing piece of information on many pedigrees.\\n\\n```\\npedigree \\\"Consanguineous union\\\"\\n I-1 [male, carrier]\\n I-2 [female, unaffected]\\n I-1 -- I-2\\n II-1 [male, carrier]\\n I-3 [male, unaffected]\\n I-4 [female, carrier]\\n I-3 -- I-4\\n II-3 [female, carrier]\\n II-1 == II-3\\n III-1 [male, affected, proband]\\n```\\n\\n---\\n\\n## 5. Multi-trait pedigrees\\n\\nFor families that carry more than one heritable condition, use `legend:` lines to define which quadrant of the shape represents which trait, then tag individuals with `affected: trait1+trait2`.\\n\\n```\\npedigree \\\"Cancer Family Syndrome\\\"\\n legend: breast = \\\"Breast cancer\\\" (fill: quad-tl)\\n legend: ovarian = \\\"Ovarian cancer\\\" (fill: quad-tr)\\n legend: prostate = \\\"Prostate cancer\\\" (fill: quad-bl)\\n legend: colon = \\\"Colon cancer\\\" (fill: quad-br)\\n\\n I-1 [male, affected: prostate, deceased]\\n I-2 [female, affected: breast, deceased]\\n I-1 -- I-2\\n II-1 [female, affected: breast+ovarian]\\n II-2 [male, unaffected]\\n```\\n\\n**Legend syntax:** `legend: id = \\\"Human label\\\" (fill: POSITION)`.\\n\\n| `fill` position | Region |\\n|---|---|\\n| `full` | Entire shape (default if `(fill: …)` omitted) |\\n| `quad-tl` / `quad-tr` / `quad-bl` / `quad-br` | Top-left / top-right / bottom-left / bottom-right quadrant |\\n| `half-left` / `half-right` / `half-top` / `half-bottom` | A half of the shape |\\n\\nIndividuals use the legend trait IDs: `[affected: breast]`, `[affected: breast+ovarian]`. The `+` joins traits; quadrants fill cumulatively.\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Title:** `pedigree \\\"BRCA1 Family\\\"` — first line only.\\n- **Individual label override:** `II-1 [female, affected, label: \\\"Jane Smith (42)\\\"]`.\\n- **Legend entry:** `legend: id = \\\"Label\\\" (fill: POSITION)` — see §5.\\n- **Comments:** `#` or `//` at the start of a line (after leading whitespace). Inline trailing comments are **not** supported.\\n\\n---\\n\\n## 7. Reserved words & escaping\\n\\n**Reserved at line start:** `pedigree` (header), `legend:` (legend entry).\\n\\n**Reserved operator tokens** inside a line — avoid using these sequences in IDs:\\n`--`, `==`, `-/-`, `~`.\\n\\n**Reserved attribute tokens** inside `[…]` — the parser will interpret these regardless of position: sex tokens (`male`, `female`, `unknown`, `amab`, `afab`, `uaab`), genetic statuses (`affected`, `carrier`, `carrier-x`, `obligate-carrier`, `presymptomatic`, `unaffected`), markers (`proband`, `consultand`, `evaluated`), and life statuses (`deceased`, `stillborn`, `pregnancy`, `sab`, `tab`, `ectopic`).\\n\\n**Strings with spaces** must be double-quoted. Single quotes and backticks are not recognized.\\n\\n---\\n\\n## 8. Common mistakes\\n\\nReal parser errors, what triggers them, and how to fix.\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `II-1 -- II-4` where `II-4` was never declared | `Unknown individual 'II-4'` | Declare `II-4` above, or use inline form: `II-1 -- II-4 [male, unaffected]` |\\n| `II-1 [nonbinary]` | Silently stored as a custom property; shape stays diamond | Bennett 2022 distinguishes assigned sex from gender — use `amab`/`afab`/`uaab` and record identity in `label:` |\\n| `II-3 [twin-mz]` | Stored as a custom property; no twin line rendered | Twin notation is §10 Roadmap |\\n| `I-1 -- I-2` followed by `II-1 [male]` at the **same indent** | Child parsed as a new top-level individual, not as offspring | Indent the child line deeper than the couple line |\\n| `I-1 [affected: breast]` with no matching `legend:` | Trait ID stored but no legend-keyed fill is rendered | Add `legend: breast = \\\"…\\\" (fill: quad-tl)` above |\\n| `II-1[affected]` (no space, no attrs split) | Works for a single token; breaks when a second attribute is added | Always separate `id` and `[…]` with a space |\\n| Line 1 is `I-1 [male]` with no `pedigree` header | `Expected \\\"pedigree\\\" header` | Start with `pedigree` or `pedigree \\\"Title\\\"` |\\n\\n---\\n\\n## 9. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | legend | individual | couple-block)*\\n\\nheader = \\\"pedigree\\\" ( WS quoted-string )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nlegend = INDENT \\\"legend:\\\" WS id WS \\\"=\\\" WS quoted-string\\n ( WS \\\"(\\\" \\\"fill:\\\" fill-value \\\")\\\" )? NEWLINE\\n\\nindividual = INDENT id ( \\\"[\\\" attrs \\\"]\\\" )? NEWLINE\\ncouple-block = INDENT id WS coupleOp WS right-side NEWLINE\\n ( deeper-indent child )*\\nchild = INDENT id ( \\\"[\\\" attrs \\\"]\\\" )? NEWLINE\\nright-side = id ( \\\"[\\\" attrs \\\"]\\\" )?\\n\\ncoupleOp = \\\"-/-\\\" | \\\"==\\\" | \\\"--\\\" | \\\"~\\\"\\n\\nid = [a-zA-Z] [a-zA-Z0-9_-]*\\nattrs = attr (\\\",\\\" attr)*\\nattr = sex\\n | genetic-status\\n | marker\\n | life-status\\n | digit digit digit digit // birth year\\n | \\\"label\\\" \\\":\\\" quoted-string\\n | \\\"affected\\\" \\\":\\\" trait-id ( \\\"+\\\" trait-id )*\\n | key \\\":\\\" value // custom\\n\\nsex = \\\"male\\\" | \\\"female\\\" | \\\"unknown\\\" | \\\"amab\\\" | \\\"afab\\\" | \\\"uaab\\\"\\ngenetic-status = \\\"unaffected\\\" | \\\"affected\\\" | \\\"carrier\\\" | \\\"carrier-x\\\"\\n | \\\"obligate-carrier\\\" | \\\"presymptomatic\\\"\\nmarker = \\\"proband\\\" | \\\"consultand\\\" | \\\"evaluated\\\"\\nlife-status = \\\"deceased\\\" | \\\"stillborn\\\" | \\\"pregnancy\\\"\\n | \\\"sab\\\" | \\\"tab\\\" | \\\"ectopic\\\"\\nfill-value = \\\"full\\\" | \\\"half-left\\\" | \\\"half-right\\\" | \\\"half-top\\\" | \\\"half-bottom\\\"\\n | \\\"quad-tl\\\" | \\\"quad-tr\\\" | \\\"quad-bl\\\" | \\\"quad-br\\\"\\n\\ncomment = INDENT ( \\\"#\\\" | \\\"//\\\" ) any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/pedigree/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"phylo\": {\n \"title\": \"Phylogenetic tree\",\n \"content\": \"## 1. Your first phylogenetic tree\\n\\nThe smallest useful tree: four taxa, two clades.\\n\\n```\\nphylo \\\"Vertebrates\\\"\\n newick: \\\"((Human:0.1,Chimp:0.08):0.03,(Dog:0.35,Cat:0.30):0.2);\\\"\\n```\\n\\nThree rules cover 80% of usage:\\n\\n1. Start with `phylo`, optionally followed by a quoted title and bracket props.\\n2. Provide the tree topology in `newick:` format — the standard Newick string, quoted, on one line. The trailing `;` is optional.\\n3. Optionally define **clade** highlight groups and a **scale** label below the newick line.\\n\\n> Comments must start with `#` on their own line. Inline trailing comments are not supported.\\n\\n---\\n\\n## 2. Input formats\\n\\n### 2.1 Newick format\\n\\nNewick is the primary input. The full grammar is:\\n\\n```\\n(A,B,(C,D)); # topology only\\n(A:0.1,B:0.2,(C:0.3,D:0.4):0.5); # with branch lengths\\n((A:0.1,B:0.2):0.05[&&NHX:B=98],(C,D):0.08); # NHX bootstrap\\n('Homo sapiens':0.1,'Mus musculus':0.2); # quoted names with spaces\\n```\\n\\nBranch lengths follow the node name after a colon. Internal node support values can appear as plain brackets `[95]` or as NHX `[&&NHX:B=95]`.\\n\\n```\\nphylo \\\"Newick examples\\\"\\n newick: \\\"((A:0.1,B:0.2):0.05[&&NHX:B=98],(C:0.3,D:0.4):0.08[&&NHX:B=87]);\\\"\\n```\\n\\n**Newick rules the parser accepts:**\\n\\n| Feature | Syntax | Notes |\\n|---|---|---|\\n| Leaf name | `A`, `Homo_sapiens` | No spaces — use `_` or quote |\\n| Quoted leaf name | `'Homo sapiens'` | Single quotes; `''` is a literal quote inside |\\n| Branch length | `:0.035` after name | Float; optional |\\n| Internal node name | `(A,B)ancestor` | After closing `)` |\\n| Bootstrap (plain) | `(A,B)[95]` | Integer or float in brackets |\\n| Bootstrap (NHX) | `(A,B)[&&NHX:B=95]` | `B=` field; other NHX fields stored but not rendered |\\n| Semicolon | `;` at end | Optional — parser strips it |\\n| Polytomy | `(A,B,C)` | More than 2 children |\\n\\n### 2.2 Indent DSL\\n\\nFor hand-written or small trees, Schematex offers an indentation-based alternative that is easier to read and edit than raw Newick:\\n\\n```\\nphylo \\\"Vertebrates (indent DSL)\\\" [mode: phylogram]\\nroot:\\n :0.03\\n Human: 0.1\\n Chimp: 0.08\\n :0.2\\n Dog: 0.35\\n Cat: 0.30\\nscale \\\"substitutions/site\\\"\\n```\\n\\n**Indent DSL rules:**\\n\\n| Syntax | Meaning |\\n|---|---|\\n| `Name: length` | Leaf node with branch length |\\n| `: length` | Unnamed internal node with branch length |\\n| `Name` | Leaf node, no branch length (cladogram) |\\n| `Name [N]` | Node with support value N |\\n| Deeper indent | Child of the node above at a shallower indent |\\n| `#` line | Comment, ignored |\\n\\nThe first line that ends with `:` and has no spaces triggers indent-tree mode (e.g. `root:`). The name before the colon becomes the root label; all indented lines below become its children.\\n\\n---\\n\\n## 3. Layout\\n\\nSet the layout in the header brackets: `phylo \\\"Title\\\" [layout: rectangular]`.\\n\\n| Layout | Value | Description |\\n|---|---|---|\\n| Rectangular | `rectangular` | Default. L-shaped branches; root on left, tips on right |\\n| Slanted | `slanted` | Diagonal lines from parent to child; more compact |\\n| Circular | `circular` | Root at center, tips around the circumference |\\n| Unrooted | `unrooted` | Equal-angle radial; emphasizes distance, not ancestry |\\n\\n`[unrooted]` as a bare flag is equivalent to `[layout: unrooted]`.\\n\\n**Circular** — root at center, tips fanning outward. Most visually striking for many-taxa trees with clade highlights.\\n\\n```\\nphylo \\\"Vertebrates — circular\\\" [layout: circular]\\n newick: \\\"((Human:0.1,Chimp:0.08,Gorilla:0.12):0.15,(Dog:0.35,Cat:0.30,Wolf:0.32):0.2,(Salmon:0.5,Zebrafish:0.45):0.3);\\\"\\n clade Primates = (Human, Chimp, Gorilla) [color: \\\"#1E88E5\\\", label: \\\"Primates\\\", highlight: both]\\n clade Carnivora = (Dog, Cat, Wolf) [color: \\\"#E53935\\\", label: \\\"Carnivora\\\", highlight: both]\\n```\\n\\n**Rectangular** — L-shaped branches; root on the left, tips on the right. The classic phylogram form for published figures.\\n\\n```\\nphylo \\\"Bacterial Diversity\\\" [layout: rectangular, mode: phylogram]\\n newick: \\\"((((Ecoli:0.1,Salmonella:0.12):0.05[&&NHX:B=98],Vibrio:0.2):0.08,((Bacillus:0.15,Staph:0.18):0.06[&&NHX:B=92],Listeria:0.22):0.1):0.15,((Myco_tb:0.3,Myco_leprae:0.28):0.12[&&NHX:B=100],(Strepto:0.25,Lactobacillus:0.2):0.08):0.2);\\\"\\n clade Gamma = (Ecoli, Salmonella, Vibrio) [color: \\\"#1E88E5\\\", label: \\\"γ-Proteobacteria\\\"]\\n clade Firmi = (Bacillus, Staph, Listeria) [color: \\\"#E53935\\\", label: \\\"Firmicutes\\\"]\\n scale \\\"substitutions/site\\\"\\n```\\n\\n**Slanted** — diagonal lines from parent to child; more compact than rectangular, same left-to-right reading direction.\\n\\n```\\nphylo \\\"Vertebrates — slanted\\\" [layout: slanted]\\n newick: \\\"((Human:0.1,Chimp:0.08,Gorilla:0.12):0.15,(Dog:0.35,Cat:0.30,Wolf:0.32):0.2,(Salmon:0.5,Zebrafish:0.45):0.3);\\\"\\n clade Primates = (Human, Chimp, Gorilla) [color: \\\"#1E88E5\\\", label: \\\"Primates\\\"]\\n clade Carnivora = (Dog, Cat, Wolf) [color: \\\"#E53935\\\", label: \\\"Carnivora\\\"]\\n scale \\\"substitutions/site\\\"\\n```\\n\\n**Unrooted** — equal-angle radial layout; de-emphasizes the root, emphasizes pairwise distance between all taxa.\\n\\n```\\nphylo \\\"Vertebrates — unrooted\\\" [unrooted]\\n newick: \\\"((Human:0.1,Chimp:0.08,Gorilla:0.12):0.15,(Dog:0.35,Cat:0.30,Wolf:0.32):0.2,(Salmon:0.5,Zebrafish:0.45):0.3);\\\"\\n clade Primates = (Human, Chimp, Gorilla) [color: \\\"#1E88E5\\\", label: \\\"Primates\\\"]\\n clade Carnivora = (Dog, Cat, Wolf) [color: \\\"#E53935\\\", label: \\\"Carnivora\\\"]\\n```\\n\\n---\\n\\n## 4. Mode\\n\\nSet with `[mode: …]` in the header (or in a `style [mode: …]` line).\\n\\n| Mode | Value | Branch length meaning |\\n|---|---|---|\\n| Phylogram | `phylogram` | Default. Proportional to evolutionary distance (substitutions/site) |\\n| Cladogram | `cladogram` | Ignored — tips align; only topology matters |\\n| Chronogram | `chronogram` | Proportional to divergence time; all tips align to \\\"present\\\" |\\n\\nChronogram requires branch lengths in units of time plus `[mrsd: \\\"YYYY\\\"]` (most-recent sampling date) in the header so the renderer can align tips to present.\\n\\n```\\nphylo \\\"SARS-CoV-2 variants\\\" [mode: chronogram, mrsd: \\\"2023\\\"]\\n newick: \\\"((Alpha:0.5,Delta:0.4):0.3,Omicron:0.8);\\\"\\n scale \\\"years\\\"\\n```\\n\\n---\\n\\n## 5. Clade highlighting\\n\\nA `clade` line marks a monophyletic group with a color, an optional label, and an optional highlight mode.\\n\\n```\\nclade ID = (member1, member2, ...) [color: \\\"#hex\\\", label: \\\"text\\\", highlight: mode]\\n```\\n\\n| Prop | Values | Effect |\\n|---|---|---|\\n| `color:` | hex string e.g. `\\\"#1E88E5\\\"` | Branch and/or background color |\\n| `label:` | quoted string | Clade label shown at right margin |\\n| `highlight:` | `branch`, `background`, `both` | `branch` colors lines; `background` shades the region; `both` does both |\\n\\nMembers are tip (leaf) IDs from the Newick string. The renderer computes the MRCA of the listed tips and highlights the entire subtree rooted there.\\n\\n```\\nphylo \\\"Mammal clades\\\" [layout: rectangular]\\n newick: \\\"(((Human:0.1,Chimp:0.08,Gorilla:0.12):0.15,Mouse:0.45):0.05,(Dog:0.35,(Cat:0.30,Tiger:0.32):0.1):0.2);\\\"\\n clade Primates = (Human, Chimp, Gorilla) [color: \\\"#1E88E5\\\", label: \\\"Primates\\\", highlight: both]\\n clade Carnivora = (Dog, Cat, Tiger) [color: \\\"#E53935\\\", label: \\\"Carnivora\\\", highlight: branch]\\n```\\n\\n---\\n\\n## 6. Scale bar and outgroup\\n\\n**Scale bar:** `scale \\\"label\\\"` — adds a bar at the bottom. The label describes the unit (e.g. `\\\"substitutions/site\\\"`, `\\\"Mya\\\"`). Omit for cladogram mode where branch lengths have no meaning.\\n\\n**Outgroup:** `outgroup: taxonId` — records the outgroup for documentation; the renderer may use it to visually mark the outgroup taxon.\\n\\n```\\nphylo \\\"Vertebrates\\\"\\n newick: \\\"((Human:0.1,Chimp:0.08):0.03,Lamprey:0.8);\\\"\\n outgroup: Lamprey\\n scale \\\"substitutions/site\\\"\\n```\\n\\n---\\n\\n## 7. Header props reference\\n\\nAll options go inside `[…]` on the `phylo` header line, or in a `style […]` line anywhere in the body.\\n\\n| Prop | Values | Default | Effect |\\n|---|---|---|---|\\n| `layout:` | `rectangular`, `slanted`, `circular`, `unrooted` | `rectangular` | Tree layout |\\n| `mode:` | `phylogram`, `cladogram`, `chronogram` | `phylogram` | Branch length semantics |\\n| `unrooted` | (flag) | — | Equivalent to `layout: unrooted` |\\n| `branch-width:` | number | `1.5` | Stroke width of branches |\\n| `openAngle:` | number (degrees) | `0` | Fan gap for circular layout (0 = full 360°) |\\n| `mrsd:` | quoted year string | — | Most-recent sampling date for chronograms |\\n\\n---\\n\\n## 8. Labels & comments\\n\\n- **Title:** `phylo \\\"Tree of Life\\\"` — first line only.\\n- **Scale label:** `scale \\\"substitutions/site\\\"` — one per document.\\n- **Clade label:** `[label: \\\"Primates\\\"]` inside a `clade` line.\\n- **Comments:** `#` at the start of a line (after leading whitespace). Inline trailing comments are not supported.\\n\\n---\\n\\n## 9. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `newick: (A,B,C);` (unquoted) | `PhyloParseError: Phylo document must start with 'phylo'` | Quote the Newick string: `newick: \\\"(A,B,C);\\\"` |\\n| Tip name with a space: `Homo sapiens:0.1` | Parsed as `Homo` — space terminates an unquoted name | Use underscore (`Homo_sapiens`) or single-quote (`'Homo sapiens'`) |\\n| Leaf ID in `clade` doesn't match Newick name | Clade silently has 0 members; no highlight | Copy names exactly as they appear in the Newick string |\\n| `clade X = (A, B)` with no `newick:` or indent tree | `PhyloParseError: No tree definition found` | Add a `newick:` line or an indent tree block |\\n| `mode: chronogram` with no branch lengths | Renderer treats all lengths as 0; tips overlap at root | Add `:length` to every edge in the Newick string |\\n| `root:` line not detected | If the `root:` line has a space in the name (e.g. `My root:`) the indent tree is not triggered | Use a single-word root label or `root:` |\\n| Newick with internal node names: `(A,B)ancestor:0.5` | Parses fine — `ancestor` is the internal node label | Supported; internal names appear on internal nodes |\\n\\n---\\n\\n## 10. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | newick-line | scale-line\\n | outgroup-line | clade-line | style-line | indent-line)*\\n\\nheader = \\\"phylo\\\" ( WS quoted-string )? ( WS \\\"[\\\" props \\\"]\\\" )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nnewick-line = \\\"newick:\\\" WS quoted-newick NEWLINE\\nscale-line = \\\"scale\\\" ( WS quoted-string )? NEWLINE\\noutgroup-line = \\\"outgroup:\\\" WS id NEWLINE\\nclade-line = \\\"clade\\\" WS id WS \\\"=\\\" WS \\\"(\\\" id (\\\",\\\" id)* \\\")\\\"\\n ( WS \\\"[\\\" clade-props \\\"]\\\" )? NEWLINE\\nstyle-line = \\\"style\\\" WS \\\"[\\\" props \\\"]\\\" NEWLINE\\n\\n// Indent tree — triggered by a line ending in \\\":\\\" with no spaces\\nindent-tree = root-line indent-node*\\nroot-line = id \\\":\\\" NEWLINE\\nindent-node = INDENT ( id \\\":\\\" length | \\\":\\\" length | id ) ( WS \\\"[\\\" number \\\"]\\\" )? NEWLINE\\n\\nprops = prop (\\\",\\\" prop)*\\nprop = \\\"layout:\\\" layout-value\\n | \\\"mode:\\\" mode-value\\n | \\\"unrooted\\\"\\n | \\\"branch-width:\\\" number\\n | \\\"openAngle:\\\" number\\n | \\\"mrsd:\\\" quoted-string\\n\\nclade-props = clade-prop (\\\",\\\" clade-prop)*\\nclade-prop = \\\"color:\\\" quoted-string\\n | \\\"label:\\\" quoted-string\\n | \\\"highlight:\\\" ( \\\"branch\\\" | \\\"background\\\" | \\\"both\\\" )\\n\\nlayout-value = \\\"rectangular\\\" | \\\"slanted\\\" | \\\"circular\\\" | \\\"unrooted\\\"\\nmode-value = \\\"phylogram\\\" | \\\"cladogram\\\" | \\\"chronogram\\\"\\n\\n// Newick grammar (embedded, parsed separately)\\nnewick = subtree \\\";\\\"?\\nsubtree = leaf | internal\\ninternal = \\\"(\\\" subtree (\\\",\\\" subtree)* \\\")\\\" name? nhx? length?\\nleaf = name nhx? length?\\nname = unquoted-name | \\\"'\\\" single-quoted \\\"'\\\")\\nlength = \\\":\\\" number\\nnhx = \\\"[\\\" number \\\"]\\\" // plain bootstrap\\n | \\\"[&&NHX:\\\" nhx-pair (\\\":\\\" nhx-pair)* \\\"]\\\"\\nnhx-pair = key \\\"=\\\" value\\n\\nid = [a-zA-Z] [a-zA-Z0-9_-]*\\nnumber = /[+-]?[0-9]+(\\\\.[0-9]+)?([eE][+-]?[0-9]+)?/\\ncomment = INDENT \\\"#\\\" any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/phylo/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"sociogram\": {\n \"title\": \"Sociogram\",\n \"content\": \"## 1. Your first sociogram\\n\\nThe smallest useful sociogram: four people, three different relationship types.\\n\\n```\\nsociogram \\\"Study group\\\"\\n alice [label: \\\"Alice\\\"]\\n bob [label: \\\"Bob\\\"]\\n carol [label: \\\"Carol\\\"]\\n dave [label: \\\"Dave\\\"]\\n alice <-> bob [label: \\\"lab partners\\\"]\\n carol -> alice\\n dave -x> bob [label: \\\"rivalry\\\"]\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with the keyword `sociogram`, optionally followed by a quoted title.\\n2. Each person is a **node** — declared explicitly with `id [label: \\\"…\\\"]` or auto-created the first time they appear in an edge.\\n3. Connect two nodes with an **edge operator** — `<->` (mutual), `->` (one-way), `-x>` (rejection), `-.-` (neutral). See §3.\\n4. Optionally declare **groups** and **config** lines to control layout and coloring.\\n\\n> Comments must start with `#` on their own line.\\n\\n---\\n\\n## 2. Nodes\\n\\nA node line is `id [attr: value, …]`. Nodes are also created implicitly when first referenced in an edge — but explicit declaration lets you set labels, groups, and roles.\\n\\n**ID rules.** Must match `[a-zA-Z][a-zA-Z0-9_-]*`. The ID is used internally; the `label:` attribute sets the display name.\\n\\n**Node attributes:**\\n\\n| Attribute | Values | Effect |\\n|---|---|---|\\n| `label: \\\"…\\\"` | quoted string | Display name (defaults to the ID) |\\n| `group: id` | group ID | Associates the node with a group for coloring |\\n| `role: …` | `star`, `isolate`, `bridge`, `neglectee`, `rejected` | Explicit sociometric role annotation |\\n| `size: …` | `small`, `medium`, `large` | Node size override |\\n\\n```\\nsociogram \\\"Group roles demo\\\"\\n config: layout = circular\\n dr_park [label: \\\"Dr. Park\\\", role: star]\\n james [label: \\\"James\\\"]\\n nina [label: \\\"Nina\\\", role: neglectee]\\n loner [label: \\\"Alex\\\", role: isolate]\\n bridge [label: \\\"Sam\\\", role: bridge, size: large]\\n james -> dr_park\\n james -> bridge\\n bridge -> dr_park\\n bridge -> nina\\n nina -> james\\n```\\n\\n---\\n\\n## 3. Edges\\n\\nAn edge line is `leftId OP rightId` optionally followed by `[label: \\\"…\\\", weight: N]`. Both IDs are auto-registered as nodes if not yet declared.\\n\\n### 3.1 Direction and valence\\n\\n```\\nsociogram \\\"Edge types\\\"\\n config: layout = circular\\n a [label: \\\"A\\\"]\\n b [label: \\\"B\\\"]\\n c [label: \\\"C\\\"]\\n d [label: \\\"D\\\"]\\n e [label: \\\"E\\\"]\\n f [label: \\\"F\\\"]\\n # Positive\\n a -> b [label: \\\"chose B\\\"]\\n b <-> c [label: \\\"mutual\\\"]\\n # Negative\\n c -x> d [label: \\\"rejects D\\\"]\\n d <x-> e [label: \\\"mutual reject\\\"]\\n # Neutral\\n e -.- f [label: \\\"indifferent\\\"]\\n f <.-> a [label: \\\"mutual neutral\\\"]\\n```\\n\\n| Operator | Direction | Valence | Meaning |\\n|---|---|---|---|\\n| `A -> B` | one-way | positive | A chose B |\\n| `A <- B` | one-way | positive | B chose A (same as `B -> A`) |\\n| `A <-> B` | mutual | positive | Both chose each other |\\n| `A -- B` | undirected | positive | Relationship known; direction not recorded |\\n| `A -x> B` | one-way | negative | A rejects B |\\n| `A <x- B` | one-way | negative | B rejects A |\\n| `A <x-> B` | mutual | negative | Mutual rejection |\\n| `A -x- B` | undirected | negative | Conflict; direction unknown |\\n| `A -.> B` | one-way | neutral | A is indifferent toward B |\\n| `A <.-> B` | mutual | neutral | Mutual indifference |\\n| `A -.- B` | undirected | neutral | Neutral relationship |\\n\\n### 3.2 Weight / strength\\n\\nHigher weight = thicker line. Use the shorthand operators or override explicitly with `[weight: N]`.\\n\\n| Weight | Shorthand | Direction | Meaning |\\n|---|---|---|---|\\n| 2 (default) | `->` `<->` `--` `-x>` `-.-` | any | Standard connection |\\n| 3 | `==>` `<==` `<==>` `===` | one-way / mutual / undirected | Strong |\\n| 4 | `===>` `<===` `<===>` | one-way / mutual | Very strong |\\n| custom | `[weight: N]` | — | Any integer |\\n\\n```\\nsociogram \\\"Relationship strengths\\\"\\n config: layout = circular\\n a [label: \\\"A\\\"]\\n b [label: \\\"B\\\"]\\n c [label: \\\"C\\\"]\\n d [label: \\\"D\\\"]\\n a -> b [label: \\\"weight 2 (default)\\\"]\\n b ==> c [label: \\\"weight 3 (strong)\\\"]\\n c ===> d [label: \\\"weight 4 (very strong)\\\"]\\n d -> a [weight: 1, label: \\\"weight 1 (weak)\\\"]\\n```\\n\\n### 3.3 Edge labels\\n\\n`A -> B [label: \\\"best friend\\\"]` — the label appears on the connecting line.\\n\\n---\\n\\n## 4. Groups\\n\\nA `group` block collects nodes into a named subgroup for coloring and layout clustering.\\n\\n**Group syntax:**\\n- `group id [label: \\\"…\\\", color: \\\"#hex\\\"]` — the group header line.\\n- Member lines follow, each indented **at least 4 spaces**, one node per line.\\n- A non-indented line (or the next `group`) closes the current group.\\n- Members can carry their own props: `anna [label: \\\"Anna K.\\\", size: large]`.\\n\\nNodes can also be assigned inline: `alice [group: girls]`.\\n\\n```\\nsociogram \\\"Classroom dynamics\\\"\\n config: layout = force-directed\\n group boys [label: \\\"Boys\\\", color: \\\"#42A5F5\\\"]\\n tom [label: \\\"Tom\\\"]\\n jack [label: \\\"Jack\\\"]\\n mike [label: \\\"Mike\\\"]\\n leo [label: \\\"Leo\\\"]\\n group girls [label: \\\"Girls\\\", color: \\\"#EF5350\\\"]\\n anna [label: \\\"Anna\\\"]\\n beth [label: \\\"Beth\\\"]\\n chloe [label: \\\"Chloe\\\"]\\n diana [label: \\\"Diana\\\"]\\n tom <-> jack\\n anna <-> beth\\n anna <-> chloe\\n beth <-> chloe\\n mike -x> leo [label: \\\"conflict\\\"]\\n diana -> anna\\n tom -> anna [label: \\\"cross-group\\\"]\\n```\\n\\n---\\n\\n## 5. Configuration\\n\\n`config:` lines tune layout and visual encoding. Each is its own line.\\n\\n| Config key | Values | Default | Effect |\\n|---|---|---|---|\\n| `layout` | `circular`, `force-directed`, `concentric` | `circular` | Placement algorithm |\\n| `sizing` | `uniform`, `in-degree`, `betweenness` | `uniform` | Node size by metric |\\n| `coloring` | `default`, `group`, `role` | `default` | Node color scheme |\\n| `highlight` | comma list: `stars`, `isolates`, `cliques` | `stars,isolates` | Which patterns to annotate |\\n\\n**Layout notes:**\\n- `circular` — nodes evenly spaced on a ring. Best for small groups (≤15).\\n- `force-directed` — spring model; clusters emerge automatically. Best for medium-sized groups with distinct subgroups.\\n- `concentric` — inner rings hold high-in-degree nodes. Best for showing core-periphery structure.\\n\\n**Circular** — uniform ring placement; every node equally visible. Best for small, tightly-knit groups.\\n\\n```\\nsociogram \\\"Therapy group — circular\\\"\\n config: layout = circular\\n dr_park [label: \\\"Dr. Park\\\", role: star]\\n james [label: \\\"James\\\"]\\n maria [label: \\\"Maria\\\"]\\n lee [label: \\\"Lee\\\"]\\n sarah [label: \\\"Sarah\\\"]\\n tom [label: \\\"Tom\\\"]\\n nina [label: \\\"Nina\\\", role: neglectee]\\n james -> dr_park\\n james <-> maria [weight: 3]\\n james -> lee\\n maria -> dr_park\\n lee -> dr_park\\n lee -x> nina\\n sarah <-> nina\\n sarah -> dr_park\\n tom -> dr_park\\n nina -> maria\\n```\\n\\n**Force-directed** — spring physics pulls connected nodes together and pushes disconnected ones apart. Subgroups cluster organically.\\n\\n```\\nsociogram \\\"Classroom dynamics — force-directed\\\"\\n config: layout = force-directed\\n group boys [label: \\\"Boys\\\", color: \\\"#42A5F5\\\"]\\n tom [label: \\\"Tom\\\"]\\n jack [label: \\\"Jack\\\"]\\n mike [label: \\\"Mike\\\"]\\n leo [label: \\\"Leo\\\"]\\n group girls [label: \\\"Girls\\\", color: \\\"#EF5350\\\"]\\n anna [label: \\\"Anna\\\"]\\n beth [label: \\\"Beth\\\"]\\n chloe [label: \\\"Chloe\\\"]\\n diana [label: \\\"Diana\\\"]\\n tom <-> jack\\n anna <-> beth\\n anna <-> chloe\\n beth <-> chloe\\n mike -x> leo [label: \\\"conflict\\\"]\\n diana -> anna\\n tom -> anna [label: \\\"cross-group\\\"]\\n jack -> beth\\n```\\n\\n**Concentric** — nodes sorted by in-degree; high-centrality nodes appear on inner rings, peripheral nodes on outer rings.\\n\\n```\\nsociogram \\\"Informal influence — concentric\\\"\\n config: layout = concentric\\n config: sizing = in-degree\\n vp [label: \\\"VP Eng\\\"]\\n lead_a [label: \\\"Lead A\\\"]\\n lead_b [label: \\\"Lead B\\\"]\\n alice [label: \\\"Alice\\\"]\\n bob [label: \\\"Bob\\\"]\\n carol [label: \\\"Carol\\\"]\\n dave [label: \\\"Dave\\\"]\\n alice -> lead_a\\n bob -> lead_a\\n carol -> lead_a\\n carol -> lead_b\\n dave -> lead_b\\n lead_a -> vp\\n lead_b -> vp\\n alice <-> bob\\n carol <-> dave\\n```\\n\\n---\\n\\n## 6. Sociometric roles\\n\\nThe parser stores role annotations on nodes. The renderer uses them to apply visual badges — a star marker for `star`, a dashed border for `isolate`, and so on.\\n\\n| Role | Meaning |\\n|---|---|\\n| `star` | Central figure chosen by many (high in-degree) |\\n| `isolate` | No connections in or out |\\n| `neglectee` | Reaches out to others but receives no choices |\\n| `rejected` | Receives rejection edges from multiple members |\\n| `bridge` | Connects two otherwise separate clusters |\\n\\n```\\nsociogram \\\"Role annotations\\\"\\n config: layout = circular\\n center [label: \\\"Maria\\\", role: star]\\n bridge_node [label: \\\"Sam\\\", role: bridge]\\n newcomer [label: \\\"New Kid\\\", role: neglectee]\\n loner [label: \\\"Alex\\\", role: isolate]\\n outcast [label: \\\"Pat\\\", role: rejected]\\n center <-> bridge_node\\n bridge_node -> outcast\\n newcomer -> center\\n newcomer -> bridge_node\\n center -x> outcast\\n bridge_node -x> outcast\\n```\\n\\n---\\n\\n## 7. Labels & comments\\n\\n- **Title:** `sociogram \\\"Study group\\\"` — first line only.\\n- **Node label:** `alice [label: \\\"Alice K.\\\"]`.\\n- **Group label:** `group boys [label: \\\"Boys\\\"]`.\\n- **Edge label:** `alice -> bob [label: \\\"lab partners\\\"]`.\\n- **Comments:** `#` at the start of a line (after leading whitespace).\\n\\n---\\n\\n## 8. Reserved words & escaping\\n\\n**Reserved at line start:** `sociogram` (header), `group`, `config:`.\\n\\n**Reserved operator tokens** — avoid these sequences inside IDs: `->`, `<-`, `<->`, `--`, `===`, `==>`, `<==`, `<===>`, `-x>`, `<x-`, `-x-`, `<x->`, `-.>`, `<.->`, `-.-`.\\n\\n**Strings with spaces** must be double-quoted in `label:` and `color:` values.\\n\\n---\\n\\n## 9. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `tom; jack; mike` on one group line | `tom;` fails the ID regex — silently ignored | One node per line, each indented ≥4 spaces |\\n| Group members indented 2 spaces | Not treated as group members (parser requires ≥4) | Use 4+ spaces indent |\\n| `alice <> bob` | No matching operator — not parsed as an edge | Use `<->` for mutual positive |\\n| `config: layout = grid` | Unknown value silently ignored; layout stays `circular` | Use `circular`, `force-directed`, or `concentric` |\\n| Node with a space in the ID: `dr park` | Parser takes `dr` as the ID and `park` as a stray token | Use underscore: `dr_park [label: \\\"Dr. Park\\\"]` |\\n\\n---\\n\\n## 10. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | config | group-block | edge | node)*\\n\\nheader = \\\"sociogram\\\" ( WS quoted-string )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nconfig = \\\"config:\\\" WS key WS \\\"=\\\" WS value NEWLINE\\nkey = \\\"layout\\\" | \\\"sizing\\\" | \\\"coloring\\\" | \\\"highlight\\\"\\n\\ngroup-block = \\\"group\\\" WS id ( \\\"[\\\" group-attrs \\\"]\\\" )? NEWLINE\\n ( INDENT≥4 member-line )*\\nmember-line = id ( \\\"[\\\" node-attrs \\\"]\\\" )? NEWLINE\\ngroup-attrs = group-attr (\\\",\\\" group-attr)*\\ngroup-attr = \\\"label:\\\" quoted-string | \\\"color:\\\" quoted-string\\n\\nnode = id ( \\\"[\\\" node-attrs \\\"]\\\" )? NEWLINE\\nnode-attrs = node-attr (\\\",\\\" node-attr)*\\nnode-attr = \\\"label:\\\" quoted-string\\n | \\\"group:\\\" id\\n | \\\"role:\\\" role\\n | \\\"size:\\\" (\\\"small\\\" | \\\"medium\\\" | \\\"large\\\")\\n\\nedge = id WS op WS id ( \\\"[\\\" edge-attrs \\\"]\\\" )? NEWLINE\\nedge-attrs = edge-attr (\\\",\\\" edge-attr)*\\nedge-attr = \\\"label:\\\" quoted-string | \\\"weight:\\\" number\\n\\nop = // positive\\n \\\"<===>\\\" | \\\"===>\\\" | \\\"<===\\\"\\n | \\\"<==>\\\"|\\\"==>\\\"|\\\"<==\\\"\\n | \\\"===\\\" | \\\"<->\\\" | \\\"->\\\" | \\\"<-\\\" | \\\"--\\\"\\n // negative\\n | \\\"<x->\\\" | \\\"-x>\\\" | \\\"<x-\\\" | \\\"-x-\\\"\\n // neutral\\n | \\\"<.->\\\" | \\\"-\\\\.>\\\" | \\\"-.-\\\"\\n\\nrole = \\\"star\\\" | \\\"isolate\\\" | \\\"bridge\\\" | \\\"neglectee\\\" | \\\"rejected\\\"\\nid = [a-zA-Z] [a-zA-Z0-9_-]*\\ncomment = \\\"#\\\" any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/sociogram/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"timing\": {\n \"title\": \"Timing diagram\",\n \"content\": \"## 1. Your first timing diagram\\n\\nThe smallest useful timing diagram: a clock and one data signal.\\n\\n```\\ntiming\\nCLK: pppppppp\\nDATA: 0011==00 data: [\\\"A\\\",\\\"B\\\"]\\n```\\n\\nThree rules cover 80% of usage:\\n\\n1. Start with the keyword `timing`, optionally followed by a quoted title and `[hscale: N]`.\\n2. Each signal is one line: `NAME: wavestring` — name, colon, then a string of wave characters (no spaces inside the wave).\\n3. Add `data: [\\\"val1\\\", \\\"val2\\\"]` after the wave string to label bus segments.\\n\\n> Comments must start with `#` on their own line.\\n\\n---\\n\\n## 2. Wave characters\\n\\nThe wave string is a sequence of characters, one per time period. The parser accepts these:\\n\\n| Character | State | Meaning |\\n|---|---|---|\\n| `0` | Logic low | Signal at GND / VSS |\\n| `1` | Logic high | Signal at VDD |\\n| `x` | Unknown | Don't-care, undefined, or uninitialized |\\n| `z` | High-Z | Tri-state / high-impedance |\\n| `p` | Clock pulse (positive) | Rising-edge-active clock; one `p` = one full period (low→high→low) |\\n| `P` | Clock pulse (positive, tall) | Same as `p`, visually taller |\\n| `n` | Clock pulse (negative) | Falling-edge-active; one `n` = one full period (high→low→high) |\\n| `N` | Clock pulse (negative, tall) | Same as `n`, visually taller |\\n| `=` | Bus data | Parallel-bus segment; add labels via `data: […]` |\\n| `2`–`9` | Named bus segment | Same as `=`, indexed into `data: […]` by position |\\n| `.` | Hold / continue | Extend the previous state for one more period |\\n| `h` / `H` | Hold high | Force-high for this period |\\n| `l` / `L` | Hold low | Force-low for this period |\\n| `u` | Rising edge | Diagonal from low to high (transition only) |\\n| `d` / `D` | Falling edge | Diagonal from high to low (transition only) |\\n\\n```\\ntiming \\\"Wave character reference\\\"\\nclk: pppppppppp\\nhigh: 1111111111\\nlow: 0000000000\\nunkn: xxxxxxxxxx\\nhiz: zzzzzzzzzz\\nbus: x========x data: [\\\"ADDR\\\",\\\"DATA\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\"]\\nhold: 0..1..0..1\\nrise: 0u1\\nfall: 1d0\\n```\\n\\n---\\n\\n## 3. Data labels\\n\\nWhen a signal carries a bus value, tag the wave with `data: [\\\"label1\\\", \\\"label2\\\", …]`. Each non-empty quoted string is placed inside the corresponding `=` (or `2`–`9`) segment.\\n\\n```\\nMOSI: x======= data: [\\\"0xAB\\\",\\\"0xCD\\\",\\\"0xEF\\\",\\\"0x01\\\",\\\"0x02\\\",\\\"0x03\\\",\\\"0x04\\\",\\\"0x05\\\"]\\n```\\n\\nEmpty strings `\\\"\\\"` leave a segment unlabeled (useful for segments that extend a previous value).\\n\\n```\\nMISO: zzzz==== data: [\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"0xFF\\\",\\\"0x12\\\",\\\"0x34\\\",\\\"0x56\\\"]\\n# first four z-periods have no label; four = segments get labels starting at 0xFF\\n```\\n\\n```\\ntiming \\\"I2C read burst\\\"\\nSCL: ppppppppppp\\nSDA: x1=======1x data: [\\\"ADDR+R\\\",\\\"ACK\\\",\\\"D0\\\",\\\"D1\\\",\\\"D2\\\",\\\"D3\\\",\\\"D4\\\",\\\"D5\\\",\\\"NACK\\\"]\\n```\\n\\n---\\n\\n## 4. Grouping signals\\n\\nWrap related signals in a `[GroupName]` block. A `---` line closes the group and also acts as a visual separator between groups.\\n\\n```\\n[Control]\\nCLK: pppppppp\\nCS_N: 10000001\\n---\\n[Data]\\nMOSI: x======= data: [\\\"0xAB\\\",\\\"0xCD\\\",\\\"0xEF\\\",\\\"0x01\\\",\\\"0x02\\\",\\\"0x03\\\",\\\"0x04\\\",\\\"0x05\\\"]\\nMISO: zzzz==== data: [\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"0xFF\\\",\\\"0x12\\\",\\\"0x34\\\",\\\"0x56\\\"]\\n```\\n\\nAlternative `group \\\"name\\\" { … }` syntax is also accepted (closing `}` closes the group).\\n\\n```\\ntiming \\\"UART frame\\\"\\n[Clock & control]\\nCLK: pppppppppppp\\nTX_EN: 0111111110\\n---\\n[Data lines]\\nTX: 1========== data: [\\\"START\\\",\\\"D0\\\",\\\"D1\\\",\\\"D2\\\",\\\"D3\\\",\\\"D4\\\",\\\"D5\\\",\\\"D6\\\",\\\"D7\\\",\\\"STOP\\\"]\\nRX: zz1=======1 data: [\\\"\\\",\\\"\\\",\\\"D0\\\",\\\"D1\\\",\\\"D2\\\",\\\"D3\\\",\\\"D4\\\",\\\"D5\\\",\\\"D6\\\",\\\"D7\\\"]\\n```\\n\\n---\\n\\n## 5. Title and hscale\\n\\n**Title:** `timing \\\"SPI Transaction\\\"` — appears at the top of the diagram.\\n\\n**hscale:** `timing \\\"title\\\" [hscale: 2]` — scales the width of each time period. Default is 1. Use 2 for wider periods when data labels need more room.\\n\\n```\\ntiming \\\"Wide bus\\\" [hscale: 2]\\nCLK: pppp\\nDATA: ==== data: [\\\"long label here\\\",\\\"another\\\",\\\"third\\\",\\\"fourth\\\"]\\n```\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Signal name:** anything before the first `:` on a signal line. Names with spaces are fine — the colon is the delimiter.\\n- **Data labels:** `data: [\\\"a\\\", \\\"b\\\"]` after the wave string.\\n- **Title:** first token after `timing` keyword, quoted.\\n- **Comments:** `#` at the start of a line (after leading whitespace).\\n\\n```\\ntiming \\\"Demo\\\"\\n# this is a comment\\nCLK: pppp # ← inline trailing comment is NOT supported\\n```\\n\\n---\\n\\n## 7. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `CLK: p p p p` (spaces in wave) | Wave string parsed as `p` only; the rest is treated as data clause | Remove spaces: `CLK: pppp` |\\n| `DATA: =====` with no `data:` | Segments render as unlabeled bus cells | Add `data: [\\\"A\\\",\\\"B\\\",\\\"C\\\",\\\"D\\\",\\\"E\\\"]` |\\n| Wave character `s` or `r` | `TimingParseError: Invalid wave string` | Only the characters listed in §2 are valid |\\n| `CLK pppp` (no colon) | Line does not match signal pattern; silently skipped | The colon after the signal name is required |\\n| `data: [A, B, C]` (unquoted) | Values not recognized — parser looks for `\\\"…\\\"` | Quote each value: `data: [\\\"A\\\",\\\"B\\\",\\\"C\\\"]` |\\n| `[Group Name with spaces]` | Group label is `Group Name with spaces` — parsed fine | Supported |\\n| `hscale: 2` on its own line | Not recognized (hscale goes on the header line) | `timing \\\"title\\\" [hscale: 2]` |\\n\\n---\\n\\n## 8. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | group-open | group-close | separator | signal)*\\n\\nheader = \\\"timing\\\" ( WS quoted-string )? ( WS \\\"[\\\" \\\"hscale:\\\" number \\\"]\\\" )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\ngroup-open = \\\"[\\\" label \\\"]\\\" NEWLINE\\n | \\\"group\\\" WS quoted-string WS \\\"{\\\"? NEWLINE\\ngroup-close = \\\"}\\\" NEWLINE\\nseparator = \\\"---\\\" NEWLINE\\n\\nsignal = name \\\":\\\" WS wave-string ( WS data-clause )? NEWLINE\\nname = any text before the first \\\":\\\"\\nwave-string = wave-char+\\nwave-char = \\\"0\\\"|\\\"1\\\"|\\\"x\\\"|\\\"z\\\"\\n | \\\"p\\\"|\\\"P\\\"|\\\"n\\\"|\\\"N\\\"\\n | \\\"h\\\"|\\\"H\\\"|\\\"l\\\"|\\\"L\\\"\\n | \\\"u\\\"|\\\"d\\\"|\\\"D\\\"\\n | \\\"=\\\"|\\\".\\\"|\\\"2\\\"|\\\"3\\\"|\\\"4\\\"|\\\"5\\\"|\\\"6\\\"|\\\"7\\\"|\\\"8\\\"|\\\"9\\\"\\n\\ndata-clause = \\\"data:\\\" WS ( \\\"[\\\" quoted-string (\\\",\\\" quoted-string)* \\\"]\\\"\\n | quoted-string+ )\\n\\ncomment = \\\"#\\\" any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/timing/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"logic\": {\n \"title\": \"Logic gate diagram\",\n \"content\": \"## 1. Your first logic gate diagram\\n\\nThe smallest useful diagram: two inputs, one gate, one output.\\n\\n```\\nlogic \\\"NAND check\\\"\\ninput A, B\\noutput F\\nF = NAND(A, B)\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with the keyword `logic`, optionally followed by a quoted title and `style: ansi` or `style: iec`.\\n2. Declare ports with `input` and `output` lines — comma-separated signal names.\\n3. Each gate is `id = GATE_TYPE(input1, input2, …)`. The `id` becomes a named signal wire.\\n4. An `output` name that matches a gate `id` is automatically wired; use `OUTPUT <- gate_id` when the names differ.\\n\\n> Comments must start with `#` or `--` on their own line (or after the last token on a gate line).\\n\\n---\\n\\n## 2. Gate types\\n\\n### 2.1 Combinational gates\\n\\n| DSL keyword | Function | ANSI shape | IEC symbol |\\n|---|---|---|---|\\n| `AND` | A · B | D-shaped body | Rectangle + `&` |\\n| `OR` | A + B | Curved body | Rectangle + `≥1` |\\n| `NOT` | Ā | Triangle + bubble | Rectangle + `1` + bubble |\\n| `NAND` | ¬(A · B) | AND + bubble | Rectangle + `&` + bubble |\\n| `NOR` | ¬(A + B) | OR + bubble | Rectangle + `≥1` + bubble |\\n| `XOR` | A ⊕ B | OR + extra arc | Rectangle + `=1` |\\n| `XNOR` | ¬(A ⊕ B) | XOR + bubble | Rectangle + `=1` + bubble |\\n| `BUF` | A (buffer) | Triangle, no bubble | Rectangle + `1` |\\n\\n```\\nlogic \\\"Gate gallery\\\" style: ansi\\ninput A, B, C\\noutput Y_and, Y_or, Y_xor, Y_nand, Y_not\\nY_and = AND(A, B)\\nY_or = OR(A, B)\\nY_xor = XOR(A, B)\\nY_nand = NAND(A, B)\\nY_not = NOT(C)\\n```\\n\\n### 2.2 Special-output buffers\\n\\n| DSL keyword | Function |\\n|---|---|\\n| `TRISTATE_BUF` | Three-state buffer — Z output when enable is low |\\n| `TRISTATE_INV` | Three-state inverting buffer |\\n| `OPEN_DRAIN` | Open-drain / open-collector output (external pull-up required) |\\n| `SCHMITT` | Schmitt trigger — hysteresis symbol inside body |\\n\\n```\\nlogic \\\"Special buffers\\\" style: ansi\\ninput A, EN\\noutput Y_tri, Y_od, Y_sch\\nY_tri = TRISTATE_BUF(A, EN)\\nY_od = OPEN_DRAIN(A)\\nY_sch = SCHMITT(A)\\n```\\n\\n### 2.3 Flip-flops and latches\\n\\n| DSL keyword | Type | Key pins |\\n|---|---|---|\\n| `DFF` | D flip-flop (edge-triggered) | D, CLK, Q, Q̄ |\\n| `JKFF` | JK flip-flop | J, K, CLK, Q, Q̄ |\\n| `SRFF` | SR flip-flop | S, R, CLK, Q, Q̄ |\\n| `TFF` | T (toggle) flip-flop | T, CLK, Q, Q̄ |\\n| `LATCH_SR` | SR latch (level-sensitive, no clock) | S, R, Q, Q̄ |\\n| `LATCH_D` | D latch (transparent when enable=1) | D, EN, Q, Q̄ |\\n\\n```\\nlogic \\\"Flip-flop gallery\\\" style: ansi\\ninput D, J, K, CLK, EN\\noutput Q_dff, Q_jk, Q_latch\\nQ_dff = DFF(D, CLK)\\nQ_jk = JKFF(J, K, CLK)\\nQ_latch = LATCH_D(D, EN)\\n```\\n\\n### 2.4 Complex combinational\\n\\n| DSL keyword | Function |\\n|---|---|\\n| `MUX` | Multiplexer |\\n| `DEMUX` | Demultiplexer |\\n| `DECODER` | Binary decoder |\\n| `ENCODER` | Priority encoder |\\n\\n```\\nlogic \\\"Combinational MSI\\\" style: ansi\\ninput A, B, S\\noutput Y_mux, Y_dec\\nY_mux = MUX(A, B, S)\\nY_dec = DECODER(A, B)\\n```\\n\\n### 2.5 Sequential complex\\n\\n| DSL keyword | Function |\\n|---|---|\\n| `COUNTER` | Generic binary counter (`CTR` label, CLK/RESET/Q0–Q3) |\\n| `SHIFT_REG` | Generic shift register (`SRG` label, CLK/SER/Q0–Q7) |\\n\\n```\\nlogic \\\"Sequential MSI\\\" style: ansi\\ninput DATA, CLK, RESET\\noutput Q_cnt, Q_sr\\nQ_cnt = COUNTER(CLK, RESET)\\nQ_sr = SHIFT_REG(DATA, CLK)\\n```\\n\\n---\\n\\n## 3. Inputs and outputs\\n\\n### 3.1 Declaring ports\\n\\n```\\ninput A, B, Cin # three input ports\\noutput Sum, Cout # two output ports\\n```\\n\\nEach name in an `input` or `output` list becomes a named signal wire available throughout the diagram.\\n\\n### 3.2 Active-low inputs\\n\\nPrefix a signal name with `~` in the input list to mark it as active-low. The renderer draws a bubble at the port symbol.\\n\\n```\\ninput ~nRESET, CLK, DATA\\n```\\n\\nActive-low notation also works inside gate input lists:\\n\\n```\\ng1 = AND(~nRESET, CLK)\\n```\\n\\n### 3.3 Wiring outputs to gates\\n\\nIf the output ID matches a gate ID, the connection is implicit:\\n\\n```\\noutput Sum # Sum is also a gate id → auto-wired\\nSum = XOR(s1, Cin)\\n```\\n\\nWhen the names differ, use the explicit assignment operator:\\n\\n```\\noutput F\\nq1 = NOR(A, B)\\nF <- q1 # F draws from q1's output\\n```\\n\\n```\\nlogic \\\"SR latch from NOR gates\\\"\\ninput S, R\\noutput Q, Qn\\nq_gate = NOR(R, Qn)\\nqn_gate = NOR(S, Q)\\nQ <- q_gate\\nQn <- qn_gate\\n```\\n\\n---\\n\\n## 4. Symbol style\\n\\nThe `style:` option on the header line selects the symbol standard. It applies to every gate in the diagram.\\n\\n| Value | Standard | Use when |\\n|---|---|---|\\n| `ansi` (default) | IEEE Std 91 — distinctive curved shapes | US education, hardware docs |\\n| `iec` | IEC 60617-12 — uniform rectangles + function label | International, European industry |\\n\\n```\\nlogic \\\"ALU slice\\\" style: iec\\n```\\n\\n```\\nlogic \\\"1-bit Full Adder\\\" style: iec\\ninput A, B, Cin\\noutput Sum, Cout\\n# XOR stage — sum bits\\ns1 = XOR(A, B)\\nSum = XOR(s1, Cin)\\n# AND/OR stage — carry\\nc1 = AND(A, B)\\nc2 = AND(s1, Cin)\\nCout = OR(c1, c2)\\n```\\n\\n```\\nlogic \\\"Gate gallery — IEC style\\\" style: iec\\ninput A, B, C\\noutput Y_and, Y_or, Y_xor, Y_nand, Y_not\\nY_and = AND(A, B)\\nY_or = OR(A, B)\\nY_xor = XOR(A, B)\\nY_nand = NAND(A, B)\\nY_not = NOT(C)\\n```\\n\\n---\\n\\n## 5. Module blocks\\n\\nUse `module` to group gates into a labeled sub-circuit box. Module blocks are useful for documenting hierarchical designs — each module renders as a named rectangle around its member gates.\\n\\n```\\nlogic \\\"Hierarchical adder\\\"\\ninput A, B, Cin\\noutput Sum, Cout\\n\\nmodule \\\"Half Adder\\\" {\\n s1 = XOR(A, B)\\n c1 = AND(A, B)\\n}\\n\\nSum = XOR(s1, Cin)\\nCout = OR(c1, AND(s1, Cin))\\n```\\n\\nModule syntax rules:\\n- `module \\\"Label\\\" {` — opens a module (quoted label or bare identifier). The `{` must be on the same line.\\n- `}` on its own line closes the most recently opened module.\\n- Modules can be nested.\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Diagram title:** `logic \\\"Full Adder\\\"` — first line only.\\n- **Gate signal names:** the `id` in `id = GATE(…)` is both the gate name and the output wire name.\\n- **Output labels:** `output Sum` — the output port label matches the signal name by default.\\n- **Active-low marker:** `~` prefix on a port or gate input.\\n- **Comments:** `#` or `--` at the start of a line, or after the last meaningful token on a line.\\n\\n---\\n\\n## 7. Reserved words & escaping\\n\\n**Reserved at line start:** `logic` (header), `input`, `output`, `module`, `}`.\\n\\n**Reserved operator tokens** — avoid these inside signal names: `=`, `(`, `)`, `,`, `<-`, `~`.\\n\\n**Signal name rules:** must match `[a-zA-Z_][a-zA-Z0-9_]*`. Lowercase and uppercase are both accepted; gate type keywords (`AND`, `OR`, etc.) are case-insensitive in the parser.\\n\\n---\\n\\n## 8. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `f = and(A, B)` (lowercase gate) | Accepted — gate types are case-insensitive | Both `AND` and `and` work |\\n| `output F` then `F <- q1` but `q1` not declared | `LogicParseError: Unknown signal \\\"q1\\\"` | Declare `q1` as a gate before referencing it |\\n| `input A B C` (spaces, no commas) | Parser takes `A` only; `B` and `C` are ignored | Use commas: `input A, B, C` |\\n| `F = BUFFER(A)` | `LogicParseError: Unknown gate type: BUFFER` | Use `BUF` |\\n| `module FullAdder {` (no `{` brace) | Line does not match module pattern — skipped silently | The opening `{` is required on the same line as `module` |\\n| `style: IEEE` on the header | Unknown style value — silently defaults to `ansi` | Use `style: ansi` or `style: iec` |\\n\\n---\\n\\n## 9. Grammar (EBNF)\\n\\n```text\\ndocument = header statement*\\n\\nheader = \\\"logic\\\" ( WS quoted-string )? ( WS \\\"style:\\\" WS style )? NEWLINE\\nstyle = \\\"ansi\\\" | \\\"iec\\\"\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nstatement = blank | comment | input-decl | output-decl | gate-def | assign | module-block\\n\\ncomment = ( \\\"#\\\" | \\\"--\\\" ) any NEWLINE\\n\\ninput-decl = \\\"input\\\" WS port-list NEWLINE\\noutput-decl = \\\"output\\\" WS port-list NEWLINE\\nport-list = port-id ( \\\",\\\" WS? port-id )*\\nport-id = \\\"~\\\"? id\\n\\ngate-def = id WS \\\"=\\\" WS gate-type \\\"(\\\" input-list \\\")\\\" NEWLINE\\ninput-list = ( \\\"~\\\"? id ) ( \\\",\\\" WS? ( \\\"~\\\"? id ) )*\\n\\nassign = id WS \\\"<-\\\" WS id NEWLINE\\n\\nmodule-block = module-open ( statement | module-block )* module-close\\nmodule-open = \\\"module\\\" WS ( quoted-string | id ) WS? \\\"{\\\" NEWLINE\\nmodule-close = \\\"}\\\" NEWLINE\\n\\ngate-type = \\\"AND\\\" | \\\"OR\\\" | \\\"NOT\\\" | \\\"NAND\\\" | \\\"NOR\\\" | \\\"XOR\\\" | \\\"XNOR\\\" | \\\"BUF\\\"\\n | \\\"TRISTATE_BUF\\\" | \\\"TRISTATE_INV\\\" | \\\"OPEN_DRAIN\\\" | \\\"SCHMITT\\\"\\n | \\\"DFF\\\" | \\\"JKFF\\\" | \\\"SRFF\\\" | \\\"TFF\\\"\\n | \\\"LATCH_SR\\\" | \\\"LATCH_D\\\"\\n | \\\"MUX\\\" | \\\"DEMUX\\\" | \\\"DECODER\\\" | \\\"ENCODER\\\"\\n | \\\"COUNTER\\\" | \\\"SHIFT_REG\\\"\\n // all case-insensitive\\n\\nid = [a-zA-Z_] [a-zA-Z0-9_]*\\n```\\n\\nAuthoritative source: `src/diagrams/logic/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"circuit\": {\n \"title\": \"Circuit schematic\",\n \"content\": \"## 1. A minimal circuit\\n\\nThe smallest useful positional circuit: a voltage source, a resistor, and a ground.\\n\\n```\\ncircuit \\\"Voltage divider\\\"\\nV1: voltage_source down value=\\\"5V\\\"\\nwire right\\nR1: resistor right value=\\\"10k\\\"\\nwire right\\nground\\n```\\n\\nFour rules cover 80% of positional-mode usage:\\n\\n1. Start with the keyword `circuit`, optionally followed by a quoted title.\\n2. Each component is `id: type direction` — or just `type direction` for anonymous components.\\n3. Each component's output end becomes the starting point for the next component (the \\\"cursor\\\").\\n4. `at: id.end` (or `at: id.start`) jumps the cursor to any named anchor — use this to branch.\\n\\n> Comments must start with `#` on their own line.\\n\\n---\\n\\n## 2. Components\\n\\n### 2.1 Positional mode syntax\\n\\nA named component line has the form:\\n\\n```\\nid: type direction [value=\\\"…\\\"] [label=\\\"…\\\"]\\n```\\n\\nAn anonymous component omits the `id:` prefix — the parser assigns an auto ID.\\n\\n```\\nR1: resistor right value=\\\"4.7k\\\" label=\\\"R1\\\"\\ncapacitor down value=\\\"100n\\\"\\n```\\n\\n**Direction** is one of `right` (default), `left`, `up`, `down`. It controls which way the component extends from the current cursor position.\\n\\n### 2.2 Passive components\\n\\n| DSL type | Description |\\n|---|---|\\n| `resistor` | Zigzag (ANSI) or rectangle (IEC) |\\n| `potentiometer` | Resistor + wiper arrow, 3-pin |\\n| `rheostat` | 2-pin variable resistor |\\n| `thermistor_ntc` | NTC thermistor (also: `therm`, `ntc`) |\\n| `thermistor_ptc` | PTC thermistor (also: `ptc`) |\\n| `ldr` | Light-dependent resistor |\\n| `varistor` | Voltage-dependent resistor |\\n| `fuse` | Standard fuse |\\n| `fuse_slow` | Slow-blow fuse (`T` designation) |\\n| `capacitor` | Non-polar capacitor |\\n| `electrolytic_cap` | Polar/electrolytic capacitor (also: `ecap`) |\\n| `variable_cap` | Variable capacitor |\\n| `inductor` | Air-core inductor |\\n| `inductor_iron` | Iron-core inductor |\\n| `inductor_ferrite` | Ferrite-core inductor |\\n| `variable_inductor` | Variable inductor |\\n| `ferrite_bead` | EMI ferrite bead |\\n| `crystal` | Quartz crystal oscillator (also: `xtal`) |\\n| `transformer` | Coupled coils (also: `xfmr`) |\\n\\n```\\ncircuit \\\"Passive components gallery\\\"\\n# Row 1: resistor → capacitor → inductor\\nR1: resistor right value=\\\"1k\\\"\\nwire right\\nC1: capacitor right value=\\\"100n\\\"\\nwire right\\nL1: inductor right value=\\\"10u\\\"\\n# Row 2: crystal and transformer, offset below\\nat: R1.start\\nwire down\\nwire down\\nX1: crystal right\\nwire right\\nwire right\\nT1: transformer right\\n```\\n\\n### 2.3 Sources and power\\n\\n| DSL type | Description |\\n|---|---|\\n| `voltage_source` | Circle + polarity (also: `vsource`) |\\n| `current_source` | Circle + arrow (also: `isource`) |\\n| `ac_source` | Circle + sine symbol (also: `acsource`) |\\n| `battery` | Alternating long/short terminal lines |\\n| `vcc` | Power rail arrow (pointing up) |\\n| `ground` | Earth ground — 3 decreasing lines (also: `gnd`) |\\n| `gnd_signal` | Signal ground — solid triangle |\\n| `gnd_chassis` | Chassis ground |\\n| `gnd_digital` | Digital ground |\\n\\n```\\ncircuit \\\"Sources and power gallery\\\"\\n# voltage source with ground\\nV1: voltage_source down value=\\\"5V\\\"\\nwire down\\nground\\nat: V1.start\\nwire right\\nwire right\\n# battery\\nB1: battery down value=\\\"9V\\\"\\nwire down\\nground\\nat: B1.start\\nwire right\\nwire right\\n# ac source\\nA1: ac_source down value=\\\"120V\\\"\\nwire down\\nground\\nat: A1.start\\nwire right\\nwire right\\n# vcc rail\\nvcc up\\nwire down\\ngnd_signal down\\n```\\n\\n### 2.4 Semiconductors — diodes\\n\\n| DSL type | Description |\\n|---|---|\\n| `diode` | Triangle + cathode bar |\\n| `zener` | Diode + bent cathode bar |\\n| `schottky` | Diode + S-bar |\\n| `led` | Diode + outward emission arrows |\\n| `photodiode` | Diode + inward light arrows |\\n| `varactor` | Diode + variable capacitor |\\n| `tvs_diode` | Bidirectional TVS (two bent bars) |\\n| `bridge_rectifier` | 4-diode bridge, 4-pin |\\n\\n```\\ncircuit \\\"Diode types gallery\\\"\\nD1: diode right\\nwire right\\nD2: zener right\\nwire right\\nD3: led right\\nwire right\\nD4: schottky right\\nwire right\\nD5: photodiode right\\nwire right\\nground\\nat: D1.start\\nwire left\\nground\\n```\\n\\n### 2.5 Semiconductors — transistors\\n\\n| DSL type | Description |\\n|---|---|\\n| `npn` | NPN BJT (also: `transistor`, `bjt_npn`) |\\n| `pnp` | PNP BJT (also: `bjt_pnp`) |\\n| `darlington_npn` | NPN Darlington pair |\\n| `darlington_pnp` | PNP Darlington pair |\\n| `nmos` | N-channel MOSFET enhancement (also: `mosfet_n`) |\\n| `pmos` | P-channel MOSFET enhancement (also: `mosfet_p`) |\\n| `nmos_depletion` | N-channel MOSFET depletion |\\n| `jfet_n` | N-channel JFET |\\n| `jfet_p` | P-channel JFET |\\n| `igbt` | IGBT |\\n| `scr` | SCR / thyristor |\\n| `triac` | TRIAC |\\n| `diac` | DIAC |\\n| `phototransistor` | NPN with light arrows |\\n| `optocoupler` | LED + phototransistor in isolation box |\\n\\n```\\ncircuit \\\"Transistor types gallery\\\"\\n# NPN BJT\\nQ1: npn right\\nwire right\\nwire right\\n# PNP BJT\\nQ2: pnp right\\nwire right\\nwire right\\n# N-channel MOSFET\\nQ3: nmos right\\nwire right\\nwire right\\n# P-channel MOSFET\\nQ4: pmos right\\n```\\n\\n### 2.6 Analog ICs and op-amps\\n\\n| DSL type | Description |\\n|---|---|\\n| `opamp` | Triangle: +/− inputs, output |\\n| `comparator` | Same shape, open-collector output |\\n| `schmitt_buffer` | Buffer + hysteresis symbol |\\n| `tri_state_buffer` | Buffer + enable pin |\\n| `instrumentation_amp` | Three-op-amp INA block |\\n| `generic_ic` | Configurable rect with labeled pins (also: `ic`) |\\n| `voltage_regulator` | 3-terminal block: IN/GND/OUT (also: `reg`) |\\n| `dc_dc_converter` | 2-port block with DC/DC label |\\n| `555_timer` | 8-pin 555 pinout block (also: `timer555`) |\\n\\n```\\ncircuit \\\"Analog IC gallery\\\"\\n# op-amp with input/output wires\\nwire right\\nU1: opamp right\\nwire right\\nwire right\\nwire right\\n# comparator\\nU2: comparator right\\nwire right\\nwire right\\nwire right\\n# generic IC block\\nU3: generic_ic right\\n```\\n\\n### 2.7 Switches and relays\\n\\n| DSL type | Description |\\n|---|---|\\n| `switch_spst` | Single-pole single-throw |\\n| `switch_spdt` | Single-pole double-throw |\\n| `switch_dpdt` | Double-pole double-throw |\\n| `push_no` | Push button normally-open |\\n| `push_nc` | Push button normally-closed |\\n| `relay_coil` | Relay coil (2-pin rect) |\\n| `relay_no` | Relay contact normally-open |\\n| `relay_nc` | Relay contact normally-closed |\\n\\n```\\ncircuit \\\"Switch and relay gallery\\\"\\n# SPST switch\\nS1: switch_spst right\\nwire right\\nwire right\\n# SPDT switch\\nS2: switch_spdt right\\nwire right\\nwire right\\n# normally-open push button\\nS3: push_no right\\nwire right\\nwire right\\n# relay coil + contact pair\\nK1: relay_coil right\\nwire right\\nK2: relay_no right\\n```\\n\\n### 2.8 Electromechanical and measurement\\n\\n| DSL type | Description |\\n|---|---|\\n| `motor` | Circle + M |\\n| `speaker` | Cone + box |\\n| `microphone` | Capsule symbol |\\n| `buzzer` | Piezo buzzer |\\n| `ammeter` | Circle + A |\\n| `voltmeter` | Circle + V |\\n| `wattmeter` | Circle + W |\\n| `oscilloscope` | Circle + waveform |\\n\\n### 2.9 Connectors and annotations\\n\\n| DSL type | Description |\\n|---|---|\\n| `wire` | Plain wire segment |\\n| `dot` | Junction dot (T-junction marker) |\\n| `label` | Net label / flag |\\n| `port` | Named port (hollow circle) |\\n| `test_point` | TP marker |\\n| `no_connect` | X — intentionally unconnected pin |\\n| `antenna` | Antenna stub |\\n\\n```\\ncircuit \\\"Passive components\\\"\\nR1: resistor right value=\\\"1k\\\" label=\\\"R1\\\"\\nwire right\\nC1: capacitor down value=\\\"100n\\\" label=\\\"C1\\\"\\nwire down\\nground\\nat: R1.start\\nwire up\\nbattery up label=\\\"9V\\\"\\n```\\n\\n---\\n\\n## 3. Wiring and branching\\n\\n### 3.1 Wire segments\\n\\n`wire direction [N]` draws a bare wire from the current cursor in the given direction. An optional number sets the length in pixels.\\n\\n```\\nwire right\\nwire down 40\\nwire left 20\\n```\\n\\n### 3.2 Jumping the cursor with `at:`\\n\\n`at: id.end` moves the cursor to a named anchor without drawing anything. Use it to branch from a previously placed component.\\n\\n```\\nR1: resistor right value=\\\"10k\\\"\\nat: R1.end\\nC1: capacitor down value=\\\"100n\\\"\\n```\\n\\nNamed anchor suffixes: `end`, `start`. Components retain their ID across the whole diagram, so you can jump back to any previously placed component.\\n\\n### 3.3 Junction dots\\n\\nPlace a `dot` (or use `net NAME: dot`) to mark a T-junction — a point where three or more wires meet. Without a dot, crossed wires are drawn as a crossover (no connection).\\n\\n```\\nR1: resistor right\\ndot\\nwire right # continues from R1.end\\nat: R1.end\\nC1: capacitor down # branches down from the same point\\n```\\n\\n### 3.4 Named nets\\n\\n`net NAME` declares a named net. `net NAME: dot` declares the net and places a junction dot at the current cursor, remembering that location. Later, `at: NAME` jumps back to that net's anchor.\\n\\n```\\nnet VOUT: dot\\nR2: resistor right value=\\\"10k\\\"\\nat: VOUT\\nC1: capacitor down value=\\\"470n\\\"\\n```\\n\\n### 3.5 Net labels\\n\\n`label \\\"text\\\" direction?` places a text label at the current cursor position. Labels do not advance the cursor. They are useful for naming power rails or inter-sheet connections.\\n\\n```\\nlabel \\\"VCC\\\" up\\nlabel \\\"GND\\\" down\\n```\\n\\n```\\ncircuit \\\"RC filter\\\"\\nV1: voltage_source down value=\\\"5V\\\"\\nwire right\\nR1: resistor right value=\\\"1k\\\" label=\\\"R1\\\"\\nnet OUT: dot\\nwire right\\nlabel \\\"Vout\\\" right\\nat: OUT\\nC1: capacitor down value=\\\"100n\\\" label=\\\"C1\\\"\\nwire down\\nground\\n```\\n\\n---\\n\\n## 4. Netlist mode\\n\\nAdd `netlist` after the title on the header line to switch to SPICE-style netlist parsing. The auto-layout engine computes component positions from the net connectivity.\\n\\n```\\ncircuit \\\"Low-pass filter\\\" netlist\\n```\\n\\n### 4.1 Netlist line format\\n\\nEach line is: `ID net1 net2 [net3…] [value] [key=value…]`\\n\\n- **ID** — component identifier. The first letter determines the default type (SPICE prefix convention).\\n- **net1, net2, …** — net names the pins connect to. Net names matching `0`, `gnd`, `ground`, `earth`, `pe`, `agnd`, `dgnd`, `gnda`, `gndd`, `vss`, or `com` (case-insensitive, with optional `_<word>` or numeric suffix — e.g. `gnd_ref`, `AGND_DIG`, `EARTH1`) all canonicalize to the ground net.\\n- **value** (optional bare token) — component value or model name.\\n- **key=value** (optional) — `label=`, `value=`, `type=` overrides.\\n\\n### 4.2 SPICE prefix → component type\\n\\n| Prefix | Default type | Pin order |\\n|---|---|---|\\n| `R` | `resistor` | p1, p2 |\\n| `C` | `capacitor` | p1, p2 |\\n| `L` | `inductor` | p1, p2 |\\n| `D` | `diode` | anode (start), cathode (end) |\\n| `V` | `voltage_source` | plus, minus |\\n| `I` | `current_source` | plus, minus |\\n| `Q` | `npn` | c, b, e |\\n| `M` | `nmos` | d, g, s |\\n| `J` | `jfet_n` | d, g, s |\\n| `S` | `switch_spst` | p1, p2 |\\n| `F` | `fuse` | p1, p2 |\\n| `B` | `battery` | plus, minus |\\n| `K` | `relay_coil` | p1, p2 |\\n| `U`, `X` | `generic_ic` | custom via `pins=` |\\n| `W` | `wire` | start, end |\\n| `T` | `terminal_block` | custom via `pins=` (also `type=junction_box`) |\\n\\n> **Scope:** schematex circuit covers **electrical schematics only** (IEEE 315 / IEC 60617). Hydraulic and pneumatic schematics (ISO 1219) use a fundamentally different visual grammar — directional valve envelopes, cylinder symbols, line styles for pressure/return/drain — and are not supported by this engine. Hydraulic prefixes such as `EV*` (electrovalve), `BOMBA*` (pump), `TANK*`, `DIPOSIT*` will be rejected with a \\\"cannot infer type\\\" error.\\n\\n### 4.3 Transistor model override\\n\\nFor `Q` lines, a trailing model name overrides the type:\\n\\n```\\nQ1 c b e npn # NPN BJT\\nQ2 c b e pnp # PNP BJT\\nM1 d g s nmos # N-channel MOSFET\\nM2 d g s pmos # P-channel MOSFET\\n```\\n\\nFor `D` lines, similarly:\\n\\n```\\nD1 anode cathode zener\\nD2 anode cathode led\\nD3 anode cathode schottky\\nD4 anode cathode photodiode\\n```\\n\\n### 4.4 Netlist example\\n\\n```\\ncircuit \\\"CE Amp (netlist)\\\" netlist\\nV1 vcc 0 9V\\nRc vcc c 2.2k\\nRb vcc b 100k\\nQ1 c b e npn\\nRe e 0 1k\\n```\\n\\n---\\n\\n## 5. Attributes\\n\\nBoth positional and netlist modes accept these key=value attributes:\\n\\n| Attribute | Accepted by | Effect |\\n|---|---|---|\\n| `label=\\\"…\\\"` | all components | Display label (reference designator) |\\n| `value=\\\"…\\\"` | all components | Value annotation (1kΩ, 100nF, 5V) |\\n| `at=id.end` | positional components | Start this component at a named anchor |\\n| `length=N` | `wire`, some passives | Length in pixels |\\n\\nIn positional mode, `at=` inside the component line is equivalent to a preceding `at:` line:\\n\\n```\\nC1: capacitor down at=R1.end value=\\\"100n\\\"\\n```\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Diagram title:** `circuit \\\"RC Filter\\\"` — first line only.\\n- **Component label:** `label=\\\"R1\\\"` attribute — reference designator shown beside the symbol.\\n- **Value annotation:** `value=\\\"4.7k\\\"` — shown beside or below the component.\\n- **Net label:** `label \\\"VOUT\\\" right` — standalone net flag at the current cursor.\\n- **Comments:** `#` at the start of a line (after leading whitespace).\\n\\n---\\n\\n## 7. Reserved words & escaping\\n\\n**Reserved at line start (positional):** `circuit` (header), `at:`, `net`, `wire`, `label`.\\n\\n**Reserved in netlist mode:** same header rules apply; all other lines are SPICE component lines.\\n\\n**Ground net aliases (netlist only):** `0`, `gnd`, `GND`, `Gnd`, `ground`, `Ground` — all treated as the same node.\\n\\n**Component IDs** must match `[a-zA-Z_][a-zA-Z0-9_]*`. Spaces in values must be quoted: `value=\\\"10 kΩ\\\"`.\\n\\n---\\n\\n## 8. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `resistor right 1k` (bare value without `value=`) | `1k` is parsed as an unknown attribute flag and ignored | Use `value=\\\"1k\\\"`: `resistor right value=\\\"1k\\\"` |\\n| `at: R1.center` | `center` is not a recognized anchor suffix — cursor stays at current position | Use `at: R1.end` or `at: R1.start` |\\n| `wire 40` (no direction) | Direction defaults to `right`; length `40` is accepted | Explicit direction recommended: `wire right 40` |\\n| `R1 vcc out 10k` in positional mode | Line matches the bare-type pattern; `R1` is read as a type name, fails lookup | In positional mode, use `R1: resistor right value=\\\"10k\\\"` |\\n| `Q1 c b e` (netlist, no model) | Type defaults to `npn` from `Q` prefix — correct | OK; add `npn` explicitly for clarity |\\n| `net OUT` then `at: OUT` without `net OUT: dot` | `OUT` net exists but has no anchor; jump has no destination | Use `net OUT: dot` to register the cursor position |\\n| `label VCC up` (unquoted label) | `VCC` is parsed as a direction token, then `up` — the label text is lost | Quote the text: `label \\\"VCC\\\" up` |\\n\\n---\\n\\n## 9. Grammar (EBNF)\\n\\n```text\\ndocument = header statement*\\n\\n-- Positional mode --\\nheader = \\\"circuit\\\" ( WS quoted-string )? NEWLINE\\nstatement = blank | comment | component | wire | at | net-decl | label-stmt\\n\\ncomponent = ( id \\\":\\\" WS )? type WS direction? attrs* NEWLINE\\nwire = \\\"wire\\\" ( WS direction )? ( WS integer )? NEWLINE\\nat = \\\"at:\\\" WS anchor NEWLINE\\nanchor = id \\\".\\\" ( \\\"start\\\" | \\\"end\\\" )\\n | id // net name anchor\\n\\nnet-decl = \\\"net\\\" WS id NEWLINE // declare net only\\n | \\\"net\\\" WS id \\\":\\\" WS \\\"dot\\\" NEWLINE // declare + place dot\\n\\nlabel-stmt = \\\"label\\\" WS quoted-string ( WS direction )? NEWLINE\\n\\ncomponent-attr = \\\"value=\\\" quoted-string\\n | \\\"label=\\\" quoted-string\\n | \\\"at=\\\" anchor\\n | \\\"length=\\\" integer\\n\\ndirection = \\\"right\\\" | \\\"left\\\" | \\\"up\\\" | \\\"down\\\"\\ntype = // any value from §2 component tables\\n\\n-- Netlist mode --\\nnetlist-header = \\\"circuit\\\" ( WS quoted-string )? WS \\\"netlist\\\" NEWLINE\\nnetlist-stmt = id WS net-ref+ ( WS kv-pair )* NEWLINE\\n | comment\\nnet-ref = id | \\\"0\\\" // net name or ground alias\\nkv-pair = id \\\"=\\\" ( quoted-string | bare-value )\\n\\nid = [a-zA-Z_] [a-zA-Z0-9_]*\\ninteger = [0-9]+\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\ncomment = \\\"#\\\" any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/circuit/parser.ts` and `src/diagrams/circuit/netlist.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"block\": {\n \"title\": \"Block diagram\",\n \"content\": \"## 1. Your first block diagram\\n\\nThe smallest useful block diagram: one controller, one plant, one feedback loop.\\n\\n```\\nblockdiagram \\\"Temperature control\\\"\\nctrl = block(\\\"PID\\\") [role: controller]\\nplant = block(\\\"Heater\\\") [role: plant]\\nsensor = block(\\\"Thermocouple\\\") [role: sensor]\\nerr = sum(+ref, -measured)\\nref = signal(\\\"Setpoint\\\")\\nmeasured = signal(\\\"T_measured\\\")\\nin -> ref\\nref -> err\\nerr -> ctrl\\nctrl -> plant\\nplant -> measured\\nmeasured -> sensor\\nsensor -> err\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with `blockdiagram`, optionally followed by a quoted title.\\n2. Declare each component with `ID = block(\\\"label\\\")`, each summing junction with `ID = sum(+a, -b)`, and each named signal with `ID = signal(\\\"label\\\")`.\\n3. Connect components with `->`. Chain multiple hops on one line: `A -> B -> C`.\\n4. Optionally annotate connections with a trailing label: `A -> B [\\\"E(s)\\\"]`.\\n\\n> Comments must start with `#` on their own line.\\n\\n---\\n\\n## 2. Blocks\\n\\nA block represents any functional element — controller, plant, filter, actuator, sensor. The label is typically a transfer function or a descriptive name.\\n\\n**Syntax:** `ID = block(\\\"label\\\") [role: X]`\\n\\n| Attribute | Values | Effect |\\n|---|---|---|\\n| `role: plant` | `plant`, `controller`, `sensor`, `actuator`, `reference`, `disturbance`, `generic` | Visual color coding; `generic` is the default |\\n| `route: above` | `above`, `below` | Routing hint for feedback and feedforward blocks |\\n\\n**ID rules.** Must start with a letter or underscore, followed by letters, digits, or underscores: `[A-Za-z_]\\\\w*`.\\n\\n```\\nblockdiagram \\\"Block roles\\\"\\nref_block = block(\\\"r(t)\\\") [role: reference]\\nctrl = block(\\\"C(s)\\\") [role: controller]\\nact = block(\\\"Actuator\\\") [role: actuator]\\nplant = block(\\\"G(s)\\\") [role: plant]\\nsensor = block(\\\"H(s)\\\") [role: sensor]\\ndist = block(\\\"d(t)\\\") [role: disturbance]\\nref_block -> ctrl\\nctrl -> act\\nact -> plant\\nplant -> sensor\\ndist -> plant\\n```\\n\\n---\\n\\n## 3. Summing junctions\\n\\nA summing junction combines multiple signals into one, with explicit polarity for each input. It renders as a circle with `+`/`−` signs — the standard control-systems symbol.\\n\\n**Syntax:** `ID = sum(+a, -b, +c, …)`\\n\\n- Each input is a signed ID: `+x` adds signal `x`, `-y` subtracts signal `y`.\\n- An input without a sign is treated as positive.\\n- The summing junction ID is then used as the target of connection lines, just like a block ID.\\n\\n```\\nblockdiagram \\\"Error with disturbance rejection\\\"\\nctrl = block(\\\"PI C(s)\\\") [role: controller]\\nplant = block(\\\"G(s)\\\") [role: plant]\\nsensor = block(\\\"H(s)\\\") [role: sensor]\\n# Main error junction: add reference, subtract feedback\\nerr = sum(+r, -ym)\\n# Disturbance junction: add plant input, add disturbance\\ndisturb = sum(+ctrl_out, +d)\\nr = signal(\\\"r (setpoint)\\\")\\nym = signal(\\\"y_m\\\")\\nctrl_out = signal(\\\"u(t)\\\")\\nin -> r\\nr -> err\\nerr -> ctrl\\nctrl -> ctrl_out\\nctrl_out -> disturb\\ndisturb -> plant\\nplant -> ym\\nym -> sensor\\nsensor -> err\\n```\\n\\n---\\n\\n## 4. Signals\\n\\nA signal declaration creates a named signal node that the parser inlines as an edge label. Signals are pass-through: when the parser sees `A -> sig` and `sig -> B`, it merges them into a single edge from `A` to `B`, labeling it with the signal's display text.\\n\\n**Syntax:** `ID = signal(\\\"label\\\") [discrete]`\\n\\n- Omit `[discrete]` for continuous signals (solid line).\\n- Add `[discrete]` for sampled-data signals (dashed line).\\n\\n```\\ne_sig = signal(\\\"E(s)\\\")\\nu_sig = signal(\\\"U(s)\\\") [discrete]\\n```\\n\\nSignals are purely a labeling convenience — you can also label edges directly with a trailing attribute (see §5).\\n\\n---\\n\\n## 5. Connections\\n\\nA connection line is `from -> to`. The `->` operator always produces a directed, arrowed line.\\n\\n**Single hop:** `A -> B`\\n\\n**Chain:** `A -> B -> C` — equivalent to `A -> B` and `B -> C`. Both are written in one line.\\n\\n**With a signal label:** append `[\\\"label text\\\"]` at the end of the chain. The label applies to the last hop only.\\n\\n```\\nctrl -> plant [\\\"U(s)\\\"]\\n```\\n\\n**With a discrete flag:** append `[discrete]` to make the last-hop arrow dashed.\\n\\n```\\nplant -> adc [\\\"y\\\"] [discrete]\\n```\\n\\n**Both label and discrete:** use `[label: \\\"Y(s)\\\", discrete]` (comma-separated).\\n\\n```\\nadc -> ctrl [label: \\\"y[k]\\\", discrete]\\n```\\n\\n```\\nblockdiagram \\\"Mixed continuous/discrete\\\"\\nctrl = block(\\\"Digital PID\\\") [role: controller]\\ndac = block(\\\"DAC\\\") [role: actuator]\\nplant = block(\\\"G(s)\\\") [role: plant]\\nadc = block(\\\"ADC\\\") [role: sensor]\\nerr = sum(+r, -yk)\\nr = signal(\\\"r[k]\\\") [discrete]\\nyk = signal(\\\"y[k]\\\") [discrete]\\nin -> r\\nr -> err\\nerr -> ctrl\\nctrl -> dac [label: \\\"u[k]\\\", discrete]\\ndac -> plant [\\\"u(t)\\\"]\\nplant -> adc [\\\"y(t)\\\"]\\nadc -> yk [discrete]\\nyk -> err\\n```\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Title:** `blockdiagram \\\"My System\\\"` — first line, quoted.\\n- **Block label:** the quoted string inside `block(\\\"…\\\")` — appears inside the box.\\n- **Signal label:** the quoted string inside `signal(\\\"…\\\")` — appears on the merged edge.\\n- **Edge label:** trailing `[\\\"text\\\"]` or `[label: \\\"text\\\"]` on a connection line — appears on that arrow.\\n- **Comments:** `#` at the start of a line (after leading whitespace). Inline trailing comments are not supported.\\n\\n---\\n\\n## 7. Reserved words & escaping\\n\\n**Reserved at line start:** `blockdiagram` (header).\\n\\n**Structural keywords** (avoid as block/signal/sum IDs to prevent ambiguity): `block`, `signal`, `sum`.\\n\\n**`in` and `out`** are conventional IDs for the external boundary of a diagram — the parser treats them as ordinary identifiers, but the renderer uses them as implicit source/sink nodes. Using `in -> r` and `plant -> out` is idiomatic.\\n\\n**Strings with spaces** must be double-quoted in `block(\\\"…\\\")` and `signal(\\\"…\\\")` labels.\\n\\n---\\n\\n## 8. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `G = block(G(s))` (no quotes) | Parse fails — label must be quoted | `G = block(\\\"G(s)\\\")` |\\n| `err = sum(r, -ym)` (no `+`) | `r` treated as `+r` — works, but ambiguous | Write `sum(+r, -ym)` for clarity |\\n| `ctrl -> plant, plant -> out` (comma on one line) | `,` is not a separator — parse fails | One connection per line or use chain: `ctrl -> plant -> out` |\\n| `s1 = signal(\\\"E(s)\\\") [label: \\\"E\\\"]` | `label:` not valid on signal; use it on connections | Drop `label:` from signal declaration |\\n| `role: filter` | Unknown role — silently defaults to `generic` | Use `plant`, `controller`, `sensor`, `actuator`, `reference`, `disturbance`, or `generic` |\\n| `A -> B [discrete, label: \\\"e\\\"]` — label first fails | Order of attrs inside `[…]` doesn't matter, but bare `\\\"text\\\"` shorthand only works when it's the only item | Use `[label: \\\"e\\\", discrete]` |\\n\\n---\\n\\n## 9. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | block-def | sum-def | signal-def | connection)*\\n\\nheader = \\\"blockdiagram\\\" ( WS quoted-string )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nblock-def = id WS \\\"=\\\" WS \\\"block\\\" \\\"(\\\" quoted-string \\\")\\\" ( \\\"[\\\" block-attrs \\\"]\\\" )? NEWLINE\\nblock-attrs = block-attr (\\\",\\\" block-attr)*\\nblock-attr = \\\"role:\\\" role | \\\"route:\\\" (\\\"above\\\" | \\\"below\\\")\\nrole = \\\"plant\\\" | \\\"controller\\\" | \\\"sensor\\\" | \\\"actuator\\\"\\n | \\\"reference\\\" | \\\"disturbance\\\" | \\\"generic\\\"\\n\\nsum-def = id WS \\\"=\\\" WS \\\"sum\\\" \\\"(\\\" sum-inputs \\\")\\\" NEWLINE\\nsum-inputs = sum-input (\\\",\\\" sum-input)*\\nsum-input = (\\\"+\\\" | \\\"-\\\")? id\\n\\nsignal-def = id WS \\\"=\\\" WS \\\"signal\\\" \\\"(\\\" quoted-string \\\")\\\" ( \\\"[\\\" \\\"discrete\\\" \\\"]\\\" )? NEWLINE\\n\\nconnection = id (\\\"->\\\" id)+ ( \\\"[\\\" conn-attrs \\\"]\\\" )? NEWLINE\\nconn-attrs = quoted-string # shorthand: bare label only\\n | conn-attr (\\\",\\\" conn-attr)*\\nconn-attr = \\\"label:\\\" quoted-string | \\\"discrete\\\"\\n\\nid = [A-Za-z_] \\\\w*\\ncomment = \\\"#\\\" any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/blockdiagram/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"ladder\": {\n \"title\": \"Ladder logic\",\n \"content\": \"## 1. Your first ladder diagram\\n\\nThe smallest useful ladder program: one rung, two contacts, one coil.\\n\\n```\\nladder \\\"First Rung\\\"\\nrung 1 \\\"Start when button pressed, stop on fault\\\":\\n XIC(START_PB)\\n XIO(FAULT)\\n OTE(MOTOR_RUN)\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with `ladder`, optionally followed by a quoted title.\\n2. Each **rung** begins with `rung N \\\"optional comment\\\":` on its own line.\\n3. Elements are listed one per line, indented under the rung — left to right means series (AND logic).\\n4. A `parallel:` / `branch:` block introduces OR logic. Every branch holds its own element list.\\n\\n> Comments must start with `#` on their own line.\\n\\n---\\n\\n## 2. Contacts\\n\\nContacts represent input conditions — they pass power when the associated bit matches the contact type.\\n\\n| Type | Name | Passes power when… |\\n|---|---|---|\\n| `XIC` | Examine If Closed | Tag bit = 1 (normally open) |\\n| `XIO` | Examine If Open | Tag bit = 0 (normally closed) |\\n| `ONS` | One-Shot Rising | Tag transitions 0 → 1 (rising edge, one scan) |\\n| `OSF` | One-Shot Falling | Tag transitions 1 → 0 (falling edge, one scan) |\\n\\n**Syntax:**\\n\\n```\\nXIC(tag)\\nXIC(tag, \\\"address\\\")\\nXIC(tag, \\\"address\\\", name=\\\"Description\\\")\\nXIC(tag, address=\\\"address\\\", name=\\\"Description\\\")\\n```\\n\\n- `tag` — required. The PLC tag name (displayed below the contact symbol).\\n- `\\\"address\\\"` — optional positional second argument. The I/O address (e.g. `\\\"IN 1.0\\\"`, `\\\"BIT 3.1\\\"`).\\n- `name=\\\"…\\\"` — optional key-value. Human-readable description (displayed above the symbol).\\n\\n```\\nladder \\\"Contact types\\\"\\nrung 1 \\\"All four contact types\\\":\\n XIC(START_PB, \\\"IN 1.0\\\", name=\\\"Start Button\\\")\\n XIO(E_STOP, \\\"IN 1.5\\\", name=\\\"Emergency Stop NC\\\")\\n ONS(PULSE_IN, \\\"BIT 5.0\\\", name=\\\"One-Shot Rising\\\")\\n OSF(RESET_SIG, \\\"BIT 5.1\\\", name=\\\"One-Shot Falling\\\")\\n OTE(OUT_RLY, \\\"OUT 2.0\\\", name=\\\"Output Relay\\\")\\n```\\n\\n---\\n\\n## 3. Coils\\n\\nCoils represent output actions — they act on a tag bit when the rung has power flow.\\n\\n| Type | Name | Effect on tag bit |\\n|---|---|---|\\n| `OTE` | Output Energize | Sets bit = 1 while rung is true; clears to 0 when rung is false |\\n| `OTL` | Output Latch | Sets bit = 1; **retains** even after rung goes false (latches) |\\n| `OTU` | Output Unlatch | Clears bit = 0; retains even after rung goes false |\\n| `OTN` | Output Negate | Sets bit = 0 while rung is true; sets to 1 when rung is false |\\n\\n**Syntax:** identical to contacts — `OTE(tag)`, `OTE(tag, \\\"address\\\")`, `OTE(tag, \\\"address\\\", name=\\\"…\\\")`.\\n\\n`OTL` and `OTU` are used in pairs to build Set/Reset flip-flops. The last rung to write wins.\\n\\n```\\nladder \\\"Set-Reset latch\\\"\\nrung 1 \\\"Set on start\\\":\\n XIC(START_PB, \\\"IN 1.0\\\", name=\\\"Start\\\")\\n OTL(MOTOR_ON, \\\"BIT 3.0\\\", name=\\\"Motor Latch\\\")\\nrung 2 \\\"Reset on stop or fault\\\":\\n parallel:\\n branch:\\n XIC(STOP_PB, \\\"IN 1.1\\\", name=\\\"Stop\\\")\\n branch:\\n XIC(E_STOP, \\\"IN 1.5\\\", name=\\\"E-Stop\\\")\\n OTU(MOTOR_ON, \\\"BIT 3.0\\\", name=\\\"Motor Latch\\\")\\n```\\n\\n---\\n\\n## 4. Function blocks\\n\\nFunction blocks perform timer, counter, math, and comparison operations. They appear inline in a rung and have keyword parameters after the mandatory tag argument.\\n\\n### 4.1 Timers\\n\\n| Type | Name | Key parameters |\\n|---|---|---|\\n| `TON` | Timer On-Delay | `PT=` preset time in milliseconds |\\n| `TOFF` | Timer Off-Delay | `PT=` preset time in milliseconds |\\n| `TP` | Timer Pulse | `PT=` preset time in milliseconds |\\n\\n```\\nTON(timer_tag, PT=5000)\\n```\\n\\nThe timer tag stores elapsed time. The timer's `Q` bit (done output) is accessed by tag name in downstream contacts.\\n\\n### 4.2 Counters\\n\\n| Type | Name | Key parameters |\\n|---|---|---|\\n| `CTU` | Count Up | `PV=` preset value (integer) |\\n| `CTD` | Count Down | `PV=` preset value |\\n| `CTUD` | Count Up/Down | `PV=` preset value |\\n\\n```\\nCTU(cycle_counter, PV=100)\\n```\\n\\n### 4.3 Math\\n\\n| Type | Operation |\\n|---|---|\\n| `ADD` | Add |\\n| `SUB` | Subtract |\\n| `MUL` | Multiply |\\n| `DIV` | Divide |\\n| `MOV` | Move (copy) |\\n\\n```\\nADD(result_tag, IN1=setpoint, IN2=offset)\\nMOV(dest_tag, IN1=source_tag)\\n```\\n\\n### 4.4 Comparisons\\n\\n| Type | Meaning |\\n|---|---|\\n| `EQU` | Equal |\\n| `NEQ` | Not equal |\\n| `GRT` | Greater than |\\n| `LES` | Less than |\\n| `GEQ` | Greater than or equal |\\n| `LEQ` | Less than or equal |\\n\\n```\\nEQU(compare_tag, IN1=speed_actual, IN2=speed_setpoint)\\n```\\n\\n```\\nladder \\\"Timer and counter\\\"\\nrung 1 \\\"Start run timer\\\":\\n XIC(MOTOR_CMD, \\\"BIT 3.0\\\", name=\\\"Motor Running\\\")\\n TON(RUN_TIMER, PT=10000)\\nrung 2 \\\"Count completed cycles\\\":\\n XIC(CYCLE_SENSOR, \\\"IN 2.0\\\", name=\\\"Cycle Sensor\\\")\\n CTU(PART_COUNT, PV=500)\\nrung 3 \\\"Alarm when batch complete\\\":\\n XIC(PART_COUNT, name=\\\"Count Done\\\")\\n OTE(BATCH_ALARM, \\\"OUT 3.0\\\", name=\\\"Batch Complete Alarm\\\")\\n```\\n\\n---\\n\\n## 5. Parallel branches\\n\\nA `parallel:` block introduces OR logic — the rung has power if **any** branch conducts. Each branch is a `branch:` sub-block with its elements indented below it.\\n\\n```\\nparallel:\\n branch:\\n XIC(LOCAL_START)\\n branch:\\n XIC(REMOTE_START)\\n```\\n\\nBranches in a `parallel:` are evaluated simultaneously. The block closes when indentation returns to the level before `parallel:`.\\n\\n**Rules:**\\n- `parallel:` must appear inside a rung.\\n- `branch:` must appear inside a `parallel:` — using it alone throws `LadderParseError`.\\n- Each branch holds one or more elements.\\n- Elements after the `parallel:` block are series with it (AND logic).\\n\\n```\\nladder \\\"Parallel OR logic\\\"\\nrung 1 \\\"Start from either local or remote\\\":\\n parallel:\\n branch:\\n XIC(LOCAL_START, \\\"IN 1.0\\\", name=\\\"Local Start\\\")\\n branch:\\n XIC(REMOTE_START, \\\"BIT 5.2\\\", name=\\\"Remote Start\\\")\\n XIO(STOP_ALL, \\\"IN 1.5\\\", name=\\\"Stop All\\\")\\n OTE(CONVEYOR, \\\"OUT 2.0\\\", name=\\\"Conveyor Run\\\")\\n```\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Title:** `ladder \\\"Motor Control\\\"` — first line only, quoted string.\\n- **Rung number:** required integer after `rung`.\\n- **Rung comment:** optional quoted string after the rung number, before the colon: `rung 3 \\\"Run indicator\\\":`.\\n- **Tag:** first argument inside the parentheses — displayed below the symbol.\\n- **Address:** second positional argument (quoted): `XIC(START_PB, \\\"IN 1.0\\\")`.\\n- **Name:** `name=\\\"…\\\"` keyword argument — human-readable description displayed above the symbol.\\n- **Line comments:** `#` at the start of a line (after leading whitespace). Inline `# …` after an element line is also stripped.\\n\\n---\\n\\n## 7. Reserved words & escaping\\n\\n**Reserved at line start (case-insensitive):** `ladder`, `rung`, `parallel:`, `branch:`.\\n\\n**Element names** are all uppercase ASCII: `XIC`, `XIO`, `ONS`, `OSF`, `OTE`, `OTL`, `OTU`, `OTN`, `TON`, `TOFF`, `TP`, `CTU`, `CTD`, `CTUD`, `ADD`, `SUB`, `MUL`, `DIV`, `MOV`, `EQU`, `NEQ`, `GRT`, `LES`, `GEQ`, `LEQ`.\\n\\n**Tag IDs** — must match `[A-Z][A-Z0-9_]*` (the parser matches `[A-Z][A-Z0-9_]*` for the element name prefix). Lowercase tags are accepted inside the parentheses.\\n\\n**Quoted strings** in address or name arguments must use double quotes `\\\"…\\\"`.\\n\\n---\\n\\n## 8. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `rung 1` (no colon) | `LadderParseError: element outside of rung` | Rung header must end with `:` — `rung 1:` |\\n| `ONF(TAG)` | `LadderParseError: unknown element type \\\"ONF\\\"` | Falling-edge contact is `OSF`, not `ONF` |\\n| `parallel:` without `branch:` | Empty parallel block — rung has no element | Add at least one `branch:` inside the `parallel:` |\\n| `branch:` before `parallel:` | `LadderParseError: branch: without parallel:` | Always open `parallel:` first |\\n| `OTE()` — no tag | `LadderParseError: element missing tag` | Tag is required: `OTE(MY_TAG)` |\\n| `var StartBtn: bool` (variable declaration) | `LadderParseError: invalid element syntax` | No variable declarations — tags are used directly |\\n| Empty rung (no elements after `rung N:`) | `LadderParseError: Rung N: empty rung` | Add at least one element to each rung |\\n| `TON(T1, T#5s)` | `LadderParseError: invalid element syntax` (T# not a valid number) | Use milliseconds integer: `TON(T1, PT=5000)` |\\n\\n---\\n\\n## 9. Grammar (EBNF)\\n\\n```text\\ndocument = header NEWLINE rung+\\n\\nheader = \\\"ladder\\\" ( WS quoted-string )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nrung = \\\"rung\\\" WS integer ( WS quoted-string )? \\\":\\\" NEWLINE\\n element+\\n\\nelement = contact-line\\n | coil-line\\n | fb-line\\n | parallel-block\\n\\ncontact-line = contact-type \\\"(\\\" tag ( \\\",\\\" arg )* \\\")\\\" NEWLINE\\ncontact-type = \\\"XIC\\\" | \\\"XIO\\\" | \\\"ONS\\\" | \\\"OSF\\\"\\n\\ncoil-line = coil-type \\\"(\\\" tag ( \\\",\\\" arg )* \\\")\\\" NEWLINE\\ncoil-type = \\\"OTE\\\" | \\\"OTL\\\" | \\\"OTU\\\" | \\\"OTN\\\"\\n\\nfb-line = fb-type \\\"(\\\" tag ( \\\",\\\" arg )* \\\")\\\" NEWLINE\\nfb-type = \\\"TON\\\" | \\\"TOFF\\\" | \\\"TP\\\"\\n | \\\"CTU\\\" | \\\"CTD\\\" | \\\"CTUD\\\"\\n | \\\"ADD\\\" | \\\"SUB\\\" | \\\"MUL\\\" | \\\"DIV\\\" | \\\"MOV\\\"\\n | \\\"EQU\\\" | \\\"NEQ\\\" | \\\"GRT\\\" | \\\"LES\\\" | \\\"GEQ\\\" | \\\"LEQ\\\"\\n\\narg = quoted-string // positional (address)\\n | key \\\"=\\\" quoted-string // keyword (e.g. name=\\\"…\\\")\\n | key \\\"=\\\" number // keyword (e.g. PT=5000)\\n\\nparallel-block = INDENT≥2 \\\"parallel:\\\" NEWLINE\\n ( INDENT branch-block )+\\n\\nbranch-block = \\\"branch:\\\" NEWLINE\\n ( INDENT element )+\\n\\ntag = [A-Za-z][A-Za-z0-9_]*\\nkey = [A-Za-z][A-Za-z0-9_]*\\ninteger = [0-9]+\\nnumber = [0-9]+ ( \\\".\\\" [0-9]+ )?\\ncomment = \\\"#\\\" any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/ladder/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"sld\": {\n \"title\": \"Single-line diagram (SLD)\",\n \"content\": \"## 1. Your first single-line diagram\\n\\nThe simplest SLD: a utility source, a transformer, a breaker, and a load.\\n\\n```\\nsld \\\"Simple feeder\\\"\\nutil = utility [label: \\\"Utility 13.8kV\\\"]\\nxfmr = transformer [rating: \\\"500 kVA\\\", voltage: \\\"13.8kV/480V\\\"]\\nbus1 = bus [voltage: \\\"480V\\\", label: \\\"480V Bus\\\"]\\ncb1 = breaker [rating: \\\"200A\\\"]\\nload1 = load [label: \\\"Panel LP-1\\\"]\\nutil -> xfmr\\nxfmr -> bus1\\nbus1 -> cb1\\ncb1 -> load1\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with `sld`, optionally followed by a quoted title.\\n2. Declare each equipment item as `id = nodeType [attributes]` — one per line.\\n3. Connect items with `from -> to`, optionally adding `[cable: \\\"…\\\", label: \\\"…\\\"]`.\\n4. IDs may contain letters, digits, underscores, and hyphens — but must start with a letter.\\n\\n> Comments must start with `#` on their own line.\\n\\n---\\n\\n## 2. Node types\\n\\nA node line is `id = nodeType [attr: value, …]`. The node type determines the symbol drawn.\\n\\n### 2.1 Sources\\n\\n| Type | Symbol | Typical use |\\n|---|---|---|\\n| `utility` | Utility source arrow | Infinite bus / grid connection |\\n| `generator` | Circle with `G` | Diesel, gas, or hydro genset |\\n| `solar` | PV panel symbol | Photovoltaic array |\\n| `wind` | Turbine symbol | Wind turbine |\\n| `ups` | Block with battery | Uninterruptible power supply |\\n\\n```\\nsld \\\"Generation sources\\\"\\nutil = utility [label: \\\"Grid 115 kV\\\"]\\ngen = generator [rating: \\\"2 MW\\\", label: \\\"Diesel Gen\\\"]\\nsol = solar [rating: \\\"500 kW\\\", label: \\\"PV Array\\\"]\\nwnd = wind [rating: \\\"1 MW\\\", label: \\\"Wind Turbine\\\"]\\nups = ups [rating: \\\"100 kVA\\\", label: \\\"UPS System\\\"]\\nutil -> gen\\nutil -> sol\\nutil -> wnd\\nutil -> ups\\n```\\n\\n### 2.2 Transformers\\n\\n| Type | Winding configuration | Notes |\\n|---|---|---|\\n| `transformer` | Generic two-winding | No winding spec |\\n| `transformer_dy` | Delta → Wye grounded (Δ-Yg) | Most common distribution |\\n| `transformer_yd` | Wye grounded → Delta (Yg-Δ) | |\\n| `transformer_yy` | Wye-Wye (both grounded) | |\\n| `transformer_dd` | Delta-Delta | |\\n| `autotransformer` | Single-winding with tap | Zigzag coil symbol |\\n| `transformer_3winding` | Three-winding | HV / MV / LV taps |\\n\\n```\\nsld \\\"Transformer configurations\\\"\\nsrc = utility [label: \\\"138kV Grid\\\"]\\nt_dy = transformer_dy [rating: \\\"30 MVA\\\", voltage: \\\"138kV/13.8kV\\\", label: \\\"Δ-Yg (most common)\\\"]\\nt_yy = transformer_yy [rating: \\\"10 MVA\\\", voltage: \\\"138kV/13.8kV\\\", label: \\\"Yg-Yg\\\"]\\nt_auto = autotransformer [rating: \\\"50 MVA\\\", voltage: \\\"138kV/69kV\\\", label: \\\"Autotransformer\\\"]\\nt_3w = transformer_3winding [rating: \\\"40 MVA\\\", voltage: \\\"138/13.8/4.16kV\\\", label: \\\"3-Winding\\\"]\\nsrc -> t_dy\\nsrc -> t_yy\\nsrc -> t_auto\\nsrc -> t_3w\\n```\\n\\n### 2.3 Buses and nodes\\n\\n| Type | Symbol | Typical use |\\n|---|---|---|\\n| `bus` | Thick horizontal line | Main voltage bus bar |\\n| `bus_tie` | Bus-tie breaker | Links two parallel buses at the same voltage |\\n| `hub` | Wide rectangle | Multi-feeder combining point |\\n\\n### 2.4 Switching and protection\\n\\n| Type | Symbol | Device number |\\n|---|---|---|\\n| `breaker` | Diagonal + arc | 52 (AC circuit breaker) |\\n| `breaker_vacuum` | Diagonal + V-oval | 52 vacuum type |\\n| `switch` | Diagonal (no arc) | 89 (disconnect / isolator) |\\n| `switch_load` | Load interrupter switch | — |\\n| `ground_switch` | Diagonal + ground symbol | Grounding disconnect |\\n| `ats` | Transfer switch symbol | Automatic transfer switch |\\n| `recloser` | Diagonal + arc + arrow | Auto-reclosing breaker |\\n| `sectionalizer` | Diagonal + S | Distribution sectionalizer |\\n| `fuse` | Oval with diagonal | Expulsion fuse cutout |\\n| `fuse_cl` | Rectangle with diagonal | Current-limiting fuse |\\n\\n```\\nsld \\\"Switching and protection\\\"\\nsrc = utility [label: \\\"Source\\\"]\\nrclsr = recloser [label: \\\"Recloser\\\"]\\nsect = sectionalizer [label: \\\"Sectionalizer\\\"]\\nfuse1 = fuse [label: \\\"Fuse\\\"]\\nsw = switch [label: \\\"Disconnect\\\"]\\ngnd_sw = ground_switch [label: \\\"Ground SW\\\"]\\nsrc -> rclsr\\nrclsr -> sect\\nsect -> fuse1\\nsect -> sw\\nsw -> gnd_sw\\n```\\n\\n### 2.5 Protection and monitoring\\n\\n| Type | Symbol | Typical use |\\n|---|---|---|\\n| `ct` | Small circle with line through | Current transformer |\\n| `pt` | Small circle | Potential / voltage transformer |\\n| `relay` | Small circle with device number | Protection relay (ANSI number via `device:`) |\\n| `surge_arrester` | Arrow + ground | Lightning arrester |\\n| `ground_fault` | GFI symbol | Ground-fault detector |\\n\\n### 2.6 Loads and equipment\\n\\n| Type | Symbol | Typical use |\\n|---|---|---|\\n| `motor` | Circle with `M` | Three-phase motor |\\n| `load` | Rectangle | Generic load or feeder |\\n| `capacitor_bank` | Two plates + switch | Power factor correction |\\n| `harmonic_filter` | LC symbol | Passive harmonic filter |\\n| `vfd` | Rectangle with VFD | Variable-frequency drive |\\n\\n### 2.7 Metering\\n\\n| Type | Symbol | Typical use |\\n|---|---|---|\\n| `watthour_meter` | Circle with `Wh` | Energy meter |\\n| `demand_meter` | Circle with `D` | Demand meter |\\n\\n```\\nsld \\\"Equipment types\\\"\\nsrc = utility [label: \\\"Grid 13.8kV\\\"]\\ntx = transformer_dy [rating: \\\"1000 kVA\\\", voltage: \\\"13.8kV/480V\\\", label: \\\"Main TX\\\"]\\nbk = breaker [rating: \\\"2000A\\\", label: \\\"Main Breaker\\\"]\\nbus = bus [voltage: \\\"480V\\\", label: \\\"480V MV Bus\\\"]\\nct1 = ct [label: \\\"CT-1\\\"]\\nrly = relay [device: \\\"51\\\", label: \\\"Overcurrent Relay\\\"]\\ncap = capacitor_bank [rating: \\\"150 kVAR\\\", label: \\\"PF Cap\\\"]\\nmtr = motor [rating: \\\"100HP\\\", label: \\\"Pump Motor\\\"]\\ngen = generator [rating: \\\"500kW\\\", label: \\\"Emergency Gen\\\"]\\nats = ats [rating: \\\"800A\\\", label: \\\"ATS-1\\\"]\\nsrc -> tx\\ntx -> bk\\nbk -> bus\\nbus -> ct1\\nct1 -> rly\\nbus -> cap\\nbus -> mtr\\ngen -> ats\\nats -> bus\\n```\\n\\n---\\n\\n## 3. Node attributes\\n\\nAttributes are written inside `[…]` after the node type, comma-separated.\\n\\n| Attribute | Values | Effect |\\n|---|---|---|\\n| `label: \\\"…\\\"` | quoted string | Display name on the diagram |\\n| `voltage: \\\"…\\\"` | quoted string, e.g. `\\\"13.8kV\\\"`, `\\\"480V\\\"` | Voltage level annotation |\\n| `rating: \\\"…\\\"` | quoted string, e.g. `\\\"1000 kVA\\\"`, `\\\"200A\\\"` | Equipment rating annotation |\\n| `device: \\\"…\\\"` | ANSI device number, e.g. `\\\"51\\\"`, `\\\"87\\\"` | Used with `relay` nodes |\\n| any other key | quoted string | Stored as nameplate data (transformer kVA, %Z, etc.) |\\n\\n**Example with all common attributes:**\\n\\n```\\nxfmr = transformer_dy [\\n label: \\\"Main Transformer\\\",\\n voltage: \\\"13.8kV/480V\\\",\\n rating: \\\"1000 kVA\\\",\\n impedance: \\\"5.75%Z\\\"\\n]\\n```\\n\\nThe attribute block may span multiple lines — the parser joins lines until the `]` is balanced.\\n\\n---\\n\\n## 4. Connections\\n\\nA connection line is `fromId -> toId`, optionally followed by `[cable: \\\"…\\\", label: \\\"…\\\"]`.\\n\\n```\\nbus1 -> cb1\\nbus1 -> cb1 [cable: \\\"3#2/0 AWG\\\"]\\nbus1 -> cb1 [cable: \\\"3#2/0 AWG\\\", label: \\\"Feeder A\\\"]\\n```\\n\\n**Rules:**\\n- Both IDs must be declared before or after the connection — all connections are validated at end of parse.\\n- Only `->` (directed, source-to-load) is accepted. The connection direction is used for layout.\\n- An unknown node ID throws `SLDParseError: Connection references unknown node \\\"…\\\"`.\\n\\n```\\nsld \\\"ATS backup with cable labels\\\"\\nUTIL = utility [label: \\\"Utility 480V\\\"]\\nGEN = generator [rating: \\\"500 kW\\\", label: \\\"Emergency Gen\\\"]\\nATS1 = ats [rating: \\\"800A\\\", label: \\\"ATS-1\\\"]\\nBUS1 = bus [voltage: \\\"480V\\\", label: \\\"Critical Bus\\\"]\\nCB1 = breaker [rating: \\\"200A\\\", label: \\\"CB-1\\\"]\\nCB2 = breaker [rating: \\\"200A\\\", label: \\\"CB-2\\\"]\\nL1 = load [label: \\\"Server Room\\\"]\\nL2 = load [label: \\\"Life Safety\\\"]\\nUTIL -> ATS1 [label: \\\"Normal source\\\"]\\nGEN -> ATS1 [label: \\\"Emergency source\\\"]\\nATS1 -> BUS1 [cable: \\\"3#2/0 AWG\\\"]\\nBUS1 -> CB1\\nBUS1 -> CB2\\nCB1 -> L1 [cable: \\\"3#4 AWG\\\"]\\nCB2 -> L2 [cable: \\\"3#4 AWG\\\"]\\n```\\n\\n---\\n\\n## 5. Labels & comments\\n\\n- **Title:** `sld \\\"Substation One-Line\\\"` — first line only.\\n- **Node label:** `id = type [label: \\\"…\\\"]` — the display name.\\n- **Connection label:** `A -> B [label: \\\"…\\\"]` — appears alongside the connecting line.\\n- **Cable annotation:** `A -> B [cable: \\\"3#2/0 AWG, 200ft\\\"]` — conductor specification.\\n- **Comments:** `#` at the start of a line. Inline `#` on the same line as a node or connection is also stripped.\\n\\n---\\n\\n## 6. Reserved words & escaping\\n\\n**Reserved at line start:** `sld` (header).\\n\\n**Operator token** — avoid `->` inside node IDs. IDs may contain `[A-Za-z][A-Za-z0-9_-]*` — hyphens are valid (e.g. `CB-101` is a legal ID).\\n\\n**Attribute block** — `[…]` brackets may span multiple physical lines. The parser joins continuation lines until the bracket depth reaches zero.\\n\\n**Duplicate IDs** throw `SLDParseError: Duplicate node id \\\"…\\\"`.\\n\\n---\\n\\n## 7. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `xfmr1 [type: transformer]` | `SLDParseError: Cannot parse line` | Use `=` assignment: `xfmr1 = transformer [...]` |\\n| `id = battery [...]` | `SLDParseError: Unknown node type \\\"battery\\\"` | No `battery` type — use `ups` or `generator` |\\n| `A -- B` (bidirectional) | `SLDParseError: Cannot parse line` | Only `->` is accepted; use two `->` lines if needed |\\n| `A -> B -> C` (chained) | `SLDParseError: Cannot parse line` | Each connection is one `->` per line |\\n| `relay [label: \\\"OC\\\"]` (no device number) | Relay renders with blank number | Add `device: \\\"51\\\"` for the ANSI device number |\\n| `voltage: 480V` (unquoted) | Attribute value not recognized | Quote all values: `voltage: \\\"480V\\\"` |\\n| Node ID starting with digit: `2BUS` | `SLDParseError: Cannot parse line` | IDs must start with a letter: `BUS2` |\\n| Connection before node declared | `SLDParseError: Connection references unknown node \\\"…\\\"` | Declare nodes before or after connections — validated at end of parse, so order is flexible |\\n\\n---\\n\\n## 8. Grammar (EBNF)\\n\\n```text\\ndocument = header NEWLINE ( blank | comment | node-def | connection )*\\n\\nheader = \\\"sld\\\" ( WS quoted-string )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nnode-def = id WS \\\"=\\\" WS node-type ( WS \\\"[\\\" attr-list \\\"]\\\" )? NEWLINE\\nnode-type = \\\"utility\\\" | \\\"generator\\\" | \\\"solar\\\" | \\\"wind\\\" | \\\"ups\\\"\\n | \\\"transformer\\\" | \\\"transformer_dy\\\" | \\\"transformer_yd\\\"\\n | \\\"transformer_yy\\\" | \\\"transformer_dd\\\"\\n | \\\"autotransformer\\\" | \\\"transformer_3winding\\\"\\n | \\\"bus\\\" | \\\"bus_tie\\\" | \\\"hub\\\"\\n | \\\"breaker\\\" | \\\"breaker_vacuum\\\" | \\\"switch\\\" | \\\"switch_load\\\"\\n | \\\"ground_switch\\\" | \\\"ats\\\" | \\\"recloser\\\" | \\\"sectionalizer\\\"\\n | \\\"fuse\\\" | \\\"fuse_cl\\\"\\n | \\\"ct\\\" | \\\"pt\\\" | \\\"relay\\\" | \\\"surge_arrester\\\" | \\\"ground_fault\\\"\\n | \\\"motor\\\" | \\\"load\\\" | \\\"capacitor_bank\\\" | \\\"harmonic_filter\\\" | \\\"vfd\\\"\\n | \\\"watthour_meter\\\" | \\\"demand_meter\\\"\\n\\nattr-list = attr ( \\\",\\\" attr )*\\nattr = key \\\":\\\" WS quoted-string\\n\\nconnection = id WS \\\"->\\\" WS id ( WS \\\"[\\\" conn-attrs \\\"]\\\" )? NEWLINE\\nconn-attrs = conn-attr ( \\\",\\\" conn-attr )*\\nconn-attr = \\\"cable\\\" \\\":\\\" WS quoted-string\\n | \\\"label\\\" \\\":\\\" WS quoted-string\\n\\nid = [A-Za-z] [A-Za-z0-9_-]*\\nkey = [A-Za-z] [A-Za-z0-9_]*\\ncomment = \\\"#\\\" any NEWLINE\\n```\\n\\nThe attribute block `[…]` may span multiple physical lines — the parser joins continuation lines until the bracket depth returns to zero.\\n\\nAuthoritative source: `src/diagrams/sld/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"entity\": {\n \"title\": \"Entity structure diagram\",\n \"content\": \"## 1. Your first entity structure\\n\\nThe smallest useful entity structure: a parent owning two subsidiaries.\\n\\n```\\nentity-structure \\\"Simple holding\\\"\\nentity holdco \\\"Holdco LLC\\\" llc@DE\\nentity opco \\\"OpCo Inc.\\\" corp@DE\\nentity sub_uk \\\"UK Sub Ltd.\\\" llc@UK\\nholdco -> opco : 100%\\nholdco -> sub_uk : 100%\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with `entity-structure`, optionally followed by a quoted title.\\n2. Each legal entity is a node: `entity ID \\\"Display Name\\\" type` — the type determines the shape.\\n3. Connect entities with `->`. Append `: pct` to label ownership percentage: `parent -> child : 60%`.\\n4. Add `@jurisdiction` after the type to show a jurisdiction badge: `corp@DE`.\\n\\n> Comments must start with `#` on their own line.\\n\\n---\\n\\n## 2. Entity types\\n\\nThe entity type determines the shape rendered for that node. Schematex maps several common aliases to a canonical type.\\n\\n| Canonical type | Aliases accepted | Rendered shape | Typical use |\\n|---|---|---|---|\\n| `corp` | `corporation`, `inc` | Rectangle (sharp corners) | C-corp, S-corp, Ltd., SA, AG |\\n| `llc` | `llp`, `gmbh`, `bv` | Rounded rectangle | LLC, LLP, GmbH, BV |\\n| `lp` | `lllp`, `fund` | Notched rectangle | LP, LLLP, investment fund |\\n| `trust` | — | Ellipse | Family trust, statutory trust |\\n| `individual` | `person` | Circle | Founder, grantor, natural person |\\n| `foundation` | `npo` | Pentagon (shield) | Non-profit, charitable foundation |\\n| `disregarded` | `branch` | Dashed rectangle | Disregarded entity, foreign branch |\\n| `pool` | — | Dashed rounded rectangle | Option pool, ESOP, unissued shares |\\n| `placeholder` | `tbf` | Dashed rectangle (faded) | To-be-formed entity, acquisition target |\\n\\n**Entity syntax:**\\n\\n```\\nentity ID \\\"Display Name\\\" type\\nentity ID \\\"Display Name\\\" type@JURISDICTION\\nentity ID \\\"Display Name\\\" type@JURISDICTION [properties]\\n```\\n\\n**ID rules.** Must start with a letter, followed by letters, digits, underscores, or hyphens: `[A-Za-z][A-Za-z0-9_-]*`.\\n\\n```\\nentity-structure \\\"Entity type shapes\\\"\\nentity c1 \\\"Delaware C-Corp\\\" corp@DE\\nentity l1 \\\"California LLC\\\" llc@CA\\nentity p1 \\\"Cayman Fund LP\\\" lp@KY\\nentity t1 \\\"Family Trust\\\" trust@SD\\nentity i1 \\\"Jane Smith\\\" individual\\nentity f1 \\\"Acme Foundation\\\" foundation\\nentity d1 \\\"Irish Branch\\\" disregarded@IE\\nentity pool1 \\\"ESOP Pool\\\" pool\\nentity tbf1 \\\"Acquisition Target\\\" placeholder\\nc1 -> l1 : 100%\\nc1 -> p1 : 80%\\nc1 -> f1\\ni1 -> t1\\nt1 -> c1 : 100%\\n```\\n\\n---\\n\\n## 3. Node properties\\n\\nOptional properties inside `[…]` annotate the entity with additional context visible in the rendered node.\\n\\n| Property | Syntax | Effect |\\n|---|---|---|\\n| `status: new` | `new`, `eliminated`, `modified`, `normal` | Visual badge for transaction-step diagrams |\\n| `tax: ccorp` | quoted string | Tax classification label below entity name |\\n| `role: \\\"Grantor\\\"` | quoted string | Role label (for individuals and trustees) |\\n| `note: \\\"…\\\"` | quoted string | Small note displayed inside the node |\\n| `est: \\\"2024-03-15\\\"` | quoted string | Formation date |\\n\\nAny key not in the reserved set (`status`, `tax`, `role`, `note`, `est`) is stored as a custom property.\\n\\n```\\nentity alice \\\"Alice Chen\\\" individual [role: \\\"Founder & CEO\\\"]\\nentity trust1 \\\"Smith Irrevocable Trust\\\" trust@SD [est: \\\"2019-06-01\\\", note: \\\"Spendthrift provisions\\\"]\\nentity opco \\\"OpCo, Inc.\\\" corp@DE [status: new, tax: ccorp]\\n```\\n\\n---\\n\\n## 4. Jurisdiction\\n\\nAppend `@CODE` after the entity type to display a jurisdiction badge on the node. The code is a 2–3 letter string — ISO 3166-1 alpha-2 country codes and US state abbreviations are both accepted.\\n\\n```\\nentity parent \\\"Parent Corp\\\" corp@US\\nentity ie_sub \\\"Ireland Sub\\\" corp@IE\\nentity ky_fund \\\"Cayman Fund\\\" lp@KY\\nentity de_llc \\\"Delaware LLC\\\" llc@DE\\n```\\n\\nCommon codes: `US` United States · `DE` Delaware · `CA` California · `NY` New York · `UK` United Kingdom · `IE` Ireland · `NL` Netherlands · `KY` Cayman Islands · `SG` Singapore · `HK` Hong Kong · `JP` Japan · `BM` Bermuda · `VG` British Virgin Islands · `CH` Switzerland · `LU` Luxembourg · `SD` South Dakota.\\n\\n`@DE` resolves as Delaware by default (most common in US legal contexts).\\n\\n### Jurisdiction clusters\\n\\nDeclare a jurisdiction with `jurisdiction CODE \\\"Name\\\" [color: \\\"#hex\\\"]` to group all entities sharing that code into a labeled, dashed-border cluster region on the diagram.\\n\\n```\\njurisdiction US \\\"United States\\\" [color: \\\"#3b82f6\\\"]\\njurisdiction IE \\\"Ireland\\\" [color: \\\"#059669\\\"]\\n```\\n\\nWhen an entity's `@CODE` matches a declared `jurisdiction`, it is automatically placed inside that cluster. If `jurisdiction` is not declared, the badge still appears but no cluster region is drawn.\\n\\n```\\nentity-structure \\\"IP holding structure\\\"\\njurisdiction US \\\"United States\\\" [color: \\\"#3b82f6\\\"]\\njurisdiction IE \\\"Ireland\\\" [color: \\\"#059669\\\"]\\njurisdiction KY \\\"Cayman Islands\\\" [color: \\\"#d97706\\\"]\\nentity parent \\\"TopCo, Inc.\\\" corp@US\\nentity ie_hold \\\"Ireland Holdings\\\" corp@IE\\nentity ie_ip \\\"IP HoldCo\\\" corp@KY [note: \\\"Group IP\\\"]\\nentity ie_op \\\"EU Opco\\\" llc@IE\\nparent -> ie_hold : 100%\\nie_hold -> ie_ip : 100%\\nie_hold -> ie_op : 100%\\nie_ip -~-> ie_op [label: \\\"IP License\\\"]\\n```\\n\\n### Manual clusters\\n\\nUse `cluster \\\"Label\\\" [members: [id1, id2], color: \\\"#hex\\\"]` to group entities that don't share a single jurisdiction code.\\n\\n```\\ncluster \\\"Ireland / Cayman IP\\\" [members: [ie_ip, nl_bv], color: \\\"#059669\\\"]\\n```\\n\\n---\\n\\n## 5. Ownership edges\\n\\nAn edge line connects two entity IDs with an operator. The operator determines the line style and visual semantics.\\n\\n| Operator | Renders as | Meaning |\\n|---|---|---|\\n| `->` | Solid arrow | Equity ownership (default) |\\n| `==>` | Double line arrow | Voting-only control (no economic interest) |\\n| `-.->` | Dashed grey arrow | Option pool / conditional ownership |\\n| `-~->` | Dashed purple arrow | IP license, management agreement, intercompany service |\\n| `-->` | Dashed green arrow | Distribution (trust to beneficiaries) |\\n\\n**Ownership percentage:** append `: pct` after the target ID.\\n\\n```\\nparent -> subsidiary : 60%\\n```\\n\\n**Share class:** add `[class: \\\"Series A Pref\\\"]` to label the share class on the edge.\\n\\n```\\nvc -> startup : 22% [class: \\\"Series A Pref\\\"]\\n```\\n\\n**Non-equity label:** use `[label: \\\"…\\\"]` for descriptive edge text on license or distribution edges.\\n\\n```\\nip_co -~-> opco [label: \\\"IP License · royalty\\\"]\\ntrust --> beneficiary [label: \\\"Discretionary distributions\\\"]\\n```\\n\\n**Combining:** `class:` and `label:` can appear together.\\n\\n```\\nfund -> portfolio : 35% [class: \\\"Common\\\", label: \\\"Post-Series B\\\"]\\n```\\n\\n```\\nentity-structure \\\"Mixed edge types\\\"\\nentity holdco \\\"HoldCo LLC\\\" llc@DE\\nentity opco \\\"OpCo Inc.\\\" corp@DE\\nentity ip_co \\\"IP Ltd\\\" corp@KY\\nentity fund \\\"Growth Fund\\\" lp@DE\\nentity esop \\\"ESOP Pool\\\" pool\\nentity trust1 \\\"Family Trust\\\" trust@SD\\nentity founder \\\"J. Smith\\\" individual\\n# Equity ownership\\nholdco -> opco : 100%\\nholdco -> ip_co : 100%\\n# IP license (non-equity)\\nip_co -~-> opco [label: \\\"Exclusive license\\\"]\\n# Option pool (dashed)\\nesop -.-> opco : 10%\\n# Voting control\\nfund ==> holdco\\n# Trust distribution\\ntrust1 --> founder [label: \\\"Income distributions\\\"]\\nfounder -> trust1 : 100%\\n```\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Title:** `entity-structure \\\"Acme Holdings\\\"` — first line, quoted.\\n- **Entity display name:** the quoted string in `entity ID \\\"Name\\\" type` — appears inside the node.\\n- **Jurisdiction badge:** `@CODE` after type — 2–3 letter code shown in the node corner.\\n- **Node properties:** `[note: \\\"…\\\", role: \\\"…\\\", status: new, …]` — annotations inside the node.\\n- **Edge percentage:** `: pct` after the target ID — appears on the ownership arrow.\\n- **Edge class:** `[class: \\\"…\\\"]` — share class label on the arrow.\\n- **Edge label:** `[label: \\\"…\\\"]` — descriptive text on the arrow.\\n- **Comments:** `#` at the start of a line (after leading whitespace). Inline trailing `#` comments inside bracket blocks are also stripped.\\n\\n---\\n\\n## 7. Reserved words & escaping\\n\\n**Reserved at line start:** `entity-structure` (header), `entity`, `jurisdiction`, `cluster`.\\n\\n**Entity type keywords** — these strings are parsed as entity types and must not be used as entity IDs: `corp`, `corporation`, `inc`, `llc`, `llp`, `gmbh`, `bv`, `lp`, `lllp`, `fund`, `trust`, `individual`, `person`, `foundation`, `npo`, `disregarded`, `branch`, `pool`, `placeholder`, `tbf`.\\n\\n**Edge operator tokens** — avoid these sequences inside IDs: `->`, `==>`, `-.->`, `-~->`, `-->`.\\n\\n**Strings with spaces** must be double-quoted in display names, notes, role labels, and color values.\\n\\n**Jurisdiction codes** are case-insensitive in the source but normalized to uppercase: `@de` and `@DE` are equivalent.\\n\\n---\\n\\n## 8. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `entity acme Acme Inc. corp@DE` (name unquoted) | Parse fails — name must be quoted | `entity acme \\\"Acme Inc.\\\" corp@DE` |\\n| `acme -> sub [50%]` (percent inside brackets) | `50%` not recognized as a property key | `acme -> sub : 50%` (colon before percent, outside brackets) |\\n| `entity acme \\\"Acme\\\" Ltd@DE` | `Ltd` is not a recognized type keyword | Use `corp` or `llc`; `Ltd` is not in the alias table |\\n| `acme -> unknown_id : 100%` | `EntityParseError: Edge references unknown entity \\\"unknown_id\\\"` | Declare every entity before using it in an edge |\\n| `cluster \\\"US entities\\\" [ids: [a, b]]` | `ids:` not recognized; property is silently ignored | Use `members: [a, b]` |\\n| `jurisdiction DE \\\"Delaware\\\"` then `entity co \\\"Co\\\" corp@DE` | Cluster draws around `co` — correct. But `@DE` in a US context is Delaware, not Germany | Use `@DE` for Delaware, `@DEU` is not valid — there is no ambiguity workaround; document the intent in a `note:` |\\n| `entity x \\\"X\\\" corp [status: pending]` | `pending` is not a valid status — property silently stored as custom prop | Use `new`, `eliminated`, `modified`, or `normal` |\\n\\n---\\n\\n## 9. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | jurisdiction-def | cluster-def | entity-def | edge)*\\n\\nheader = \\\"entity-structure\\\" ( WS quoted-string )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\njurisdiction-def = \\\"jurisdiction\\\" CODE WS quoted-string ( \\\"[\\\" jur-attrs \\\"]\\\" )? NEWLINE\\njur-attrs = jur-attr (\\\",\\\" jur-attr)*\\njur-attr = \\\"color:\\\" quoted-string\\n\\ncluster-def = \\\"cluster\\\" WS quoted-string ( \\\"[\\\" cluster-attrs \\\"]\\\" )? NEWLINE\\ncluster-attrs = cluster-attr (\\\",\\\" cluster-attr)*\\ncluster-attr = \\\"members:\\\" \\\"[\\\" id (\\\",\\\" id)* \\\"]\\\"\\n | \\\"color:\\\" quoted-string\\n\\nentity-def = \\\"entity\\\" WS id WS quoted-string WS entity-type ( \\\"@\\\" CODE )?\\n ( \\\"[\\\" entity-attrs \\\"]\\\" )? NEWLINE\\nentity-attrs = entity-attr (\\\",\\\" entity-attr)*\\nentity-attr = \\\"status:\\\" status\\n | \\\"tax:\\\" quoted-string\\n | \\\"role:\\\" quoted-string\\n | \\\"note:\\\" quoted-string\\n | \\\"est:\\\" quoted-string\\n | key \\\":\\\" quoted-string # custom property\\n\\nentity-type = \\\"corp\\\" | \\\"corporation\\\" | \\\"inc\\\"\\n | \\\"llc\\\" | \\\"llp\\\" | \\\"gmbh\\\" | \\\"bv\\\"\\n | \\\"lp\\\" | \\\"lllp\\\" | \\\"fund\\\"\\n | \\\"trust\\\"\\n | \\\"individual\\\" | \\\"person\\\"\\n | \\\"foundation\\\" | \\\"npo\\\"\\n | \\\"disregarded\\\" | \\\"branch\\\"\\n | \\\"pool\\\"\\n | \\\"placeholder\\\" | \\\"tbf\\\"\\n\\nedge = id WS op WS id ( \\\":\\\" WS pct-text )? ( \\\"[\\\" edge-attrs \\\"]\\\" )? NEWLINE\\nop = \\\"->\\\" | \\\"==>\\\" | \\\"-.->\\\" | \\\"-~->\\\" | \\\"-->\\\"\\npct-text = any text up to \\\"[\\\" or end of line # e.g. \\\"100%\\\" or \\\"V 75% / E 50%\\\"\\n\\nedge-attrs = edge-attr (\\\",\\\" edge-attr)*\\nedge-attr = \\\"class:\\\" quoted-string\\n | \\\"label:\\\" quoted-string\\n\\nstatus = \\\"new\\\" | \\\"eliminated\\\" | \\\"modified\\\" | \\\"normal\\\"\\nCODE = [A-Za-z]{2,3}\\nid = [A-Za-z] [A-Za-z0-9_-]*\\ncomment = \\\"#\\\" any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/entity/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"fishbone\": {\n \"title\": \"Fishbone diagram\",\n \"content\": \"## 1. Your first fishbone\\n\\nThe smallest useful fishbone: three categories, one cause each, one with a sub-cause.\\n\\n```\\nfishbone \\\"API latency spike\\\"\\neffect \\\"P99 > 2 s after deploy\\\"\\ncategory code \\\"Code\\\"\\ncategory infra \\\"Infra\\\"\\ncategory data \\\"Data\\\"\\ncode : \\\"N+1 query in new endpoint\\\"\\n - \\\"Missing eager-load on orders\\\"\\ninfra : \\\"DB connection pool exhausted\\\"\\ndata : \\\"Index missing on accounts table\\\"\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with `fishbone`, optionally followed by a quoted title.\\n2. Declare each branch with `category id \\\"Label\\\"` — the `id` is a short internal key, `\\\"Label\\\"` is what prints on the diagram.\\n3. Add causes with `id : \\\"cause text\\\"` on their own lines.\\n4. Indent a line by at least 2 spaces and start it with `-` to create a sub-cause (second-order branch) under the preceding cause.\\n\\n> Comments must start with `#` on their own line.\\n\\n---\\n\\n## 2. Building blocks\\n\\n### The spine and effect\\n\\n`effect \\\"Problem statement\\\"` places text in the fish's head. If `effect` is omitted the parser falls back to the diagram title.\\n\\n```\\nfishbone \\\"Title\\\"\\neffect \\\"Specific problem statement\\\"\\n```\\n\\nThe head sits on the right by default (`config direction = right`). Use `config direction = left` to flip it.\\n\\n### Categories (major bones)\\n\\n`category id \\\"Label\\\"` declares a branch. The `id` is used internally to assign causes; the quoted `\\\"Label\\\"` appears on the diagram.\\n\\nCategories also accept optional properties in `[…]`:\\n\\n| Property | Values | Effect |\\n|---|---|---|\\n| `color: \\\"#hex\\\"` | hex color string | Branch and label color |\\n| `side: top` / `side: bottom` | `top`, `bottom` | Forces this branch to the top or bottom rail (default: alternating) |\\n| `order: N` | integer | Position within its rail — lower numbers sit closer to the tail |\\n\\n```\\ncategory rework \\\"Rework\\\" [color: \\\"#E53935\\\", side: top, order: 1]\\n```\\n\\n### Causes (minor bones)\\n\\nTwo styles are accepted and can be mixed in one diagram:\\n\\n**Style A — structured.** Declare categories first, then assign causes with `id : \\\"text\\\"`:\\n\\n```\\ncategory code \\\"Code\\\"\\ncategory infra \\\"Infra\\\"\\ncode : \\\"N+1 query in endpoint\\\"\\ncode : \\\"Missing cache layer\\\"\\ninfra : \\\"Auto-scaling lag\\\"\\n```\\n\\n**Style B — compact.** Category label and causes in one line, separated by `;` or `,`:\\n\\n```\\ncategory Code: N+1 query; Missing cache; Synchronous call\\ncategory Infra: Auto-scaling lag; CDN misconfigured\\n```\\n\\nIn compact style the `id` is auto-derived from the label text (lowercased, spaces → hyphens). Quotes are optional for cause text.\\n\\n```\\nfishbone \\\"Conversion rate drop\\\"\\neffect \\\"Checkout conversion -12% MoM\\\"\\n# Style A — structured\\ncategory ux \\\"UX\\\"\\ncategory trust \\\"Trust\\\"\\nux : \\\"Confusing multi-step form\\\"\\nux : \\\"Slow page on mobile\\\"\\ntrust : \\\"No payment security badge\\\"\\n# Style B — compact\\ncategory Pricing: Price-anchoring missing; No annual discount shown; Coupon field too prominent\\n```\\n\\n---\\n\\n## 3. Sub-causes (second-order branches)\\n\\nIndent a `-` line by at least 2 spaces after a Level-1 cause to attach a sub-cause to it. The `-` dash is part of the syntax; the text follows it.\\n\\n```\\nmethod : \\\"Stencil aperture undersized\\\"\\n - \\\"Tolerance spec from 2018 board revision\\\"\\n - \\\"No re-validation after material change\\\"\\nmethod : \\\"Pick-and-place speed too high\\\"\\n - \\\"Speed limit lifted during overtime run\\\"\\n```\\n\\nSub-causes appear as shorter, narrower twigs branching off their parent rib.\\n\\n```\\nfishbone \\\"Medication error increase\\\"\\neffect \\\"Errors up 18% in Q3\\\"\\ncategory process \\\"Process\\\"\\ncategory people \\\"People\\\"\\nprocess : \\\"CPOE alert fatigue\\\"\\n - \\\"47 non-critical alerts per shift\\\"\\n - \\\"Override too easy — one click\\\"\\nprocess : \\\"5-Rights verification skipped\\\"\\n - \\\"No barcode scanner at bedside\\\"\\npeople : \\\"Float staff unfamiliar with unit\\\"\\n - \\\"No unit-specific orientation checklist\\\"\\npeople : \\\"Handoff communication gaps\\\"\\n```\\n\\n---\\n\\n## 4. Config options\\n\\n`config key = value` lines can appear anywhere after the header. Unknown keys and values are silently ignored.\\n\\n| Config key | Values | Default | Effect |\\n|---|---|---|---|\\n| `direction` | `right` / `left` (also `ltr` / `rtl`) | `right` | Which side the effect head appears on |\\n| `sides` | `both`, `top`, `bottom` | `both` | Which half of the spine hosts branches |\\n| `density` | `compact`, `normal`, `spacious` | `normal` | Spacing between ribs — affects how many branches fit before overlap |\\n| `slope` (or `ribslope`) | `gentle`, `normal`, `steep`, or a number (0–3) | `normal` (0.6) | Rib angle — shallow vs. steep diagonal |\\n| `causeside` (or `cause-side`) | `head`, `tail`, `both` | `head` | Which side of a rib sub-causes branch off from |\\n| `width` | integer px | auto | Override canvas width |\\n| `height` | integer px | auto | Override canvas height |\\n\\n```\\nconfig direction = left\\nconfig density = compact\\nconfig slope = gentle\\nconfig sides = top\\n```\\n\\n---\\n\\n## 5. Labels & comments\\n\\n- **Diagram title:** `fishbone \\\"Website Traffic Drop\\\"` — first line, optional.\\n- **Effect label:** `effect \\\"30% organic traffic decline\\\"` — the problem at the fish's head.\\n- **Category label:** `category id \\\"Human-readable name\\\"` — printed on the branch.\\n- **Cause text:** quoted `\\\"like this\\\"` or unquoted (spaces allowed in compact style).\\n- **Sub-cause text:** after the leading `-`, quoted or unquoted.\\n- **Comments:** `#` at the start of a line (after optional leading whitespace). The `#` character inside a double-quoted string is not treated as a comment.\\n\\n---\\n\\n## 6. Reserved words & escaping\\n\\n**Reserved at line start:** `fishbone` (header), `effect`, `category`, `config`.\\n\\n**The `-` prefix** on an indented line is reserved as the sub-cause marker. To include a literal hyphen-dash at the start of cause text, quote it: `code : \\\"- old deprecated path\\\"`.\\n\\n**Strings with spaces** in structured-style cause text should be double-quoted: `code : \\\"N+1 query\\\"`. In compact style (`category Label: ...`) the text runs to the `;` or `,` separator and quoting is optional.\\n\\n**The `#` character** starts a comment unless inside a double-quoted string.\\n\\n| Reserved sequence | Context | Alternative |\\n|---|---|---|\\n| `#` at line start | Comment marker | Quote the text if `#` is part of content |\\n| `-` at start after ≥2-space indent | Sub-cause marker | Quote: `- \\\"- text with dash\\\"` |\\n| `category`, `effect`, `config`, `fishbone` | Line-start keywords | Cannot be used as category IDs |\\n\\n---\\n\\n## 7. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `cause1 : \\\"text\\\"` with no prior `category cause1` | `FishboneParseError: Unknown category \\\"cause1\\\"` | Declare `category cause1 \\\"Label\\\"` before assigning causes |\\n| `- \\\"sub-cause\\\"` at the start of the file (no preceding Level-1 cause) | `FishboneParseError: Sub-cause … has no preceding Level-1 cause` | Place the sub-cause line immediately after a `id : \\\"cause\\\"` line |\\n| `- \\\"sub-cause\\\"` with only 1-space indent | Treated as a cause line, not a sub-cause | Indent with at least 2 spaces |\\n| `category Code: cause one, cause two` | Parsed as compact style — `,` and `;` are both separators | Intended behavior; both separators work |\\n| `config direction = center` | Unknown value — silently ignored, stays `right` | Use `right` or `left` |\\n| `config slope = 45` | Out of range (must be 0–3 exclusive); silently ignored | Use a preset (`gentle`, `normal`, `steep`) or a value like `0.5` |\\n| `fishbone: \\\"Title\\\"` | Parsed correctly — colon after keyword is optional | Both `fishbone \\\"Title\\\"` and `fishbone: \\\"Title\\\"` work |\\n\\n---\\n\\n## 8. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | effect | category | config | cause | sub-cause)*\\n\\nheader = \\\"fishbone\\\" \\\":\\\"? ( WS quoted-string )? NEWLINE\\neffect = \\\"effect\\\" \\\":\\\"? WS quoted-string NEWLINE\\nconfig = \\\"config\\\" WS config-key WS \\\"=\\\" WS config-value NEWLINE\\nconfig-key = \\\"direction\\\" | \\\"width\\\" | \\\"height\\\" | \\\"sides\\\"\\n | \\\"slope\\\" | \\\"ribslope\\\" | \\\"density\\\" | \\\"causeside\\\" | \\\"cause-side\\\"\\nconfig-value = bare-word | number | quoted-string\\n\\ncategory = \\\"category\\\" WS id WS label-or-compact ( \\\"[\\\" category-attrs \\\"]\\\" )? NEWLINE\\n\\nlabel-or-compact\\n = quoted-string # structured form: category id \\\"Label\\\"\\n | id WS \\\":\\\" WS compact-causes # compact form: category Label: cause; cause\\n\\ncategory-attrs = category-attr (\\\",\\\" category-attr)*\\ncategory-attr = \\\"color:\\\" quoted-string\\n | \\\"side:\\\" ( \\\"top\\\" | \\\"bottom\\\" )\\n | \\\"order:\\\" integer\\n\\ncause = id WS \\\":\\\" WS cause-text NEWLINE # structured form\\ncause-text = quoted-string | bare-text\\n\\nsub-cause = INDENT≥2 \\\"-\\\" WS cause-text NEWLINE\\n\\ncompact-causes = compact-cause ( (\\\";\\\" | \\\",\\\") compact-cause )*\\ncompact-cause = quoted-string | bare-text\\n\\ncomment = \\\"#\\\" any NEWLINE\\nid = [a-zA-Z] [a-zA-Z0-9_-]*\\nquoted-string = '\\\"' any-char-but-unescaped-quote* '\\\"'\\n```\\n\\nAuthoritative source: `src/diagrams/fishbone/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"venn\": {\n \"title\": \"Venn / Euler diagram\",\n \"content\": \"## 1. Your first Venn diagram\\n\\nThe smallest useful diagram: two sets, one overlap, two exclusive regions.\\n\\n```\\nvenn \\\"Support channels\\\"\\nset A \\\"Email support\\\" [color: \\\"#1E88E5\\\"]\\nset B \\\"Live chat\\\" [color: \\\"#E53935\\\"]\\nA & B : 320\\nA only : 1450\\nB only : 890\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with `venn`, optionally followed by a quoted title.\\n2. Declare each **set** with `set ID \\\"Label\\\"` — the id is used internally, the label appears in the diagram.\\n3. Assign values to **regions** using `A & B : value` for intersections and `A only : value` for exclusive regions.\\n4. Configure appearance with `config:` lines; the diagram mode (`venn` vs `euler`) can be set explicitly or left as `auto`.\\n\\n> Comments must start with `#` on their own line.\\n\\n---\\n\\n## 2. Sets\\n\\nA set declaration creates one circle in the diagram.\\n\\n```\\nset ID \\\"Label\\\" [color: \\\"#hex\\\"]\\n```\\n\\n| Part | Required | Notes |\\n|---|---|---|\\n| `ID` | Yes | Must match `[A-Za-z][A-Za-z0-9_-]*` |\\n| `\\\"Label\\\"` | Yes | Quoted string displayed on the circle |\\n| `[color: \\\"#hex\\\"]` | No | Override fill color for this set |\\n\\nSets must be declared before they are referenced in region or relation lines.\\n\\n```\\nvenn \\\"Programming paradigms\\\"\\nset oop \\\"Object-Oriented\\\" [color: \\\"#1E88E5\\\"]\\nset fp \\\"Functional\\\" [color: \\\"#E53935\\\"]\\nset logic \\\"Logic\\\" [color: \\\"#43A047\\\"]\\noop & fp : 180\\noop & logic : 45\\nfp & logic : 90\\noop & fp & logic : 12\\noop only : 620\\nfp only : 340\\nlogic only : 95\\n```\\n\\n---\\n\\n## 3. Regions\\n\\nA region assigns a value to an intersection or exclusive area. The four DSL modes can be mixed in one diagram.\\n\\n### 3.1 Declarative mode — counts and percentages\\n\\nAssign a number or percentage to a named region. The region key is either an `&`-separated list of set ids (for intersections) or `ID only` (for the part of that set not covered by any other set).\\n\\n```\\nA & B : 320 # integer count\\nA & B & C : 45 # three-way intersection\\nA only : 1450 # A minus all other sets\\nA & B : 18.5% # percentage value\\n```\\n\\n```\\nvenn \\\"Market research\\\"\\nset aware \\\"Awareness\\\" [color: \\\"#7B1FA2\\\"]\\nset consider \\\"Consideration\\\" [color: \\\"#0288D1\\\"]\\nset convert \\\"Conversion\\\" [color: \\\"#388E3C\\\"]\\naware & consider : 3400\\nconsider & convert : 890\\naware & convert : 210\\naware & consider & convert : 150\\naware only : 18200\\nconsider only : 2100\\nconvert only : 540\\n```\\n\\n### 3.2 Region labels (text)\\n\\nUse the `region` keyword prefix and assign a quoted string instead of a number. The string is rendered inside the region.\\n\\n```\\nregion A & B : \\\"Nurture\\\"\\nregion B & C : \\\"Convert\\\"\\nregion A & B & C : \\\"Loyal customer\\\"\\n```\\n\\n```\\nvenn \\\"Go-to-market funnel\\\"\\nset A \\\"Awareness\\\" [color: \\\"#7B1FA2\\\"]\\nset B \\\"Consideration\\\" [color: \\\"#0288D1\\\"]\\nset C \\\"Purchase\\\" [color: \\\"#388E3C\\\"]\\nregion A & B : \\\"Nurture\\\"\\nregion B & C : \\\"Convert\\\"\\nregion A only : \\\"Cold audience\\\"\\nregion A & B & C : \\\"Loyal\\\"\\nregion C only : \\\"Direct buyers\\\"\\n```\\n\\n### 3.3 Enumeration mode — element lists\\n\\nList the actual elements of each set. Schematex computes all intersections automatically.\\n\\n```\\nID = { element1, element2, element3 }\\n```\\n\\nElements are comma-separated bare words or quoted strings. Enumeration sets do not need an explicit `set` declaration — the declaration is implied.\\n\\n```\\nvenn \\\"Full-stack team skills\\\"\\nFrontend = { React, TypeScript, CSS, Jest, Webpack }\\nBackend = { TypeScript, Node.js, PostgreSQL, Jest, Redis }\\nDevOps = { Docker, Kubernetes, PostgreSQL, Terraform, Redis }\\n```\\n\\n---\\n\\n## 4. Euler relations\\n\\nEuler relations express structural containment or separation — that one set is a subset of another, that two sets are completely disjoint, or that they merely overlap. They must reference set ids already declared with `set`.\\n\\n```\\nfrom subset to # from is fully inside to (also: \\\"in\\\")\\nfrom in to # alias for subset\\nfrom disjoint to # from and to do not overlap\\nfrom overlap to # from and to partially overlap (explicit — the default for unrelated sets)\\n```\\n\\n```\\nvenn \\\"Biology taxonomy\\\"\\nset animals \\\"Animals\\\"\\nset vertebrates \\\"Vertebrates\\\"\\nset mammals \\\"Mammals\\\"\\nset birds \\\"Birds\\\"\\nset fish \\\"Fish\\\"\\nvertebrates subset animals\\nmammals subset vertebrates\\nbirds subset vertebrates\\nfish subset vertebrates\\nmammals disjoint birds\\nmammals disjoint fish\\nbirds disjoint fish\\n```\\n\\n| Keyword | Alias | Meaning |\\n|---|---|---|\\n| `subset` | `in` | `from` is fully contained within `to` |\\n| `disjoint` | — | `from` and `to` do not intersect |\\n| `overlap` | — | `from` and `to` intersect but neither contains the other |\\n\\n---\\n\\n## 5. Configuration\\n\\n`config:` lines tune diagram behavior. Each goes on its own line.\\n\\n| Config key | Values | Default | Effect |\\n|---|---|---|---|\\n| `diagram` | `venn`, `euler`, `auto` | `auto` | Force Venn (all circles fixed) or Euler (subset nesting). `auto` infers from the presence of Euler relations. |\\n| `proportional` | `true`, `false` | `false` | Scale circle area proportional to region count values |\\n| `showCounts` | `true`, `false` | `auto` | Always / never show count labels. `auto` shows them when counts are provided. |\\n| `showPercent` | `true`, `false` | `false` | Show each region value as a percentage of the grand total |\\n| `palette` | `default`, `brand`, `monochrome` | `default` | Color palette for sets (overridden by per-set `color:`) |\\n| `blendMode` | `multiply`, `screen`, `none` | `multiply` | How overlapping fill colors blend |\\n\\nConfig can also be written inline on the header line: `venn \\\"Title\\\" [proportional: true, showPercent: true]`.\\n\\n```\\nvenn \\\"Segment overlap\\\"\\nconfig: proportional = true\\nconfig: showPercent = true\\nconfig: blendMode = screen\\n```\\n\\n**Layout selection:**\\n\\n- `layout venn` — alternative form of `config: diagram = venn` (parses identically).\\n- `layout euler` — alternative form of `config: diagram = euler`.\\n- `layout auto` — alternative form of `config: diagram = auto`.\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Title:** `venn \\\"My diagram\\\"` — first line only.\\n- **Set label:** `set A \\\"Email subscribers\\\"` — quoted string on the `set` line.\\n- **Region value:** integer, percentage, quoted string, or element list assigned after the `:`.\\n- **Comments:** `#` at the start of a line (after leading whitespace).\\n\\n---\\n\\n## 7. Reserved words & escaping\\n\\n**Reserved at line start:** `venn` (header), `set`, `config:`, `layout`, `region`.\\n\\n**Reserved operators in region keys:** `&` (intersection), `only` (exclusive region).\\n\\n**Euler relation keywords:** `subset`, `in`, `disjoint`, `overlap` — cannot be used as set ids.\\n\\n**ID rules:** must match `[A-Za-z][A-Za-z0-9_-]*`. Labels with spaces go in the quoted `\\\"Label\\\"` field.\\n\\n---\\n\\n## 8. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `A & B : 320` before `set A …` and `set B …` | `VennParseError: unknown set id \\\"A\\\" in region key` | Declare sets with `set` before referencing them in region lines |\\n| `dogs subset mammals` before `set dogs …` | `VennParseError: unknown set \\\"dogs\\\" in relation` | Declare sets first, then write Euler relations |\\n| `set A Email subscribers` (unquoted label with space) | Parser error — label is expected to be a quoted string | Quote it: `set A \\\"Email subscribers\\\"` |\\n| `A & B = 320` (equals instead of colon) | Line doesn't match region pattern; parse error | Use colon: `A & B : 320` |\\n| `Frontend = { React TypeScript }` (no commas) | `React TypeScript` treated as one element | Comma-separate: `Frontend = { React, TypeScript }` |\\n| `config: mode = venn` | `mode` is not a recognized key (key is `diagram`) | Use `config: diagram = venn` |\\n| Mixing enumeration sets with explicit `A & B :` regions | Enumeration auto-derive only runs when `regions.length === 0` | Use one style per diagram or add all regions explicitly |\\n\\n---\\n\\n## 9. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | config | layout-stmt | set-decl | enum-decl | euler-rel | region)*\\n\\nheader = \\\"venn\\\" ( \\\":\\\"? WS quoted-string )? ( WS \\\"[\\\" config-props \\\"]\\\" )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nconfig = \\\"config\\\" WS \\\":\\\" WS config-key WS \\\"=\\\" WS config-value NEWLINE\\nconfig-key = \\\"diagram\\\" | \\\"proportional\\\" | \\\"palette\\\" | \\\"blendMode\\\" | \\\"showCounts\\\" | \\\"showPercent\\\"\\n\\nlayout-stmt = \\\"layout\\\" WS ( \\\"venn\\\" | \\\"euler\\\" | \\\"auto\\\" ) NEWLINE\\n\\nset-decl = \\\"set\\\" WS id WS quoted-string ( WS \\\"[\\\" set-props \\\"]\\\" )? NEWLINE\\nset-props = \\\"color:\\\" quoted-hex | \\\"fill:\\\" quoted-hex\\n\\nenum-decl = id WS \\\"=\\\" WS \\\"{\\\" element-list \\\"}\\\" NEWLINE\\nelement-list = element ( \\\",\\\" element )*\\nelement = quoted-string | bare-word\\n\\neuler-rel = id WS euler-op WS id NEWLINE\\neuler-op = \\\"subset\\\" | \\\"in\\\" | \\\"disjoint\\\" | \\\"overlap\\\"\\n\\nregion = \\\"region\\\"? WS region-key WS \\\":\\\" WS region-value NEWLINE\\nregion-key = id WS \\\"only\\\"\\n | id ( WS \\\"&\\\" WS id )+\\nregion-value = integer | percent | quoted-string | \\\"[\\\" element-list \\\"]\\\"\\n\\npercent = number \\\"%\\\"\\nid = [A-Za-z] [A-Za-z0-9_-]*\\ncomment = \\\"#\\\" any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/venn/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"decisiontree\": {\n \"title\": \"Decision tree diagram\",\n \"content\": \"## 1. Your first decision tree\\n\\nThe smallest useful decision tree: a root question with two branches.\\n\\n```\\ndecisiontree \\\"Laptop troubleshoot\\\"\\n\\nquestion \\\"Does it power on?\\\"\\n yes: answer \\\"Check display — connect external monitor\\\"\\n no: question \\\"Is the charger light on?\\\"\\n yes: answer \\\"Hold power button 10 s — try again\\\"\\n no: answer \\\"Check outlet and charging cable\\\"\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with `decisiontree`, optionally with `:mode` and a quoted title.\\n2. Each question node uses `question \\\"text\\\"` (or shorthand `q \\\"text\\\"`).\\n3. Each answer/leaf uses `answer \\\"text\\\"` (or `a \\\"text\\\"` or `leaf \\\"text\\\"`).\\n4. Branch labels — `yes:`, `no:`, or a custom `label \\\"X\\\":` — prefix the child node on the same line.\\n\\nIndentation controls nesting: each level adds 2 spaces. The parser computes parent-child relationships from indent depth.\\n\\n> Comments must start with `#` or `//` on their own line.\\n\\n---\\n\\n## 2. Modes\\n\\nThe mode is set in the header line:\\n\\n| Header | Mode | Used for |\\n|---|---|---|\\n| `decisiontree` | taxonomy | Yes/no question flows, troubleshooting guides, clinical decision support |\\n| `decisiontree:decision` (or `decisiontree:da`) | decision analysis | Investment decisions, risk analysis, expected value calculation |\\n| `decisiontree:ml` | machine learning | Visualizing trained CART classifiers (scikit-learn, XGBoost, etc.) |\\n\\nDefault direction is `top-down` for taxonomy and ML, `left-right` for decision analysis.\\n\\n---\\n\\n## 3. Taxonomy mode\\n\\nBest for: troubleshooting guides, FAQs, clinical protocols, product recommendation flows.\\n\\n### Node keywords\\n\\n| Keyword | Aliases | Meaning |\\n|---|---|---|\\n| `question \\\"…\\\"` | `q \\\"…\\\"` | Internal node — a question with children |\\n| `answer \\\"…\\\"` | `a \\\"…\\\"`, `leaf \\\"…\\\"` | Leaf node — a terminal outcome |\\n\\n### Branch labels\\n\\n| Syntax | Meaning |\\n|---|---|\\n| `yes: question \\\"…\\\"` | Branch labeled \\\"yes\\\" |\\n| `no: answer \\\"…\\\"` | Branch labeled \\\"no\\\" |\\n| `label \\\"Custom text\\\": answer \\\"…\\\"` | Branch with any custom label |\\n\\nCustom labels let you go beyond yes/no for multi-way decisions from one question.\\n\\n```\\ndecisiontree \\\"Triage — chest pain onset\\\"\\n\\nq \\\"Onset sudden?\\\"\\n yes: q \\\"ECG changes present?\\\"\\n yes: a \\\"ACS protocol — cardiology consult\\\"\\n no: q \\\"D-dimer elevated?\\\"\\n yes: a \\\"PE workup — CT pulmonary angiography\\\"\\n no: a \\\"Aortic dissection — CT angiography\\\"\\n no: q \\\"Pain reproducible on palpation?\\\"\\n yes: a \\\"Musculoskeletal — NSAIDs, follow-up PCP\\\"\\n no: a \\\"GI / anxiety — further history\\\"\\n```\\n\\n```\\ndecisiontree \\\"Pain level triage\\\"\\n\\nquestion \\\"Reported pain level?\\\"\\n label \\\"Severe (8-10)\\\": answer \\\"Emergency — send to ER immediately\\\"\\n label \\\"Moderate (4-7)\\\": answer \\\"Urgent care — within 2 hours\\\"\\n label \\\"Mild (1-3)\\\": answer \\\"Schedule next available — OTC care\\\"\\n label \\\"None\\\": answer \\\"Monitor — patient may be post-medication\\\"\\n```\\n\\n---\\n\\n## 4. Decision analysis mode\\n\\nBest for: investment decisions, build-vs-buy analysis, risk-weighted strategy evaluation.\\n\\n### Node keywords\\n\\n| Keyword | Aliases | Meaning |\\n|---|---|---|\\n| `decision \\\"…\\\"` | — | Decision node — the actor chooses a branch |\\n| `chance \\\"…\\\"` | — | Chance node — an uncertain outcome |\\n| `end \\\"…\\\"` | `outcome \\\"…\\\"` | Terminal node — final payoff |\\n\\n### Branch keywords\\n\\n| Keyword | Meaning |\\n|---|---|\\n| `choice \\\"label\\\"` | Names the incoming branch from a decision node |\\n| `prob N` | Sets the probability (0–1) on the incoming branch from a chance node |\\n\\n### Payoff attribute\\n\\n`payoff=N` on any node sets the payoff value. On `end` / `outcome` nodes it defines the terminal value. The parser runs expected-value rollback automatically: each `chance` node's EV is the probability-weighted sum of its children's EVs; each `decision` node's EV is the maximum child EV, and the optimal branch is flagged.\\n\\n**Constraint:** probabilities on all direct children of a `chance` node must sum to 1.0 (±0.01). The parser throws a `DTreeParseError` if they do not.\\n\\n```\\ndecisiontree:decision \\\"Cloud vendor selection\\\"\\n\\ndecision \\\"Which vendor?\\\"\\n choice \\\"Build in-house\\\"\\n chance \\\"Project outcome\\\"\\n prob 0.6 end \\\"On-time delivery\\\" payoff=900000\\n prob 0.4 end \\\"Over budget / delayed\\\" payoff=150000\\n choice \\\"Managed SaaS vendor\\\"\\n end \\\"Predictable cost\\\" payoff=500000\\n choice \\\"Hybrid approach\\\"\\n chance \\\"Integration complexity\\\"\\n prob 0.5 end \\\"Smooth integration\\\" payoff=700000\\n prob 0.5 end \\\"Integration rework\\\" payoff=300000\\n```\\n\\n---\\n\\n## 5. Machine learning mode\\n\\nBest for: explaining trained CART classifiers, model transparency reports, feature importance analysis.\\n\\n### Node keywords\\n\\n| Keyword | Meaning |\\n|---|---|\\n| `split \\\"…\\\"` | Internal split node — contains a feature test |\\n| `leaf \\\"…\\\"` | Leaf node — class or regression value |\\n\\n### Branch prefixes\\n\\n`true` and `false` prefix child nodes to mark which branch each child represents.\\n\\n### Properties (key=value, no colon, no quotes around values)\\n\\n| Property | Applies to | Meaning |\\n|---|---|---|\\n| `feature=name` | split | Feature name used at the split |\\n| `op=\\\"<=\\\"` | split | Comparison operator (quote if contains special chars) |\\n| `threshold=5.9` | split | Split threshold value |\\n| `samples=150` | split, leaf | Sample count at this node |\\n| `gini=0.5` | split, leaf | Gini impurity |\\n| `entropy=0.5` | split, leaf | Entropy impurity |\\n| `mse=0.3` | split, leaf | Mean squared error (regression) |\\n| `gain=0.2` | split, leaf | Information gain |\\n| `class=name` | leaf | Predicted class name |\\n| `value=50` | leaf | Sample count; use `value=[50,30,20]` for class distribution |\\n\\n```\\ndecisiontree:ml \\\"Iris classification (CART)\\\"\\ndirection: top-down\\nimpurity: gini\\n\\nsplit \\\"Petal length ≤ 2.45\\\" feature=petal_length op=\\\"<=\\\" threshold=2.45 samples=150 gini=0.667\\n true leaf \\\"Setosa\\\" class=Iris-setosa value=50 gini=0.0\\n false split \\\"Petal width ≤ 1.75\\\" feature=petal_width op=\\\"<=\\\" threshold=1.75 samples=100 gini=0.5\\n true leaf \\\"Versicolor\\\" class=Iris-versicolor value=50 gini=0.0\\n false leaf \\\"Virginica\\\" class=Iris-virginica value=50 gini=0.0\\n```\\n\\n---\\n\\n## 6. Config options\\n\\nConfig lines appear between the header and the first node. Each is `key: value` (colon, no `config` keyword).\\n\\n### Shared config (all modes)\\n\\n| Key | Values | Default | Effect |\\n|---|---|---|---|\\n| `direction:` | `top-down`, `left-right` | `top-down` (taxonomy/ML), `left-right` (decision) | Layout direction |\\n| `edgeStyle:` (or `edge-style:`) | `diagonal`, `orthogonal`, `bracket` | mode-dependent | Edge drawing style |\\n\\n### Taxonomy config\\n\\n| Key | Values | Default | Effect |\\n|---|---|---|---|\\n| `branchLabels:` (or `branch-labels:`) | `boolean`, `relation` | `boolean` | Branch label style |\\n\\n### Decision analysis config\\n\\n| Key | Values | Default | Effect |\\n|---|---|---|---|\\n| `branchLength:` (or `branch-length:`) | `probability` | off | Scale branch length proportional to probability |\\n\\n### ML config\\n\\n| Key | Values | Default | Effect |\\n|---|---|---|---|\\n| `impurity:` | `gini`, `entropy`, `mse`, `gain` | `gini` | Impurity metric shown on nodes |\\n| `classes:` | comma-separated list | — | Class label names for display |\\n\\n```\\ndecisiontree:ml \\\"Loan classifier\\\"\\ndirection: top-down\\nimpurity: gini\\nclasses: Approved, Denied, Review\\n```\\n\\n---\\n\\n## 7. Labels & comments\\n\\n- **Diagram title:** `decisiontree \\\"Title\\\"` — the quoted string after the header keyword.\\n- **Node label:** the quoted string immediately after the node keyword — `question \\\"Is the fee waived?\\\"`.\\n- **Branch label:** `yes:`, `no:`, or `label \\\"Custom\\\":` before the child node — on the same line as the child.\\n- **Payoff:** `payoff=250000` at the end of a decision/end node line.\\n- **ML properties:** `key=value` tokens after the node's label string (no `[…]` brackets, no colons).\\n- **Comments:** `#` or `//` at the start of a line (after optional leading whitespace). Only full-line comments are supported — inline trailing comments are not.\\n\\n---\\n\\n## 8. Reserved words & escaping\\n\\n**Reserved node keywords:** `decision`, `chance`, `end`, `outcome`, `choice`, `prob`, `split`, `leaf`, `question`, `q`, `answer`, `a`.\\n\\n**Reserved branch prefixes:** `yes:`, `no:`, `true`, `false`, `label`.\\n\\n**Reserved header forms:** `decisiontree`, `decisiontree:decision`, `decisiontree:da`, `decisiontree:ml`.\\n\\n**Strings with spaces** must be double-quoted: `question \\\"Annual revenue > $1M?\\\"`. Node labels, branch labels from `label \\\"…\\\":` syntax, and the diagram title all require double quotes.\\n\\n| Reserved token | Context | Notes |\\n|---|---|---|\\n| `yes:` / `no:` | Line start in taxonomy | Cannot be used as a label — use `label \\\"yes\\\":` if you need literal text \\\"yes\\\" |\\n| `true` / `false` | Line start in ML mode | Cannot be a node label |\\n| `choice` | Line start in decision mode | Acts as branch wrapper, not a node |\\n| `prob` | Line start in decision mode | Must be followed by a number |\\n\\n---\\n\\n## 9. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `yes: \\\"Approve\\\"` (no node keyword) | `DTreeParseError: Missing taxonomy node kind` | `yes: answer \\\"Approve\\\"` |\\n| Probabilities on `chance` children summing to 0.8 | `DTreeParseError: probabilities do not sum to 1.0` | Adjust so all `prob` values sum to exactly 1.0 (±0.01) |\\n| `question \\\"text\\\"` with a child at the same indent level | Child not parsed as a child — becomes a sibling | Indent children by 2 more spaces than the parent |\\n| `config direction = top-down` (using `config` keyword) | `config` is a fishbone keyword — not recognized here | Use `direction: top-down` (no `config` prefix) |\\n| `feature = petal_length` (spaces around `=`) | Parsed as separate tokens; property not recognized | No spaces: `feature=petal_length` |\\n| `[payoff: 500000]` bracket syntax | Not recognized — parser ignores brackets for payoff | Use `payoff=500000` (no brackets, no spaces around `=`) |\\n| `decisiontree:taxonomy` | `DTreeParseError: Invalid header` | Use `decisiontree` (no mode suffix for taxonomy) |\\n\\n---\\n\\n## 10. Grammar (EBNF)\\n\\n```text\\ndocument = header ( config-line )* node\\n\\nheader = \\\"decisiontree\\\" ( \\\":\\\" mode )? ( WS quoted-string )? NEWLINE\\nmode = \\\"decision\\\" | \\\"da\\\" | \\\"ml\\\"\\n // omitted → taxonomy\\n\\nconfig-line = config-key \\\":\\\" WS config-value NEWLINE\\nconfig-key = \\\"direction\\\" | \\\"edgeStyle\\\" | \\\"edge-style\\\"\\n | \\\"branchLabels\\\" | \\\"branch-labels\\\"\\n | \\\"branchLength\\\" | \\\"branch-length\\\"\\n | \\\"impurity\\\" | \\\"classes\\\"\\n\\n// ── Taxonomy mode ──────────────────────────────\\nnode = ( branch-prefix WS )? tax-node ( WS \\\"[\\\" tax-attrs \\\"]\\\" )? NEWLINE\\n INDENT child-node*\\ntax-node = ( \\\"question\\\" | \\\"q\\\" ) WS quoted-string\\n | ( \\\"answer\\\" | \\\"a\\\" | \\\"leaf\\\" ) WS quoted-string\\nbranch-prefix = \\\"yes:\\\" | \\\"no:\\\" | \\\"label\\\" WS quoted-string \\\":\\\"\\n\\n// ── Decision-analysis mode ─────────────────────\\nda-node = \\\"decision\\\" WS quoted-string NEWLINE INDENT da-child+\\n | \\\"chance\\\" WS quoted-string NEWLINE INDENT da-prob-child+\\n | ( \\\"end\\\" | \\\"outcome\\\" ) WS quoted-string ( WS \\\"payoff=\\\" number )? NEWLINE\\nda-child = \\\"choice\\\" WS quoted-string NEWLINE INDENT da-node\\nda-prob-child = \\\"prob\\\" WS number WS da-node // prob, value, and child all on one line\\n\\n// ── ML mode ───────────────────────────────────\\nml-node = ( \\\"true\\\" | \\\"false\\\" )? ml-kind WS quoted-string ml-prop* NEWLINE\\n INDENT ml-child*\\n // \\\"true\\\"/\\\"false\\\" and ml-kind must be on the same line\\nml-kind = \\\"split\\\" | \\\"leaf\\\"\\nml-prop = WS key \\\"=\\\" value // no spaces around \\\"=\\\"\\n\\ncomment = ( \\\"#\\\" | \\\"//\\\" ) any NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n```\\n\\nAuthoritative source: `src/diagrams/decisiontree/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"flowchart\": {\n \"title\": \"Flowchart\",\n \"content\": \"## 1. Your first flowchart\\n\\nThe smallest useful flowchart: a decision with two outcomes.\\n\\n```\\nflowchart TD\\n A([Start]) --> B{File exists?}\\n B -->|Yes| C[Read file]\\n B -->|No| D[Return error]\\n C --> E([Done])\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with `flowchart` followed by a direction: `TD`, `LR`, `BT`, or `RL`.\\n2. Each node is `ID[Label]` — the shape brackets determine the node type (see §2).\\n3. Connect nodes with `-->`. Add a label between pipe characters: `-->|Yes|`.\\n4. Nodes are created automatically when first referenced in an edge — but explicit declarations let you set shapes and labels independently.\\n\\n> Comments start with `%%` on their own line.\\n\\n---\\n\\n## 2. Node shapes\\n\\nEach node shape is written as `ID<brackets>Label<brackets>`. The ID must start with a letter and may contain letters, digits, `_`, and `-`.\\n\\n| Syntax | Shape | Typical use |\\n|---|---|---|\\n| `A[Label]` | Rectangle | Process step, operation |\\n| `A(Label)` | Rounded rectangle | Subprocess, soft step |\\n| `A([Label])` | Stadium (pill) | Start / end terminal |\\n| `A{Label}` | Diamond | Decision / condition |\\n| `A{{Label}}` | Hexagon | Preparation, configuration |\\n| `A[[Label]]` | Subroutine | Predefined process |\\n| `A[(Label)]` | Cylinder | Database, storage |\\n| `A((Label))` | Circle | Connector, junction |\\n| `A(((Label)))` | Double circle | End state |\\n| `A[/Label/]` | Parallelogram | Input / output |\\n| `A[\\\\Label\\\\]` | Parallelogram (alt) | Manual operation |\\n| `A[/Label\\\\]` | Trapezoid | Manual input |\\n| `A[\\\\Label/]` | Trapezoid (alt) | Off-page connector |\\n| `A>Label]` | Asymmetric | Tag, annotation |\\n\\n```\\nflowchart TD\\n t([Terminal / stadium])\\n r[Rectangle process]\\n d{Diamond decision}\\n p[/Parallelogram input/]\\n db[(Cylinder database)]\\n sub[[Subroutine]]\\n t --> r --> d\\n d -->|branch A| p\\n d -->|branch B| db\\n p --> sub\\n db --> sub\\n```\\n\\n---\\n\\n## 3. Edges\\n\\nAn edge connects two nodes. The connector symbol determines the visual style and whether a label or arrowhead is present.\\n\\n### 3.1 Edge types\\n\\n```\\nflowchart TD\\n A --> B\\n C --- D\\n E -.-> F\\n G ==> H\\n I <--> J\\n K --x L\\n M --o N\\n```\\n\\n| Syntax | Style | Arrow | Typical use |\\n|---|---|---|---|\\n| `A --> B` | Solid | Arrow | Normal flow |\\n| `A --- B` | Solid | None | Association, undirected link |\\n| `A -.-> B` | Dotted | Arrow | Optional / async path |\\n| `A ==> B` | Thick | Arrow | Critical / primary path |\\n| `A <--> B` | Solid | Both ends | Bidirectional flow |\\n| `A --x B` | Solid | Cross | Blocked / rejected path |\\n| `A --o B` | Solid | Circle | Aggregation / composition |\\n\\n### 3.2 Edge labels\\n\\nTwo syntaxes attach a label to an edge:\\n\\n**Pipe label** — placed between `|` characters directly after the arrow:\\n```\\nA -->|Yes| B\\nA -.->|optional| B\\nA ==>|critical| B\\n```\\n\\n**Inline label** — text placed between the dashes, before the arrow character:\\n```\\nA -- success --> B\\nA -- error --x C\\n```\\n\\nBoth produce identical results. Pipe label is more common when sharing a diagram with Mermaid tools.\\n\\n```\\nflowchart TD\\n req[Request received]\\n req -->|valid| proc[Process]\\n req -->|invalid| err[Return 400]\\n proc -- success --> ok([Done])\\n proc -.->|timeout| retry[Retry queue]\\n retry ==>|max retries| dead[(Dead letter)]\\n```\\n\\n### 3.3 Chains\\n\\nConnect three or more nodes in a single line:\\n\\n```\\nA --> B --> C --> D\\n```\\n\\nThis is equivalent to three separate edge statements.\\n\\n### 3.4 Fan-out with `&`\\n\\nUse `&` to include multiple nodes on either side of an arrow. The parser generates the full cross-product of edges:\\n\\n```\\nA & B --> C %% A→C and B→C\\nA --> B & C %% A→B and A→C\\nA & B --> C & D %% four edges: A→C, A→D, B→C, B→D\\n```\\n\\n```\\nflowchart LR\\n deploy[Deploy service]\\n smoke[Smoke test]\\n health[Health check]\\n notify_slack[Slack alert]\\n notify_email[Email alert]\\n deploy --> smoke & health\\n smoke & health -->|fail| notify_slack & notify_email\\n```\\n\\n---\\n\\n## 4. Subgraphs\\n\\nA `subgraph` groups related nodes into a labeled cluster with a visible border.\\n\\n```\\nsubgraph \\\"Title\\\"\\n A --> B\\nend\\n```\\n\\nThree subgraph header forms are accepted:\\n\\n| Form | ID | Label |\\n|---|---|---|\\n| `subgraph \\\"My Group\\\"` | auto-generated | `My Group` |\\n| `subgraph sg1 \\\"My Group\\\"` | `sg1` | `My Group` |\\n| `subgraph sg1 [My Group]` | `sg1` | `My Group` |\\n\\nSubgraphs can have their own `direction` override:\\n\\n```\\nsubgraph sg1 \\\"Frontend\\\"\\n direction LR\\n ui[React App] --> api[API Client]\\nend\\n```\\n\\n```\\nflowchart TB\\n subgraph ingestion [Ingestion]\\n raw[/Raw events/] --> parse[Parse & validate]\\n parse --> enrich[Enrich]\\n end\\n subgraph storage [Storage]\\n dw[(Data warehouse)]\\n cache[(Redis cache)]\\n end\\n enrich --> dw\\n enrich --> cache\\n dw --> report[Generate report]\\n```\\n\\n---\\n\\n## 5. Styling\\n\\n### 5.1 Semantic classes\\n\\nAssign CSS class names to nodes for theme-level visual grouping. Classes are defined with `classDef` and applied with `class`.\\n\\n```\\nclassDef danger fill:#f9c,stroke:#c00\\nclassDef safe fill:#cfc,stroke:#090\\nclass errorNode danger\\nclass successNode safe\\n```\\n\\n### 5.2 Per-node style overrides\\n\\n```\\nstyle nodeId fill:#f9f,stroke:#333,stroke-width:4px\\n```\\n\\nAccepts standard CSS property names. Multiple properties are comma-separated.\\n\\n### 5.3 Per-edge style overrides\\n\\n`linkStyle` targets edges by their **declaration index** (0-based, in the order they appear in the source). Multiple comma-separated indices apply the same props to several edges:\\n\\n```\\nflowchart TD\\n A --> B\\n B ==> C\\n B -.-> D\\n C --> E\\n D --> E\\n linkStyle 1 stroke:#d32f2f,stroke-width:4px\\n linkStyle 2,4 stroke:#f57c00,stroke-dasharray:5 5\\n```\\n\\nUse this to highlight a critical path or distinguish an alternate flow.\\n\\n### 5.4 Inline label formatting\\n\\nNode labels accept three inline formatting tags:\\n\\n| Tag | Effect |\\n|---|---|\\n| `<br/>` or `<br>` | Line break |\\n| `<b>…</b>` | Bold |\\n| `<i>…</i>` | Italic |\\n\\n```\\nflowchart TD\\n M1[\\\"0 \\\\| 0<br/><b>START</b>\\\"]\\n M2[\\\"4 \\\\| 4<br/><b>Phase 1</b><br/><i>est. 4h</i>\\\"]\\n M1 --> M2\\n```\\n\\nTags can be nested and mixed mid-line (`Hello <b>world</b>!`). Edge labels are single-line and do not currently support these tags.\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Direction:** `flowchart TD` — first token after `flowchart` or `graph`. `TD` and `TB` are equivalent.\\n- **Title:** `flowchart LR \\\"My diagram\\\"` — optional quoted string after the direction.\\n- **Edge labels:** pipe syntax `-->|label|` or inline `-- label -->`.\\n- **Comments:** `%%` at the start of a line (after leading whitespace).\\n\\n```\\nflowchart LR\\n%% This is a comment — ignored by the parser\\nA[Step 1] --> B[Step 2] %% inline %% is NOT supported — only line-start %%\\n```\\n\\n---\\n\\n## 7. Reserved words & escaping\\n\\n**Reserved at line start:** `flowchart`, `graph` (header), `subgraph`, `end`, `direction`, `class`, `classDef`, `style`, `linkStyle`.\\n\\n**Reserved ID characters:** IDs match `[A-Za-z0-9_-]` starting with a letter. Do not use spaces or operator characters in node IDs.\\n\\n**Operator tokens to avoid inside IDs:** `-->`, `---`, `-.->`, `==>`, `<-->`, `--x`, `--o`, `|`, `&`.\\n\\n**Labels with special characters:** The label is everything inside the shape brackets. Special characters are supported inside labels as-is — brackets/braces that would be ambiguous are closed by the matching closing token.\\n\\n---\\n\\n## 8. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `flowchart` with no direction | Direction defaults to `TB` | Add a direction: `flowchart TD` |\\n| `A --> B` before declaring shapes | Works — nodes created as rectangles with the ID as label | Declare explicitly when you need a non-rect shape: `A([Start])` |\\n| `A[Label with [brackets]]` | Inner `]` closes the shape early | Avoid nested brackets in labels |\\n| `subgraph My Group` (unquoted, with space) | Parser takes `My` as subgraph id, `Group` as unknown token | Quote: `subgraph \\\"My Group\\\"` |\\n| `%% comment` mid-line after code | Inline comments are not supported; `%%` must be at line start | Move comments to their own line |\\n| `A --> B --> C` mixed with `A --> B` | Chains are additive — duplicate edges may appear | Use chains OR separate lines, not both for the same pair |\\n| `direction LR` outside a subgraph | Silently ignored — `direction` override only applies inside `subgraph … end` | Set direction on the `flowchart` header line |\\n\\n---\\n\\n## 9. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | subgraph-block | direction-stmt\\n | class-stmt | classdef-stmt | style-stmt\\n | linkstyle-stmt | chain-stmt)*\\n\\nheader = (\\\"flowchart\\\" | \\\"graph\\\") ( WS direction )? ( WS title )? NEWLINE\\ndirection = \\\"TD\\\" | \\\"TB\\\" | \\\"BT\\\" | \\\"LR\\\" | \\\"RL\\\"\\ntitle = '\\\"' any-char-but-quote* '\\\"' | bare-word\\n\\nsubgraph-block = \\\"subgraph\\\" ( WS subgraph-header )? NEWLINE\\n ( WS? \\\"direction\\\" WS direction NEWLINE )?\\n statement*\\n \\\"end\\\" NEWLINE\\nsubgraph-header = id WS \\\"[\\\" label \\\"]\\\"\\n | id WS quoted-string\\n | quoted-string\\n | id\\n\\nchain-stmt = node-group ( WS edge-op WS pipe-label? WS node-group )* NEWLINE\\nnode-group = node-ref ( WS \\\"&\\\" WS node-ref )*\\nnode-ref = id shape-suffix?\\nshape-suffix = \\\"[\\\" label \\\"]\\\" %% rect\\n | \\\"(\\\" label \\\")\\\" %% round\\n | \\\"([\\\" label \\\"])\\\" %% stadium\\n | \\\"{\\\" label \\\"}\\\" %% diamond\\n | \\\"{{\\\" label \\\"}}\\\" %% hexagon\\n | \\\"[[\\\" label \\\"]]\\\" %% subroutine\\n | \\\"[(\\\" label \\\")]\\\" %% cylinder\\n | \\\"((\\\" label \\\"))\\\" %% circle\\n | \\\"(((\\\" label \\\")))\\\" %% double-circle\\n | \\\"[/\\\" label \\\"/]\\\" %% parallelogram\\n | \\\"[\\\\\\\" label \\\"\\\\]\\\" %% parallelogram-alt\\n | \\\"[/\\\" label \\\"\\\\]\\\" %% trapezoid\\n | \\\"[\\\\\\\" label \\\"/]\\\" %% trapezoid-alt\\n | \\\">\\\" label \\\"]\\\" %% asymmetric\\n\\nedge-op = \\\"-->\\\" | \\\"---\\\" | \\\"-.\\\"-\\\".->\\\" | \\\"==>\\\" | \\\"<-->\\\" | \\\"--x\\\" | \\\"--o\\\"\\n | inline-label variants of the above\\npipe-label = \\\"|\\\" text \\\"|\\\"\\n\\nclass-stmt = \\\"class\\\" WS id-list WS class-name NEWLINE\\nclassdef-stmt = \\\"classDef\\\" WS class-name WS css-props NEWLINE\\nstyle-stmt = \\\"style\\\" WS id WS css-props NEWLINE\\nlinkstyle-stmt = \\\"linkStyle\\\" WS index-list WS css-props NEWLINE\\nindex-list = NUMBER ( \\\",\\\" NUMBER )* | \\\"default\\\"\\n\\ncomment = \\\"%%\\\" any NEWLINE\\nid = [A-Za-z] [A-Za-z0-9_-]*\\n```\\n\\nAuthoritative source: `src/diagrams/flowchart/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"matrix\": {\n \"title\": \"Matrix / Quadrant diagram\",\n \"content\": \"## 1. Your first matrix\\n\\nThe smallest useful matrix: a custom 2×2 with two labeled axes and three points.\\n\\n```\\nmatrix \\\"Feature Prioritization\\\"\\nx-axis: Low Effort → High Effort\\ny-axis: Low Value → High Value\\n\\n\\\"Add search\\\" at (0.3, 0.8)\\n\\\"Rebuild pipeline\\\" at (0.85, 0.7)\\n\\\"Update footer\\\" at (0.2, 0.2)\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with the keyword `matrix`, optionally followed by a template name or a quoted title.\\n2. Set the axes with `x-axis:` and `y-axis:` — or use a built-in template and skip this step entirely.\\n3. Each point is `\\\"Label\\\" at (x, y)` where `x` and `y` are decimal fractions from 0.0 (low/left/bottom) to 1.0 (high/right/top).\\n4. Add optional properties — `size:`, `category:`, `color:`, `shape:`, `highlight:` — after the coordinates.\\n\\n> Comments must start with `#` anywhere on a line (outside quoted strings).\\n\\n---\\n\\n## 2. Built-in templates\\n\\nA template pre-configures axes, quadrant labels, and grid size. Just use the template name as the second token on the header line.\\n\\n| Template | Grid | Use case |\\n|---|---|---|\\n| `eisenhower` | 2×2 | Urgency / Importance task prioritization |\\n| `impact-effort` | 2×2 | Feature prioritization by impact vs. effort |\\n| `rice` | 2×2 | RICE scoring — Reach × Impact vs. Effort |\\n| `bcg` | 2×2 | Portfolio — Market Share vs. Growth rate |\\n| `ansoff` | 2×2 | Product/market growth strategy |\\n| `johari` | 2×2 | Self-awareness — known-to-self vs. known-to-others |\\n| `9-box` | 3×3 | HR talent review — Performance vs. Potential |\\n| `risk-matrix` | 5×5 | Risk assessment — Likelihood vs. Severity (heatmap) |\\n\\n```\\nmatrix eisenhower \\\"This Week\\\"\\n\\\"Ship hotfix\\\" at (0.1, 0.9) size: 5 highlight: true\\n\\\"Team 1:1s\\\" at (0.1, 0.7) size: 3\\n\\\"Write Q3 OKRs\\\" at (0.8, 0.85) size: 4\\n\\\"Inbox zero\\\" at (0.1, 0.3) size: 2\\n\\\"Refactor auth\\\" at (0.75, 0.4) size: 3\\n```\\n\\nAxes and quadrant labels from a template can be overridden with explicit `x-axis:` / `y-axis:` / `quadrant` directives.\\n\\n---\\n\\n## 3. Axes\\n\\nAxis lines declare the semantic poles of each dimension.\\n\\n```\\nx-axis: Low Effort → High Effort\\ny-axis: Low Value → High Value\\n```\\n\\nThe arrow separates the low label (left / bottom) from the high label (right / top). All of these separators are equivalent:\\n\\n| Separator | Example |\\n|---|---|\\n| `→` (Unicode) | `x-axis: Rare → Certain` |\\n| `->` (ASCII) | `x-axis: Rare -> Certain` |\\n| `↑` | `y-axis: Cheap ↑ Expensive` |\\n| `←` / `<-` / `<` | Reversed axis — high label is on the left |\\n\\nA **reversed axis** is for conventions where the \\\"high\\\" value sits at the left or bottom:\\n\\n```\\nx-axis: High Market Share ← Low Market Share\\n```\\n\\n```\\nmatrix \\\"Product Portfolio\\\"\\nx-axis: High Market Share ← Low Market Share\\ny-axis: Low Growth → High Growth\\n\\nquadrant Q1 \\\"Question Marks\\\"\\nquadrant Q2 \\\"Stars\\\"\\nquadrant Q3 \\\"Cash Cows\\\"\\nquadrant Q4 \\\"Dogs\\\"\\n\\n\\\"Analytics Suite\\\" at (0.25, 0.35) size: 5\\n\\\"ChatBot Pro\\\" at (0.2, 0.8) size: 4 highlight: true\\n\\\"Legacy CRM\\\" at (0.75, 0.25) size: 6\\n\\\"Mobile App\\\" at (0.65, 0.75) size: 3\\n```\\n\\n---\\n\\n## 4. Points\\n\\nEach point is a bubble positioned by a normalized (x, y) coordinate pair.\\n\\n```\\n\\\"Label\\\" at (x, y)\\n\\\"Label\\\" at (x, y) size: 4 category: design color: #7B1FA2 highlight: true note: \\\"clarify spec\\\"\\n```\\n\\n| Property | Values | Meaning |\\n|---|---|---|\\n| `size:` | positive number | Bubble area weight (default: 3) |\\n| `category:` | bareword | Color group; drives the legend |\\n| `color:` | hex string | Override bubble color for this point |\\n| `shape:` | `circle` \\\\| `square` \\\\| `triangle` \\\\| `diamond` | Bubble shape (default: `circle`) |\\n| `highlight:` | `true` | Draws an emphasis ring around the bubble |\\n| `note:` | quoted string | Tooltip annotation |\\n| `label:` | quoted string | Replaces the display label (different from the ID) |\\n\\nCoordinates outside `[0, 1]` are clamped to the chart boundary and flagged with a badge — the original value is stored for tooltip display.\\n\\n```\\nmatrix \\\"Risk Register\\\"\\nx-axis: Low Impact → High Impact\\ny-axis: Rare → Certain\\n\\n\\\"Vendor delay\\\" at (0.45, 0.7) size: 4 category: schedule highlight: true\\n\\\"Security breach\\\" at (0.9, 0.3) size: 5 category: security shape: diamond\\n\\\"Budget overrun\\\" at (0.5, 0.65) size: 3 category: finance\\n\\\"Key hire falls through\\\" at (0.6, 0.55) size: 3 category: people\\n\\\"Scope creep\\\" at (0.4, 0.8) size: 4 category: schedule\\n```\\n\\n---\\n\\n## 5. Quadrant labels\\n\\nLabel each quadrant with a name and an optional subtitle.\\n\\n```\\nquadrant Q1 \\\"Do First\\\"\\nquadrant Q2 \\\"Schedule\\\"\\nquadrant Q3 \\\"Delete\\\"\\nquadrant Q4 \\\"Delegate\\\"\\n\\n# With an optional subtitle:\\nquadrant Q1 \\\"Do First\\\" description: \\\"High urgency, high importance\\\"\\n```\\n\\nQuadrant numbering follows the standard mathematical convention: **Q1 = top-right, Q2 = top-left, Q3 = bottom-left, Q4 = bottom-right**. The `Q` prefix is optional — `quadrant 1 \\\"Label\\\"` is equally valid.\\n\\n---\\n\\n## 6. Heatmap mode\\n\\nHeatmap mode fills N×M cells with color intensity instead of plotting bubble positions.\\n\\n```\\nmatrix heatmap 4x3 \\\"Skill Matrix\\\"\\nrows: [Strategy, Execution, Communication, Technical]\\ncols: [Junior, Mid, Senior]\\n\\ncell (0,0) level: weak\\ncell (1,0) level: medium\\ncell (2,0) level: strong\\ncell (0,1) value: 7\\ncell (1,2) label: \\\"Top 10%\\\"\\n```\\n\\n- `matrix heatmap COLxROW` — header sets the grid dimensions.\\n- `rows:` and `cols:` — comma-separated or bracket-list of axis labels.\\n- `cell (col, row)` — zero-indexed, column first, row second (row 0 = bottom).\\n- `level:` — `strong` (3), `medium` (2), or `weak` (1) — shorthand for heat intensity.\\n- `value:` — explicit numeric value (overrides `level:`).\\n- `label:` — quoted text placed inside the cell.\\n\\n```\\nmatrix heatmap 4x4 \\\"Competency Heat Map\\\"\\nrows: [Leadership, Execution, Communication, Technical]\\ncols: [Junior, Mid, Senior, Staff]\\n\\ncell (0,0) level: weak\\ncell (1,0) level: medium\\ncell (2,0) level: strong\\ncell (3,0) level: strong\\ncell (0,1) level: medium\\ncell (1,1) level: medium\\ncell (2,1) level: strong\\ncell (3,1) level: strong\\ncell (0,2) level: weak\\ncell (1,2) level: medium\\ncell (2,2) level: medium\\ncell (3,2) level: strong\\ncell (0,3) level: weak\\ncell (1,3) level: weak\\ncell (2,3) level: medium\\ncell (3,3) level: strong\\n```\\n\\n---\\n\\n## 7. Correlation mode\\n\\nCorrelation mode renders an N×M dot matrix where intensity represents the relationship strength between row and column variables.\\n\\n```\\nmatrix correlation 4x4 \\\"Product Metrics\\\"\\nrows: [DAU, Retention, Revenue, NPS]\\ncols: [DAU, Retention, Revenue, NPS]\\n\\ncell (0,0) value: 1\\ncell (1,0) value: 0.82\\ncell (2,0) value: 0.54\\ncell (3,0) value: 0.71\\n```\\n\\nThe same `cell` syntax applies. `level: strong | medium | weak` is also accepted in correlation mode.\\n\\n---\\n\\n## 8. Config options\\n\\nA `config:` block tunes visual rendering. Each option goes on its own indented line below the `config:` header.\\n\\n```\\nconfig:\\n quadrantBg: true\\n gridLines: true\\n axisArrows: true\\n bubbleScale: area\\n legendPosition: bottom-right\\n```\\n\\n| Key | Values | Default | Effect |\\n|---|---|---|---|\\n| `quadrantBg` | `true` \\\\| `false` | `true` | Colored quadrant background fills |\\n| `gridLines` | `true` \\\\| `false` | `true` | Grid lines overlay |\\n| `axisArrows` | `true` \\\\| `false` | `true` | Arrows at axis ends |\\n| `bubbleScale` | `area` \\\\| `radius` | `area` | Whether `size:` scales bubble area or radius |\\n| `quadrantAnnotations` | `true` \\\\| `false` | `true` | Show quadrant label text in corners |\\n| `legendPosition` | `bottom-right` \\\\| `right` \\\\| `bottom-center` \\\\| `none` | `bottom-right` | Category legend placement |\\n| `labelCollision` | `auto` \\\\| `offset-only` \\\\| `leader-only` \\\\| `off` | `auto` | Overlap avoidance strategy for point labels |\\n| `offChartPolicy` | `clamp-badge` \\\\| `drop` | `clamp-badge` | What to do with points outside [0,1] |\\n\\nTwo shorthand directives also work at the top level (not inside the `config:` block):\\n\\n```\\naxis: off # off | on | auto — show or hide the axis lines\\nmargins: true # true | false — show Score + Rank margins (correlation mode)\\n```\\n\\n---\\n\\n## 9. Labels & comments\\n\\n- **Title:** `matrix \\\"My Title\\\"` or `title: My Title` as a standalone line.\\n- **Point label:** the quoted string before `at (…)`.\\n- **Axis labels:** `x-axis:` and `y-axis:` directives.\\n- **Quadrant labels:** `quadrant Q1 \\\"Name\\\"` directive.\\n- **Comments:** `#` anywhere on a line, outside quoted strings.\\n\\n```\\nmatrix \\\"Prioritization\\\"\\n# This is a comment\\nx-axis: Low Cost → High Cost # inline comment after a directive\\n\\\"Fix bug\\\" at (0.1, 0.9) size: 3 # comment after a point\\n```\\n\\n---\\n\\n## 10. Reserved words & escaping\\n\\n**Reserved at line start:** `matrix` (header), `x-axis:`, `y-axis:`, `quadrant`, `config:`, `title:`, `rows:`, `cols:`, `grid:`, `axis:`, `margins:`, `cell`.\\n\\n**Point lines must start with a quote character** (`\\\"` or `'`). A line that does not start with a quote is not treated as a point.\\n\\n**Strings with spaces** in axis labels do not need quoting — the text after the colon (and after the arrow) is taken verbatim. In `note:` and `label:` point properties, use double quotes.\\n\\n---\\n\\n## 11. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `\\\"Fix bug\\\" at (1, 2)` | Point parsed; x=1 clamped, y=1 clamped; off-chart badge shown | Keep coordinates in [0.0, 1.0] or accept the clamp-badge |\\n| `quadrant 1 \\\"Quick Wins\\\"` (no Q prefix) | Accepted — `Q` prefix is optional | Both `quadrant 1` and `quadrant Q1` work |\\n| `config: gridLines: false` (on same line) | Only `config:` keyword recognized; `gridLines: false` silently ignored | Put options on their own indented lines below `config:` |\\n| `x-axis: \\\"Low\\\" → \\\"High\\\"` (quoted labels) | Arrow not found inside quotes — treated as plain text | Remove quotes: `x-axis: Low → High` |\\n| `matrix heatmap` without dimensions | Defaults to 2×2; rows/cols directives set actual size | Specify dimensions on the header: `matrix heatmap 4x4` |\\n| `cell (0, 0) level: Strong` (capital S) | `level` match is case-insensitive — accepted | Both `strong` and `Strong` work |\\n| `shape: oval` | Unknown shape value — silently ignored | Use `circle`, `square`, `triangle`, or `diamond` |\\n\\n---\\n\\n## 12. Grammar (EBNF)\\n\\n```text\\ndocument = header directive*\\n\\nheader = \\\"matrix\\\" ( template-name | mode-header | title )? NEWLINE\\ntemplate-name = \\\"eisenhower\\\"|\\\"impact-effort\\\"|\\\"rice\\\"|\\\"bcg\\\"|\\\"ansoff\\\"|\\\"johari\\\"|\\\"9-box\\\"|\\\"risk-matrix\\\"\\nmode-header = ( \\\"heatmap\\\" | \\\"correlation\\\" ) ( number \\\"x\\\" number )? title?\\ntitle = quoted-string | bare-text\\n\\ndirective = x-axis | y-axis | quadrant-dir | config-block\\n | point | cell | rows-dir | cols-dir | grid-dir\\n | title-dir | axis-dir | margins-dir | comment | blank\\n\\nx-axis = \\\"x-axis:\\\" WS axis-spec NEWLINE\\ny-axis = \\\"y-axis:\\\" WS axis-spec NEWLINE\\naxis-spec = text arrow text | text # plain text → high label only\\narrow = \\\"→\\\" | \\\"->\\\" | \\\"↑\\\" | \\\"←\\\" | \\\"<-\\\" | \\\"<\\\" | \\\"↓\\\"\\n\\nquadrant-dir = \\\"quadrant\\\" WS \\\"Q\\\"? digit WS quoted-string ( WS \\\"description:\\\" quoted-string )? NEWLINE\\n\\nconfig-block = \\\"config:\\\" NEWLINE ( INDENT key \\\":\\\" WS value NEWLINE )*\\n\\npoint = quoted-string WS \\\"at\\\" WS \\\"(\\\" number \\\",\\\" number \\\")\\\" ( WS point-prop )* NEWLINE\\npoint-prop = \\\"size:\\\" number\\n | \\\"category:\\\" bareword\\n | \\\"color:\\\" hex-color\\n | \\\"shape:\\\" ( \\\"circle\\\"|\\\"square\\\"|\\\"triangle\\\"|\\\"diamond\\\" )\\n | \\\"highlight:\\\" \\\"true\\\"\\n | \\\"note:\\\" quoted-string\\n | \\\"label:\\\" quoted-string\\n\\ncell = \\\"cell\\\" WS \\\"(\\\" digit \\\",\\\" digit \\\")\\\" ( WS cell-prop )* NEWLINE\\ncell-prop = \\\"value:\\\" number\\n | \\\"label:\\\" quoted-string\\n | \\\"level:\\\" ( \\\"strong\\\" | \\\"medium\\\" | \\\"weak\\\" )\\n\\nrows-dir = \\\"rows:\\\" WS label-list NEWLINE\\ncols-dir = \\\"cols:\\\" WS label-list NEWLINE\\ngrid-dir = \\\"grid:\\\" WS number \\\"x\\\" number NEWLINE\\naxis-dir = \\\"axis:\\\" WS ( \\\"off\\\" | \\\"on\\\" | \\\"auto\\\" ) NEWLINE\\nmargins-dir = \\\"margins:\\\" WS ( \\\"true\\\" | \\\"false\\\" | \\\"on\\\" | \\\"1\\\" ) NEWLINE\\n\\nlabel-list = \\\"[\\\" text (\\\",\\\" text)* \\\"]\\\" | text (\\\",\\\" text)*\\nquoted-string = '\\\"' any-char-but-quote* '\\\"' | \\\"'\\\" any-char-but-quote* \\\"'\\\"\\ncomment = \\\"#\\\" any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/matrix/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"orgchart\": {\n \"title\": \"Org chart\",\n \"content\": \"## 1. Your first org chart\\n\\nThe smallest useful org chart: a three-level hierarchy with one open role.\\n\\n```\\norgchart \\\"Engineering Team\\\"\\ncto: \\\"Wei Zhang\\\" | CTO [role: cto]\\n lead: \\\"Sam Obi\\\" | Engineering Lead [role: engineer]\\n eng: \\\"Ana Rossi\\\" | Engineer [role: engineer]\\n open1: open \\\"TBH\\\" | Engineer [role: engineer]\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with the keyword `orgchart`, optionally followed by a quoted title.\\n2. Each person is a **node** — `id: \\\"Name\\\" | \\\"Title\\\" | \\\"Department\\\" [props]`. The `|` separates the name, title, and department fields.\\n3. **Indentation determines hierarchy** — each extra two (or more) spaces moves a node one level deeper under the nearest less-indented node above it.\\n4. Declare open roles with `open id:` or `draft id:`, and external advisors with `advisor id:`.\\n\\n> Comments must start with `#` on their own line (inline trailing `//` is also stripped).\\n\\n---\\n\\n## 2. Nodes\\n\\nA node line has the form `[kind] id: fields [props]`. The `id` must match `[A-Za-z][A-Za-z0-9_-]*`.\\n\\n### 2.1 Fields (pipe-separated)\\n\\nThe part after the `:` and before the optional `[props]` block is split on `|`:\\n\\n```\\nalice: \\\"Alice Zhang\\\" # name only\\nalice: \\\"Alice Zhang\\\" | \\\"VP Engineering\\\" # name + title\\nalice: \\\"Alice Zhang\\\" | \\\"VP Eng\\\" | \\\"Platform\\\" # name + title + department\\nalice: \\\"Alice Zhang\\\" | \\\"VP Eng\\\" | \\\"Platform\\\" | \\\"x@co.com\\\" # + info line\\n```\\n\\n| Position | Content | Notes |\\n|---|---|---|\\n| 1st | Name | Quoted or unquoted |\\n| 2nd | Title / job level | Optional |\\n| 3rd | Department | Optional |\\n| 4th | Info line | Optional; also settable via `note:`, `email:`, `phone:`, `location:` props |\\n\\n```\\norgchart \\\"Field count demo\\\"\\nalice: \\\"Alice\\\"\\n bob: \\\"Bob\\\" | \\\"CTO\\\"\\n carol: \\\"Carol\\\" | \\\"Engineer\\\" | \\\"Engineering\\\"\\n dave: \\\"Dave\\\" | \\\"VP Sales\\\" | \\\"Sales\\\" | \\\"dave@co.com\\\"\\n```\\n\\n### 2.2 Node kinds\\n\\nThe optional **kind keyword** before the id changes how the node is rendered:\\n\\n| Keyword(s) | Kind | Meaning |\\n|---|---|---|\\n| *(none)* | `person` | Regular person |\\n| `role`, `open` | `role` | Open / unfilled position |\\n| `draft`, `tbh` | `draft` | Planned position, not actively recruiting |\\n| `advisor`, `external` | `advisor` | External advisor, board member, or contractor |\\n\\n```\\norgchart \\\"Node kinds\\\"\\nceo: \\\"Jordan Kim\\\" | CEO [role: ceo]\\n eng_lead: \\\"Priya Nair\\\" | Engineering Lead [role: engineer]\\n open eng_open: \\\"TBH\\\" | Senior Engineer [role: engineer]\\n draft eng_draft: \\\"TBH\\\" | Staff Engineer [role: engineer]\\n advisor adv1: \\\"Dr. Lee\\\" | Board Advisor [role: advisor]\\n```\\n\\n### 2.3 Node properties\\n\\nProperties go in `[key: value, …]` at the end of a node line.\\n\\n| Property | Values | Effect |\\n|---|---|---|\\n| `role:` | see table below | Role icon displayed in the avatar |\\n| `icon:` | same as `role:` | Alias for `role:` |\\n| `department:` | text | Overrides the department field |\\n| `status:` | `new` \\\\| `leaving` \\\\| `on-leave` | Status pill on the card |\\n| `avatar-color:` | hex color (e.g. `\\\"#7B1FA2\\\"`) | Avatar background color |\\n| `gender:` | `male` \\\\| `female` | Avatar silhouette (used when no role icon is set) |\\n| `note:` | text | Info line (first one wins) |\\n| `email:` | text | Info line |\\n| `phone:` | text | Info line |\\n| `location:` | text | Info line |\\n| `assistant-of:` | node id | Renders this node as an assistant to the named node |\\n| `matrix:` | space- or comma-separated node ids | Adds dotted matrix lines from those nodes to this one |\\n| `reports:` | node id | Explicit parent override (instead of indentation) |\\n| `open` | *(bare flag)* | Marks node as open; equivalent to using `role` kind keyword |\\n| `draft` or `tbh` | *(bare flag)* | Marks node as draft; equivalent to `draft` kind keyword |\\n| `external` | *(bare flag)* | Marks node as external; equivalent to `advisor` kind keyword |\\n\\n**Role icons** — the `role:` value resolves to a display icon. Accepted keywords (case-insensitive):\\n\\n| Keywords | Icon |\\n|---|---|\\n| `ceo` | CEO |\\n| `cto` | CTO |\\n| `cfo` | CFO |\\n| `coo` | COO |\\n| `cmo` | CMO |\\n| `cpo` | CPO |\\n| `vp` | VP |\\n| `engineer`, `engineering` | Engineer |\\n| `designer`, `design` | Designer |\\n| `sales` | Sales |\\n| `hr` | HR |\\n| `legal` | Legal |\\n| `ops`, `operations` | Ops |\\n| `marketing` | Marketing |\\n| `product` | Product |\\n| `data` | Data |\\n| `advisor` | Advisor |\\n| `intern` | Intern |\\n| `vacant` | Vacant |\\n\\n```\\norgchart \\\"People directory\\\"\\nceo: \\\"Jamie Torres\\\" | CEO [role: ceo, avatar-color: \\\"#1E88E5\\\"]\\n cto: \\\"Raj Patel\\\" | CTO [role: cto]\\n eng1: \\\"Priya Nair\\\" | Staff Engineer [role: engineer, status: new]\\n eng2: \\\"Jordan Lee\\\" | Senior Engineer [role: engineer]\\n cfo: \\\"Maria Santos\\\" | CFO [role: cfo]\\n fin1: \\\"Nour Ahmed\\\" | Finance Manager [role: ops, status: on-leave]\\n advisor adv1: \\\"Dr. Alan Ford\\\" | Board Advisor [role: advisor]\\n draft draft1: \\\"TBH\\\" | General Counsel [role: legal]\\n```\\n\\n---\\n\\n## 3. Hierarchy\\n\\nHierarchy is expressed by **indentation** — the most common pattern. Each node becomes a child of the nearest node above it that has a smaller indent. Tabs are treated as two spaces.\\n\\n```\\nceo: \\\"CEO\\\"\\n cto: \\\"CTO\\\" # child of ceo (indent 2)\\n eng: \\\"Engineer\\\" # child of cto (indent 4)\\n cfo: \\\"CFO\\\" # child of ceo (indent 2, same level as cto)\\n```\\n\\nAlternatively, use the `reports:` property to set a parent explicitly regardless of indentation:\\n\\n```\\norgchart \\\"Flat file\\\"\\nceo: \\\"CEO\\\"\\ncto: \\\"CTO\\\" [reports: ceo]\\neng: \\\"Engineer\\\" [reports: cto]\\n```\\n\\n```\\norgchart \\\"Series A Startup\\\"\\nceo: \\\"Lena Brandt\\\" | CEO [role: ceo]\\n cto: \\\"James Osei\\\" | CTO [role: cto]\\n be: \\\"Platform Lead\\\" | Engineering [role: engineer]\\n be1: \\\"Fatima Al-Rashid\\\" | Backend Engineer [role: engineer]\\n be2: \\\"Marco Ricci\\\" | Backend Engineer [role: engineer]\\n fe: \\\"Frontend Lead\\\" | Engineering [role: engineer]\\n fe1: \\\"Yumi Tanaka\\\" | Frontend Engineer [role: engineer, status: new]\\n open_fe: open \\\"TBH\\\" | Frontend Engineer [role: engineer]\\n cpo: \\\"Diana Russo\\\" | CPO [role: cpo]\\n pm1: \\\"Chris Obi\\\" | Product Manager [role: product]\\n cmo: \\\"Kai Nakamura\\\" | CMO [role: cmo]\\n mkt1: draft \\\"TBH\\\" | Growth Marketer [role: marketing]\\n```\\n\\n---\\n\\n## 4. Edges\\n\\nTwo kinds of edges exist. Most reporting lines come from the indentation hierarchy automatically. You can also write them explicitly — or add matrix (dotted) lines.\\n\\n| Operator | Edge kind | Meaning |\\n|---|---|---|\\n| `from -> to` | `report` | Solid reporting line |\\n| `from -.-> to` | `matrix` | Dotted matrix (indirect) reporting line |\\n\\nExplicit edges require that both node ids are already declared. A `report` edge created explicitly is not duplicated if the same relationship is already implied by indentation.\\n\\nEdge labels are supported:\\n\\n```\\npm1 -.-> design [label: \\\"product partnership\\\"]\\n```\\n\\n```\\norgchart \\\"Matrix org\\\"\\nconfig: direction = LR\\ncpo: \\\"Ellen Wu\\\" | CPO [role: cpo]\\n pm_core: \\\"Core PM\\\" | Product Manager [role: product]\\n pm_growth: \\\"Growth PM\\\" | Product Manager [role: product]\\n design: \\\"Suki Ito\\\" | Design Lead [role: designer]\\n data_lead: \\\"Ben Park\\\" | Data Lead [role: data]\\npm_core -.-> design [label: \\\"design partner\\\"]\\npm_growth -.-> design\\npm_growth -.-> data_lead\\n```\\n\\n---\\n\\n## 5. Configuration\\n\\n`config:` lines adjust layout and orientation. Each goes on its own line.\\n\\n| Config key | Values | Default | Effect |\\n|---|---|---|---|\\n| `direction` | `TD`, `LR` | `TD` | Top-down or left-right flow |\\n| `layout` | `tree`, `list`, `directory`, `compact` | `tree` | Visual layout mode |\\n\\n**Layout notes:**\\n- `tree` — standard hierarchical tree with branching connectors. Best for most org charts.\\n- `list` / `directory` / `compact` — compact indented directory view. Good for large headcount lists where tree branching becomes unwieldy.\\n\\n**Tree layout** (default) — avatar cards with branching connectors and department color-coding. Best for teams up to ~30 people.\\n\\n```\\norgchart \\\"Acme Engineering — tree\\\"\\ncto: \\\"Wei Zhang\\\" | CTO [role: cto]\\n platform: \\\"Platform Lead\\\" | Engineering [role: engineer]\\n p1: \\\"Amara Diallo\\\" | Staff Engineer [role: engineer]\\n p2: \\\"Ben Novak\\\" | Senior Engineer [role: engineer]\\n p3: draft \\\"TBH\\\" | Engineer [role: engineer]\\n growth: \\\"Growth Lead\\\" | Engineering [role: engineer, status: new]\\n g1: \\\"Fatima Al-Rashid\\\" | Senior Engineer [role: engineer]\\n g2: \\\"Marco Ricci\\\" | Engineer [role: engineer]\\n```\\n\\n**List layout** — compact directory rows with indent guides. Best for large teams where tree branching becomes unwieldy.\\n\\n```\\norgchart \\\"Engineering Directory — list\\\"\\nconfig: layout = list\\ncto: \\\"Wei Zhang\\\" | CTO [role: cto]\\n platform: \\\"Platform Team\\\"\\n p1: \\\"Amara Diallo\\\" | Staff Eng [role: engineer]\\n p2: \\\"Ben Novak\\\" | Senior Eng [role: engineer]\\n p3: draft \\\"TBH\\\" | Engineer [role: engineer]\\n growth: \\\"Growth Team\\\"\\n g1: \\\"Fatima Al-Rashid\\\" | Senior Eng [role: engineer, status: new]\\n g2: \\\"Marco Ricci\\\" | Engineer [role: engineer, status: leaving]\\n```\\n\\n---\\n\\n## 6. Labels & comments\\n\\n- **Title:** `orgchart \\\"Acme Corp\\\"` — first line only.\\n- **Name field:** first pipe-delimited field after the colon; may be quoted (`\\\"Alice Zhang\\\"`) or unquoted (`Alice`).\\n- **Title/department fields:** second and third pipe-delimited fields.\\n- **Info line:** fourth pipe-delimited field, or set via `note:`, `email:`, `phone:`, or `location:` props. First one encountered wins.\\n- **Comments:** `#` at the start of a line (after leading whitespace). Inline `//` is also stripped.\\n\\n---\\n\\n## 7. Reserved words & escaping\\n\\n**Reserved at line start:** `orgchart` (header), `config:`, `role`, `open`, `draft`, `tbh`, `advisor`, `external`.\\n\\n**Reserved operator tokens** — avoid these sequences inside ids: `->`, `-.->`\\n\\n**ID rules:** must match `[A-Za-z][A-Za-z0-9_-]*`. Names with spaces go in the quoted name field, not the id.\\n\\n**Strings with spaces** in props values (e.g. `department: \\\"Platform Eng\\\"`) must be double-quoted.\\n\\n---\\n\\n## 8. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `alice: Alice Zhang` (unquoted name with space) | Parses `Alice` as the name, `Zhang` is lost or misread | Quote the name: `alice: \\\"Alice Zhang\\\"` |\\n| `open1 open: \\\"TBH\\\"` | `open` after the id is not a kind keyword; fails id regex | Kind keyword comes first: `open open1: \\\"TBH\\\"` |\\n| `alice -> bob` before either node is declared | `OrgchartParseError: Edge references unknown node` | Declare nodes first, then write edges at the end |\\n| `config: direction = top-down` | Unknown value ignored; direction stays `TD` | Use `TD` or `LR` |\\n| `config: layout = compact` | Accepted — maps to `list` layout | Correct; `compact`, `directory`, and `list` all work |\\n| `alice [matrix: bob charlie]` | Space-separated ids in `matrix:` — both are added | Also works with commas: `matrix: \\\"bob, charlie\\\"` |\\n| Node id with a space: `fe lead` | Parser takes `fe` as the id; `lead` fails | Use underscore: `fe_lead` |\\n| Duplicate id | `OrgchartParseError: Duplicate node id` | Each node needs a unique id |\\n\\n---\\n\\n## 9. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | config | edge | node)*\\n\\nheader = \\\"orgchart\\\" ( WS quoted-string )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nconfig = \\\"config\\\" WS \\\":\\\" WS key WS \\\"=\\\" WS value NEWLINE\\nkey = \\\"direction\\\" | \\\"layout\\\"\\n\\nnode = INDENT* kind? id \\\":\\\" WS fields ( \\\"[\\\" node-attrs \\\"]\\\" )? NEWLINE\\nkind = \\\"role\\\" | \\\"open\\\" | \\\"draft\\\" | \\\"tbh\\\" | \\\"advisor\\\" | \\\"external\\\" | \\\"person\\\"\\nfields = field ( \\\"|\\\" field )*\\nfield = quoted-string | unquoted-text\\nnode-attrs = node-attr (\\\",\\\" node-attr)*\\nnode-attr = \\\"role:\\\" role-keyword\\n | \\\"icon:\\\" role-keyword\\n | \\\"department:\\\" text\\n | \\\"status:\\\" ( \\\"new\\\" | \\\"leaving\\\" | \\\"on-leave\\\" )\\n | \\\"avatar-color:\\\" quoted-hex\\n | \\\"gender:\\\" ( \\\"male\\\" | \\\"female\\\" )\\n | \\\"note:\\\" text\\n | \\\"email:\\\" text\\n | \\\"phone:\\\" text\\n | \\\"location:\\\" text\\n | \\\"assistant-of:\\\" id\\n | \\\"matrix:\\\" id-list\\n | \\\"reports:\\\" id\\n | bare-flag\\n\\nbare-flag = \\\"open\\\" | \\\"draft\\\" | \\\"tbh\\\" | \\\"external\\\"\\n\\nedge = id WS edge-op WS id ( \\\"[\\\" edge-attrs \\\"]\\\" )? NEWLINE\\nedge-op = \\\"->\\\" | \\\".->\\\" // -.-> for matrix\\nedge-attrs = \\\"label:\\\" quoted-string\\n\\nid = [A-Za-z] [A-Za-z0-9_-]*\\ncomment = ( \\\"#\\\" | \\\"//\\\" ) any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/orgchart/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"mindmap\": {\n \"title\": \"Mind map\",\n \"content\": \"## 1. Your first mind map\\n\\nThe smallest useful mind map: a central topic with two branches, one with a sub-item.\\n\\n```\\nmindmap\\n\\n# Team retrospective\\n\\n## What went well\\n- Clear sprint goals\\n- Good test coverage\\n\\n## What to improve\\n- Slower PR reviews\\n - Add a review SLA\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with an optional `mindmap` keyword on its own line, then a blank line.\\n2. The root is the single `#` heading — exactly one is allowed.\\n3. Use `##`, `###`, and deeper headings to set branch depth. Heading level equals tree depth.\\n4. Use `-`, `*`, or `+` bullets to add sub-items under any heading. Each 2-space indent adds one more depth level.\\n\\n> Comments are not supported. Use `%%` directives (before the `#` root) for configuration only.\\n\\n---\\n\\n## 2. Headings and depth\\n\\nHeading level maps directly to tree depth. `#` is always the root (depth 0). `##` is depth 1. `###` is depth 2, and so on up to `######` (depth 5).\\n\\n```\\nmindmap\\n\\n# Root\\n## Branch A ← depth 1\\n### Sub-branch ← depth 2\\n#### Leaf ← depth 3\\n## Branch B\\n```\\n\\nHeadings can jump levels — `####` after `##` is valid and produces a node at depth 3. The tree depth is relative to the root, not to the previous heading.\\n\\n---\\n\\n## 3. Bullets\\n\\nBullets extend a heading branch with further detail. Any of `-`, `*`, or `+` is accepted as the bullet marker. Each **2 spaces** of indentation adds one level of depth relative to the enclosing heading.\\n\\n```\\n## Risks\\n- Technical complexity ← depth 2 (one level under ## Risks)\\n - Legacy integrations ← depth 3 (2 spaces indent)\\n - Auth service ← depth 4 (4 spaces indent)\\n- Team availability ← depth 2 again\\n```\\n\\n```\\nmindmap\\n\\n# Book outline\\n\\n## Chapter 1 — Introduction\\n- Why this matters\\n - Historical context\\n - Current state\\n- What you will learn\\n\\n## Chapter 2 — Core concepts\\n- Concept A\\n - Definition\\n - Examples\\n- Concept B\\n - Definition\\n - Worked example\\n - Step-by-step walkthrough\\n```\\n\\n---\\n\\n## 4. Inline formatting\\n\\nNode labels support a subset of Markdown inline formatting. The parser tokenizes labels at parse time; the renderer uses the tokens to emit styled text.\\n\\n| Syntax | Effect | Example |\\n|---|---|---|\\n| `**text**` | Bold | `**Critical path**` |\\n| `*text*` | Italic | `*optional*` |\\n| `` `code` `` | Monospace code | `` `npm install` `` |\\n| `[text](url)` | Link | `[RFC 7519](https://tools.ietf.org/html/rfc7519)` |\\n| `[ ] item` | Unchecked task | `[ ] Write tests` |\\n| `[x] item` | Checked task | `[x] Design review` |\\n\\nThe checkbox must be at the very start of the label (before any other text). Inline formatting can be nested: `**[bold link](url)**`.\\n\\n```\\nmindmap\\n\\n# Sprint 24 review\\n\\n## Completed\\n- [x] **Auth redesign** — JWT + refresh tokens\\n- [x] API rate limiting \\\\`per-user\\\\`\\n- [x] [Error budget dashboard](https://metrics.example.com)\\n\\n## In progress\\n- [ ] *Mobile push notifications*\\n - [ ] iOS APNs integration\\n - [ ] Android FCM setup\\n\\n## Blocked\\n- [ ] **Payment webhook** — waiting on Stripe team\\n - *Escalated to account manager*\\n```\\n\\n---\\n\\n## 5. Layout styles\\n\\nThe `%% style:` directive selects the layout algorithm. Place it before the `#` root heading.\\n\\n| Style | Layout | Best for |\\n|---|---|---|\\n| `map` (default) | Radial — branches spread in all directions from the center | Brainstorming, concept maps, free-form exploration |\\n| `logic-right` | Horizontal tree — all branches extend to the right | Structured outlines, hierarchies, sequential breakdowns |\\n\\n```\\n%% style: map\\n%% style: logic-right\\n```\\n\\n**`map`** (default) — radial layout, branches spread in all directions from the center. Best for brainstorming and concept maps.\\n\\n```\\nmindmap\\n\\n# Machine learning\\n\\n## Supervised\\n### Classification\\n- Decision tree\\n- SVM\\n- Neural net\\n### Regression\\n- Linear\\n- Gradient boosting\\n\\n## Unsupervised\\n### Clustering\\n- K-means\\n- DBSCAN\\n### Reduction\\n- PCA\\n- t-SNE\\n\\n## Reinforcement\\n- Q-learning\\n- Policy gradient\\n```\\n\\n**`logic-right`** — horizontal tree, all branches extend to the right. Best for structured outlines and sequential hierarchies.\\n\\n```\\nmindmap\\n%% style: logic-right\\n\\n# Machine learning\\n\\n## Supervised\\n### Classification\\n- Decision tree\\n- SVM\\n- Neural net\\n### Regression\\n- Linear\\n- Gradient boosting\\n\\n## Unsupervised\\n### Clustering\\n- K-means\\n- DBSCAN\\n### Reduction\\n- PCA\\n- t-SNE\\n\\n## Reinforcement\\n- Q-learning\\n- Policy gradient\\n```\\n\\n---\\n\\n## 6. Directives\\n\\nDirectives are `%%` lines placed **before** the `#` root heading. They configure the diagram globally.\\n\\n| Directive | Values | Default | Effect |\\n|---|---|---|---|\\n| `%% style: …` | `map`, `logic-right` | `map` | Layout algorithm |\\n| `%% theme: …` | any string | (none) | Theme override passed to renderer |\\n| `%% maxLabelWidth: …` | integer 80–1000 | `240` | Max pixel width before label wraps |\\n\\n```\\nmindmap\\n%% style: logic-right\\n%% maxLabelWidth: 320\\n\\n# Wide label root\\n```\\n\\n```\\nmindmap\\n%% style: logic-right\\n%% maxLabelWidth: 200\\n\\n# Schematex features\\n\\n## DSL-first design\\n- One keyword per diagram\\n- AI-friendly syntax\\n- CJK support\\n\\n## Zero dependencies\\n- Hand-written parser\\n- No D3, no dagre\\n- ~KB-level bundle\\n\\n## Standards-compliant\\n- IEEE for logic gates\\n- IEC for circuits\\n- McGoldrick for genograms\\n```\\n\\n---\\n\\n## 7. Labels & comments\\n\\n- **Root title:** the text after `#` on the root heading line.\\n- **Branch labels:** the text after `##`, `###`, etc.\\n- **Bullet labels:** the text after the `- ` / `* ` / `+ ` marker.\\n- **Inline formatting:** `**bold**`, `*italic*`, `` `code` ``, `[text](url)`, `[ ]` / `[x]`.\\n- **Comments:** not supported in the body. Use `%%` directives before the `#` root for configuration; `%%` lines in the body are treated as directives (silently ignored if unrecognized).\\n\\n---\\n\\n## 8. Reserved words & escaping\\n\\n**Reserved at document start:** `mindmap` (optional keyword) and `%%` (directive prefix).\\n\\n**Reserved as root:** exactly one `#` heading; a second `#` heading throws a parse error.\\n\\n**Bullet markers:** `-`, `*`, `+` followed by a space. A `*` that is not followed by a space is treated as an italic marker if it appears inside label text.\\n\\n**Inline conflicts:** a label beginning with `[ ]` or `[x] ` is parsed as a checkbox, not a Markdown link. If you need a label that literally starts with `[`, write `\\\\[` — the backslash escapes the bracket.\\n\\n---\\n\\n## 9. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| Two `#` headings | `Error: multiple # center nodes not allowed` | Use exactly one `#` heading as the root |\\n| `##Branch` (no space after `##`) | Line is not recognized as a heading; silently skipped | Always put a space: `## Branch` |\\n| Bullet indented 3 spaces | Depth = `lastHeadingDepth + 1 + floor(3/2) = lastHeadingDepth + 2` — may create an unexpected level | Use multiples of 2 spaces: 0, 2, 4, 6… |\\n| `%% style: radial` | Unknown value silently ignored; layout stays `map` | Use `map` or `logic-right` |\\n| `mindmap` keyword mid-document | Treated as a plain text line (the keyword is only recognized on the very first line) | Place `mindmap` on line 1, before any content |\\n| `[ ]text` (no space after bracket) | Checkbox not recognized; rendered as literal `[ ]text` | `[ ] text` — space required after the closing bracket |\\n\\n---\\n\\n## 10. Grammar (EBNF)\\n\\n```text\\ndocument = (\\\"mindmap\\\" NEWLINE)? (blank | directive)* node*\\n\\ndirective = \\\"%%\\\" WS key \\\":\\\" WS value NEWLINE\\nkey = \\\"style\\\" | \\\"theme\\\" | \\\"maxlabelwidth\\\"\\n\\nnode = heading | bullet\\nheading = INDENT? \\\"#\\\"+ SPACE label NEWLINE\\nbullet = SPACE* bullet-marker SPACE label NEWLINE\\nbullet-marker = \\\"-\\\" | \\\"*\\\" | \\\"+\\\"\\n\\nlabel = inline-token*\\ninline-token = checkbox\\n | \\\"**\\\" inline-token* \\\"**\\\"\\n | \\\"*\\\" inline-token* \\\"*\\\"\\n | \\\"`\\\" code-text \\\"`\\\"\\n | \\\"[\\\" inline-token* \\\"]\\\" \\\"(\\\" url \\\")\\\"\\n | plain-text\\n\\ncheckbox = \\\"[ ]\\\" SPACE | \\\"[x]\\\" SPACE | \\\"[X]\\\" SPACE\\n\\nINDENT = WS* %% headings may have leading whitespace (ignored)\\nSPACE = \\\" \\\" | \\\"\\\\t\\\"\\n```\\n\\n**Depth rules:**\\n- Heading `#` → depth 0 (root)\\n- Heading `##` → depth 1, `###` → depth 2, etc.\\n- Bullet at `n` leading spaces → depth = `lastHeadingDepth + 1 + floor(n / 2)`\\n\\nAuthoritative source: `src/diagrams/mindmap/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"timeline\": {\n \"title\": \"Timeline diagram\",\n \"content\": \"## 1. Your first timeline\\n\\nThe smallest useful timeline: a title, two ordinary events, and a milestone.\\n\\n```\\ntimeline \\\"Product Launch\\\"\\n2024-06-01: \\\"Development complete\\\"\\n2024-08-15: \\\"Closed beta\\\"\\n2024-09-01: milestone \\\"Public launch\\\"\\n```\\n\\nFour rules cover 80% of usage:\\n\\n1. Start with the keyword `timeline`, optionally followed by a quoted title.\\n2. Each event is `DATE: \\\"Label\\\"` — a date, a colon, then a quoted label. A `milestone` keyword before the label marks the event as a milestone.\\n3. Date ranges use `DATE - DATE:` or `DATE .. DATE:` (both are equivalent).\\n4. Add `[key: value]` properties after the quoted label to set color, side placement, shape, or category.\\n\\n> Comments must start with `#` or `//` on their own line.\\n\\n---\\n\\n## 2. Events\\n\\nAn event line places a labeled marker at a point in time (or a bar across a span).\\n\\n### 2.1 Point events\\n\\n```\\n2024-09-15: \\\"Conference keynote\\\"\\n```\\n\\n### 2.2 Range events\\n\\n```\\n2024-06-01 - 2024-08-31: \\\"Q3 development sprint\\\"\\n2024-06-01 .. 2024-08-31: \\\"Q3 development sprint\\\"\\n```\\n\\nBoth separators (`-` surrounded by spaces, or `..`) are equivalent.\\n\\n### 2.3 Milestones\\n\\n```\\n2024-09-01: milestone \\\"Public launch\\\"\\n```\\n\\nThe `milestone` keyword before the quoted label switches the marker to a diamond shape.\\n\\n### 2.4 Event properties\\n\\nProperties go in `[key: value, …]` after the quoted label, before the newline.\\n\\n| Property | Values | Meaning |\\n|---|---|---|\\n| `color:` | hex string | Custom color for this marker or bar |\\n| `side:` | `above` \\\\| `below` | Force placement above or below the axis (swimlane / lollipop) |\\n| `icon:` | any text (e.g. emoji) | Icon displayed with the event |\\n| `shape:` | `circle` \\\\| `square` \\\\| `diamond` \\\\| `star` \\\\| `flag` | Point marker shape |\\n| `category:` | quoted string | Group label — drives color in gantt legend |\\n\\n```\\n2024-09-01: milestone \\\"Launch day\\\" [color: #E53935, side: above]\\n2024-07-15: \\\"Beta ships\\\" [icon: 🚀, category: \\\"product\\\"]\\n```\\n\\n```\\ntimeline \\\"Engineering Milestones\\\"\\nconfig: style = lollipop\\n\\n2024-01-08: \\\"Sprint planning complete\\\"\\n2024-02-14: milestone \\\"Design system shipped\\\" [side: above, color: #1565C0]\\n2024-03-22: \\\"Incident — 4h outage\\\" [icon: ⚠️]\\n2024-04-01 - 2024-06-30: \\\"API v2 build\\\"\\n2024-07-01: milestone \\\"API v2 GA\\\" [side: above, color: #2E7D32]\\n```\\n\\n---\\n\\n## 3. Date formats\\n\\nAll date formats can appear anywhere a date is expected — in events, eras, and ranges.\\n\\n| Format | Example | Notes |\\n|---|---|---|\\n| Full date | `2024-09-15` | Day-precision; `YYYY-MM-DD` |\\n| Year-month | `2024-09` | Month-precision |\\n| Year | `2024` | Year-precision |\\n| Quarter | `2024-Q3` | Maps to start of that quarter |\\n| BC year (negative) | `-753` | 753 BC |\\n| BC year (suffix) | `753BC` or `753BCE` | Same as `-753` |\\n| Geological | `65Ma`, `4.6Ga`, `12ka` | Million / billion / thousand years ago |\\n\\n```\\ntimeline \\\"Age of Earth\\\"\\nconfig: style = lollipop\\nconfig: scale = log\\n\\n4600Ma: \\\"Earth forms\\\"\\n540Ma: \\\"Cambrian explosion\\\"\\n252Ma: milestone \\\"Permian extinction — 96% of species lost\\\"\\n66Ma: milestone \\\"K-Pg extinction — end of non-avian dinosaurs\\\"\\n3Ma: \\\"Earliest stone tools (Lomekwi)\\\"\\n0: \\\"Present day\\\"\\n```\\n\\n---\\n\\n## 4. Eras (background spans)\\n\\nAn `era` line draws a shaded background band across the time axis. It always requires a date range.\\n\\n```\\nera 2020 - 2022: \\\"Foundation Phase\\\"\\nera 2022 .. 2025: \\\"Growth Phase\\\" [color: #E8F5E9]\\n```\\n\\nThe `[color: …]` property is optional. Overlapping eras stack into separate bands automatically.\\n\\n```\\ntimeline \\\"Company History\\\"\\nconfig: style = swimlane\\n\\nera 2019..2021: \\\"Early Stage\\\" [color: #FFF8E1]\\nera 2021..2024: \\\"Series A & B\\\" [color: #E8F5E9]\\n\\n2019-03-01: \\\"Founded\\\"\\n2019-11-15: \\\"First paying customer\\\"\\n2020-05-01: milestone \\\"Product-market fit\\\" [side: above]\\n2021-02-10: \\\"Series A — $8M\\\"\\n2022-09-01: \\\"Series B — $30M\\\"\\n2023-06-15: milestone \\\"Profitability\\\" [color: #1B5E20]\\n```\\n\\n---\\n\\n## 5. Tracks (swimlane grouping)\\n\\nA `track` block groups related events into a named swimlane. Indented event lines (2 spaces) belong to the track.\\n\\n```\\ntrack \\\"Engineering\\\":\\n 2024-01-15 - 2024-03-31: \\\"API design\\\"\\n 2024-04-01 - 2024-07-31: \\\"Implementation\\\"\\n\\ntrack \\\"Marketing\\\":\\n 2024-06-01: \\\"Campaign kick-off\\\"\\n 2024-09-01: milestone \\\"Launch campaign\\\" [color: #1B5E20]\\n```\\n\\nTracks are most useful in `gantt` style, where each track becomes its own labeled row.\\n\\n```\\ntimeline \\\"Q3 Project Plan\\\"\\nconfig: style = gantt\\n\\ntrack \\\"Backend\\\":\\n 2024-07-01 - 2024-08-15: \\\"Database migration\\\"\\n 2024-08-16 - 2024-09-30: \\\"API hardening\\\"\\n\\ntrack \\\"Frontend\\\":\\n 2024-07-01 - 2024-08-31: \\\"New design system\\\"\\n 2024-09-01 - 2024-09-30: \\\"Integration & QA\\\"\\n\\ntrack \\\"DevOps\\\":\\n 2024-07-15 - 2024-08-01: \\\"Staging environment\\\"\\n 2024-09-30: milestone \\\"Go-live\\\" [color: #2E7D32]\\n```\\n\\n---\\n\\n## 6. Notes\\n\\nA `note:` line indented under an event attaches a tooltip annotation to it.\\n\\n```\\n2024-06-01: \\\"Beta launch\\\"\\n note: \\\"First external users; NPS target 40+\\\"\\n```\\n\\nNotes work both for flat events and for events inside tracks.\\n\\n---\\n\\n## 7. Configuration\\n\\n`config:` lines tune the layout and visual style. Each is its own line in the form `config: key = value`.\\n\\n| Key | Values | Default | Effect |\\n|---|---|---|---|\\n| `style` | `swimlane` \\\\| `gantt` \\\\| `lollipop` | `swimlane` | Visual rendering mode |\\n| `orientation` | `horizontal` \\\\| `vertical` | `horizontal` | Axis direction |\\n| `scale` | `proportional` \\\\| `equidistant` \\\\| `log` | `proportional` | How time maps to screen space |\\n| `axis` | `bottom` \\\\| `center` | `bottom` | Where the time axis is drawn |\\n\\n**Style notes:**\\n- `swimlane` — events alternate above and below a horizontal axis; `side:` controls per-event placement. Eras add colored background bands. Best for roadmaps and biographies.\\n- `gantt` — each named track becomes a horizontal bar lane; milestones become pins above the bars. `gantt-project` is an alias. Best for project scheduling.\\n- `lollipop` — a center axis with labeled cards on alternating stems. Best for historical retrospectives with sparse, memorable events.\\n\\n**Swimlane** — roadmaps, product timelines, biographies. Eras add color bands; events alternate above and below.\\n\\n```\\ntimeline \\\"SaaS Platform Roadmap\\\"\\nconfig: style = swimlane\\n\\nera 2024-Q1..2024-Q2: \\\"Foundation\\\" [color: #E3F2FD]\\nera 2024-Q3..2024-Q4: \\\"Growth\\\" [color: #E8F5E9]\\n\\n2024-01-15: \\\"Kick-off & team onboarding\\\"\\n2024-02-01: milestone \\\"Architecture sign-off\\\" [side: above]\\n2024-03-01 - 2024-05-31: \\\"API v2 build\\\"\\n2024-04-10: \\\"Security audit\\\" [color: #F9A825]\\n2024-06-30: milestone \\\"Beta launch\\\" [side: above]\\n2024-07-01 - 2024-09-30: \\\"Performance & scaling\\\"\\n2024-10-15: milestone \\\"General availability\\\" [color: #2E7D32]\\n```\\n\\n**Gantt** — parallel tracks with horizontal duration bars and milestone pins. Best for project scheduling.\\n\\n```\\ntimeline \\\"Product Launch — Q4\\\"\\nconfig: style = gantt\\n\\ntrack \\\"Engineering\\\":\\n 2024-10-01 - 2024-11-15: \\\"Feature freeze & hardening\\\"\\n 2024-11-16 - 2024-11-30: \\\"Load testing\\\"\\n\\ntrack \\\"Design\\\":\\n 2024-10-01 - 2024-10-31: \\\"Final UI polish\\\"\\n 2024-11-01 - 2024-11-14: \\\"Asset delivery\\\"\\n\\ntrack \\\"Marketing\\\":\\n 2024-10-15 - 2024-11-14: \\\"Campaign prep\\\"\\n 2024-11-15 - 2024-11-30: \\\"Launch campaign\\\"\\n 2024-12-01: milestone \\\"Launch day\\\" [color: #2E7D32]\\n```\\n\\n**Lollipop** — sparse milestones on a centered axis with alternating cards. Best for historical retrospectives and brand stories.\\n\\n```\\ntimeline \\\"History of Computing\\\"\\nconfig: style = lollipop\\nconfig: scale = equidistant\\n\\n1936: \\\"Turing — On Computable Numbers\\\"\\n1945: \\\"ENIAC — first general-purpose computer\\\"\\n1969: \\\"ARPANET\\\"\\n1971: milestone \\\"Intel 4004 microprocessor\\\" [side: above]\\n1976: \\\"Apple I\\\"\\n1991: \\\"World Wide Web (Berners-Lee)\\\"\\n2007: milestone \\\"iPhone\\\" [color: #1565C0, side: above]\\n```\\n\\n---\\n\\n## 8. Labels & comments\\n\\n- **Title:** `timeline \\\"My Title\\\"` — first line only.\\n- **Event label:** quoted string after the colon: `DATE: \\\"Label\\\"`.\\n- **Milestone label:** `DATE: milestone \\\"Label\\\"`.\\n- **Era label:** `era DATE - DATE: \\\"Label\\\"`.\\n- **Track name:** `track \\\"Name\\\":`.\\n- **Note:** `note: \\\"text\\\"` indented under an event.\\n- **Comments:** `#` or `//` at the start of a line (after leading whitespace).\\n\\n---\\n\\n## 9. Reserved words & escaping\\n\\n**Reserved at line start:** `timeline` (header), `config:`, `era`, `track`, `note:`.\\n\\n**Date range separators:** ` - ` (space-hyphen-space) and `..` — avoid these sequences inside label text that appears before the colon.\\n\\n**BC years as negative integers:** `-753` is the year 753 BC. The parser distinguishes the negative sign from a range separator by checking for surrounding whitespace — ` - ` (with spaces) is a range; `-753` (no leading space after a colon) is a BC year.\\n\\n**Property blocks:** `[key: value]` must appear *after* the quoted label on the same line. The closing `]` must be present; unclosed brackets produce a parse error.\\n\\n---\\n\\n## 10. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `2024-06-01: Launch day` (unquoted label) | Line not recognized as an event — `TimelineParseError` | Quote the label: `2024-06-01: \\\"Launch day\\\"` |\\n| `2024-06 - 2024-09: \\\"Q3\\\"` (year-month range) | Parsed correctly | This works — all date formats are valid in ranges |\\n| `era 2024: \\\"Whole year\\\"` (no range) | `TimelineParseError: era requires a date range` | Use a range: `era 2024 - 2024: \\\"Whole year\\\"` |\\n| `track \\\"Backend\\\"` (no colon) | `TimelineParseError: Expected ':' after track name` | Add the colon: `track \\\"Backend\\\":` |\\n| `2024-01-01: \\\"Event\\\" [side: left]` | `side: left` silently ignored; only `above` and `below` are valid | Use `side: above` or `side: below` |\\n| `config: style = Gantt` (capital G) | `TimelineParseError: Invalid style: Gantt` | Use lowercase: `config: style = gantt` |\\n| `2024-01-01-2024-03-31: \\\"Q1\\\"` (no spaces around `-`) | Parser reads `2024-01-01-2024` as a date — fails | Use spaces: `2024-01-01 - 2024-03-31:` or `..`: `2024-01-01..2024-03-31:` |\\n| Indented event without a track | Indented lines under the timeline header that aren't inside a `track` block — parsed as flat events | Only indent events that are inside a `track \\\"Name\\\":` block |\\n\\n---\\n\\n## 11. Grammar (EBNF)\\n\\n```text\\ndocument = header ( blank | comment | config | era | track | event )*\\n\\nheader = \\\"timeline\\\" ( WS quoted-string )? NEWLINE\\nquoted-string = '\\\"' any-char-but-quote* '\\\"'\\n\\nconfig = \\\"config:\\\" WS key WS \\\"=\\\" WS value NEWLINE\\nkey = \\\"style\\\" | \\\"orientation\\\" | \\\"scale\\\" | \\\"axis\\\"\\n\\nera = \\\"era\\\" WS date-range \\\":\\\" WS quoted-string ( WS props )? NEWLINE\\ntrack = \\\"track\\\" WS quoted-string \\\":\\\" NEWLINE\\n ( INDENT≥2 event | INDENT≥2 note )*\\n\\nevent = date-spec \\\":\\\" WS event-body ( WS props )? NEWLINE\\n ( INDENT note )?\\nevent-body = ( \\\"milestone\\\" WS )? quoted-string\\ndate-spec = date ( ( \\\" - \\\" | \\\"..\\\" ) date )?\\n\\nnote = \\\"note:\\\" WS quoted-string NEWLINE\\n\\nprops = \\\"[\\\" prop-list \\\"]\\\"\\nprop-list = prop ( \\\",\\\" prop )*\\nprop = key \\\":\\\" WS value\\n | key \\\":\\\" WS quoted-string\\n\\ndate = iso-date | year-month | year | quarter | bc-year | geological\\niso-date = digit{4} \\\"-\\\" digit{2} \\\"-\\\" digit{2}\\nyear-month = digit{4} \\\"-\\\" digit{2}\\nyear = \\\"-\\\"? digit{1,5}\\nquarter = digit{4} \\\"-\\\"? \\\"Q\\\" [1-4]\\nbc-year = digit+ ( \\\"BC\\\" | \\\"BCE\\\" )\\ngeological = number ( \\\"Ma\\\" | \\\"Ga\\\" | \\\"ka\\\" )\\n\\ncomment = ( \\\"#\\\" | \\\"//\\\" ) any NEWLINE\\n```\\n\\nAuthoritative source: `src/diagrams/timeline/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"state\": {\n \"title\": \"State diagram\",\n \"content\": \"## 1. Your first state diagram\\n\\nThe smallest useful state diagram has an initial state, a final state, and one transition.\\n\\n```\\nstateDiagram-v2\\n[*] --> Running\\nRunning --> [*] : done\\n```\\n\\nThree rules cover 80% of usage:\\n\\n1. Start with `stateDiagram-v2` (Mermaid-style) or `state` (Schematex-style) header.\\n2. Use `[*]` for the implicit start node (when on the left of `-->`) or end node (when on the right).\\n3. Connect states with `-->`. Add a label after `:` — full UML form is `trigger [guard] / action`.\\n\\nThe default direction is **TB** (top-to-bottom) to match Mermaid. Override with `direction LR` on its own line, or `[direction: LR]` on the header.\\n\\n> Comments use `%%` (Mermaid), `#`, or `//`.\\n\\n---\\n\\n## 2. State declarations\\n\\nStates are auto-created when first referenced in a transition, but explicit declarations let you control the label and shape.\\n\\n```\\nstate Authenticating\\nstate \\\"Awaiting Approval\\\" as Approval\\nIdle: Waiting for input\\n```\\n\\n| Form | Effect |\\n|---|---|\\n| `Idle` | Bare ID — created as a simple state with `Idle` as both id and label |\\n| `state Idle` | Explicit declaration; same effect |\\n| `state \\\"Awaiting Approval\\\" as Approval` | Aliased — display `Awaiting Approval`, refer to it by `Approval` in transitions |\\n| `Idle: Waiting for input` | Inline label — `Idle` is the id, `Waiting for input` is the visible label |\\n\\n---\\n\\n## 3. Pseudo-states\\n\\nPseudo-states control the flow of a state machine without representing a stable resting state.\\n\\n| Mermaid | Schematex | Symbol | Purpose |\\n|---|---|---|---|\\n| `[*]` (source) | `initial id` | Filled black circle | Entry point of a region |\\n| `[*]` (target) | `final id` | Filled circle inside outer ring | Successful exit |\\n| `state X <<choice>>` | `choice X` | Diamond | Dynamic branch (guards evaluated at runtime) |\\n| `state X <<fork>>` | `fork X` | Thick black bar | One-input → N parallel outputs |\\n| `state X <<join>>` | `join X` | Thick black bar | N inputs → one output |\\n| — | `junction X` | Small filled circle | Static merge point |\\n| — | `history X` | Circle with `H` | Re-enter at last visited sub-state |\\n| — | `dhistory X` | Circle with `H*` | Deep history (recursive) |\\n| — | `terminate X` | `×` mark | Abnormal termination (no cleanup) |\\n| — | `entry_point X` / `exit_point X` | Hollow circle on composite border | Named entry / exit points |\\n\\n`[*]` is the Mermaid alias and is always direction-resolved: on the **source** side of `-->` it's an `initial`, on the **target** side it's a `final`. Each composite scope gets its own pair.\\n\\n```\\nstateDiagram-v2\\n[*] --> Idle\\nstate Decide <<choice>>\\nstate Joiner <<join>>\\nIdle --> Decide : evaluate\\nDecide --> Authorized : [role == \\\"admin\\\"]\\nDecide --> Joiner : [role == \\\"user\\\"]\\nAuthorized --> Joiner\\nJoiner --> [*]\\n```\\n\\n---\\n\\n## 4. Transitions\\n\\nThe full UML 2.5 transition label has three optional parts:\\n\\n```\\ntrigger [guard] / action\\n```\\n\\n| Field | Meaning | Example |\\n|---|---|---|\\n| `trigger` | Event that fires the transition | `submit`, `tick`, `timeout(30s)` |\\n| `[guard]` | Boolean expression evaluated at trigger time | `[count > 0]`, `[role == \\\"admin\\\"]` |\\n| `/ action` | Action(s) executed when transition is taken | `/ log(); increment()` |\\n\\nAll three are optional — an unlabeled transition is anonymous (completion transition).\\n\\n```\\nA --> B %% anonymous completion\\nA --> B : tick %% trigger only\\nA --> B : [count > 0] %% guard only\\nA --> B : / clearErrors() %% action only\\nA --> B : tick [count > 0] / log() %% all three\\nA --> B : tick, tock [enabled] / handle() %% multi-trigger\\n```\\n\\nLong labels wrap automatically when they exceed the lane width.\\n\\n---\\n\\n## 5. Composite states\\n\\nA composite state contains a nested sub-statechart. The outer state acts as a container with its own initial / final pseudo-states.\\n\\n```\\nstate Playing {\\n [*] --> Buffering\\n Buffering --> Streaming : buffer_full\\n Streaming --> Buffering : underflow\\n}\\n```\\n\\nBoth Mermaid syntax (`state X { … }`) and the Schematex form (`composite X { … }`) are accepted. Activities can be declared inside the composite block:\\n\\n```\\nstate Playing {\\n entry / startBuffer()\\n exit / stopBuffer()\\n do / decodeFrames()\\n\\n [*] --> Buffering\\n Buffering --> Streaming : buffer_full\\n Streaming --> Buffering : underflow\\n}\\n```\\n\\nThe renderer draws composites as a styled cluster with title bar + activity compartment.\\n\\n```\\nstateDiagram-v2\\n\\n[*] --> Stopped\\n\\nStopped --> Playing : play / loadSource()\\nPlaying --> Paused : pause\\nPaused --> Playing : play\\nPlaying --> Stopped : stop / releaseSource()\\n\\nstate Playing {\\n entry / startBuffer()\\n exit / stopBuffer()\\n do / decodeFrames()\\n\\n [*] --> Buffering\\n Buffering --> Streaming : buffer_full\\n Streaming --> Buffering : underflow\\n}\\n```\\n\\nCross-boundary transitions (`Outside --> Inside`) are routed automatically — the Sugiyama layout pulls the source/target through the composite border.\\n\\n---\\n\\n## 6. Concurrent regions\\n\\nInside a composite, the `--` separator (Mermaid) or `---` (Schematex) splits the body into orthogonal regions that run concurrently.\\n\\n```\\nstate Active {\\n [*] --> r1_idle\\n r1_idle --> Connected : connect\\n --\\n [*] --> r2_idle\\n r2_idle --> Working : start\\n}\\n```\\n\\nUse `fork` and `join` to spawn / synchronize across regions:\\n\\n```\\nstateDiagram-v2 [direction: LR]\\n\\n[*] --> F\\nstate F <<fork>>\\nstate J <<join>>\\nF --> A\\nF --> B\\nA --> J : done_a\\nB --> J : done_b\\nJ --> [*]\\n```\\n\\n---\\n\\n## 7. Notes\\n\\nA short annotation can be attached to either side of any state.\\n\\n```\\nnote right of Checking : Calls /api/verify synchronously.\\nnote left of Idle : Anonymous landing state\\n```\\n\\nMulti-line notes use the Mermaid `end note` block form, or the Schematex `{ … }` form:\\n\\n```\\nnote right of Authenticating\\n Stores the JWT in localStorage\\n on success.\\nend note\\n\\nnote left_of Idle {\\n Anonymous landing state.\\n Returns here on 401.\\n}\\n```\\n\\n---\\n\\n## 8. Self-transitions\\n\\nA transition `A --> A` renders as a curved arc on the right side of the node.\\n\\n```\\nIdle --> Idle : poll / refresh()\\n```\\n\\nThe label is placed beside the arc, outside the bounding box.\\n\\n---\\n\\n## 9. Layout direction\\n\\nSchematex defaults to **`TB`** (top-to-bottom), matching Mermaid. Override on the header:\\n\\n```\\nstateDiagram-v2\\ndirection LR\\n\\n[*] --> Loading\\nLoading --> Ready\\n```\\n\\nOr with the Schematex bracket-attr form:\\n\\n```\\nstate \\\"Order Lifecycle\\\" [direction: TB]\\n\\n[*] --> Pending\\nPending --> Paid\\n```\\n\\n`BT` and `RL` are accepted by the parser but normalized to `TB` and `LR` respectively (the layout engine doesn't yet flip the visual order).\\n\\n---\\n\\n## 10. Common mistakes\\n\\n| You wrote | Parser says | Fix |\\n|---|---|---|\\n| `[*] -> [*]` | Treated as both initial alias and final alias on the same line | Always have at least one named state between `[*]` aliases |\\n| `state X <<branch>>` | `branch` is not a stereotype | Use `<<choice>>` (dynamic) or `<<fork>>` / `<<join>>` |\\n| `note right of`<br/>`text` | Multi-line note must end with `end note` | Add `end note` on its own line |\\n| `composite X` (no braces) | Treated as a bare state declaration | Open the block: `composite X {` |\\n| `direction LR` inside composite | Per-region direction not yet supported | Set direction on the header line |\\n\\n---\\n\\n## 11. Grammar (EBNF)\\n\\n```text\\ndocument = header statement*\\nheader = (\\\"stateDiagram-v2\\\" | \\\"stateDiagram\\\" | \\\"state\\\")\\n ( title )? ( \\\"[\\\" attrs \\\"]\\\" )? NEWLINE\\nattrs = attr (\\\",\\\" attr)*\\nattr = \\\"direction:\\\" (\\\"TB\\\" | \\\"LR\\\")\\n\\nstatement = comment\\n | direction-stmt %% direction LR / TB / BT / RL\\n | state-decl\\n | alias-decl %% state \\\"Long\\\" as ID\\n | stereotype-decl %% state ID <<choice|fork|join|end>>\\n | pseudo-decl %% initial / final / choice / ... ID\\n | composite-block %% (state | composite) ID { ... }\\n | label-stmt %% ID : description\\n | transition\\n | note-stmt\\n | region-sep %% -- or ---\\n\\ntransition = (ID | \\\"[*]\\\") \\\"-->\\\" (ID | \\\"[*]\\\") ( \\\":\\\" trans-label )? NEWLINE\\ntrans-label = trigger? ( \\\"[\\\" guard \\\"]\\\" )? ( \\\"/\\\" action )?\\n\\nnote-stmt = \\\"note\\\" side ID \\\":\\\" inline-text NEWLINE\\n | \\\"note\\\" side ID NEWLINE text-line+ (\\\"end note\\\" | \\\"}\\\") NEWLINE\\nside = \\\"left of\\\" | \\\"right of\\\" | \\\"left_of\\\" | \\\"right_of\\\"\\n\\ncomment = \\\"%%\\\" any | \\\"#\\\" any | \\\"//\\\" any\\n\\nID = [A-Za-z_] [A-Za-z0-9_]*\\n```\\n\\nAuthoritative source: `src/diagrams/state/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n },\n \"pid\": {\n \"title\": \"P&ID (Piping & Instrumentation Diagram)\",\n \"content\": \"## 1. Your first P&ID\\n\\nA minimal P&ID has at least one piece of equipment and one process line.\\n\\n```\\npid\\n\\nequip T-1 : tank_atm\\nequip P-1 : pump_centrifugal\\nequip V-1 : vessel_v\\n\\nline L1 from T-1.bottom to P-1.in [size: \\\"2\\\\\\\\\\\"\\\", type: \\\"process\\\"]\\nline L2 from P-1.out to V-1.in [size: \\\"2\\\\\\\\\\\"\\\", type: \\\"process\\\"]\\n```\\n\\nThree rules cover 80% of usage:\\n\\n1. Start the document with `pid` (optional title and `[direction: LR]` attrs).\\n2. Declare each piece of equipment: `equip : <type> [tag: \\\"label\\\"]`.\\n3. Connect them with `line from <equip>.<port> to <equip>.<port> [type: \\\"process\\\", size: \\\"4\\\\\\\"\\\"]`.\\n\\nInstrumentation is added separately with `inst : <category>` plus indented `measures` / `controls` clauses.\\n\\n> Comments use `#` at the start of a line.\\n\\n---\\n\\n## 2. Equipment\\n\\nThe `equip` statement declares process equipment. The catalog follows ISO 10628 / ISA-5.1 conventions.\\n\\n```\\nequip T-101 : tank_atm [tag: \\\"Feed Tank\\\"]\\nequip P-101 : pump_centrifugal\\nequip E-201 : hx_shell_tube [tag: \\\"Overhead Cond\\\"]\\nequip T-201 : column_tray [tag: \\\"Stripper\\\"]\\n```\\n\\n### 2.1 Equipment catalog\\n\\n| Type | Symbol | Purpose |\\n|---|---|---|\\n| `tank_atm` | Cylinder + dome top | Atmospheric storage tank |\\n| `tank_cone_roof` | Cylinder + cone roof | Cone-roof storage tank |\\n| `vessel_v` | Vertical capsule | Vertical pressure vessel |\\n| `vessel_h` | Horizontal capsule | Horizontal pressure vessel |\\n| `sphere` | Filled circle | LPG / ammonia sphere |\\n| `column_tray` | Tall capsule + horizontal tray lines | Distillation tray column |\\n| `column_packed` | Tall capsule + cross-hatch | Packed absorption column |\\n| `hx_shell_tube` | Horizontal capsule + tube bundle | Shell-and-tube heat exchanger |\\n| `hx_air_cooled` | Rectangle + fan circle | Air-cooled (fin-fan) cooler |\\n| `reboiler` | Capsule + parallel tube lines | Kettle reboiler |\\n| `condenser` | Horizontal capsule + tubes | Overhead condenser |\\n| `pump_centrifugal` | Circle + right-side triangle outlet | Centrifugal pump |\\n| `pump_pd` | Circle + internal gears | Positive-displacement pump |\\n| `compressor` | Trapezoid (narrow on right) | Centrifugal compressor |\\n| `blower` | Circle + 3-blade fan | Blower / fan |\\n| `reactor_cstr` | Vertical capsule + agitator | Stirred tank reactor (CSTR) |\\n| `reactor_pfr` | Horizontal capsule + packed bed dots | Plug-flow / fixed-bed reactor |\\n| `filter` | Rectangle + diagonal hatch | Filter |\\n| `cyclone` | Cylinder + cone bottom | Cyclone separator |\\n| `flare` | Tall stack + flame | Flare stack |\\n| `cooling_tower` | Hourglass | Induced-draft cooling tower |\\n\\n### 2.2 Valve catalog\\n\\nValves are equipment that sit on the piping line. Render in `bowtie` style with type-specific actuator decoration.\\n\\n| Type | Decoration | Purpose |\\n|---|---|---|\\n| `valve_gate` | Plain bowtie | Manual on/off (full-port) |\\n| `valve_ball` | Bowtie + filled center circle | Manual on/off (quarter-turn) |\\n| `valve_globe` | Bowtie + small top circle | Manual flow control |\\n| `valve_butterfly` | Bowtie + center vertical line | Quarter-turn throttle |\\n| `valve_check` | Bowtie + arc | Non-return check valve |\\n| `valve_control` | Bowtie + diaphragm actuator | Pneumatic control valve (paired with FIC) |\\n| `valve_psv` | Bowtie + 45° outlet + spring stack | Pressure safety relief valve |\\n\\n```\\nequip V-101 : valve_control [tag: \\\"V-101 (FC)\\\"]\\nequip V-303 : valve_psv [tag: \\\"V-303 · 150 psig\\\"]\\n```\\n\\n---\\n\\n## 3. Piping & signal lines\\n\\nThe `line` statement connects two anchor points (equipment ports or instrument tags).\\n\\n```\\nline L1 from T-101.bottom to P-101.in [size: \\\"4\\\\\\\"\\\", service: \\\"water\\\", type: \\\"process\\\"]\\nline s1 from FT-101 to FIC-101 [type: \\\"electric\\\"]\\nline s2 from FIC-101 to V-101 [type: \\\"pneumatic\\\"]\\n```\\n\\n### 3.1 Anchor syntax\\n\\nEach end of a line is either:\\n- `<equip-id>.<port>` — port name from §2.2 (`in`, `out`, `top`, `bottom`, `feed`, `shell_in`, `tube_out`, `reflux`, etc.)\\n- `<equip-id>` — port omitted; defaults to `in` (target) / `out` (source) per equipment family\\n- `<inst-tag>` — instrument bubble center (signal lines)\\n\\n### 3.2 Line types (ISA-5.1 §5)\\n\\n| `type:` | Stroke | Use |\\n|---|---|---|\\n| `process` | Solid, thick | Major process line (default) |\\n| `process_minor` | Solid, thin | Auxiliary / utility |\\n| `pneumatic` | Solid + diagonal tick marks | Air-actuator signal |\\n| `electric` | Long-dash | Electric / 4–20 mA signal |\\n| `hydraulic` | Long-dash + pause | Hydraulic actuator |\\n| `capillary` | Dotted (round caps) | Filled-system temperature |\\n| `software` | Short-dash, light | DCS / PLC internal data link |\\n| `mechanical` | Mixed dash | Mechanical linkage |\\n\\n### 3.3 Line tags\\n\\nThe standard PIP PIC001 tag format is `<size>\\\"-<service>-<sequence>-<spec>`. Pass it via the `tag:` attribute and the renderer places a small white tag rectangle at the line midpoint.\\n\\n```\\nline L1 from T-101.bottom to P-101.in [size: \\\"4\\\\\\\"\\\", service: \\\"PG\\\", tag: \\\"4\\\\\\\"-PG-101-A1B\\\"]\\n```\\n\\n---\\n\\n## 4. Instrumentation (ISA-5.1 §4)\\n\\nThe `inst` statement declares an instrument bubble. The **tag** uses the ISA letter-code convention: first letter is the *measured variable*, subsequent letters are *modifiers* and *function*.\\n\\n```\\ninst FT-101 : field_discrete %% Flow Transmitter, loop 101\\ninst FIC-101 : cr_shared %% Flow Indicating Controller (DCS)\\ninst PSHH-301: cr_plc %% Pressure Switch High-High (PLC)\\ninst LIC-201 : cr_shared\\n measures D-201\\n controls V-202\\n```\\n\\n### 4.1 Letter codes (first letter)\\n\\nMost-used: `F` flow · `L` level · `P` pressure · `T` temperature · `A` analysis · `S` speed · `H` hand · `Y` event/state. Full list in ISA-5.1 Table 1.\\n\\n### 4.2 Function modifiers\\n\\n`I` indicator · `R` recorder · `C` controller · `T` transmitter · `E` element · `V` valve · `S` switch · `A` alarm · `H`/`L` high/low. Combine into the multi-letter tag: `FIC` = Flow Indicating Controller; `PSHH` = Pressure Switch High-High.\\n\\n### 4.3 Bubble categories\\n\\nISA-5.1 distinguishes **location** (where the instrument lives) and **type** (analog vs. shared vs. computer vs. PLC). Schematex implements the four most common combinations:\\n\\n| Category | Bubble shape | Use |\\n|---|---|---|\\n| `field_discrete` | Plain circle | Field-mounted analog instrument (FT, PT) |\\n| `cr_shared` | Circle + horizontal line + inscribed hexagon | DCS-controlled HMI display |\\n| `cr_computer` | Circle + horizontal line + inscribed diamond | Computer function (FY, calculation) |\\n| `cr_plc` | Circle + horizontal line + inscribed square | PLC-driven logic |\\n\\n`field_*` variants omit the horizontal centerline; `local_*` variants use a dashed centerline; `cr_*` variants use a solid centerline indicating \\\"main control panel — front.\\\"\\n\\n### 4.4 measures / controls\\n\\nIndented under an `inst` declaration:\\n\\n| Clause | Effect |\\n|---|---|\\n| `measures <equip-id>` | Auto-routed dashed-electric signal line from the equipment to the bubble |\\n| `controls <equip-id>` | Auto-routed pneumatic signal line from the bubble to the equipment (typically a `valve_control`) |\\n\\n```\\ninst FT-101 : field_discrete\\n measures P-101\\ninst FIC-101 : cr_shared\\n controls V-101\\n```\\n\\nThese auto-signals are independent of the explicit `line` statements — they get rendered with the appropriate signal-line style based on the relation type.\\n\\n---\\n\\n## 5. Layout direction\\n\\nThe default direction is **`LR`** (left-to-right) — process feed enters on the left, product exits on the right. Override on the header:\\n\\n```\\npid \\\"Distillation Tower\\\" [direction: TB]\\n\\nequip T-201 : column_tray\\n…\\n```\\n\\nThe MVP layout places equipment in declaration order along the primary direction with Manhattan signal-line routing. **Multi-row / parallel-flow layouts and tee junctions are roadmap items** — see §9.\\n\\n---\\n\\n## 6. Worked example: Distillation column\\n\\nA real overhead-condenser loop with reboiler, reflux drum, and instrumentation:\\n\\n```\\npid \\\"Distillation T-201\\\"\\n\\nequip T-201 : column_tray [tag: \\\"T-201\\\"]\\nequip E-201 : condenser [tag: \\\"Overhead Cond\\\"]\\nequip D-201 : vessel_h [tag: \\\"Reflux Drum\\\"]\\nequip P-201 : pump_centrifugal [tag: \\\"Reflux Pump\\\"]\\nequip E-202 : reboiler [tag: \\\"Reboiler\\\"]\\n\\nline L1 from T-201.top to E-201.shell_in [size: \\\"8\\\\\\\\\\\"\\\", service: \\\"vapor\\\", type: \\\"process\\\"]\\nline L2 from E-201.shell_out to D-201.in [size: \\\"8\\\\\\\\\\\"\\\", type: \\\"process\\\"]\\nline L3 from D-201.bottom to P-201.in [size: \\\"3\\\\\\\\\\\"\\\", type: \\\"process\\\"]\\nline L4 from P-201.out to T-201.reflux [size: \\\"3\\\\\\\\\\\"\\\", type: \\\"process\\\"]\\nline L5 from T-201.bottom to E-202.in [size: \\\"6\\\\\\\\\\\"\\\", type: \\\"process\\\"]\\n\\ninst PT-201 : field_discrete\\n measures T-201\\ninst LIC-201 : cr_shared\\n measures D-201\\ninst TIC-201 : cr_shared\\n measures T-201\\n```\\n\\n---\\n\\n## 7. Grammar (EBNF)\\n\\n```text\\ndocument = header statement*\\nheader = \\\"pid\\\" ( title )? ( \\\"[\\\" attrs \\\"]\\\" )? NEWLINE\\nattrs = attr (\\\",\\\" attr)*\\nattr = \\\"direction:\\\" (\\\"LR\\\" | \\\"TB\\\")\\n | \\\"units:\\\" (\\\"imperial\\\" | \\\"metric\\\")\\n\\nstatement = comment\\n | equipment-decl\\n | line-decl\\n | instrument-decl\\n\\nequipment-decl = \\\"equip\\\" ID \\\":\\\" equip-type ( \\\"[\\\" attr-list \\\"]\\\" )? NEWLINE\\nequip-type = \\\"tank_atm\\\" | \\\"tank_cone_roof\\\"\\n | \\\"vessel_v\\\" | \\\"vessel_h\\\" | \\\"sphere\\\"\\n | \\\"column_tray\\\" | \\\"column_packed\\\"\\n | \\\"hx_shell_tube\\\" | \\\"hx_air_cooled\\\" | \\\"reboiler\\\" | \\\"condenser\\\"\\n | \\\"pump_centrifugal\\\" | \\\"pump_pd\\\"\\n | \\\"compressor\\\" | \\\"blower\\\"\\n | \\\"reactor_cstr\\\" | \\\"reactor_pfr\\\"\\n | \\\"filter\\\" | \\\"cyclone\\\" | \\\"flare\\\" | \\\"cooling_tower\\\"\\n | \\\"valve_gate\\\" | \\\"valve_ball\\\" | \\\"valve_globe\\\" | \\\"valve_butterfly\\\"\\n | \\\"valve_check\\\" | \\\"valve_control\\\" | \\\"valve_psv\\\"\\n\\nline-decl = \\\"line\\\" ID \\\"from\\\" anchor \\\"to\\\" anchor ( \\\"[\\\" attr-list \\\"]\\\" )? NEWLINE\\nanchor = ID ( \\\".\\\" port )?\\nport = \\\"in\\\" | \\\"out\\\" | \\\"top\\\" | \\\"bottom\\\" | \\\"left\\\" | \\\"right\\\"\\n | \\\"feed\\\" | \\\"reflux\\\" | \\\"shell_in\\\" | \\\"shell_out\\\"\\n | \\\"tube_in\\\" | \\\"tube_out\\\" | \\\"vapor_out\\\" | \\\"liquid_out\\\"\\n | \\\"bottom_return\\\"\\n\\ninstrument-decl = \\\"inst\\\" tag \\\":\\\" inst-category ( \\\"[\\\" attr-list \\\"]\\\" )? NEWLINE\\n ( indented \\\"measures\\\" anchor NEWLINE )*\\n ( indented \\\"controls\\\" ID NEWLINE )*\\ntag = letter-code \\\"-\\\" loop-num %% e.g., \\\"FIC-101\\\"\\ninst-category = \\\"field_discrete\\\" | \\\"field_shared\\\" | \\\"field_computer\\\" | \\\"field_plc\\\"\\n | \\\"cr_discrete\\\" | \\\"cr_shared\\\" | \\\"cr_computer\\\" | \\\"cr_plc\\\"\\n | \\\"local_discrete\\\" | \\\"local_shared\\\"\\n\\nattr-list = attr (\\\",\\\" attr)*\\nattr = key \\\":\\\" value\\nkey = \\\"tag\\\" | \\\"size\\\" | \\\"service\\\" | \\\"type\\\" | \\\"set_pressure\\\"\\n | \\\"actuator\\\" | \\\"fail\\\" | \\\"trays\\\" | …\\nvalue = quoted-string | bare-word\\n\\nID = [A-Za-z] [A-Za-z0-9_-]*\\n```\\n\\nAuthoritative source: `src/diagrams/pid/parser.ts`. If this diverges from the parser, the parser wins — please open an issue.\\n\\n---\"\n }\n};\n","/**\n * Example library — runtime lookup over the bundled MDX examples.\n */\nimport { EXAMPLES, type GeneratedExample } from \"./_generated\";\n\nexport type Example = GeneratedExample;\n\nexport interface GetExamplesOptions {\n /** Maximum number of examples to return. Default 5. */\n limit?: number;\n /** Prefer examples marked `featured: true` when set. */\n preferFeatured?: boolean;\n /** Maximum complexity (1–5). */\n maxComplexity?: number;\n}\n\n/**\n * Normalise a diagram-registry type to the diagram key used in example\n * frontmatter. Most are identical; a few legacy keys differ.\n */\nfunction normaliseDiagramKey(type: string): string[] {\n // The frontmatter uses short keys like \"block\" while the plugin type is\n // \"blockdiagram\". Return all aliases to match on.\n switch (type) {\n case \"blockdiagram\":\n return [\"block\", \"blockdiagram\"];\n default:\n return [type];\n }\n}\n\nexport function getExamplesForType(\n type: string,\n opts: GetExamplesOptions = {}\n): Example[] {\n const keys = normaliseDiagramKey(type);\n const all = EXAMPLES.filter((e) => keys.includes(e.diagram));\n let filtered = all;\n const maxComplexity = opts.maxComplexity;\n if (typeof maxComplexity === \"number\") {\n filtered = filtered.filter((e) => e.complexity <= maxComplexity);\n }\n // Featured first when requested, then by complexity ascending.\n const sorted = [...filtered].sort((a, b) => {\n if (opts.preferFeatured) {\n if (a.featured !== b.featured) return a.featured ? -1 : 1;\n }\n return a.complexity - b.complexity;\n });\n const limit = opts.limit ?? 5;\n return sorted.slice(0, limit);\n}\n\nexport function listAllExampleSlugs(): string[] {\n return EXAMPLES.map((e) => e.slug);\n}\n","/**\n * Syntax lookup — LLM-facing per-diagram grammar reference.\n *\n * v1: returns the stripped-MDX content from `website/content/docs/*.mdx`\n * (JSX components replaced with fenced DSL code blocks).\n *\n * Later: may be replaced with curated compact summaries per diagram if\n * the stripped docs prove too long for good LLM performance.\n */\nimport { SYNTAX, type GeneratedSyntax } from \"./_generated\";\n\nexport type SyntaxDoc = GeneratedSyntax & { key: string };\n\nexport function getSyntaxForType(syntaxKey: string): SyntaxDoc | undefined {\n const s = SYNTAX[syntaxKey];\n if (!s) return undefined;\n return { key: syntaxKey, ...s };\n}\n\nexport function listSyntaxKeys(): string[] {\n return Object.keys(SYNTAX);\n}\n","/**\n * AI-facing tool functions — the five tools an LLM uses to work with Schematex.\n *\n * Pure TypeScript, zero framework deps. Both the Vercel AI SDK adapter\n * (ai-sdk.ts) and the MCP server wrap these functions.\n */\nimport { parse, render, type SchematexConfig } from \"../core/api\";\nimport {\n DIAGRAM_REGISTRY,\n getDiagramMeta,\n type DiagramMeta,\n} from \"./registry\";\nimport { extractError, type SchematexValidationError } from \"./errors\";\nimport { getExamplesForType, type Example, type GetExamplesOptions } from \"./examples\";\nimport { getSyntaxForType, type SyntaxDoc } from \"./syntax\";\n\n// ─── listDiagrams ───────────────────────────────────────────────\n\nexport interface DiagramListItem {\n type: string;\n name: string;\n tagline: string;\n useWhen: string;\n cluster: DiagramMeta[\"cluster\"];\n standard: string;\n}\n\nexport function listDiagrams(): DiagramListItem[] {\n return DIAGRAM_REGISTRY.map((d) => ({\n type: d.type,\n name: d.name,\n tagline: d.tagline,\n useWhen: d.useWhen,\n cluster: d.cluster,\n standard: d.standard,\n }));\n}\n\n// ─── getSyntax ──────────────────────────────────────────────────\n\nexport interface GetSyntaxResult {\n type: string;\n name: string;\n standard: string;\n syntax: SyntaxDoc;\n}\n\nexport function getSyntax(type: string): GetSyntaxResult {\n const meta = getDiagramMeta(type);\n if (!meta) {\n throw new Error(\n `Unknown diagram type '${type}'. Call listDiagrams() for valid types.`\n );\n }\n const syntax = getSyntaxForType(meta.syntaxKey);\n if (!syntax) {\n throw new Error(`No syntax doc available for '${type}' (key: ${meta.syntaxKey}).`);\n }\n return {\n type: meta.type,\n name: meta.name,\n standard: meta.standard,\n syntax,\n };\n}\n\n// ─── getExamples ────────────────────────────────────────────────\n\nexport interface GetExamplesResult {\n type: string;\n count: number;\n examples: Example[];\n}\n\nexport function getExamples(\n type: string,\n opts: GetExamplesOptions = {}\n): GetExamplesResult {\n const meta = getDiagramMeta(type);\n if (!meta) {\n throw new Error(\n `Unknown diagram type '${type}'. Call listDiagrams() for valid types.`\n );\n }\n const examples = getExamplesForType(meta.type, opts);\n return { type: meta.type, count: examples.length, examples };\n}\n\n// ─── validateDsl ────────────────────────────────────────────────\n\nexport type ValidateDslResult =\n | { ok: true; type: string | null }\n | { ok: false; type: string | null; errors: SchematexValidationError[] };\n\nexport function validateDsl(type: string | undefined, dsl: string): ValidateDslResult {\n const config: SchematexConfig | undefined = type\n ? { type: type as SchematexConfig[\"type\"] }\n : undefined;\n try {\n parse(dsl, config);\n return { ok: true, type: type ?? resolveTypeFromText(dsl) };\n } catch (err) {\n return {\n ok: false,\n type: type ?? resolveTypeFromText(dsl),\n errors: [extractError(err)],\n };\n }\n}\n\n// ─── renderDsl ──────────────────────────────────────────────────\n\nexport type RenderDslResult =\n | { ok: true; type: string | null; svg: string }\n | { ok: false; type: string | null; errors: SchematexValidationError[] };\n\nexport function renderDsl(\n type: string | undefined,\n dsl: string,\n options: Omit<SchematexConfig, \"type\"> = {}\n): RenderDslResult {\n const config: SchematexConfig = {\n ...options,\n ...(type ? { type: type as SchematexConfig[\"type\"] } : {}),\n };\n try {\n const svg = render(dsl, config);\n return { ok: true, type: type ?? resolveTypeFromText(dsl), svg };\n } catch (err) {\n return {\n ok: false,\n type: type ?? resolveTypeFromText(dsl),\n errors: [extractError(err)],\n };\n }\n}\n\n// ─── helpers ────────────────────────────────────────────────────\n\nfunction resolveTypeFromText(text: string): string | null {\n const first = text.trim().split(/\\s+|\\n/)[0]?.toLowerCase() ?? \"\";\n const meta = DIAGRAM_REGISTRY.find((d) => d.type === first);\n return meta?.type ?? null;\n}\n"]}
|