schematex 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/README.md +24 -3
  2. package/dist/ai/ai-sdk.cjs +17 -15
  3. package/dist/ai/ai-sdk.cjs.map +1 -1
  4. package/dist/ai/ai-sdk.d.cts +2 -2
  5. package/dist/ai/ai-sdk.d.ts +2 -2
  6. package/dist/ai/ai-sdk.js +12 -10
  7. package/dist/ai/ai-sdk.js.map +1 -1
  8. package/dist/ai/index.cjs +20 -18
  9. package/dist/ai/index.d.cts +3 -3
  10. package/dist/ai/index.d.ts +3 -3
  11. package/dist/ai/index.js +12 -10
  12. package/dist/{api-BIj9t4Oc.d.cts → api-C5UcmT7n.d.cts} +1 -1
  13. package/dist/{api-BIj9t4Oc.d.ts → api-C5UcmT7n.d.ts} +1 -1
  14. package/dist/browser.cjs +14 -12
  15. package/dist/browser.cjs.map +1 -1
  16. package/dist/browser.d.cts +2 -2
  17. package/dist/browser.d.ts +2 -2
  18. package/dist/browser.js +12 -10
  19. package/dist/browser.js.map +1 -1
  20. package/dist/chunk-2KTQ75LN.js +67 -0
  21. package/dist/chunk-2KTQ75LN.js.map +1 -0
  22. package/dist/chunk-3KRL2EGN.cjs +70 -0
  23. package/dist/chunk-3KRL2EGN.cjs.map +1 -0
  24. package/dist/{chunk-YWDODCW2.js → chunk-3M6T7KB4.js} +60 -14
  25. package/dist/chunk-3M6T7KB4.js.map +1 -0
  26. package/dist/{chunk-OIZ4MYSF.cjs → chunk-3M6WB62Y.cjs} +43 -9
  27. package/dist/chunk-3M6WB62Y.cjs.map +1 -0
  28. package/dist/{chunk-DHHVYSQX.cjs → chunk-3YUUC6RN.cjs} +5145 -295
  29. package/dist/chunk-3YUUC6RN.cjs.map +1 -0
  30. package/dist/{chunk-JDBG5DU2.js → chunk-6OSUNBZY.js} +43 -9
  31. package/dist/chunk-6OSUNBZY.js.map +1 -0
  32. package/dist/{chunk-O5POCPXZ.cjs → chunk-EGSUMHCS.cjs} +21 -10
  33. package/dist/chunk-EGSUMHCS.cjs.map +1 -0
  34. package/dist/{chunk-Z5NGIM4Z.js → chunk-GTDQAN2Z.js} +5134 -287
  35. package/dist/chunk-GTDQAN2Z.js.map +1 -0
  36. package/dist/{chunk-J7JWMQD5.js → chunk-HUPDIRBX.js} +119 -4
  37. package/dist/chunk-HUPDIRBX.js.map +1 -0
  38. package/dist/{chunk-5FF2IT3C.js → chunk-IBRW3UOA.js} +51 -10
  39. package/dist/chunk-IBRW3UOA.js.map +1 -0
  40. package/dist/{chunk-YTLGK5BZ.js → chunk-IM4RCUHA.js} +238 -1788
  41. package/dist/chunk-IM4RCUHA.js.map +1 -0
  42. package/dist/{chunk-LPKVIRYT.js → chunk-JHDR56XO.js} +18 -9
  43. package/dist/chunk-JHDR56XO.js.map +1 -0
  44. package/dist/{chunk-GZY4CJ23.cjs → chunk-N7W5KZK7.cjs} +51 -10
  45. package/dist/chunk-N7W5KZK7.cjs.map +1 -0
  46. package/dist/{chunk-Z5UECSNM.cjs → chunk-NWPCY65Z.cjs} +240 -1790
  47. package/dist/chunk-NWPCY65Z.cjs.map +1 -0
  48. package/dist/chunk-PIV2A3HG.js +55 -0
  49. package/dist/chunk-PIV2A3HG.js.map +1 -0
  50. package/dist/{chunk-E2EJRVWS.cjs → chunk-QTNPMIO2.cjs} +158 -15
  51. package/dist/chunk-QTNPMIO2.cjs.map +1 -0
  52. package/dist/chunk-SBB6C4OP.cjs +57 -0
  53. package/dist/chunk-SBB6C4OP.cjs.map +1 -0
  54. package/dist/{chunk-K6RAUXRQ.cjs → chunk-TZTCIAYW.cjs} +7 -6
  55. package/dist/chunk-TZTCIAYW.cjs.map +1 -0
  56. package/dist/{chunk-KVP7AORH.js → chunk-UFTYX73U.js} +158 -15
  57. package/dist/chunk-UFTYX73U.js.map +1 -0
  58. package/dist/{chunk-6WORZSL3.cjs → chunk-VFZOPRQP.cjs} +60 -14
  59. package/dist/chunk-VFZOPRQP.cjs.map +1 -0
  60. package/dist/{chunk-WHNIK4LK.js → chunk-VJGMEGMR.js} +7 -6
  61. package/dist/chunk-VJGMEGMR.js.map +1 -0
  62. package/dist/{chunk-3MJKJX27.cjs → chunk-VZ5LDNHK.cjs} +18 -9
  63. package/dist/chunk-VZ5LDNHK.cjs.map +1 -0
  64. package/dist/{chunk-2Z543TC3.cjs → chunk-XRCY75UV.cjs} +119 -4
  65. package/dist/chunk-XRCY75UV.cjs.map +1 -0
  66. package/dist/{chunk-6XGSEG3K.js → chunk-ZL5RB4UV.js} +21 -10
  67. package/dist/chunk-ZL5RB4UV.js.map +1 -0
  68. package/dist/diagrams/blockdiagram/index.d.cts +1 -1
  69. package/dist/diagrams/blockdiagram/index.d.ts +1 -1
  70. package/dist/diagrams/circuit/index.cjs +7 -7
  71. package/dist/diagrams/circuit/index.d.cts +1 -1
  72. package/dist/diagrams/circuit/index.d.ts +1 -1
  73. package/dist/diagrams/circuit/index.js +1 -1
  74. package/dist/diagrams/ecomap/index.cjs +7 -6
  75. package/dist/diagrams/ecomap/index.d.cts +1 -1
  76. package/dist/diagrams/ecomap/index.d.ts +1 -1
  77. package/dist/diagrams/ecomap/index.js +2 -1
  78. package/dist/diagrams/entity/index.d.cts +1 -1
  79. package/dist/diagrams/entity/index.d.ts +1 -1
  80. package/dist/diagrams/fishbone/index.cjs +8 -7
  81. package/dist/diagrams/fishbone/index.d.cts +1 -1
  82. package/dist/diagrams/fishbone/index.d.ts +1 -1
  83. package/dist/diagrams/fishbone/index.js +2 -1
  84. package/dist/diagrams/flowchart/index.cjs +8 -7
  85. package/dist/diagrams/flowchart/index.d.cts +2 -2
  86. package/dist/diagrams/flowchart/index.d.ts +2 -2
  87. package/dist/diagrams/flowchart/index.js +2 -1
  88. package/dist/diagrams/genogram/index.cjs +9 -8
  89. package/dist/diagrams/genogram/index.d.cts +1 -1
  90. package/dist/diagrams/genogram/index.d.ts +1 -1
  91. package/dist/diagrams/genogram/index.js +2 -1
  92. package/dist/diagrams/ladder/index.cjs +7 -5
  93. package/dist/diagrams/ladder/index.d.cts +1 -1
  94. package/dist/diagrams/ladder/index.d.ts +1 -1
  95. package/dist/diagrams/ladder/index.js +3 -1
  96. package/dist/diagrams/logic/index.d.cts +1 -1
  97. package/dist/diagrams/logic/index.d.ts +1 -1
  98. package/dist/diagrams/orgchart/index.d.cts +1 -1
  99. package/dist/diagrams/orgchart/index.d.ts +1 -1
  100. package/dist/diagrams/pedigree/index.cjs +7 -6
  101. package/dist/diagrams/pedigree/index.d.cts +1 -1
  102. package/dist/diagrams/pedigree/index.d.ts +1 -1
  103. package/dist/diagrams/pedigree/index.js +2 -1
  104. package/dist/diagrams/phylo/index.d.cts +1 -1
  105. package/dist/diagrams/phylo/index.d.ts +1 -1
  106. package/dist/diagrams/sld/index.cjs +7 -5
  107. package/dist/diagrams/sld/index.d.cts +1 -1
  108. package/dist/diagrams/sld/index.d.ts +1 -1
  109. package/dist/diagrams/sld/index.js +3 -1
  110. package/dist/diagrams/sociogram/index.d.cts +1 -1
  111. package/dist/diagrams/sociogram/index.d.ts +1 -1
  112. package/dist/diagrams/timing/index.d.cts +1 -1
  113. package/dist/diagrams/timing/index.d.ts +1 -1
  114. package/dist/diagrams/venn/index.d.cts +1 -1
  115. package/dist/diagrams/venn/index.d.ts +1 -1
  116. package/dist/{index-CGK0xVls.d.ts → index-C9A0h-CB.d.cts} +1 -2
  117. package/dist/{index-OW8eDrKj.d.cts → index-CJai_TEZ.d.ts} +1 -2
  118. package/dist/index.cjs +37 -23
  119. package/dist/index.d.cts +10 -4
  120. package/dist/index.d.ts +10 -4
  121. package/dist/index.js +11 -9
  122. package/dist/react.cjs +12 -10
  123. package/dist/react.cjs.map +1 -1
  124. package/dist/react.d.cts +1 -1
  125. package/dist/react.d.ts +1 -1
  126. package/dist/react.js +11 -9
  127. package/dist/react.js.map +1 -1
  128. package/dist/{types-Gkyab1sL.d.ts → types-BOAsqHoU.d.cts} +3 -3
  129. package/dist/{types-Gkyab1sL.d.cts → types-BOAsqHoU.d.ts} +3 -3
  130. package/package.json +2 -2
  131. package/dist/chunk-2Z543TC3.cjs.map +0 -1
  132. package/dist/chunk-3MJKJX27.cjs.map +0 -1
  133. package/dist/chunk-5FF2IT3C.js.map +0 -1
  134. package/dist/chunk-6WORZSL3.cjs.map +0 -1
  135. package/dist/chunk-6XGSEG3K.js.map +0 -1
  136. package/dist/chunk-DHHVYSQX.cjs.map +0 -1
  137. package/dist/chunk-E2EJRVWS.cjs.map +0 -1
  138. package/dist/chunk-GZY4CJ23.cjs.map +0 -1
  139. package/dist/chunk-J7JWMQD5.js.map +0 -1
  140. package/dist/chunk-JDBG5DU2.js.map +0 -1
  141. package/dist/chunk-K6RAUXRQ.cjs.map +0 -1
  142. package/dist/chunk-KVP7AORH.js.map +0 -1
  143. package/dist/chunk-LPKVIRYT.js.map +0 -1
  144. package/dist/chunk-O5POCPXZ.cjs.map +0 -1
  145. package/dist/chunk-OIZ4MYSF.cjs.map +0 -1
  146. package/dist/chunk-WHNIK4LK.js.map +0 -1
  147. package/dist/chunk-YTLGK5BZ.js.map +0 -1
  148. package/dist/chunk-YWDODCW2.js.map +0 -1
  149. package/dist/chunk-Z5NGIM4Z.js.map +0 -1
  150. package/dist/chunk-Z5UECSNM.cjs.map +0 -1
@@ -0,0 +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":[],"mappings":"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","file":"chunk-IM4RCUHA.js","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 | \"research\"\n | \"project-management\";\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: \"fbd\",\n name: \"Function Block Diagram (FBD)\",\n tagline: \"IEC 61131-3 §6.4 function blocks wired through named ports.\",\n useWhen:\n \"Use for PLC programs that are easier to read as data-flow than as ladder rungs — boolean logic (AND/OR/NOT/NAND/NOR/XOR), timers (TON/TOF/TP), counters (CTU/CTD), edge detectors (R_TRIG/F_TRIG), comparison (EQ/NE/GT/GE/LT/LE), math (ADD/SUB/MUL/DIV/MOVE), selection (SEL/MUX/MAX/MIN/LIMIT). Inline expression notation `Out = OR(A, AND(B, C))`. Sister language to `ladder` (§10) and `sfc` (§24); together they form the visual half of IEC 61131-3.\",\n cluster: \"electrical-industrial\",\n standard: \"IEC 61131-3:2013 §6.4 + §2.5 standard FB library; see 23-FBD-STANDARD.md\",\n syntaxKey: \"fbd\",\n },\n {\n type: \"sfc\",\n name: \"Sequential Function Chart (SFC)\",\n tagline: \"IEC 61131-3 §6.5 step + transition state machine for cyclic PLC sequences.\",\n useWhen:\n \"Use for PLC sequential control — batch reactors, robotic cells, packaging lines, assembly stations — where the program has explicit phases that hand off to each other on boolean conditions. Steps with action qualifiers (N/S/R/L/D/P), transitions with conditions, alternative branches (single bar, OR semantics) and simultaneous branches (double bar, AND semantics), jumps for loops. Distinct from `state` (UML — Schematex `state` covers reactive UI/lifecycle FSMs, not cyclic PLC scans) and from `flowchart` (no bars, no qualifiers).\",\n cluster: \"electrical-industrial\",\n standard: \"IEC 61131-3:2013 §6.5 + IEC 60848 GRAFCET visual subset; see 24-SFC-STANDARD.md\",\n syntaxKey: \"sfc\",\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 {\n type: \"breadboard\",\n name: \"Breadboard / Physical wiring\",\n tagline: \"Fritzing-style breadboard view — physical wiring of Arduino / ESP32 / Pi prototypes.\",\n useWhen:\n \"Use for maker / Arduino / ESP32 / Raspberry Pi tutorials and lab handouts where the user wants to see *how to physically wire components on a breadboard* — not the abstract circuit schematic. Address tie-points by `@col-row` (e.g. `@5e`, `@+t8`). Smooth Bézier jumper-wires with conventional colors. Distinct from `circuit` (IEEE 315 schematic — same prototype, different view).\",\n cluster: \"electrical-industrial\",\n standard: \"Fritzing visual conventions + Wokwi DSL precedent (no ISO standard exists for this view; see 26-BREADBOARD-STANDARD.md)\",\n syntaxKey: \"breadboard\",\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. NOT for database schema diagrams — use `erd` for those.\",\n cluster: \"corporate-legal\",\n standard: \"Tier-based ownership hierarchy\",\n syntaxKey: \"entity\",\n },\n // ── Data modeling ────────────────────────────────────────────\n {\n type: \"erd\",\n name: \"Entity-Relationship Diagram (ERD)\",\n tagline: \"Database schema diagram (crow's-foot tabular entities + cardinality glyphs).\",\n useWhen:\n \"Use for relational database schema diagrams — tables, columns, primary/foreign keys, and cardinality (1..1 / 0..N / 1..N) between tables. DBML-like text DSL plus Mermaid `}o--||` glyph aliases. Distinct from `entity` (which is for corporate/legal ownership). v0.1 supports crow's-foot only; Chen and Barker notations are deferred.\",\n cluster: \"corporate-legal\",\n standard: \"Chen 1976 / Everest 1976 (crow's foot) — implements the crow's-foot subset; see 27-ERD-STANDARD.md\",\n syntaxKey: \"erd\",\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 {\n type: \"bpmn\",\n name: \"BPMN business process\",\n tagline: \"OMG BPMN 2.0 — pools and lanes, events, gateways, tasks for organizational processes.\",\n useWhen:\n \"Use for business processes that span multiple roles, departments, or systems — claims handling, hiring, order-to-cash, incident response, ISO-9001 / SOX audits. Pools = participants, lanes = roles, events = start/intermediate/end, gateways = XOR/AND/OR/event-based branches, message flows cross pool boundaries (`~~>`). Distinct from `flowchart` (no pools/lanes/event taxonomy), `state` (mode-centric, not activity-centric), and `pid` (physical equipment, not organisational work).\",\n cluster: \"behavior-modeling\",\n standard: \"OMG BPMN 2.0.2 / ISO/IEC 19510:2013 visual subset; see 25-BPMN-STANDARD.md\",\n syntaxKey: \"bpmn\",\n },\n {\n type: \"usecase\",\n name: \"UML use case diagram\",\n tagline: \"UML 2.5 use case — actors, use cases, subject boundary, include/extend/generalization.\",\n useWhen:\n \"Use for software-engineering requirements and scope diagrams — 'what does this system do, and for whom'. Actors (stick figures, or `(external)` rectangles for other systems) sit outside a subject boundary; use cases are ellipses inside it. `--` association, `..>` «include» (source includes target), `<..` «extend» (left extends right, with optional `[condition]` and extension points), `--|>` generalization (hollow triangle to parent, between actors or between use cases). Accepts a PlantUML-style inline form (`:Actor:`, `(Use case)`, `as ID`). Distinct from `state` (intra-object behavior, not system scope), `flowchart` (no actor/subject/include-extend semantics), and `bpmn` (how a process executes, not what a system offers).\",\n cluster: \"behavior-modeling\",\n standard: \"OMG UML 2.5.1 §18 visual subset; see 29-USECASE-STANDARD.md\",\n syntaxKey: \"usecase\",\n },\n // ── Research / evidence synthesis ────────────────────────────\n {\n type: \"prisma\",\n name: \"PRISMA 2020 flow diagram\",\n tagline: \"PRISMA 2020 — the mandatory four-row flow diagram for systematic reviews and meta-analyses.\",\n useWhen:\n \"Use whenever the user mentions 'PRISMA', 'systematic review', 'meta-analysis flow', 'scoping review', 'evidence screening', or 'Cochrane review' — this is the dedicated, standards-correct engine (prefer it over a generic flowchart). The author writes record counts and exclusion reasons; the rigid four-row layout (Identification → Screening → Eligibility → Included) is correct by construction, with mandatory `n = …` counts, parallel exclusion side-boxes, and an optional second 'other methods' column (`mode: 2020-dual`). Vocabulary swaps for scoping reviews (`kind: scoping-review`) and IPD (`kind: ipd`). Count arithmetic is validated (`validate-counts: warn|strict|off`). Distinct from `flowchart` (no mandatory stages/counts/exclusion-box convention).\",\n cluster: \"research\",\n standard: \"PRISMA 2020 (Page MJ et al., BMJ 2021;372:n71); see 28-PRISMA-STANDARD.md\",\n syntaxKey: \"prisma\",\n },\n // ── Project management / scheduling ──────────────────────────\n {\n type: \"pert\",\n name: \"PERT / CPM network\",\n tagline:\n \"Activity-on-node project schedule that computes ES/EF/LS/LF, slack, and the critical path.\",\n useWhen:\n \"Use whenever the user mentions 'PERT', 'CPM', 'critical path', 'project network', 'precedence diagram', or wants a project schedule from tasks + durations + dependencies. Unlike a flowchart, this engine *computes* the schedule: write `task <id> \\\"label\\\" duration: <n> after: <preds>` and it runs the forward/backward pass and returns Early/Late Start & Finish, total slack, project duration, and highlights the critical path in red. Supports PDM dependency types (FS/SS/FF/SF) with lag/lead (`after: A SS+2d`), three-point estimation (`duration: 4/6/10` → te + variance), milestones (`milestone`), swimlanes (`lane: \\\"Team\\\"`), a `layout: timescaled` mode (x ∝ ES, width ∝ duration) for a network-Gantt hybrid, and a legacy `layout: aoa` mode (activity-on-arrow: numbered event circles + arrow activities + dummy activities, FS-only). Distinct from `flowchart` (no scheduling), `timeline`/Gantt (no critical-path computation), and `bpmn` (organisational process, not a one-off schedule).\",\n cluster: \"project-management\",\n standard: \"PMI PMBOK 7 + Moder 1983 (AON/PDM); see 32-PERT-STANDARD.md\",\n syntaxKey: \"pert\",\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 * Error class names that indicate a *runtime* failure inside the engine\n * (a bug we wrote) rather than a user-input parse/validation error. When\n * we see one of these, the extracted error is flagged with an `[engine\n * bug: …]` prefix and carries a single trimmed stack-frame as `source`,\n * so production integrators (ChatDiagram et al.) can file a bug report\n * with enough detail to actually reproduce.\n *\n * Captures the 2026-05 genogram TDZ class of issue: 92 occurrences with\n * just `\"Cannot access 'x' before initialization\"` as the message and no\n * structured fields. With this list, the same failure would have shown\n * up as `[engine bug: ReferenceError] Cannot access 'x' before\n * initialization` plus the offending frame.\n */\nconst ENGINE_BUG_NAMES = new Set([\n \"ReferenceError\",\n \"TypeError\",\n \"RangeError\",\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 *\n * Engine-internal runtime errors (TypeError, ReferenceError, RangeError)\n * are distinguished from user-input parse errors so callers can route\n * them to bug tracking rather than user-facing \"fix your DSL\" UI.\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 // User-defined parse-error classes also extend Error but set their own\n // `.name` and usually attach `.line`. Anything with a numeric line is\n // by definition a parse error, not a runtime bug — even if its `.name`\n // happened to collide with a built-in.\n const hasParseFields = typeof anyErr.line === \"number\";\n const isEngineBug = !hasParseFields && ENGINE_BUG_NAMES.has(err.name);\n const sourceHint = isEngineBug\n ? firstStackFrame(err.stack)\n : typeof anyErr.source === \"string\"\n ? anyErr.source\n : undefined;\n return {\n line: typeof anyErr.line === \"number\" ? anyErr.line : undefined,\n column: typeof anyErr.column === \"number\" ? anyErr.column : undefined,\n source: sourceHint,\n message: isEngineBug\n ? `[engine bug: ${err.name}] ${err.message}`\n : err.message,\n hint:\n typeof anyErr.hint === \"string\"\n ? anyErr.hint\n : isEngineBug\n ? \"This looks like a Schematex internal error rather than a DSL syntax problem. Please file an issue with the failing DSL at https://github.com/SchemaTex/Schematex/issues.\"\n : undefined,\n };\n }\n return { message: String(err) };\n}\n\n/**\n * Pick the topmost stack frame, with absolute paths normalized to bare\n * filenames so we don't leak the user's disk layout into their logs.\n */\nfunction firstStackFrame(stack: string | undefined): string | undefined {\n if (!stack) return undefined;\n for (const line of stack.split(\"\\n\")) {\n const trimmed = line.trim();\n if (trimmed.startsWith(\"at \")) {\n return trimmed.replace(/\\((?:.*\\/)?([^/]+)\\)/, \"($1)\");\n }\n }\n return undefined;\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\": \"bpmn-incident-response\",\n \"diagram\": \"bpmn\",\n \"title\": \"Production incident response (BPMN)\",\n \"description\": \"Three-lane BPMN of an on-call rotation handling a production page — L1 triage, L2 investigation, and a Comms lane that posts status updates. Exercises timer intermediate event, severity-based XOR routing, and a self-loop on the triage gate.\",\n \"standard\": \"OMG BPMN 2.0.2 / ISO/IEC 19510:2013\",\n \"tags\": [\n \"bpmn\",\n \"business-process\",\n \"incident-response\",\n \"timer-event\",\n \"on-call\",\n \"sre\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"bpmn\\ndirection: LR\\ntitle: \\\"Production incident response\\\"\\n\\npool \\\"Monitoring\\\" blackbox\\n\\npool \\\"Engineering\\\" {\\n lane \\\"On-call L1\\\" {\\n A: start message \\\"Page received\\\"\\n ACK: task user \\\"Acknowledge\\\"\\n TR: task user \\\"Triage\\\"\\n G1: gateway xor \\\"Severity?\\\"\\n }\\n lane \\\"On-call L2\\\" {\\n INV: task user \\\"Investigate\\\"\\n ROOT: task service \\\"Root cause\\\"\\n FIX: task user \\\"Implement fix\\\"\\n DEPLOY: task service \\\"Deploy patch\\\"\\n }\\n lane \\\"Comms\\\" {\\n SP: task send \\\"Status page\\\"\\n T1: intermediate timer \\\"30 min\\\"\\n UP: task send \\\"Update users\\\"\\n PM: task user \\\"Post-mortem\\\"\\n Z: end \\\"Resolved\\\"\\n }\\n}\\n\\nflows\\nA --> ACK\\nACK --> TR\\nTR --> G1\\nG1 --? \\\"P1\\\" --> SP\\nG1 --? \\\"P2-P3\\\" --> INV\\nG1 --* \\\"info\\\" --> TR\\nSP --> INV\\nINV --> ROOT\\nROOT --> FIX\\nFIX --> DEPLOY\\nDEPLOY --> T1\\nT1 --> UP\\nUP --> PM\\nPM --> Z\\n\\\"Monitoring\\\" ~~> A : \\\"Alert\\\"\\nSP ~~> \\\"Monitoring\\\" : \\\"Status\\\"\",\n \"notes\": \"The shape of an incident-response runbook is exactly what BPMN was designed for: multiple roles handing off to each other under time pressure, with a clear escalation path and a hard line between \\\"what we did internally\\\" and \\\"what we told users\\\". Generic flowcharts blur those distinctions; BPMN's lanes make them auditable.\\n\\n**Three lanes, three roles.** L1 owns the page-acknowledgement SLA. L2 owns the technical fix. Comms owns the user-facing narrative. Every cross-lane handoff is visible as a sequence-flow that crosses a lane partition. When the post-mortem arrives, you can answer \\\"why did Comms post the all-clear before L2 confirmed?\\\" by reading the lane crossings.\\n\\n**Timer intermediate event.** `T1: intermediate timer \\\"30 min\\\"` is a double-ringed circle with a clock-face inner glyph. It says: *wait 30 minutes after the patch deploys before telling users the problem is fixed*. That delay isn't an arbitrary process step — it's the empirical bake time most SRE teams use to confirm a regression hasn't surfaced. Encoding it as a timer event (rather than a vague \\\"wait\\\" task) lets process-mining tools measure the actual wait distribution in production.\\n\\n**Self-loop on the triage XOR.** The default flow `G1 --* \\\"info\\\" --> TR` routes back to triage for info-level pages — the engineer keeps revisiting the page in their queue rather than escalating. This is a real cycle in the process graph; the layout's DFS cycle-break detects the back edge and lays out the rest as a forward DAG so the columns don't collapse.\\n\\n**Message flow to the monitoring system.** Status-page updates go *out* to the monitoring system as a message flow (`SP ~~> \\\"Monitoring\\\"`). The monitoring system is rendered as a black-box pool — Datadog / PagerDuty / Sentry / whatever your stack uses, you don't model their internals, you just model the message exchange.\"\n },\n {\n \"slug\": \"bpmn-loan-approval\",\n \"diagram\": \"bpmn\",\n \"title\": \"Loan application approval (BPMN)\",\n \"description\": \"Single-pool, two-lane BPMN of a bank loan approval workflow. Clerk completeness check feeds underwriter risk-scoring + review with two end states (approved / rejected). Canonical BPMN test case 7.2.\",\n \"standard\": \"OMG BPMN 2.0.2 / ISO/IEC 19510:2013\",\n \"tags\": [\n \"bpmn\",\n \"business-process\",\n \"loan\",\n \"banking\",\n \"xor-gateway\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"bpmn\\ndirection: LR\\ntitle: \\\"Loan Application Approval\\\"\\n\\npool \\\"Bank\\\" {\\n lane \\\"Clerk\\\" {\\n A: start \\\"Application received\\\"\\n B: task user \\\"Check completeness\\\"\\n G1: gateway xor \\\"Complete?\\\"\\n }\\n lane \\\"Underwriter\\\" {\\n C: task service \\\"Risk score\\\"\\n D: task user \\\"Underwriter review\\\"\\n G2: gateway xor \\\"Decision\\\"\\n E: end \\\"Approved\\\"\\n F: end \\\"Rejected\\\"\\n }\\n}\\n\\nflows\\nA --> B\\nB --> G1\\nG1 --? \\\"yes\\\" --> C\\nG1 --* \\\"no\\\" --> F\\nC --> D\\nD --> G2\\nG2 --? \\\"approve\\\" --> E\\nG2 --* \\\"reject\\\" --> F\",\n \"notes\": \"A real loan approval has more steps — KYC, fraud screening, document collection, escalation tiers — but every variant collapses onto this skeleton. The reason a *flowchart* doesn't suffice is the **swim-lane semantics**: Clerk and Underwriter are different roles with different SLAs, often different software, sometimes different legal liability. BPMN encodes that division as first-class lanes; a generic flowchart can only hint at it with shape colours.\\n\\nThe two **XOR gateways** (G1, G2) each have one **conditional flow** (`--?`) and one **default flow** (`--*`). The default flow is rendered with a slash mark at the source — it's the branch taken when no condition matches, and Schematex enforces *exactly one* default per gateway. Both conditional and default flows share the same gateway label (\\\"Complete?\\\" / \\\"Decision\\\"); the branch labels live on the connectors themselves.\\n\\nThe two end events (Approved / Rejected) sit in the underwriter lane because that's who owns the outcome. F is referenced from both gateways — the rejection path joins from either \\\"incomplete\\\" or \\\"rejected\\\" decisions, which is exactly how a human auditor would expect to read the process.\"\n },\n {\n \"slug\": \"bpmn-pizza-order\",\n \"diagram\": \"bpmn\",\n \"title\": \"Pizza order with black-box customer (BPMN)\",\n \"description\": \"Two-pool BPMN — external Customer (black-box) and a Pizzeria with Clerk / Chef / Delivery lanes. Exercises message flows across pools, manual / send task markers, and a rework loop on the chef's quality check. Canonical BPMN test case 7.1.\",\n \"standard\": \"OMG BPMN 2.0.2 / ISO/IEC 19510:2013\",\n \"tags\": [\n \"bpmn\",\n \"business-process\",\n \"message-flow\",\n \"black-box-pool\",\n \"rework-loop\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"bpmn\\ndirection: LR\\ntitle: \\\"Pizza order\\\"\\n\\npool \\\"Customer\\\" blackbox\\n\\npool \\\"Pizzeria\\\" {\\n lane \\\"Clerk\\\" {\\n A: start message \\\"Order received\\\"\\n B: task user \\\"Take order\\\"\\n }\\n lane \\\"Chef\\\" {\\n C: task manual \\\"Make pizza\\\"\\n G1: gateway xor \\\"Pizza ok?\\\"\\n D: task manual \\\"Rework\\\"\\n }\\n lane \\\"Delivery\\\" {\\n E: task send \\\"Deliver\\\"\\n F: end \\\"Done\\\"\\n }\\n}\\n\\nflows\\nA --> B\\nB --> C\\nC --> G1\\nG1 --? \\\"yes\\\" --> E\\nG1 --* \\\"no\\\" --> D\\nD --> C\\nE --> F\\n\\\"Customer\\\" ~~> A : \\\"Place order\\\"\\nE ~~> \\\"Customer\\\" : \\\"Pizza delivered\\\"\",\n \"notes\": \"This is the OMG-tutorial *hello world* for BPMN — every BPM textbook and certification course uses some version of it. It's a deceptively rich example: in 12 elements it covers every concept that distinguishes BPMN from a generic flowchart.\\n\\n**Black-box pool.** The customer's internal process is unknown (and irrelevant) to the pizzeria, so Customer is rendered as an empty rectangle with no flow objects. Schematex enforces this — adding a lane or a task inside a `blackbox` pool fails the parse. Black-box pools exist purely as message-flow endpoints.\\n\\n**Message flows cross pools, sequence flows do not.** The two `~~>` connectors are dashed with an open arrowhead and a small unfilled circle at the source. That's the *only* legal way information crosses a pool boundary in BPMN. Schematex's parser will reject `A --> \\\"Customer\\\"` with a clear error.\\n\\n**Rework loop.** `D --> C` creates a back-edge. The layout's longest-path layering would normally fail on cycles; Schematex's DFS-based cycle-break tags `D → C` as a back edge, runs longest-path on the remaining DAG, then routes the loop manually. This is exactly how real processes look — quality gates always have a \\\"go back and redo it\\\" branch.\\n\\n**Task markers communicate intent.** `start message` (envelope) means \\\"wait for an inbound message before starting\\\"; `task manual` means a person performs the work without software (\\\"hand-toss the dough\\\"); `task send` means \\\"send a message and continue\\\". An LLM picking markers correctly turns a vague flowchart into a precise process spec.\"\n },\n {\n \"slug\": \"breadboard-blink-led\",\n \"diagram\": \"breadboard\",\n \"title\": \"Blink LED on Arduino Uno\",\n \"description\": \"The maker hello-world. Arduino Uno + 220Ω resistor + 5mm red LED — D13 → resistor → LED → GND. Tests basic part placement, beside-left MCU, and the iconic Bézier wire arc.\",\n \"standard\": \"Fritzing visual conventions (no ISO standard)\",\n \"tags\": [\n \"breadboard\",\n \"arduino\",\n \"uno\",\n \"led\",\n \"blink\",\n \"tutorial\"\n ],\n \"complexity\": 1,\n \"featured\": true,\n \"dsl\": \"breadboard\\nboard: half\\ntitle: \\\"Blink LED — Arduino Uno hello-world\\\"\\n\\nparts\\n uno: mcu uno @beside-left\\n r1: resistor 220 @5e..9e\\n d1: led red @10e..10f\\n\\nwires\\n uno:5V --red-- @+t1\\n uno:GND --black-- @-t1\\n @+t1 --red-- @5a\\n uno:D13 --yellow-- @9a\\n @10j --black-- @-t1\",\n \"notes\": \"The \\\"Blink\\\" sketch is the Arduino equivalent of `printf(\\\"hello\\\\n\\\")` — the smallest program that proves the toolchain, board, and wiring all work. The breadboard wiring routes D13 through a 220Ω current-limiting resistor into the LED's anode (long lead, row e), and pulls the cathode (short lead, row f) down to ground via the bottom rail. Power and ground rails are jumpered from the Uno's 5V and GND pins on the left.\\n\\nIf the LED doesn't light up, three things to check, in order: LED polarity (the flat-side flag in the diagram is the cathode); resistor value (220–330Ω works for a 5mm red LED at 5V); and that D13 is the actual pin you're toggling in your sketch.\"\n },\n {\n \"slug\": \"breadboard-esp32-oled\",\n \"diagram\": \"breadboard\",\n \"title\": \"ESP32 + SSD1306 OLED I²C\",\n \"description\": \"ESP32 DevKit driving a 128×64 OLED display over I²C — four wires (3V3 / GND / SDA / SCL). The standard \\\"first display\\\" project for ESP32 learners.\",\n \"standard\": \"Fritzing visual conventions (no ISO standard)\",\n \"tags\": [\n \"breadboard\",\n \"esp32\",\n \"oled\",\n \"ssd1306\",\n \"i2c\",\n \"display\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"breadboard\\nboard: half\\ntitle: \\\"ESP32 + SSD1306 OLED I²C\\\"\\n\\nparts\\n esp: mcu esp32 @beside-left\\n oled: display oled-ssd1306 @8a\\n\\nwires\\n oled:GND --black-- @-t1\\n oled:VCC --red-- @+t1\\n oled:SCL --white-- @10c\\n oled:SDA --green-- @11c\\n esp:3V3 --red-- @+t1\\n esp:GND --black-- @-t1\\n esp:GPIO22 --white-- @10c\\n esp:GPIO21 --green-- @11c\",\n \"notes\": \"The SSD1306 OLED is the canonical first display project for an ESP32. Four wires get a 128×64 pixel monochrome display running over I²C. Two things differ from the equivalent Arduino Uno setup:\\n\\n**3.3 V supply, not 5 V.** The ESP32 runs at 3.3 V on every pin including its supply rail. Wiring an OLED to the Uno's 5 V will work, but mixing in 5 V signals from a Uno into the same I²C bus as the ESP32 will damage the ESP32. Stay on the 3V3 rail.\\n\\n**Default I²C pins are GPIO21 (SDA) and GPIO22 (SCL).** The Adafruit / Espressif convention is green for SDA and white for SCL — preserved here. These pins are remappable in firmware via `Wire.begin(sda, scl)` if you need them elsewhere.\"\n },\n {\n \"slug\": \"breadboard-hcsr04-distance\",\n \"diagram\": \"breadboard\",\n \"title\": \"HC-SR04 distance sensor + Arduino Uno\",\n \"description\": \"Four-wire ultrasonic distance sensor wiring — VCC / GND / TRIG / ECHO. The textbook Adafruit / SparkFun tutorial layout used in robotics intro classes.\",\n \"standard\": \"Fritzing visual conventions (no ISO standard)\",\n \"tags\": [\n \"breadboard\",\n \"arduino\",\n \"ultrasonic\",\n \"sensor\",\n \"hc-sr04\",\n \"robotics\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"breadboard\\nboard: half\\ntitle: \\\"HC-SR04 distance sensor + Arduino Uno\\\"\\n\\nparts\\n uno: mcu uno @beside-left\\n s1: sensor hcsr04 @8a\\n\\nwires\\n s1:VCC --red-- @+t1\\n s1:GND --black-- @-t1\\n s1:TRIG --yellow-- @9c\\n s1:ECHO --green-- @10c\\n uno:5V --red-- @+t1\\n uno:GND --black-- @-t1\\n uno:D9 --yellow-- @9c\\n uno:D10 --green-- @10c\",\n \"notes\": \"The HC-SR04 is the most common ultrasonic distance sensor in beginner Arduino kits. Four pins, four wires, no driver IC needed. The sensor module is rendered as a blue PCB tile with its four pin labels (VCC / TRIG / ECHO / GND) sitting above the breadboard rows where they plug in.\\n\\nThe TRIG line is a digital output from the Arduino — pulse it high for 10 µs and the sensor fires an ultrasonic chirp. The ECHO line is a digital input — its high-time, in microseconds, is twice the round-trip distance divided by the speed of sound. Conventional wiring uses yellow for TRIG and green for ECHO so the two signals are visually distinguishable, though the colors carry no electrical meaning.\"\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\": \"erd-ecommerce-schema\",\n \"diagram\": \"erd\",\n \"title\": \"E-commerce schema\",\n \"description\": \"Production-style schema with Customer / Order / OrderLine / Product / Category — typical mid-complexity backend ERD with composite PK on the line-item table.\",\n \"standard\": \"Crow's-foot (Everest 1976) / DBML compatible\",\n \"tags\": [\n \"ERD\",\n \"crow's-foot\",\n \"schema\",\n \"e-commerce\",\n \"FK\",\n \"composite-PK\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"erd\\ntitle: \\\"E-commerce Schema\\\"\\ndirection: LR\\n\\ntable Customer {\\n customer_id int PK\\n email varchar UK\\n name varchar\\n created_at timestamp\\n}\\n\\ntable Address {\\n address_id int PK\\n customer_id int FK -> Customer.customer_id\\n line1 varchar\\n city varchar\\n zip varchar\\n}\\n\\ntable Category {\\n category_id int PK\\n name varchar\\n}\\n\\ntable Product {\\n product_id int PK\\n category_id int FK -> Category.category_id\\n name varchar\\n price decimal\\n}\\n\\ntable Order {\\n order_id int PK\\n customer_id int FK -> Customer.customer_id\\n placed_at timestamp\\n status varchar\\n}\\n\\ntable OrderLine {\\n order_id int PK FK -> Order.order_id\\n line_no int PK\\n product_id int FK -> Product.product_id\\n qty int\\n price decimal\\n}\\n\\nref Address.customer_id many-mandatory -- one-mandatory Customer.customer_id\\nref Order.customer_id many-mandatory -- one-mandatory Customer.customer_id : \\\"places\\\"\\nref Product.category_id many-mandatory -- one-mandatory Category.category_id\\nref OrderLine.order_id many-mandatory -- one-mandatory Order.order_id\\nref OrderLine.product_id many-mandatory -- one-mandatory Product.product_id\",\n \"notes\": \"A six-table schema spanning four FK-depth layers: Category and Customer at the root; Product and Address at depth one; Order at depth two; OrderLine at depth three with a composite PK `(order_id, line_no)`. Demonstrates layered LR routing and labelled relationships (\\\"places\\\").\"\n },\n {\n \"slug\": \"erd-university-schema\",\n \"diagram\": \"erd\",\n \"title\": \"University schema (academic ERD)\",\n \"description\": \"Classic textbook schema with Student / Course / Enrollment associative entity and labelled relationships — the canonical Elmasri & Navathe ch.3 example.\",\n \"standard\": \"Crow's-foot (Everest 1976) / DBML compatible\",\n \"tags\": [\n \"ERD\",\n \"crow's-foot\",\n \"schema\",\n \"associative-entity\",\n \"M-N\"\n ],\n \"complexity\": 1,\n \"featured\": true,\n \"dsl\": \"erd\\ntitle: \\\"University Schema\\\"\\n\\ntable Student {\\n student_id int PK\\n name varchar\\n email varchar UK\\n major_id int FK -> Major.major_id\\n}\\n\\ntable Major {\\n major_id int PK\\n name varchar\\n}\\n\\ntable Course {\\n course_id int PK\\n title varchar\\n credits int\\n}\\n\\ntable Enrollment {\\n student_id int PK FK -> Student.student_id\\n course_id int PK FK -> Course.course_id\\n grade char\\n}\\n\\nref Student.major_id many-mandatory -- one-mandatory Major.major_id : \\\"majors in\\\"\\nref Enrollment.student_id many-mandatory -- one-mandatory Student.student_id\\nref Enrollment.course_id many-mandatory -- one-mandatory Course.course_id\",\n \"notes\": \"The university schema is the canonical introduction to associative entities. The M:N relationship between Student and Course is resolved through Enrollment, which carries the relationship attribute `grade`. Both Student and Course join Enrollment via composite primary keys (each FK column doubles as part of the PK).\"\n },\n {\n \"slug\": \"fbd-bottle-counter\",\n \"diagram\": \"fbd\",\n \"title\": \"Bottle counter — debounce → edge → count (FBD)\",\n \"description\": \"Three-network FBD pipeline that debounces a bottle sensor with a 50ms TON, takes the rising edge with R_TRIG, counts up to 24 bottles per case with CTU, and self-resets when the case is full. A real production-line pattern that exercises timer + edge-detector + counter + named-instance references in sequence.\",\n \"standard\": \"IEC 61131-3:2013 §6.4 + §2.5\",\n \"tags\": [\n \"fbd\",\n \"plc\",\n \"ton\",\n \"ctu\",\n \"r-trig\",\n \"debounce\",\n \"sensor\",\n \"packaging\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"fbd \\\"Bottle Counter\\\"\\n\\nvar BottleSensor: bool\\nvar BatchDone: bool\\nvar BatchSize: counter\\nvar DwellTimer: timer\\n\\nnetwork 0 \\\"Debounce sensor with 50ms dwell\\\":\\n Dwell = TON(IN: BottleSensor, PT: T#50ms)\\n\\nnetwork 1 \\\"Count one bottle on rising edge of debounced signal\\\":\\n Pulse = R_TRIG(CLK: Dwell.Q)\\n BatchSize = CTU(CU: Pulse.Q, R: BatchDone, PV: 24)\\n\\nnetwork 2 \\\"Latch batch done\\\":\\n BatchDone = MOVE(BatchSize.Q)\",\n \"notes\": \"Counting bottles on a conveyor sounds trivial — the photoelectric sensor goes high when a bottle interrupts the beam, increment the counter, done. In practice it's where most beginner PLC code first goes wrong, because real sensors *bounce*: the beam is broken, then briefly restored as light reflects off the bottle's edge, then broken again. Without debounce, a single bottle can register as three or four counts. This FBD captures the canonical fix in three short networks, and shows how named-instance references chain timers, edge-detectors, and counters into a pipeline.\\n\\n**Network 0 — debounce.** A TON (on-delay timer) ignores the sensor unless it stays high for at least 50ms continuously. `PT: T#50ms` is the IEC duration literal for the preset; it renders as a small yellow constant box at the PT input port. The Q output goes true 50ms after the IN goes true and resets immediately when IN drops — exactly the \\\"is this signal *really* asserted, or just noise?\\\" semantic the line needs.\\n\\n**Network 1 — edge → count.** Two named instances chain together. `Pulse = R_TRIG(CLK: Dwell.Q)` references the debounced output from network 0 by its instance.port name. R_TRIG emits a one-scan pulse on the rising edge — without it, the counter would increment continuously while the sensor is held high (e.g. if the conveyor jammed with a bottle in the beam). `BatchSize = CTU(CU: Pulse.Q, R: BatchDone, PV: 24)` then increments on each pulse, and resets when `BatchDone` goes true. The PV=24 is the case size for a six-pack of four-packs.\\n\\n**Network 2 — feedback latch.** The counter's Q output (true when CV ≥ PV) is moved into the `BatchDone` variable, which feeds back to network 1's reset input. This is one full case cycle: count to 24 → done → reset → start over. As with the motor latch, Schematex doesn't simulate the scan order; it renders the graph. The \\\"feedback\\\" appears in the wire from network 2 back to network 1's CTU.R.\\n\\n**What the data-type colors are telling you.** The TON.IN wire is BOOL (black). The TON.PT wire is TIME (magenta — but it's an inline constant here, so no wire). R_TRIG.CLK is BOOL (black). CTU.CV would be INT (blue) if it were wired anywhere — it's left dangling here because we only need Q. Different wire colors per data type are the TIA Portal de-facto convention, ported over to Schematex for at-a-glance validation: if a REAL wire (orange) lands on a BOOL port, you have a type error in your DSL.\"\n },\n {\n \"slug\": \"fbd-motor-latch\",\n \"diagram\": \"fbd\",\n \"title\": \"Motor start/stop latch (FBD)\",\n \"description\": \"Two-network FBD of the canonical PLC seal-in circuit — start-button rising edge sets a latch, stop and emergency-stop break it. Exercises declared variables, multi-network programs, inline expression notation, and negation bubbles.\",\n \"standard\": \"IEC 61131-3:2013 §6.4 + §2.5\",\n \"tags\": [\n \"fbd\",\n \"plc\",\n \"iec-61131-3\",\n \"latch\",\n \"motor-control\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"fbd \\\"Motor Control\\\"\\n\\nvar Start: bool\\nvar Stop: bool\\nvar EmergencyStop: bool\\nvar MotorOut: bool\\nvar Latch: bool\\n\\nnetwork 0 \\\"Start latch\\\":\\n Latch = OR(Start, AND(Latch, ~Stop, ~EmergencyStop))\\n\\nnetwork 1 \\\"Drive output\\\":\\n MotorOut = MOVE(Latch)\",\n \"notes\": \"The seal-in circuit — also called a start/stop latch — is the most common pattern in PLC code. Once the operator presses Start, the motor stays running even after they release the button, until either Stop or an emergency-stop input goes high. Every controls engineer writes this on day one, and every PLC textbook opens with the ladder version. The FBD form is just as concise but reads like data flow rather than like a relay schematic.\\n\\n**Network 0 — the latch.** `Latch = OR(Start, AND(Latch, ~Stop, ~EmergencyStop))` is the entire seal-in expressed as one nested call. The OR fires when *either* the start button is pressed *or* the latch is already set AND neither stop button is held. The `~` prefix on Stop and EmergencyStop adds a **negation bubble** at each input port — equivalent to inserting a NOT block on that wire, but rendered inline. The variadic AND expands automatically to three inputs because three positional arguments were passed.\\n\\n**The `Latch` feedback edge.** Notice that `Latch` appears on both the left side (as an input to the AND) and the right side (as the output of the OR). This is the *seal-in* feedback that gives the circuit its name. In a real PLC, the previous scan's `Latch` value is what feeds back — the current scan reads it, evaluates the boolean expression, and writes the new value. Schematex doesn't simulate the scan cycle; it just renders the data-flow graph faithfully, which is exactly what an engineer reviewing the program needs to see.\\n\\n**Network 2 — drive output.** `MotorOut = MOVE(Latch)` is a separate concern: take the latch state and assign it to the physical output that energises the motor contactor. Splitting it into its own network is a real engineering practice — it keeps the \\\"logic\\\" (when should the motor be on?) separate from the \\\"actuation\\\" (what physical output gets driven?), so swapping in test harnesses or fault-injection logic only requires modifying network 1, not network 0.\\n\\n**Why this is FBD and not ladder.** Both languages can express this circuit, and many PLC projects mix them. FBD wins when the engineer is thinking in terms of \\\"this signal flows into that operator\\\"; ladder wins when thinking in terms of \\\"this contact closes the path to that coil.\\\" For the seal-in pattern specifically, FBD's three-input AND collapses what ladder requires drawing as three series contacts on one rung — a small win, but a representative one.\"\n },\n {\n \"slug\": \"fbd-tank-setpoint-limiter\",\n \"diagram\": \"fbd\",\n \"title\": \"Tank setpoint limiter + alarm (FBD)\",\n \"description\": \"Two-network FBD that clamps an operator-entered tank-level setpoint to the safe range [0, 95]% via LIMIT, and raises an alarm if the operator types an out-of-range value. Uses LT and GT comparison blocks, the LIMIT selection block, and demonstrates REAL (orange) and BOOL (black) wires carrying different data types in one diagram.\",\n \"standard\": \"IEC 61131-3:2013 §6.4 + §2.5\",\n \"tags\": [\n \"fbd\",\n \"plc\",\n \"comparison\",\n \"limit\",\n \"real\",\n \"hmi\",\n \"operator-input\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"fbd \\\"Tank Level Setpoint Limiter\\\"\\n\\nvar DesiredSetpoint: real\\nvar SafeSetpoint: real\\nvar Alarm: bool\\n\\nnetwork 0 \\\"Clamp setpoint to safe range\\\":\\n SafeSetpoint = LIMIT(MN: 0.0, IN: DesiredSetpoint, MX: 95.0)\\n\\nnetwork 1 \\\"Alarm on out-of-range request\\\":\\n OutOfRange = OR(LT(DesiredSetpoint, 0.0), GT(DesiredSetpoint, 95.0))\\n Alarm = MOVE(OutOfRange.OUT)\",\n \"notes\": \"When an operator types a tank-level setpoint into the HMI, you can't just trust the number. Maybe they meant to type 75 and hit 750. Maybe they typed -5 because they were copying from a spec sheet that used a different reference. Maybe the HMI's input field doesn't have validation and the value is whatever bit-pattern the OPC UA bridge happened to land on. The PLC always validates, and the canonical pattern is: clamp to a safe range, then raise an alarm if the requested value was out of range so a human knows to check.\\n\\n**Network 0 — LIMIT.** `SafeSetpoint = LIMIT(MN: 0.0, IN: DesiredSetpoint, MX: 95.0)` is the clamp. LIMIT takes three REAL inputs and returns a REAL: the value of `IN` if it's in [MN, MX], otherwise MN or MX. Both bounds are inline constants — they render as yellow boxed text at their ports, no wire needed. The downstream control loop reads `SafeSetpoint`, never `DesiredSetpoint` directly. Even if the operator's input is corrupted, the tank can never be commanded outside the physically safe range.\\n\\n**Network 1 — out-of-range alarm.** `OutOfRange = OR(LT(DesiredSetpoint, 0.0), GT(DesiredSetpoint, 95.0))` is an inline expression that nests two comparison blocks inside an OR. LT (less-than) returns BOOL when its first input is less than the second; GT (greater-than) is the opposite. The OR fires when *either* fires — the operator's value was either too low or too high. The `Alarm = MOVE(OutOfRange.OUT)` then drives whatever HMI alarm channel — a red banner, a Slack notification, a SCADA event log entry.\\n\\n**Two data types in one diagram.** Look at the wire colors in the rendered SVG: the wire from `DesiredSetpoint` into LIMIT.IN is REAL (orange — IEEE 754 floating-point); the wires from LT.OUT and GT.OUT into OR are BOOL (black). The LIMIT.OUT is also REAL (orange). One of the FBD engine's small but pleasant features: the renderer infers each wire's type from the source port and colors it accordingly, following the TIA Portal convention. If you accidentally wired an INT to a REAL port the colors would mismatch at the junction and you'd notice immediately.\\n\\n**Why not ladder?** Ladder logic excels at boolean signal routing — contacts in series and parallel feeding into output coils. It has zero affordance for REAL arithmetic and comparison; you'd write the LIMIT expression as a structured-text \\\"function block\\\" call inside a ladder rung, which kills the visual semantic. FBD makes the math first-class. For per-scan combinational logic involving any non-BOOL signal, FBD is what the IEC 61131-3 standard expects you to use.\"\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-9-box-talent\",\n \"diagram\": \"matrix\",\n \"title\": \"9-box talent grid\",\n \"description\": \"3×3 performance × potential talent grid — the GE/McKinsey HR review tool used to plan promotions, succession, and performance management.\",\n \"standard\": \"9-Box Talent Grid (GE / McKinsey)\",\n \"tags\": [\n \"matrix\",\n \"9-box\",\n \"talent\",\n \"hr\",\n \"succession\",\n \"performance\",\n \"table\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"matrix 9-box \\\"Engineering — H1 Talent Review\\\"\\nstyle: table\\ncell (0,2) label: \\\"Enigma\\\"\\ncell (0,2) label: \\\"Samir K. (sr. eng)\\\"\\ncell (1,2) label: \\\"Growth Employee\\\"\\ncell (1,2) label: \\\"Priya R. (eng II)\\\"\\ncell (1,2) label: \\\"Tomás L. (eng II)\\\"\\ncell (2,2) label: \\\"Future Leader\\\"\\ncell (2,2) label: \\\"Maya O. (sr. eng)\\\"\\ncell (0,1) label: \\\"Dilemma\\\"\\ncell (0,1) label: \\\"David C. (eng II)\\\"\\ncell (1,1) label: \\\"Core Player\\\"\\ncell (1,1) label: \\\"Lin H. (sr. eng)\\\"\\ncell (1,1) label: \\\"Kofi A. (eng II)\\\"\\ncell (2,1) label: \\\"High Impact\\\"\\ncell (2,1) label: \\\"Reina S. (staff)\\\"\\ncell (0,0) label: \\\"Under-performer\\\"\\ncell (0,0) label: \\\"— PIP candidate —\\\"\\ncell (1,0) label: \\\"Effective\\\"\\ncell (1,0) label: \\\"Jordan P. (eng I)\\\"\\ncell (2,0) label: \\\"Trusted Pro\\\"\\ncell (2,0) label: \\\"Elena V. (staff)\\\"\",\n \"notes\": \"## Scenario\\n\\nA VP of People runs the half-year talent review with eng managers. Each direct report lands in one of nine cells based on **performance** (the x-axis: how they're doing today) and **potential** (the y-axis: how much room they have to grow). The three top-row cells are the succession bench. The three bottom-row cells are the performance-management agenda. The middle row is the steady-state core that holds the org together.\\n\\n## Annotation key\\n\\n- `matrix 9-box` — preset 3×3 grid (the canonical nine cell names ship with the template)\\n- `style: table` — top-aligned bullet-list rendering inside each cell\\n- `cell (col, row) label: \\\"...\\\"` — each line adds one bullet to that cell. First line per cell is the canonical role name (Enigma / Future Leader / Core Player / …); subsequent lines list the people in that cell\\n- Coordinates: `(0, 0)` is bottom-left (low performance, low potential); `(2, 2)` is top-right (high performance, high potential)\\n\\n## How to read\\n\\n**Future Leader** (top-right) is the natural-successor cell — the person you'd promote if a senior role opens tomorrow. **Dilemma** (top-left) and **Enigma** (top-middle-left) are the high-potential / low-performance cells: stuck in the wrong role, or under-coached, or in a bad team-fit — usually a managerial action item, not a performance issue. **Under-performer** (bottom-left) is the only cell that should ever be empty; if it has names, plan the next conversation now.\\n\\nThe canonical 9-box discipline is to **anchor each calibration discussion on observable evidence** — a recent shipped project, a missed deadline, a mentee promotion. Without that anchor, the grid drifts into bias. The table format is what the calibration committee literally prints and marks up in the room.\"\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 table grouping a week's tasks into Do First / Schedule / Delegate / Delete — the canonical text-in-cell layout, not a scatter chart.\",\n \"standard\": \"Eisenhower (1954)\",\n \"tags\": [\n \"matrix\",\n \"eisenhower\",\n \"prioritization\",\n \"productivity\",\n \"planning\",\n \"table\"\n ],\n \"complexity\": 1,\n \"featured\": true,\n \"dsl\": \"matrix eisenhower \\\"This Week\\\"\\nstyle: table\\nQ2: \\\"Ship hotfix\\\"\\nQ2: \\\"Customer demo prep\\\"\\nQ1: \\\"Write Q3 OKRs\\\"\\nQ1: \\\"Refactor auth layer\\\"\\nQ4: \\\"LinkedIn updates\\\"\\nQ4: \\\"Inbox zero\\\"\\nQ3: \\\"Reorganize Slack channels\\\"\",\n \"notes\": \"## Scenario\\n\\nAn engineering manager triages her week at Monday planning. The point of an Eisenhower matrix in real use isn't a scatter plot — it's a **four-cell list**: tasks dropped into the quadrant that matches their urgency × importance. The table tells her at a glance where her attention should go this week (Do First + Schedule), what to push down (Delegate), and what to drop (Delete).\\n\\n## Annotation key\\n\\n- `matrix eisenhower` — preset axes (urgency × importance) and four quadrant titles\\n- `style: table` — text-in-cell layout: hides axes/arrows/grid, renders quadrant titles as cell headers, lists each item as a bullet\\n- `Q1:` … `Q4:` — shorthand for `cell (col, row) label: …`. Q1=top-right (Schedule), Q2=top-left (Do First), Q3=bottom-left (Delete), Q4=bottom-right (Delegate)\\n- Repeating `Q2:` stacks multiple tasks in the same cell as a bullet list\\n\\n## How to read\\n\\nThe \\\"Do First\\\" cell (top-left in eisenhower's convention with urgent on the left axis) is the urgent + important pile — work the morning sprint. \\\"Schedule\\\" is the trap quadrant: important but not yet urgent (Q3 OKRs, refactor) — it silently slips until it becomes urgent and badly done. \\\"Delegate\\\" is the busy-work the AI generates — urgent on someone's calendar but not actually load-bearing for the org. \\\"Delete\\\" is the candidate for \\\"no\\\": neither urgent nor important.\\n\\n## Why a table, not a chart\\n\\nThe classic Eisenhower output is a 2×2 grid with task lists, not a scatter of dots. If you want to encode a third dimension (time cost, owner) on top of the cell layout, drop `style: table` and use `\\\"Label\\\" at (x, y) size: N` instead — that switches to bubble mode.\"\n },\n {\n \"slug\": \"matrix-impact-effort\",\n \"diagram\": \"matrix\",\n \"title\": \"Impact-effort feature prioritization\",\n \"description\": \"2×2 impact × effort table sorting a backlog into Quick Wins / Major Projects / Fill-ins / Thankless — the classic PM prioritization grid.\",\n \"standard\": \"Impact–Effort (Sondhi 1999)\",\n \"tags\": [\n \"matrix\",\n \"impact\",\n \"effort\",\n \"prioritization\",\n \"product\",\n \"table\"\n ],\n \"complexity\": 1,\n \"featured\": false,\n \"dsl\": \"matrix impact-effort \\\"Sprint Planning — Q2 Backlog\\\"\\nstyle: table\\nQ2: \\\"Add bulk-delete to inbox\\\"\\nQ2: \\\"Surface error message inline\\\"\\nQ2: \\\"Fix mobile menu z-index bug\\\"\\nQ1: \\\"Rebuild billing on new stripe API\\\"\\nQ1: \\\"Multi-tenant workspace support\\\"\\nQ3: \\\"Brand color audit\\\"\\nQ3: \\\"Update tooltip copy\\\"\\nQ4: \\\"Animated empty-state illustrations\\\"\\nQ4: \\\"Internal admin dashboard polish\\\"\",\n \"notes\": \"## Scenario\\n\\nA solo PM running quarterly planning ranks the backlog. The table form is what stakeholders actually want to see in the planning doc — a sortable list per quadrant, not a scatter plot. Quick Wins go in the top-left (high impact, low effort) — ship them this sprint. Major Projects are the bets (high impact, high effort) — pick at most one per quarter. Thankless work (low impact, high effort) is where teams accidentally burn quarters.\\n\\n## Annotation key\\n\\n- `matrix impact-effort` — preset axes (effort × impact) with the four PM-canonical quadrant names\\n- `style: table` — render as a 4-cell text grid, no axis arrows, no chart\\n- `Q1` … `Q4` — Q1=high impact + high effort (Major Projects), Q2=high impact + low effort (Quick Wins), Q3=low impact + low effort (Fill-ins), Q4=low impact + high effort (Thankless)\\n\\n## How to read\\n\\nIf your Quick Wins cell is empty, that's a smell — it usually means the PM hasn't broken down work small enough. If your Major Projects cell has more than one item per engineer-quarter, you're overcommitting. The Thankless cell is where you have the conversation about cutting scope or pushing to next quarter.\"\n },\n {\n \"slug\": \"matrix-johari-window\",\n \"diagram\": \"matrix\",\n \"title\": \"Johari window — manager self-assessment\",\n \"description\": \"2×2 Johari window placing self-traits across Open / Blind / Hidden / Unknown — the classic coaching exercise rendered as a four-cell table.\",\n \"standard\": \"Johari window (Luft & Ingham 1955)\",\n \"tags\": [\n \"matrix\",\n \"johari\",\n \"coaching\",\n \"self-awareness\",\n \"hr\",\n \"table\"\n ],\n \"complexity\": 1,\n \"featured\": false,\n \"dsl\": \"matrix johari \\\"Self vs. Team — Q2 Reflection\\\"\\nstyle: table\\nQ2: \\\"Strong technical instincts\\\"\\nQ2: \\\"Direct in code review\\\"\\nQ2: \\\"Patient with juniors\\\"\\nQ1: \\\"Interrupts in meetings\\\"\\nQ1: \\\"Hard to read when stressed\\\"\\nQ3: \\\"Imposter syndrome about leadership\\\"\\nQ3: \\\"Anxiety about cross-team politics\\\"\\nQ4: \\\"Capacity for difficult conversations under pressure\\\"\",\n \"notes\": \"## Scenario\\n\\nA newly-promoted engineering manager runs a Johari exercise with her team during a 1:1 retro. She populates the **Open** cell (things both she and the team see); the team adds to **Blind** (things they see that she doesn't); she fills **Hidden** privately; **Unknown** is the open hypothesis space — capabilities and limitations that haven't surfaced yet.\\n\\nThe table form is the canonical Johari output. Coaches print it on a single page and walk through it with the coachee — a scatter plot of dots would defeat the entire purpose.\\n\\n## Annotation key\\n\\n- `matrix johari` — preset axes (Known to Self × Known to Others) with the four window panes\\n- `style: table` — flips off axes/grid, places each pane title as a cell header, lists items as bullets\\n- `Q1` = Blind (top-right: not known to self, known to others)\\n- `Q2` = Open / Arena (top-left: known to self + others)\\n- `Q3` = Hidden / Façade (bottom-left: known to self, not to others)\\n- `Q4` = Unknown (bottom-right: not known to either — the growth hypothesis space)\\n\\n## How to read\\n\\nThe classic Johari coaching prompt: **how do you move items from Hidden → Open** (vulnerability work) **and from Blind → Open** (feedback-acceptance work)? An overstuffed Hidden pane signals psychological-safety debt; an empty Blind pane usually means the team hasn't been asked.\"\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\": \"pert-aoa-software-project\",\n \"diagram\": \"pert\",\n \"title\": \"Activity-on-arrow (AOA) network\",\n \"description\": \"The classic textbook PERT notation — numbered event circles, activities as labelled arrows, and dotted dummy activities auto-inserted at multi-predecessor merges. Written in the same activity-on-node DSL; Schematex builds and numbers the event graph.\",\n \"standard\": \"PMI PMBOK 7 + Moder 1983 — Activity-on-Arrow (ADM, legacy)\",\n \"tags\": [\n \"pert\",\n \"aoa\",\n \"activity-on-arrow\",\n \"critical-path\",\n \"education\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"pert\\ntitle: \\\"Software project (AOA)\\\"\\nlayout: aoa\\nunit: days\\n\\ntask A \\\"create schedule\\\" duration: 10\\ntask B \\\"buy hardware\\\" duration: 5\\ntask C \\\"programming\\\" duration: 20 after: A\\ntask D \\\"installation\\\" duration: 5 after: B\\ntask E \\\"conversion\\\" duration: 15 after: D\\ntask F \\\"test code\\\" duration: 20 after: C, E\\ntask G \\\"write manual\\\" duration: 15 after: E\\ntask H \\\"test system\\\" duration: 10 after: F\\ntask I \\\"training\\\" duration: 5 after: G\\ntask J \\\"user test\\\" duration: 10 after: H, I\",\n \"notes\": \"Before activity-on-node won, project networks were drawn **activity-on-arrow** (AOA / ADM): the *arrows* are the work and the *nodes* are events — the instants when activities start and finish. It's the form on Investopedia and in older textbooks, and it's what `layout: aoa` produces.\\n\\n**You still write activity-on-node.** The DSL is identical to every other PERT example — tasks, durations, `after:` dependencies. Schematex converts it: each activity gets a head event, and the events are numbered so every arrow runs from a lower id to a higher one (the classic *i-j* rule).\\n\\n**Dummy activities.** AOA can't show two activities merging into one without help, so wherever an activity has multiple predecessors a **dummy** (dotted, zero-duration arrow) is inserted. Here `test code` depends on both `programming` and `conversion`, and `user test` on both `test system` and `training` — each merge gets dummies that carry the dependency without representing real work.\\n\\n**The critical path is still computed.** The forward/backward pass runs on the event graph and the critical activities are drawn as bold red arrows: `create schedule → programming → test code → test system → user test` (70 days).\\n\\n**One caveat.** AOA can only express finish-to-start logic — there's no way to draw a start-to-start or finish-to-finish relationship as an arrow-between-events. If your network uses SS/FF/SF or lag, those are flattened to FS with a warning. For real scheduling, prefer the default `network` (AON) layout; reach for `aoa` when you need to match a textbook figure.\"\n },\n {\n \"slug\": \"pert-migration-timescaled\",\n \"diagram\": \"pert\",\n \"title\": \"Time-scaled migration plan (network-Gantt)\",\n \"description\": \"A data-centre migration in the time-scaled layout — x-position proportional to Early Start, width proportional to duration, with a unit time axis. Exercises start-to-start and finish-to-finish dependencies, lag/lead, a milestone, and lane packing.\",\n \"standard\": \"PMI PMBOK 7 + Moder 1983 (AON/PDM)\",\n \"tags\": [\n \"pert\",\n \"cpm\",\n \"timescaled\",\n \"gantt\",\n \"dependencies\",\n \"milestone\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"pert\\ntitle: \\\"Data-centre migration\\\"\\nunit: days\\nlayout: timescaled\\n\\ntask A \\\"Inventory systems\\\" duration: 5\\ntask B \\\"Stakeholder review\\\" duration: 6 after: A SS+1\\ntask C \\\"Vendor selection\\\" duration: 8 after: A, B\\ntask D \\\"Architecture\\\" duration: 10 after: C\\ntask E \\\"Procurement\\\" duration: 12 after: C+2\\ntask F \\\"Code refactor\\\" duration: 15 after: D\\ntask G \\\"Pilot env\\\" duration: 5 after: E, F FF\\ntask H \\\"Pilot run\\\" duration: 7 after: G\\ntask I \\\"Cutover\\\" milestone after: H\\ntask J \\\"Hypercare\\\" duration: 5 after: I\",\n \"notes\": \"`layout: timescaled` is the bridge between the network view and a Gantt chart. Each activity's **x-position is proportional to its Early Start** and its **width is proportional to its duration**, with a unit time axis along the bottom — so distance reads as time.\\n\\n**Full PDM dependencies.** This network uses every relationship type beyond the default finish-to-start. `B after: A SS+1` is start-to-start with a 1-day lag (stakeholder review begins a day into the inventory). `E after: C+2` is FS with a 2-day lag (procurement waits two days after vendor selection finishes). `G after: ... F FF` is finish-to-finish (the pilot environment must be ready *when* the code refactor finishes, not after it). Each non-FS edge is labelled on the connector (`SS+1d`, `FS+2d`, `FF`).\\n\\n**Milestones and lane packing.** `Cutover` is declared with the `milestone` flag — a zero-duration checkpoint drawn as a diamond at its scheduled date. To avoid overlaps, activities are greedily packed into horizontal lanes by start time, and task names sit above their bars (Gantt convention) so long labels never overflow a short bar.\\n\\n**Still computed, still critical-aware.** Even in the time-scaled view the critical path is highlighted in red and slack is shown on each bar. `Procurement` (E) carries 6 days of slack and renders in blue, visibly off the critical chain `A → C → D → F → G → H → I → J`.\"\n },\n {\n \"slug\": \"pert-product-launch\",\n \"diagram\": \"pert\",\n \"title\": \"Q3 product launch PERT/CPM network\",\n \"description\": \"A seven-task product-launch schedule where the engine computes every Early/Late Start & Finish, total slack, and the critical path (A → C → D → E → G) automatically — the canonical activity-on-node network with the six-field box.\",\n \"standard\": \"PMI PMBOK 7 + Moder 1983 (AON/PDM)\",\n \"tags\": [\n \"pert\",\n \"cpm\",\n \"critical-path\",\n \"schedule\",\n \"project-management\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"pert\\ntitle: \\\"Q3 Product Launch\\\"\\nunit: days\\n\\ntask A \\\"Market research\\\" duration: 5\\ntask B \\\"Design mockups\\\" duration: 8 after: A\\ntask C \\\"Backend API\\\" duration: 15 after: A\\ntask D \\\"Frontend build\\\" duration: 10 after: B, C\\ntask E \\\"QA / testing\\\" duration: 5 after: D\\ntask F \\\"Marketing collateral\\\" duration: 7 after: B\\ntask G \\\"Launch event\\\" duration: 2 after: E, F\",\n \"notes\": \"This is what separates Schematex's `pert` engine from a drag-and-drop \\\"PERT chart maker\\\": you write only the tasks, durations, and dependencies — the schedule is *computed*.\\n\\n**The six-field box.** Each activity renders as the canonical 3×2 PERT/CPM rectangle: Early Start / Duration / Early Finish on top, the task name and id in the middle, and Late Start / Slack / Late Finish on the bottom. Those four schedule fields are never typed by hand — a forward pass walks the network to find ES/EF, a backward pass finds LS/LF, and total slack is `LS − ES`.\\n\\n**The critical path falls out of the math.** Any activity with zero slack is on the critical path and is drawn in red: `A → C → D → E → G`. Here `C` (Backend API, 15 days) is the long pole — it dominates `B` (Design mockups, 8 days) at the `D` merge, so the schedule pivots on the backend, not the design. Change `C` to 6 days and the critical path shifts to run through `B` instead; the render updates because the computation does.\\n\\n**Parallel work, one merge.** `B` and `C` both start once research finishes, run concurrently, and re-converge at `D` (Frontend build). `F` (Marketing collateral) branches off `B` and carries slack — it can slip several days without moving the launch. The project duration, 37 days, is the maximum Early Finish over the terminal tasks, not simply the last task you declared.\"\n },\n {\n \"slug\": \"pert-swimlane-online-shop\",\n \"diagram\": \"pert\",\n \"title\": \"PERT swimlanes grouped by team\",\n \"description\": \"The same computed activity-on-node schedule, re-grouped into horizontal swimlanes by responsible team (Customer Account, Shopping Site, Shopping Cart, Testing) — the way Visual Paradigm and PMO templates present a project network.\",\n \"standard\": \"PMI PMBOK 7 + Moder 1983 (AON/PDM)\",\n \"tags\": [\n \"pert\",\n \"cpm\",\n \"swimlane\",\n \"critical-path\",\n \"project-management\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"pert\\ntitle: \\\"Online Shop Project\\\"\\nunit: days\\n\\ntask T1 \\\"Support Account Deletion\\\" duration: 3 lane: \\\"Customer Account\\\"\\ntask T2 \\\"Design a New Theme\\\" duration: 8 lane: \\\"Shopping Site\\\"\\ntask T3 \\\"Apply New Theme to the Site\\\" duration: 15 after: T2 lane: \\\"Shopping Site\\\"\\ntask T4 \\\"Improve Searching\\\" duration: 7 after: T3 lane: \\\"Shopping Site\\\"\\ntask T5 \\\"Enhance Shopping Cart Functionality\\\" duration: 8 after: T2 lane: \\\"Shopping Cart\\\"\\ntask T6 \\\"Enhance Shopping Cart Checkout\\\" duration: 6 after: T5 lane: \\\"Shopping Cart\\\"\\ntask T7 \\\"Ready Testing Environment\\\" duration: 2 after: T1, T4, T6 lane: \\\"Testing\\\"\\ntask T8 \\\"Test Online Shop\\\" duration: 8 after: T7 lane: \\\"Testing\\\"\",\n \"notes\": \"A flat network answers \\\"what's the critical path?\\\"; a swimlane network also answers \\\"who owns what?\\\". Add `lane: \\\"…\\\"` to any task and the diagram re-groups into horizontal bands — by team, phase, or owner — without changing a single number in the schedule.\\n\\n**Same engine, different presentation.** This is still pure activity-on-node: the forward/backward pass runs exactly as it would without lanes, so the critical path `T2 → T3 → T4 → T7 → T8` and every slack value are identical to the ungrouped layout. Lanes are a *view*, not a computation — they activate automatically the moment any task declares one.\\n\\n**Reading the bands.** Lanes appear in first-declared order, each as a banded row with a label gutter on the left. A task sits in the column for its dependency depth but the row for its team, so you can trace both the time order (left to right) and the ownership (top to bottom) at a glance. Dependencies that cross teams — `T1` (Customer Account) and `T4` (Shopping Site) and `T6` (Shopping Cart) all feeding `T7` (Testing) — render as orthogonal connectors hopping between bands, making hand-off points obvious.\\n\\n**Where this helps.** This is the format PMO templates and tools like Visual Paradigm use for status reviews, because a stakeholder can find their team's row instantly and see what it's waiting on and what's waiting on it.\"\n },\n {\n \"slug\": \"pert-three-point-estimation\",\n \"diagram\": \"pert\",\n \"title\": \"Three-point (PERT) estimation with variance\",\n \"description\": \"Durations written as optimistic/most-likely/pessimistic (O/M/P). The engine computes the beta-distribution expected duration te = (O+4M+P)/6 per activity, the per-task variance, and the project-level standard deviation along the critical path.\",\n \"standard\": \"PMI PMBOK 7 — three-point (beta-PERT) estimation\",\n \"tags\": [\n \"pert\",\n \"three-point\",\n \"estimation\",\n \"variance\",\n \"critical-path\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"pert\\ntitle: \\\"Three-point project\\\"\\nunit: days\\ncritical-tolerance: 0.01\\n\\ntask A \\\"Spec\\\" duration: 2/3/5\\ntask B \\\"Build\\\" duration: 5/8/14 after: A\\ntask C \\\"Test\\\" duration: 3/4/6 after: B\\ntask D \\\"Deploy\\\" duration: 1/2/3 after: C\",\n \"notes\": \"PERT's original 1959 contribution wasn't the box — it was handling *uncertainty* in durations. Write a duration as `O/M/P` (optimistic / most-likely / pessimistic) and the engine treats it as a beta-PERT estimate.\\n\\n**Expected duration.** Each three-point activity collapses to its beta-distribution mean **te = (O + 4M + P) / 6**, which is what the Duration field shows. `Build` at `5/8/14` becomes `te = 8.5` — pulled above the most-likely 8 because the pessimistic tail (14) is fatter than the optimistic one (5). The whole forward/backward pass then runs on these `te` values, so the project duration here is ≈ 17.83 days, not an integer.\\n\\n**Variance and project risk.** Each activity also carries `σ² = ((P − O) / 6)²`, surfaced as a small `σ=…` annotation under the name and on a `data-pert-variance` attribute. Summed over the critical-path activities (under the classical independence assumption), the project standard deviation is ≈ 1.69 days — a one-line risk figure you can quote: \\\"≈68% chance of finishing within ±1.69 days of 17.83.\\\"\\n\\n**Why `critical-tolerance: 0.01`.** Once durations are fractional, accumulated floating-point error can give a truly-critical activity a slack of `0.0000001`. The tolerance treats anything within 0.01 of zero as critical, so the red path stays stable across runs. For all-integer projects you can leave it at the default of `0`.\"\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\": \"prisma-dual-pipeline\",\n \"diagram\": \"prisma\",\n \"title\": \"PRISMA 2020 dual pipeline (databases + other methods)\",\n \"description\": \"PRISMA 2020 flow diagram in dual-pipeline mode — identification via databases and registers plus a second column for other methods (citation searching, hand searches, expert recommendations), merged into screening via a Y-junction.\",\n \"standard\": \"PRISMA 2020 (Page et al., BMJ 2021;372:n71)\",\n \"tags\": [\n \"prisma\",\n \"systematic-review\",\n \"dual-pipeline\",\n \"citation-search\",\n \"evidence-synthesis\",\n \"meta-analysis\"\n ],\n \"complexity\": 3,\n \"featured\": false,\n \"dsl\": \"prisma\\nmode: 2020-dual\\ntitle: Effect of yoga on chronic back pain — SR\\n\\nidentification:\\n databases:\\n n: 1234\\n sources: PubMed=600, Embase=450, Cochrane=184\\n duplicates-removed: 254\\n other:\\n n: 56\\n sources: citation-search=30, hand-search=20, expert-recommendation=6\\n\\nscreening:\\n records-screened: 1036\\n excluded:\\n n: 810\\n reasons: duplicate=120, irrelevant title=560, non-English=130\\n reports-sought: 226\\n reports-not-retrieved: 12\\n\\neligibility:\\n full-text-assessed: 214\\n excluded:\\n n: 191\\n reasons: wrong population=80, wrong intervention=60, wrong outcome=51\\n\\nincluded:\\n studies: 23\\n reports: 25\",\n \"notes\": \"## Scenario\\n\\nThe 2020 update to PRISMA added a **second identification pathway** for studies found outside formal database searches — citation chasing, hand-searching reference lists, and expert recommendations. Reviewers must report these separately and show both streams merging into a single screening process. The `prisma` engine renders the two columns side by side with a Y-junction merge.\\n\\n## Annotation key\\n\\n- **`mode: 2020-dual`** — adds the right-hand \\\"Identification via other methods\\\" column. The engine auto-detects dual mode whenever an `other:` block is present, so the explicit `mode` line is optional here.\\n- **`identification.other`** — its own `n:` total and `sources:` breakdown (citation search, hand search, expert recommendation).\\n- **`reports-sought:` / `reports-not-retrieved:`** — optional Screening detail lines for the retrieval step.\\n- **Y-junction merge** — the databases stream is the trunk; the other-methods stream drops and runs left to join it just above Screening.\\n\\n## Standard reference\\n\\nPage MJ, McKenzie JE, Bossuyt PM, et al. *The PRISMA 2020 statement.* BMJ 2021;372:n71, Figure 2 (dual-source variant).\"\n },\n {\n \"slug\": \"prisma-systematic-review\",\n \"diagram\": \"prisma\",\n \"title\": \"PRISMA 2020 systematic review (single pipeline)\",\n \"description\": \"Canonical PRISMA 2020 flow diagram for a systematic review using the dedicated prisma engine — records identified across four databases, deduplicated, screened, assessed for eligibility, and included, with exclusion side-boxes and mandatory n = counts.\",\n \"standard\": \"PRISMA 2020 (Page et al., BMJ 2021;372:n71)\",\n \"tags\": [\n \"prisma\",\n \"systematic-review\",\n \"meta-analysis\",\n \"research\",\n \"evidence-synthesis\",\n \"cochrane\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"prisma\\nmode: 2020-single\\ntitle: Effect of exercise on chronic low-back pain — SR\\n\\nidentification:\\n databases:\\n n: 1418\\n sources: PubMed=600, Embase=450, Cochrane=184, Web of Science=184\\n duplicates-removed: 318\\n\\nscreening:\\n records-screened: 1100\\n excluded:\\n n: 870\\n reasons: irrelevant title=750, non-English=120\\n\\neligibility:\\n full-text-assessed: 230\\n excluded:\\n n: 195\\n reasons: wrong population=80, wrong intervention=60, wrong outcome=55\\n\\nincluded:\\n studies: 35\\n reports: 38\",\n \"notes\": \"## Scenario\\n\\nA research librarian produces the PRISMA 2020 flow diagram for a Cochrane review submission. The journal **requires** the diagram in the exact four-row structure — Identification → Screening → Eligibility → Included — with the count `(n = …)` shown in every box and the excluded boxes itemizing reasons. With the dedicated `prisma` engine the librarian writes only counts and reasons; the rigid layout, the \\\"Records removed before screening\\\" side-box, and the exclusion side-boxes are produced automatically and are correct by construction.\\n\\n## Annotation key\\n\\n- **`mode: 2020-single`** — one Identification column (databases & registers). Use `mode: 2020-dual` to add the \\\"other methods\\\" column.\\n- **`identification.databases`** — `n:` is the mandatory total; `sources:` renders the per-database breakdown; `duplicates-removed:` splits out into the right-column \\\"Records removed before screening\\\" box automatically.\\n- **`screening` / `eligibility` `excluded:` blocks** — each has its own `n:` total and an optional `reasons:` breakdown rendered in a side-box to the right, connected by a horizontal arrow.\\n- **`included.studies` / `reports`** — one study can yield several reports, so both counts render.\\n\\n## How to read\\n\\nTop to bottom mirrors the PRISMA 2020 template exactly. The left capsule bands label the three canonical stages; the orange bar spans the Identification column group. Counts reconcile across stages (1418 − 318 = 1100 screened; 1100 − 870 = 230 assessed; 230 − 195 = 35 included) — the engine warns if they don't.\\n\\n## Standard reference\\n\\nPage MJ, McKenzie JE, Bossuyt PM, et al. *The PRISMA 2020 statement: an updated guideline for reporting systematic reviews.* BMJ 2021;372:n71. Template: [prisma-statement.org/prisma-2020-flow-diagram](https://www.prisma-statement.org/prisma-2020-flow-diagram).\"\n },\n {\n \"slug\": \"sfc-bake-cool-concurrent\",\n \"diagram\": \"sfc\",\n \"title\": \"Bake & cool concurrent batch (SFC)\",\n \"description\": \"Simultaneous-branch SFC of a batch oven that bakes (15-minute D-qualified action) and cools the chamber jacket (5-minute L-qualified action) concurrently after a heat-up phase. Both branches must complete before the converge bar fires. Exercises the IEC 61131-3 §6.5.4 double-bar simultaneous divergence and time-parameterized action qualifiers.\",\n \"standard\": \"IEC 61131-3:2013 §6.5\",\n \"tags\": [\n \"sfc\",\n \"plc\",\n \"simultaneous-branch\",\n \"time-qualified\",\n \"batch-reactor\",\n \"oven\"\n ],\n \"complexity\": 3,\n \"featured\": true,\n \"dsl\": \"sfc \\\"Bake and cool concurrently\\\"\\n\\nvar BakeReady: bool\\nvar Bake_Done: bool\\nvar Cool_Done: bool\\n\\nstep S0 [initial]\\n\\nstep S_Heat\\n N Heater_On\\n\\nsim from: S_Heat: TRUE\\n branch:\\n step S_Bake\\n D Oven_Run T#15m\\n branch:\\n step S_Cool\\n L Cooler_On T#5m\\nmerge_to: S_Done: Bake_Done AND Cool_Done\\n\\nstep S_Done\\n N AnnounceDone\\n\\ntransition from: S0 to: S_Heat: BakeReady\\ntransition from: S_Done to: S0: NOT BakeReady\",\n \"notes\": \"A real batch reactor or industrial oven rarely runs phases one at a time — once the chamber is hot, you want to start the bake timer and the chamber-jacket cooling cycle simultaneously, because the cooling jacket protects equipment downstream and has its own independent timeout. SFC's simultaneous-branch construct captures this exactly: one entry transition fires both branches in lockstep, both branches run concurrently, and the chart waits at the convergence until *every* branch is done.\\n\\n**Two parallel horizontal lines = simultaneous bar.** The IEC 61131-3 §6.5.4 grammar uses **single-line bars** for alternative (OR-semantic) branches and **double-line bars** for simultaneous (AND-semantic) branches — visually distinct so an experienced eye can tell at a glance which kind of fork they're looking at. Schematex renders the double bar with a 4px gap between the two lines, exactly per spec.\\n\\n**The shared transition above and below.** Notice the `TRUE` transition just above the simultaneous divergence — that's the *single shared* condition that triggers the fork. Both branches start at once when `TRUE` fires (in this case unconditionally, because the heater step's job was already complete). At the bottom, the `Bake_Done AND Cool_Done` is the merge condition: the chart waits at the simultaneous convergence until *both* branch outcomes are asserted, then moves to S_Done. This shared-transition placement (above the divergence, below the convergence) is the inverse of how alternative branches work — alternative branches have their condition *between* the bar and each branch.\\n\\n**D vs L qualifiers — different time semantics.** S_Bake's `D Oven_Run T#15m` is a **time-delayed** action: when the step becomes active, wait 15 minutes, *then* start running `Oven_Run`. This matches a real oven: you want a soak interval before the heating element actually fires. S_Cool's `L Cooler_On T#5m` is **time-limited**: start running `Cooler_On` immediately on step entry, but stop after 5 minutes regardless. Both qualifiers render with a small bottom row inside the action block showing the duration literal — `T#15m` and `T#5m` — so anyone reviewing the chart can read the timing at a glance without cross-referencing a separate timing table.\\n\\n**Why concurrency matters here.** If the bake and cool ran sequentially, the total recipe time would be `15m + 5m = 20m` per batch. Running them in parallel cuts to `max(15m, 5m) = 15m`. Across 50 batches per shift, that's nearly an extra hour of throughput — the kind of optimization that's invisible in ladder-only programs but obvious in SFC. The reason most plants don't have it: the engineer didn't have a graphical sequential language in their toolbox. Schematex fills that gap.\"\n },\n {\n \"slug\": \"sfc-bottle-filling\",\n \"diagram\": \"sfc\",\n \"title\": \"Bottle filling sequence (SFC)\",\n \"description\": \"Three-step sequential function chart for a bottle-filling station — idle with valve closed, fill while tank level rises, signal done. Exercises the IEC 61131-3 §6.5 initial-step double border, N-qualified actions, and condition expressions on transitions.\",\n \"standard\": \"IEC 61131-3:2013 §6.5\",\n \"tags\": [\n \"sfc\",\n \"plc\",\n \"iec-61131-3\",\n \"sequential-control\",\n \"packaging\"\n ],\n \"complexity\": 1,\n \"featured\": true,\n \"dsl\": \"sfc \\\"Bottle Filling\\\"\\n\\nvar StartBtn: bool\\nvar TankLevel: real\\nvar DoneBtn: bool\\n\\nstep S0 [initial]\\n N FillValve_Closed\\n\\nstep S1 [label: \\\"Filling\\\"]\\n N FillValve_Open\\n\\nstep S2 [label: \\\"Done\\\"]\\n N Confirm_Done\\n\\ntransition from: S0 to: S1: StartBtn\\ntransition from: S1 to: S2: TankLevel >= 80.0\\ntransition from: S2 to: S0: DoneBtn\",\n \"notes\": \"Sequential function charts are the most underused PLC language. Most automation code is written in ladder, but ladder describes per-scan combinational logic — it can't express \\\"we're in the *fill* phase right now and the next phase begins when the tank hits 80%.\\\" Engineers fake state machines in ladder using auxiliary boolean tags (one per state, set/reset by transition logic), and the result is unreadable six months later. SFC makes the state machine explicit: each step is a phase, each transition is a boolean condition that triggers the handoff.\\n\\n**Three steps, three phases.** S0 is the **initial step** — drawn with a double border per IEC 61131-3 §6.5.1.2, marking the entry point when the program starts. While S0 is active, its single N-qualified action `FillValve_Closed` runs every scan. The chart sits in S0 until the StartBtn transition fires; then control moves to S1.\\n\\n**S1 — filling phase.** While in S1, the action `FillValve_Open` runs every scan. The chart waits in S1 until the transition `TankLevel >= 80.0` evaluates true. Note that condition is just plain text — Schematex doesn't parse or evaluate IEC structured-text expressions; it stores them verbatim and renders them next to the bar. The PLC runtime is what actually evaluates.\\n\\n**S2 — done phase.** S2 holds with `Confirm_Done` running until the operator presses the DoneBtn (or whatever signals the cycle complete) — at which point we loop back to S0. Because S0 is not linearly adjacent to S2 in the body, this transition renders as a **margin jump arrow** on the side of the chart, with the target step id and the condition both labeled.\\n\\n**N qualifier — non-stored.** The `N` letter on each action means *non-stored*: the action runs only while its owning step is active, and stops automatically when the step deactivates. This is by far the most common qualifier — about 80% of real SFC actions use it. The SFC standard supports ten others (S/R for stored, L/D for time-limited/delayed, P for one-scan pulses, etc.) but for this example N is exactly right.\\n\\n**Why this is SFC and not a flowchart.** A flowchart of the same machine would show diamond decisions (\\\"is start pressed? if yes, ...\\\") and you'd lose the explicit \\\"we're *in* phase X\\\" semantics. SFC's active-step token model — one step holds the token at any moment, transitions move it — is the formal definition of *cyclic sequential control*, and it maps directly to how real PLC scan engines execute. When you bring this diagram to a code review with the maintenance team, they'll instantly recognize the pattern.\"\n },\n {\n \"slug\": \"sfc-order-routing-alt\",\n \"diagram\": \"sfc\",\n \"title\": \"Order routing — express vs standard shipping (SFC)\",\n \"description\": \"Alternative-branch SFC of an order-fulfillment routing decision. After picking the product, exactly one branch fires per scan based on the leftmost-true entry transition (priority 1 = express shipping first). Both branches converge to a common shipping step. Exercises the IEC 61131-3 §6.5.4 single-bar OR semantics, branch priority annotations, and per-branch entry/exit transitions.\",\n \"standard\": \"IEC 61131-3:2013 §6.5\",\n \"tags\": [\n \"sfc\",\n \"plc\",\n \"alternative-branch\",\n \"priority\",\n \"routing\",\n \"e-commerce\"\n ],\n \"complexity\": 2,\n \"featured\": false,\n \"dsl\": \"sfc \\\"Order routing\\\"\\n\\nvar ProductOrdered: bool\\nvar IsExpressShipping: bool\\nvar IsStandardShipping: bool\\nvar Shipped: bool\\n\\nstep S0 [initial]\\n\\nstep S_Pick\\n N PickFromBin\\n\\nalt from: S_Pick:\\n branch [priority: 1]:\\n transition: IsExpressShipping\\n step S_Express\\n N PrepExpressBox\\n transition: TRUE\\n branch [priority: 2]:\\n transition: IsStandardShipping\\n step S_Standard\\n N PrepStandardBox\\n transition: TRUE\\nmerge_to: S_Ship\\n\\nstep S_Ship\\n N CarrierPickup\\n\\ntransition from: S0 to: S_Pick: ProductOrdered\\ntransition from: S_Ship to: S0: Shipped\",\n \"notes\": \"A modern fulfillment center handles both express (next-day) and standard (3–5 day) shipping on the same physical line, with the same picking robots and the same carrier-pickup conveyor. The only divergence is the boxing step: express boxes are different sizes, use rigid corrugate, and get extra tracking labels. SFC's alternative branch is the right shape for this: one step (picking) splits into two paths (express boxing OR standard boxing) and they converge back to a common end step (carrier pickup).\\n\\n**Single horizontal bar = alternative.** IEC 61131-3 §6.5.4 uses a **single horizontal line** for alternative (OR-semantic) divergence and convergence. Visually it's the simpler cousin of the double-bar simultaneous fork; semantically it's the opposite — only *one* branch fires per scan, picked by which entry transition's condition evaluates true first under the priority order.\\n\\n**Priority annotation matters.** Both branches have `[priority: N]` markers — express is priority 1, standard is priority 2. If both `IsExpressShipping` and `IsStandardShipping` were somehow true at the same time (a bug, but a common one in early integration), the priority forces the express path. The renderer puts a small red number near each branch entry to make the priority visible at review time.\\n\\n**Per-branch entry and exit transitions.** Inside each branch, there are two `transition: ...` lines — the first is the **entry transition** (fires when control reaches the divergence bar; renders between the bar and the first step in that branch), and the second is the **exit transition** (fires when the branch's last step completes; renders between that step and the convergence bar). Most exit transitions are `TRUE` (unconditional) because the *step* itself is what's gating the work — once the step completes, you want to leave the branch. The entry transitions are where the actual decision logic lives.\\n\\n**Why not flowchart?** A flowchart of this would render the routing as a diamond with two branches and labels on each branch. You'd lose the explicit step semantics — in flowchart, the diamond *is* the decision point and there's no notion of \\\"the picking step is currently active and the next step depends on shipping type.\\\" That distinction matters for PLC code: while the picking step is active, the picking robot is physically holding the product, and you need to know exactly when the routing decision happens (at step exit, not step entry) to coordinate the conveyor handoff. SFC's \\\"step active → transition → next step active\\\" semantics are what real PLC scan engines do; flowchart's diamond/box semantics are not.\\n\\n**The shared S_Ship destination.** Both branches converge to S_Ship, where `CarrierPickup` runs. This is one of the strengths of alternative-branch SFC: the post-routing step is shared infrastructure, drawn exactly once. If you moved to multiple destinations (e.g. express has its own carrier door, standard has another), you'd skip the convergence and let each branch end at its own terminal step — also a valid SFC pattern.\"\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-residential-iec-60364-consumer-unit\",\n \"diagram\": \"sld\",\n \"title\": \"Residential consumer unit — generic IEC 60364\",\n \"description\": \"Generic European residential consumer unit per IEC 60364 / EN 61439-3 — service head, isolator, type-AC main RCD, and per-circuit MCBs for lighting, sockets, kitchen, water heater, EV charger (with type-B RCBO), and outdoor circuit.\",\n \"standard\": \"IEC 60364 / EN 61439-3\",\n \"tags\": [\n \"sld\",\n \"residential\",\n \"iec-60364\",\n \"en-61439\",\n \"europe\",\n \"consumer-unit\",\n \"rcd\",\n \"rcbo\",\n \"mcb\",\n \"isolator\",\n \"ev-charger\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"sld \\\"Residential Consumer Unit — IEC 60364\\\"\\nSERVICE = utility [voltage: \\\"230V\\\", label: \\\"Service head 230V/63A\\\"]\\nKWHM = watthour_meter [label: \\\"kWh meter\\\"]\\nISO = breaker [rating: \\\"63A 6kA\\\", label: \\\"Main isolator\\\"]\\nRCD = ground_fault [rating: \\\"63A / 30mA Type AC\\\", label: \\\"Main RCD\\\"]\\nCU = bus [voltage: \\\"230V\\\", label: \\\"Consumer-unit busbar\\\"]\\nMCB1 = breaker [rating: \\\"6A B-curve\\\", label: \\\"MCB lighting\\\"]\\nMCB2 = breaker [rating: \\\"16A B-curve\\\", label: \\\"MCB sockets ground floor\\\"]\\nMCB3 = breaker [rating: \\\"16A B-curve\\\", label: \\\"MCB sockets first floor\\\"]\\nMCB4 = breaker [rating: \\\"20A C-curve\\\", label: \\\"MCB kitchen ring\\\"]\\nMCB5 = breaker [rating: \\\"20A C-curve\\\", label: \\\"MCB water heater\\\"]\\nRCBO_EV = ground_fault [rating: \\\"32A / 30mA Type B\\\", label: \\\"RCBO EV charger\\\"]\\nMCB6 = breaker [rating: \\\"10A B-curve\\\", label: \\\"MCB outdoor\\\"]\\nL1 = load [label: \\\"Lighting circuit\\\"]\\nL2 = load [label: \\\"Sockets — ground floor\\\"]\\nL3 = load [label: \\\"Sockets — first floor\\\"]\\nL4 = load [label: \\\"Kitchen ring final\\\"]\\nL5 = load [label: \\\"Water heater 4.5 kW\\\"]\\nEV = load [label: \\\"EV charger 7.4 kW Mode 3\\\"]\\nL6 = load [label: \\\"Outdoor / garden\\\"]\\nSERVICE -> KWHM\\nKWHM -> ISO\\nISO -> RCD\\nRCD -> CU\\nCU -> MCB1\\nMCB1 -> L1 [cable: \\\"1.5 mm² Cu PVC\\\"]\\nCU -> MCB2\\nMCB2 -> L2 [cable: \\\"2.5 mm² Cu PVC\\\"]\\nCU -> MCB3\\nMCB3 -> L3 [cable: \\\"2.5 mm² Cu PVC\\\"]\\nCU -> MCB4\\nMCB4 -> L4 [cable: \\\"4 mm² Cu PVC\\\"]\\nCU -> MCB5\\nMCB5 -> L5 [cable: \\\"4 mm² Cu PVC\\\"]\\nCU -> RCBO_EV\\nRCBO_EV -> EV [cable: \\\"6 mm² Cu XLPE\\\"]\\nCU -> MCB6\\nMCB6 -> L6 [cable: \\\"2.5 mm² Cu XLPE\\\"]\",\n \"notes\": \"## Scenario\\n\\nA domestic electrical installer in any IEC 60364 jurisdiction — France (NF C 15-100), Germany (DIN VDE 0100), the United Kingdom (BS 7671), Italy (CEI 64-8), Australia / New Zealand (AS/NZS 3000), and most of the rest of the world outside the NEC — files this single-line as part of the *Certificat de Conformité* / *Elektroinstallationsattest* / *Electrical Installation Condition Report* required after a new installation or major alteration. EN 61439-3 is the European harmonized standard for *Distribution Boards intended to be operated by Ordinary persons* (DBO), and IEC 60364-4-41 governs the protection-against-shock chain (isolation → 30 mA RCD → branch protection). Unlike NEC residential practice where the *panel* lives as a leaf on the SLD with internals on a separate panel schedule, the IEC 60364 single-line is expected to expand the consumer unit's internal protection chain.\\n\\n## Annotation key\\n\\n- `utility [voltage:..., label: \\\"Service head…\\\"]` — the cut-out / service-fuse head where the distribution network terminates and the customer installation begins\\n- `watthour_meter` — utility-owned kWh meter (smart meter in most modern installations)\\n- `breaker [label: \\\"Main isolator\\\"]` — main switch / isolator that disconnects the entire installation, sized at or above the cut-out fuse rating\\n- `ground_fault [rating: \\\"…A / 30mA Type AC\\\", label: \\\"Main RCD\\\"]` — whole-installation residual-current device, 30 mA sensitivity, Type AC (the IEC 60364 default for circuits without significant DC residual content)\\n- `bus [voltage: \\\"230V\\\", label: \\\"Consumer-unit busbar\\\"]` — the internal busbar of the consumer unit, rendered as a horizontal rail to which every branch device connects\\n- `breaker [rating: \\\"…A B-curve\\\" or \\\"…A C-curve\\\", label: \\\"MCB …\\\"]` — branch *miniature circuit breaker*: B-curve (3–5 × In) for resistive / lighting / general-sockets circuits, C-curve (5–10 × In) for inductive loads (kitchen ring, immersion heater) — per IEC 60898-1\\n- `ground_fault [rating: \\\"32A / 30mA Type B\\\", label: \\\"RCBO EV charger\\\"]` — Type B RCBO is mandatory on EV charging circuits per IEC 60364-7-722 / HD 60364-7-722 because Mode-3 chargers can leak DC residual currents that blind a Type AC RCD\\n- `load` — the final circuit served by each MCB / RCBO\\n- `[cable: \\\"… mm² Cu PVC|XLPE\\\"]` — conductor cross-sectional area, copper, with PVC insulation for indoor cables and XLPE for outdoor / EV runs\\n\\n## How to read\\n\\nThe service head delivers single-phase 230 V at the cut-out fuse rating (63 A here). The kWh meter records consumption, then the main isolator gives a single-action means of disconnecting the entire installation for maintenance. Downstream of the isolator the main RCD provides shock protection at 30 mA Type AC for the bulk of branch circuits; from the consumer-unit busbar, six MCBs feed lighting, two socket circuits, a kitchen ring, a water heater, and an outdoor circuit. The EV charger does not share the main RCD — IEC 60364-7-722 requires a Type B RCBO on the charger circuit because the Mode-3 charge controller can produce smooth DC residual currents that a Type AC RCD cannot detect, leaving people unprotected against shock during a DC fault. Cable sizes scale with the MCB rating per the relevant national reference method (1.5 mm² for 6 A lighting, 2.5 mm² for 16 A sockets, 4 mm² for 20 A kitchen / heater, 6 mm² for the 32 A EV charger). An inspector reads the diagram top-down to verify the protection chain and the discrimination between the main RCD and the EV-circuit RCBO.\"\n },\n {\n \"slug\": \"sld-residential-rebt-cgmp\",\n \"diagram\": \"sld\",\n \"title\": \"Residential CGMP — Spanish REBT vivienda\",\n \"description\": \"Cuadro General de Mando y Protección for a Spanish residence per REBT ITC-BT-17 — acometida, contador/ICP, IGA, diferencial Tipo A 30 mA, and per-circuit PIAs (MCBs) feeding lighting, sockets, kitchen, washer, bathroom, and HVAC circuits with H07V-K cables.\",\n \"standard\": \"REBT ITC-BT-17\",\n \"tags\": [\n \"sld\",\n \"residential\",\n \"rebt\",\n \"spain\",\n \"latam\",\n \"cgmp\",\n \"iga\",\n \"diferencial\",\n \"pia\",\n \"mcb\",\n \"iec-60364\",\n \"h07v-k\",\n \"esquema-unifilar\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"sld \\\"Vivienda — CGMP REBT ITC-BT-17\\\"\\nACOM = utility [voltage: \\\"230V\\\", label: \\\"Acometida 230V mono\\\"]\\nICP = watthour_meter [label: \\\"Contador + ICP 25A\\\"]\\nIGA = breaker [rating: \\\"40A curva C, 6kA\\\", label: \\\"IGA\\\"]\\nID = ground_fault [rating: \\\"40A / 30mA Tipo A\\\", label: \\\"Diferencial general\\\"]\\nBUS = bus [voltage: \\\"230V\\\", label: \\\"Embarrado CGMP\\\"]\\nC1 = breaker [rating: \\\"10A curva C\\\", label: \\\"PIA C1\\\"]\\nC2 = breaker [rating: \\\"16A curva C\\\", label: \\\"PIA C2\\\"]\\nC3 = breaker [rating: \\\"25A curva C\\\", label: \\\"PIA C3\\\"]\\nC4 = breaker [rating: \\\"20A curva C\\\", label: \\\"PIA C4\\\"]\\nC5 = breaker [rating: \\\"16A curva C\\\", label: \\\"PIA C5\\\"]\\nC6 = breaker [rating: \\\"25A curva C\\\", label: \\\"PIA C6\\\"]\\nL1 = load [label: \\\"C1 Iluminación\\\"]\\nL2 = load [label: \\\"C2 Tomas uso general\\\"]\\nL3 = load [label: \\\"C3 Cocina + horno\\\"]\\nL4 = load [label: \\\"C4 Lavadora / lavavajillas\\\"]\\nL5 = load [label: \\\"C5 Tomas baño y cocina\\\"]\\nL6 = load [label: \\\"C6 Aerotermia / climatización\\\"]\\nACOM -> ICP\\nICP -> IGA\\nIGA -> ID\\nID -> BUS\\nBUS -> C1\\nC1 -> L1 [cable: \\\"1.5 mm² Cu H07V-K\\\"]\\nBUS -> C2\\nC2 -> L2 [cable: \\\"2.5 mm² Cu H07V-K\\\"]\\nBUS -> C3\\nC3 -> L3 [cable: \\\"6 mm² Cu H07V-K\\\"]\\nBUS -> C4\\nC4 -> L4 [cable: \\\"4 mm² Cu H07V-K\\\"]\\nBUS -> C5\\nC5 -> L5 [cable: \\\"2.5 mm² Cu H07V-K\\\"]\\nBUS -> C6\\nC6 -> L6 [cable: \\\"6 mm² Cu H07V-K\\\"]\",\n \"notes\": \"## Scenario\\n\\nA Spanish electrician (or an electrical-engineering student preparing the *Esquema Unifilar* for a course or permit) documents the *Cuadro General de Mando y Protección* (CGMP) of a single-family home. Spain's *Reglamento Electrotécnico para Baja Tensión* (REBT) §ITC-BT-17 is explicit about what the residential single-line must show: the incoming *acometida*, the utility-owned *Interruptor de Control de Potencia* (ICP) inside the meter cabinet, the consumer-owned *Interruptor General Automático* (IGA), one or more residual-current devices (*Diferenciales*), and a per-circuit *Pequeño Interruptor Automático* (PIA / MCB) for every final circuit — typically C1 through C6 in a basic *grado de electrificación básica* dwelling. The same pattern applies across Latin America (ABNT NBR 5410 in Brazil, NMX-J-098 in Mexico) and to most IEC-60364 jurisdictions, in contrast to NEC residential practice where the panel internals would normally live on a separate panel schedule.\\n\\n## Annotation key\\n\\n- `utility [voltage:..., label:...]` — *Acometida*: the 230 V single-phase service drop from the distribution network up to the meter cabinet\\n- `watthour_meter [label:...]` — *Contador + ICP*: revenue meter and the utility's tamper-sealed power-limiter breaker (25 A here, contracted at 5.75 kW)\\n- `breaker [rating:\\\"…A curva C, …kA\\\", label: \\\"IGA\\\"]` — *Interruptor General Automático*: customer-owned main breaker; REBT requires curve C and ≥ 6 kA breaking capacity for residential\\n- `ground_fault [rating: \\\"…A / 30mA Tipo A\\\", label: \\\"Diferencial general\\\"]` — Type-A residual-current device tripping at 30 mA per ITC-BT-17 §1.2 (Tipo A covers AC + pulsating DC residual currents from electronics and inverter loads)\\n- `bus [voltage: \\\"230V\\\", label: \\\"Embarrado CGMP\\\"]` — common busbar inside the consumer unit; every PIA taps off this rail\\n- `breaker [rating:\\\"…A curva C\\\", label: \\\"PIA Cn\\\"]` — branch-circuit *PIA*: one MCB per final circuit, sized per ITC-BT-25 Table 1 (10 A lighting, 16 A general sockets, 20–25 A kitchen / washer / HVAC)\\n- `load [label: \\\"Cn …\\\"]` — the final circuit's loads grouped under their REBT circuit designation (C1–C6)\\n- `[cable: \\\"… mm² Cu H07V-K\\\"]` — conductor cross-sectional area, copper, single-core insulated H07V-K — the canonical cable type for residential indoor wiring per UNE-EN 50525\\n\\n## How to read\\n\\nPower enters at the *acometida* and reaches the meter cabinet, where the utility's ICP enforces the contracted 5.75 kW power limit. Past the meter, the customer's CGMP begins: the IGA isolates the whole installation; the *Diferencial general* (40 A, 30 mA, Type A) trips on any earth-leakage fault to protect against electric shock per ITC-BT-24 §4.1; downstream of the differential, the busbar fans out to six branch circuits — lighting (1.5 mm²), general sockets (2.5 mm²), kitchen and oven (6 mm²), high-current appliances such as washer or dishwasher (4 mm²), bathroom and kitchen sockets (2.5 mm²), and the heat-pump / aerotermia (6 mm²). Each branch passes through its own PIA (curve C, sized per ITC-BT-25) before reaching the final circuit, so a fault in any one circuit drops only that circuit, not the whole dwelling. An REBT inspector reads this diagram top-down to verify selectivity (PIA < differential < IGA < ICP) and that conductor sizing matches PIA rating per the §ITC-BT-19 ampacity table.\"\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\": \"usecase-atm\",\n \"diagram\": \"usecase\",\n \"title\": \"ATM use case diagram (UML)\",\n \"description\": \"The canonical software-engineering use case diagram — a Customer and an external Bank system around an ATM, with four withdraw/deposit/balance/transfer use cases. Exercises stick-figure actors, the external-system rectangle, subject sizing, and primary/supporting actor flanking.\",\n \"standard\": \"OMG UML 2.5.1 §18\",\n \"tags\": [\n \"usecase\",\n \"uml\",\n \"requirements\",\n \"actor\",\n \"subject\"\n ],\n \"complexity\": 1,\n \"featured\": true,\n \"dsl\": \"usecase\\ntitle: \\\"ATM\\\"\\nsystem: \\\"ATM System\\\"\\n\\nactor: Customer\\nactor: Bank (external)\\n\\nusecase: \\\"Withdraw Cash\\\" as Withdraw\\nusecase: \\\"Deposit Funds\\\" as Deposit\\nusecase: \\\"Check Balance\\\" as Check\\nusecase: \\\"Transfer Funds\\\" as Transfer\\n\\nCustomer -- Withdraw\\nCustomer -- Deposit\\nCustomer -- Check\\nCustomer -- Transfer\\n\\nWithdraw -- Bank\\nDeposit -- Bank\\nCheck -- Bank\\nTransfer -- Bank\",\n \"notes\": \"The ATM is the hello-world of UML use case diagrams — it appears in nearly every software-engineering textbook because it shows the whole notation with no clutter. The diagram answers \\\"what can you do at an ATM, and who does the work behind the scenes?\\\"\\n\\n**Two actors, two roles.** `Customer` is the **primary actor** — the human who initiates the interaction — so Schematex places the stick figure on the **left**. `Bank` is declared `(external)`, marking it as another software system rather than a person; it renders as a rectangle carrying the `«actor»` stereotype and is placed on the **right** as a supporting actor. This left-primary / right-supporting convention comes from the Bittner & Spence style guide and matches what reviewers expect to see.\\n\\n**Four use cases, one subject.** Each `usecase:` becomes an ellipse, and the `system: \\\"ATM System\\\"` header wraps them in a rounded **subject** rectangle with the system name at the top. The subject is sized *after* the use cases are placed, so it always hugs its contents.\\n\\n**Plain associations.** Every `--` line is an undirected association — a solid line from the actor to the use-case ellipse perimeter. The Customer triggers all four operations; each operation in turn talks to the Bank to settle the transaction. There are no `«include»` or `«extend»` relationships here — that's deliberate. The ATM diagram's job is to establish *scope*, and at this level of detail plain associations are exactly right. Reach for include/extend only when use cases genuinely compose.\"\n },\n {\n \"slug\": \"usecase-online-bookstore\",\n \"diagram\": \"usecase\",\n \"title\": \"Online bookstore use case diagram (include + extend)\",\n \"description\": \"An e-commerce checkout use case diagram showing «include» chains, an «extend» relationship with a condition and extension point, and the extension-point compartment inside the base ellipse. A primary customer, an external payment gateway, and a supporting warehouse actor.\",\n \"standard\": \"OMG UML 2.5.1 §18\",\n \"tags\": [\n \"usecase\",\n \"uml\",\n \"include\",\n \"extend\",\n \"extension-point\",\n \"requirements\"\n ],\n \"complexity\": 2,\n \"featured\": true,\n \"dsl\": \"usecase\\ntitle: \\\"Online Bookstore — Checkout\\\"\\nsystem: \\\"Bookstore System\\\"\\n\\nactor: Customer\\nactor: \\\"Payment Gateway\\\" as PG (external)\\nactor: \\\"Warehouse Staff\\\" as WH\\n\\nusecase: \\\"Browse Catalog\\\" as Browse\\nusecase: \\\"Add to Cart\\\" as AddCart\\nusecase: \\\"Checkout\\\" as Checkout {\\n extension point: payment failed\\n extension point: stock depleted\\n}\\nusecase: \\\"Pay\\\" as Pay\\nusecase: \\\"Validate Card\\\" as ValidateCard\\nusecase: \\\"Cancel Order\\\" as Cancel\\nusecase: \\\"Ship Order\\\" as Ship\\n\\nCustomer -- Browse\\nCustomer -- AddCart\\nCustomer -- Checkout\\nCheckout ..> Pay : «include»\\nPay ..> ValidateCard : «include»\\nPay -- PG\\nCancel <.. Checkout : «extend» [payment failed] (extension point: payment failed)\\nShip -- WH\\nCheckout ..> Ship : «include»\",\n \"notes\": \"This is the diagram a business analyst hands to engineering when an online store is being scoped. It uses every relationship in the use case vocabulary, and each one means something precise.\\n\\n**Three actors on two sides.** `Customer` is the primary actor (left). `Payment Gateway` is `(external)` — a third-party system, drawn as an `«actor»` rectangle on the right. `Warehouse Staff` is a supporting human actor, also on the right. Schematex flanks the subject automatically.\\n\\n**`«include»` — mandatory composition.** `Checkout ..> Pay` reads \\\"Checkout *includes* Pay\\\": every checkout always runs the payment sub-flow, so the arrow points from the base toward the included use case. The chain continues — `Pay ..> ValidateCard` — so payment always validates the card. Include is the right tool when a step *always* happens and you want to factor it out for reuse. The dashed line, open arrowhead, and `«include»` pill are all UML-standard.\\n\\n**`«extend»` — optional, conditional behavior.** `Cancel <.. Checkout` reads \\\"Cancel Order *extends* Checkout\\\": cancellation is *optional* behavior that only kicks in under a condition. The `[payment failed]` clause is that condition, and `(extension point: payment failed)` ties it to the named extension point declared inside the Checkout ellipse. Note the arrow still points at the base (Checkout) even though Cancel is written first — that's the UML rule, and Schematex normalises it for you. Extend lines are drawn in the accent color because they're the rarer, more surprising relationship.\\n\\n**Extension points live in the ellipse.** Because `Checkout` declares `extension point: payment failed` and `stock depleted`, those appear in a compartment below the name, separated by a divider. They're the hooks an `«extend»` relationship can attach to.\\n\\nThe result reads top-to-bottom as a flow: browse → add to cart → checkout, with checkout fanning out into pay (→ validate card, → payment gateway), ship (→ warehouse), and an optional cancel path. That's exactly the scope picture a stakeholder review needs.\"\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 `#`, `//`, or Mermaid-style `%%`. 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:** `#`, `//`, or `%%` 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 %% Mermaid-style comment — also 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 `#`, `//`, or Mermaid-style `%%`. 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- **Mode suffix:** `ecomap:strengths \\\"Smith family\\\"` is accepted. The suffix is stored as `metadata.mode`; current renderers ignore it.\\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 %% Mermaid-style comments are 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\\\" ( \\\":\\\" mode )? ( WS quoted-string )? NEWLINE\\nmode = [A-Za-z] [A-Za-z0-9_-]*\\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 `#`, `//`, or Mermaid-style `%%`. 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- **Mode suffix:** `pedigree:autosomal-dominant \\\"Family X\\\"` is accepted. The suffix is stored as `metadata.mode`; current renderers ignore it.\\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\\\" ( \\\":\\\" mode )? ( WS quoted-string )? NEWLINE\\nmode = [A-Za-z] [A-Za-z0-9_-]*\\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. The trailing colon is optional.\\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 may start with `#`, `//`, or Mermaid-style `%%` 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| `RES` | Reset | Rockwell / Allen-Bradley counter or timer reset coil |\\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 optional colon: `rung 3 \\\"Run indicator\\\":` or `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:** `#`, `//`, or `%%` at the start of a line (after leading whitespace). The same markers also start trailing comments.\\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) | Parsed correctly | The trailing colon is optional |\\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\\\" | \\\"RES\\\"\\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 may start with `#`, `//`, or Mermaid-style `%%` 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- **Residential aliases:** IEC / REBT vocabulary such as `mcb`, `rcd`, `rcbo`, `rccb`, `pia`, `iga`, `main_switch`, `consumer_unit`, `distribution_board`, `panel`, and `panelboard` is accepted as input and mapped to existing SLD primitives.\\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| `id = breakerz` | `SLDParseError: Unknown node type ... (did you mean 'breaker'?)` | Use the suggested canonical type or alias |\\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 | residential-alias\\n\\nresidential-alias\\n = \\\"mcb\\\" | \\\"mccb\\\" | \\\"rcd\\\" | \\\"rcbo\\\" | \\\"rccb\\\"\\n | \\\"differential\\\" | \\\"diferencial\\\" | \\\"pia\\\" | \\\"iga\\\"\\n | \\\"main_switch\\\" | \\\"isolator\\\" | \\\"disconnector\\\"\\n | \\\"consumer_unit\\\" | \\\"distribution_board\\\" | \\\"panel\\\" | \\\"panelboard\\\"\\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 may start with `#`, `//`, or Mermaid-style `%%` 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**Style C — Mermaid-mindmap shorthand.** A top-level bare line becomes a category,\\nand indented `-` items become sibling Level-1 causes under that category:\\n\\n```txt\\nfishbone \\\"Why is the site slow?\\\"\\neffect \\\"Page LCP > 4s\\\"\\nContent\\n - heavy hero image\\n - too much above-the-fold text\\nTech\\n - JS bundle too large\\n - render-blocking CSS\\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:** `#`, `//`, or `%%` at the start of a line (after optional leading whitespace). The same markers also start trailing comments outside double-quoted strings.\\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**Comment markers** (`#`, `//`, `%%`) start 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| Mermaid mindmap-style bare category | Parsed as an implicit category | `Content` followed by indented `- item` lines works without `category` |\\n\\n---\\n\\n## 8. Grammar (EBNF)\\n\\n```text\\ndocument = header (blank | comment | effect | category | config | cause | sub-cause | implicit-category)*\\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\\nimplicit-category\\n = bare-text NEWLINE # top-level, no \\\":\\\"\\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`. Mermaid inline class syntax is also accepted: `A[Start]:::critical`.\\n\\n```\\nclassDef danger fill:#f9c,stroke:#c00\\nclassDef safe fill:#cfc,stroke:#090\\nclass errorNode danger\\nclass successNode safe\\nB[Review]:::danger\\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\\n | node-ref \\\":::\\\" class-name NEWLINE # Mermaid inline form\\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. Table mode (`style: table`)\\n\\nThe default matrix rendering is a **scatter / bubble chart** — points float at (x, y) coordinates. For frameworks where the output is a list of items grouped by quadrant (Eisenhower, Johari, Impact-Effort, 9-box), use `style: table` to switch to a **text-in-cell layout** instead.\\n\\n```\\nmatrix eisenhower \\\"This Week\\\"\\nstyle: table\\nQ2: \\\"Ship hotfix\\\"\\nQ2: \\\"Customer demo prep\\\"\\nQ1: \\\"Write Q3 OKRs\\\"\\nQ1: \\\"Refactor auth layer\\\"\\nQ4: \\\"LinkedIn updates\\\"\\nQ3: \\\"Reorganize Slack channels\\\"\\n```\\n\\n`style: table` applies these changes automatically:\\n\\n| Effect | Detail |\\n|---|---|\\n| Axes and arrows hidden | No axis lines, labels, or arrowheads |\\n| Grid lines hidden | Only the outer border and cell dividers remain |\\n| Quadrant titles move inside cells | Each title becomes a cell header instead of a corner overlay |\\n| Items stack as a bullet list | Multiple entries for the same quadrant stack top-down |\\n\\n### `Q1` … `Q4` shorthand (2×2 only)\\n\\nFor 2×2 templates, use `Qn: \\\"item\\\"` instead of the longer `cell (col, row) label: \\\"item\\\"` form. Mapping:\\n\\n| Shorthand | Cell | Eisenhower | Johari |\\n|---|---|---|---|\\n| `Q1:` | top-right | Schedule | Blind |\\n| `Q2:` | top-left | Do First | Open / Arena |\\n| `Q3:` | bottom-left | Delete | Hidden / Façade |\\n| `Q4:` | bottom-right | Delegate | Unknown |\\n\\nRepeat a shorthand key to add multiple items to the same cell:\\n\\n```\\nQ2: \\\"Ship hotfix\\\"\\nQ2: \\\"Customer demo prep\\\"\\n```\\n\\nFor 3×3 grids (9-box), use `cell (col, row) label: \\\"…\\\"` directly — the `Q` shorthand is 2×2 only.\\n\\n### When to use table vs scatter\\n\\n| Use `style: table` for | Use scatter (default) for |\\n|---|---|\\n| Eisenhower with task lists | Eisenhower with `size:` effort weights |\\n| Johari window coaching | Impact-Effort with bubble = revenue |\\n| Backlog grouping (no numeric third dimension) | RICE / BCG portfolio (third dimension IS the bubble size) |\\n| 9-box talent review | Risk heatmap (5×5 with numeric severity) |\\n\\n```\\nmatrix eisenhower \\\"This Week\\\"\\nstyle: table\\nQ2: \\\"Ship hotfix\\\"\\nQ2: \\\"Customer demo prep\\\"\\nQ1: \\\"Write Q3 OKRs\\\"\\nQ1: \\\"Refactor auth layer\\\"\\nQ4: \\\"LinkedIn updates\\\"\\nQ3: \\\"Reorganize Slack channels\\\"\\n```\\n\\n---\\n\\n## 11. 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## 12. 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| `\\\"Fix bug\\\" at (0.1, 0.9)` on an Eisenhower with a task list | Valid scatter point — but you probably wanted a list in a cell | Add `style: table` and use `Q2: \\\"Fix bug\\\"` instead |\\n| `Q1: \\\"item\\\"` on a 3×3 template | `Q` shorthand is parsed as a point line — silently dropped | Use `cell (col, row) label: \\\"item\\\"` for 3×3 grids |\\n\\n---\\n\\n## 13. 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 | q-short | rows-dir | cols-dir | grid-dir\\n | style-dir | 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\\nstyle-dir = \\\"style:\\\" WS \\\"table\\\" NEWLINE\\nq-short = \\\"Q\\\" ( \\\"1\\\" | \\\"2\\\" | \\\"3\\\" | \\\"4\\\" ) \\\":\\\" WS quoted-string NEWLINE # 2×2 only\\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 \"erd\": {\n \"title\": \"ERD (Entity-Relationship Diagram)\",\n \"content\": \"## 1. Your first ERD\\n\\nThe smallest useful ERD: a parent table referenced by a child via foreign key.\\n\\n```\\nerd\\ntable Customer {\\n customer_id int PK\\n email varchar UK\\n}\\ntable Order {\\n order_id int PK\\n customer_id int FK -> Customer.customer_id\\n}\\nref Order.customer_id many-mandatory -- one-mandatory Customer.customer_id : \\\"places\\\"\\n```\\n\\nFour rules cover 80 % of usage:\\n\\n1. Start with `erd`. Optional headers `title:`, `direction: LR | TB`.\\n2. Each table is a block: `table Name { col type marker }`. Markers are `PK`, `FK`, `UK`, `NN` (or `*`) for NOT NULL.\\n3. Inline foreign-key target: append `FK -> Other.column` to a column. The renderer adds an `FK` pill automatically.\\n4. Connect tables with a `ref` line: `ref Source <left-card> -- <right-card> Target [: \\\"label\\\"]`. Cardinality is one of `one-mandatory`, `one-optional`, `many-mandatory`, `many-optional` (named form), `1..1` / `0..1` / `1..N` / `0..N` (Min-Max), or Mermaid glyphs (`}o--||`, etc.) as alias.\\n\\n> Comments use `//` or `#`. Block forms can be either multi-line (one column per line) or inline (`table A { id int PK; name varchar }`).\\n\\n---\\n\\n## 2. Cardinality glyphs\\n\\nCrow's foot encodes the cardinality at each end of the relationship line:\\n\\n| Glyph | Reading | Min..Max | Named token |\\n|---|---|---|---|\\n| `─┃` (perpendicular bar) | Exactly one (mandatory one) | 1..1 | `one-mandatory` |\\n| `─○` (open circle) | Zero or one (optional one) | 0..1 | `one-optional` |\\n| `─┃<` (bar + crow's foot) | One or more (mandatory many) | 1..N | `many-mandatory` |\\n| `─○<` (circle + crow's foot) | Zero or more (optional many) | 0..N | `many-optional` |\\n\\nEach end of a line is annotated independently. A typical 1:N relationship reads \\\"exactly one CUSTOMER places zero-or-more ORDERs\\\":\\n\\n```\\nref Order.customer_id many-mandatory -- one-mandatory Customer.customer_id : \\\"places\\\"\\n```\\n\\nReading right-to-left: the **right end** of the line attaches to Customer with a single bar (one-mandatory); the **left end** attaches to Order with a bar + crow's foot (many-mandatory).\\n\\n---\\n\\n## 3. Mermaid alias\\n\\nIf you already use Mermaid `erDiagram`, the same glyphs work as input:\\n\\n```\\nerd\\ntable Customer { customer_id int PK; email varchar UK }\\ntable Order { order_id int PK; customer_id int FK -> Customer.customer_id }\\ntable Product { product_id int PK; name varchar }\\n\\nref Order }o--|| Customer : \\\"places\\\"\\nref Order }o--o{ Product : \\\"contains\\\"\\n```\\n\\n| Mermaid token | Schematex meaning |\\n|---|---|\\n| `\\\\|o` left / `o\\\\|` right | 0..1 |\\n| `\\\\|\\\\|` | 1..1 |\\n| `}o` left / `o{` right | 0..N |\\n| `}\\\\|` left / `\\\\|{` right | 1..N |\\n| `--` | identifying / solid line |\\n| `..` | non-identifying / dashed line |\\n\\n---\\n\\n## 4. Identifying vs non-identifying\\n\\nUse `--` for identifying relationships (solid line) and `..` for non-identifying (dashed):\\n\\n```\\nerd\\ntable User { id int PK; email varchar }\\ntable SessionLog { id int PK; user_id int FK -> User.id; created_at timestamp }\\n\\nref SessionLog.user_id many-optional .. one-optional User.id : \\\"logs (optional FK)\\\"\\n```\\n\\nThe dashed line follows the Barker / IDEF1X non-identifying convention.\\n\\n---\\n\\n## 5. Composite primary keys (associative tables)\\n\\nAssociative (\\\"junction\\\") tables resolve M:N relationships. Mark each PK + FK column with both `PK` and `FK`:\\n\\n```\\nerd\\ntable Student { student_id int PK; name varchar }\\ntable Course { course_id int PK; title varchar }\\ntable Enrollment {\\n student_id int PK FK -> Student.student_id\\n course_id int PK FK -> Course.course_id\\n grade char\\n}\\nref Enrollment.student_id many-mandatory -- one-mandatory Student.student_id\\nref Enrollment.course_id many-mandatory -- one-mandatory Course.course_id\\n```\\n\\nSchematex renders both pills (PK + FK) on the same row. The PK underline applies to the column name when any `PK` marker is present.\\n\\n---\\n\\n## 6. Layout direction\\n\\n`direction: LR` (default) places parent tables left, child tables right. `direction: TB` flips to top-to-bottom — useful for narrow embeds:\\n\\n```\\nerd\\ntitle: \\\"Library (top-down)\\\"\\ndirection: TB\\n\\ntable Member { id int PK; name varchar; email varchar UK }\\ntable Book { id int PK; title varchar; author varchar }\\ntable Loan { id int PK; member_id int FK -> Member.id; book_id int FK -> Book.id; due_date date }\\n\\nref Loan.member_id many-mandatory -- one-mandatory Member.id : \\\"borrowed by\\\"\\nref Loan.book_id many-mandatory -- one-mandatory Book.id : \\\"of\\\"\\n```\\n\\nLayered orthogonal Manhattan routing with single-bend edges. v0.1 uses appearance-order stacking inside layers; barycenter crossing-reduction is deferred to v0.2.\\n\\n---\\n\\n## 7. Notation modes\\n\\nThe DSL header can pin the notation:\\n\\n```\\nerd\\nnotation: crowsfoot // default — only mode supported in v0.1\\n```\\n\\n`notation: chen` (rectangle-diamond-oval, weak entities, ternary, ISA) and `notation: barker` (per-half dashed) are documented in `27-ERD-STANDARD.md` but rejected by the parser today. Targets v0.2.\\n\\n---\\n\\n## 8. Limitations of v0.1\\n\\n- **Crow's foot only.** Chen and Barker rendering deferred.\\n- **No edge-crossing minimization.** Layered placement uses declaration order within layers; large ERDs (10+ tables, dense FKs) will show some crossings. Reorder `table` blocks if a specific layout matters.\\n- **No self-referential C-loops.** Recursive relationships (`Employee.manager_id -> Employee.id`) parse but route as straight orthogonal lines, not the canonical C-shape.\\n- **No M:N auto-resolution.** Express the associative entity explicitly (this is the standard practice in production schemas anyway).\\n\\nFor the full standard reference, see `docs/reference/27-ERD-STANDARD.md`.\"\n },\n \"breadboard\": {\n \"title\": \"Breadboard / Physical Wiring\",\n \"content\": \"## 1. Your first breadboard\\n\\nThree sections: a one-line `breadboard` header, a `parts` block, and a `wires` block. Optional `board:` and `title:` lines come right after the header.\\n\\n```\\nbreadboard\\nparts\\n uno: mcu uno @beside-left\\n r1: resistor 220 @5e..9e\\n d1: led red @10e..10f\\n\\nwires\\n uno:5V --red-- @+t1\\n uno:GND --black-- @-t1\\n uno:D13 --yellow-- @5a\\n @10j --black-- @-t1\\n```\\n\\nEvery part is `id: kind [args] @placement`. Every wire is `<endpoint> --color-- <endpoint>`. That's the whole grammar.\\n\\n---\\n\\n## 2. Coordinates\\n\\nBreadboards have a 2D address grid. Schematex coordinates always start with `@`.\\n\\n| Form | Meaning | Example |\\n|---|---|---|\\n| `@<col><row>` | Main grid hole. Rows `a–e` (top half), `f–j` (bottom half). | `@5e`, `@12g` |\\n| `@+t<col>` | Top **positive** rail (red stripe). | `@+t8` |\\n| `@-t<col>` | Top **negative / GND** rail (blue stripe). | `@-t8` |\\n| `@+b<col>` | Bottom positive rail. | `@+b14` |\\n| `@-b<col>` | Bottom negative rail. | `@-b14` |\\n| `@<a>..<b>` | Span — used in part placement (resistor, diode, LED). | `@5e..9e` |\\n| `@beside-left` | Off-board placement for MCU boards. | `mcu uno @beside-left` |\\n\\nMini boards (`board: mini`) have **no power rails** — `@+t…` / `@-b…` are rejected by the parser.\\n\\n---\\n\\n## 3. Board sizes\\n\\n```\\nbreadboard\\nboard: half // default — 30 columns, 400 tie-points, rails (continuous)\\n```\\n\\n| Form | Tie points | Columns | Power rails |\\n|---|---|---|---|\\n| `mini` | 170 | 17 | none |\\n| `half` (default) | 400 | 30 | continuous |\\n| `full` | 830 | 63 | break at column 30/31 |\\n\\n> **Pitfall** — on full-size boards the rails break at the middle. If your circuit uses both halves you must jumper the rails together explicitly.\\n\\n---\\n\\n## 4. Parts catalog\\n\\nEach part is `id: <kind> [args] @<placement>`. The catalog covers the most common Arduino / ESP32 maker components:\\n\\n**Discrete components** (sit on the breadboard):\\n\\n| DSL | Args | Example |\\n|---|---|---|\\n| `resistor` | `value` (Ω; supports `k`/`M`) | `r1: resistor 220 @5e..9e` |\\n| `led` | `color` (red/green/blue/yellow/white/orange) | `d1: led red @10e..10f` |\\n| `cap-elec` | — | `c1: cap-elec @4e..4f` |\\n| `cap-ceramic` | — | `c2: cap-ceramic @6e..6f` |\\n| `diode` | — | `d2: diode @5e..8e` |\\n| `button` | — | `btn: button @8e` |\\n| `dip` | `pins=N` | `ic: dip pins=8 @4e` |\\n| `header` | `pins=N` | `h1: header pins=4 @20a` |\\n\\n**Microcontroller boards** (placed beside / above / below the substrate):\\n\\n| DSL | Pin labels |\\n|---|---|\\n| `mcu uno` | `5V`, `3V3`, `GND`, `VIN`, `RST`, `D2…D13`, `A0…A5`, `RX`, `TX` |\\n| `mcu nano` | Subset of Uno labels |\\n| `mcu esp32` | `3V3`, `GND`, `VIN`, `GPIO2`, `GPIO4`, `GPIO5`, `GPIO12…GPIO33` |\\n| `mcu pico` | Same generic GPIO labels |\\n\\n**Sensors / displays / actuators** (modules sit on the breadboard with pin row anchored at the supplied coordinate):\\n\\n| DSL | Pins |\\n|---|---|\\n| `sensor hcsr04` | `VCC`, `TRIG`, `ECHO`, `GND` |\\n| `sensor dht11` / `sensor dht22` | `VCC`, `DATA`, `GND` |\\n| `display oled-ssd1306` | `GND`, `VCC`, `SCL`, `SDA` |\\n| `display lcd-1602-i2c` | `GND`, `VCC`, `SDA`, `SCL` |\\n| `module rotary-ky040` | `CLK`, `DT`, `SW`, `VCC`, `GND` |\\n| `actuator servo-sg90` | `GND`, `VCC`, `SIG` |\\n\\nResistor color bands are decorated automatically from `value` — `220` → red-red-brown-gold, `10000` → brown-black-orange-gold.\\n\\n---\\n\\n## 5. Wires\\n\\nEvery wire connects two endpoints. An endpoint is either a **part pin** (`partId:pinName`) or a **breadboard coordinate** (`@…`).\\n\\n```\\nwires\\n uno:5V --red-- @+t1\\n uno:GND --black-- @-t1\\n uno:D9 --yellow-- @9c\\n @9a --green-- @+t9\\n```\\n\\n| Color | Conventional role |\\n|---|---|\\n| `red` | +V (5V, 3.3V, VCC) |\\n| `black` / `blue` | GND |\\n| `yellow` / `orange` / `green` / `white` / `purple` | Signal |\\n| `brown` / `grey` | Arbitrary signal |\\n\\nColor is purely visual — the engine does not validate it against electrical role.\\n\\nFor visually crowded boards, `via @<coord>` lets you pin an intermediate hole that biases the Bézier control points:\\n\\n```\\nwires\\n uno:D13 --yellow-- @9a via @8c\\n```\\n\\nMost wires don't need `via` — the layout engine produces a natural arc on its own.\\n\\n---\\n\\n## 6. Sensor with pull-up resistor (DHT11 motif)\\n\\nThe iconic Arduino tutorial pattern: a 10 kΩ pull-up between VCC and the sensor's data line.\\n\\n```\\nbreadboard\\nboard: half\\ntitle: \\\"DHT11 + 10kΩ pull-up\\\"\\n\\nparts\\n uno: mcu uno @beside-left\\n s1: sensor dht11 @6a\\n r1: resistor 10000 @8e..14e\\n\\nwires\\n s1:VCC --red-- @+t1\\n s1:GND --black-- @-t1\\n s1:DATA --yellow-- @8e\\n @14e --red-- @+t14\\n uno:5V --red-- @+t1\\n uno:GND --black-- @-t1\\n uno:D2 --yellow-- @8a\\n```\\n\\n---\\n\\n## 7. ESP32 + I²C OLED\\n\\nESP32 runs at 3.3 V (not 5 V). I²C convention: green = SDA, white = SCL.\\n\\n```\\nbreadboard\\nboard: half\\ntitle: \\\"ESP32 + SSD1306 OLED I²C\\\"\\n\\nparts\\n esp: mcu esp32 @beside-left\\n oled: display oled-ssd1306 @8a\\n\\nwires\\n oled:GND --black-- @-t1\\n oled:VCC --red-- @+t1\\n oled:SCL --white-- @10c\\n oled:SDA --green-- @11c\\n esp:3V3 --red-- @+t1\\n esp:GND --black-- @-t1\\n esp:GPIO22 --white-- @10c\\n esp:GPIO21 --green-- @11c\\n```\\n\\n---\\n\\n## 8. Limitations of v0.1\\n\\n- **No leader-line callouts** — reference designators (R1, C2) are drawn near the part body. Off-board callout boxes are deferred.\\n- **No `.fzz` import** — Schematex consumes only its own DSL; Fritzing files are not parsed.\\n- **No simulation** — this is a renderer, not a Wokwi-style simulator. Component-value validation (Ohm's law, current limits) is out of scope.\\n- **No PCB / schematic round-trip** — `breadboard` and `circuit` are independent engines. Authoring two views of the same prototype currently means writing two DSLs.\\n- **Fixed parts catalog** — user-defined part types are deferred. v0.1 ships the maker-tutorial 80% catalog (resistors, LEDs, caps, DIPs, headers, four MCU families, six sensor / display / actuator modules).\\n- **Power-rail break visual** — full-size boards mark the 30/31 break with a hole-wide gap; the rail stripes still draw through the gap (cosmetic).\"\n },\n \"bpmn\": {\n \"title\": \"BPMN / Business Process\",\n \"content\": \"## 1. Your first BPMN diagram\\n\\nThree sections: a one-line `bpmn` header, one or more `pool { … }` blocks, and a `flows` block. Inside each pool you put `lane { … }` blocks, and inside each lane you list flow objects as `id: kind \\\"label\\\"`.\\n\\n```\\nbpmn\\npool \\\"Service\\\" {\\n lane \\\"Worker\\\" {\\n A: start \\\"Request\\\"\\n B: task service \\\"Process\\\"\\n F: end \\\"Done\\\"\\n }\\n}\\n\\nflows\\nA --> B\\nB --> F\\n```\\n\\nEvery flow object starts with an **id**, a colon, the **kind**, an optional sub-keyword (trigger / marker / gateway type), and a quoted **label**. Ids are referenced by flow lines.\\n\\n---\\n\\n## 2. Pools and lanes\\n\\nA **pool** represents one participant — an organisation, a department, or a system. A **lane** subdivides a pool into roles. The pool label is rendered rotated 90° on the left edge of a horizontal pool.\\n\\n```\\npool \\\"Customer\\\" blackbox // black-box pool — no internal flow\\npool \\\"Bank\\\" {\\n lane \\\"Clerk\\\" { … }\\n lane \\\"Underwriter\\\" { … }\\n}\\n```\\n\\nA **black-box pool** is a participant whose internal process you don't model — typically an external customer or partner. Black-box pools must contain zero flow objects (Schematex enforces this in the parser).\\n\\nSequence flow (`-->`) is **not allowed** to cross pool boundaries. Use a message flow (`~~>`) for cross-pool communication.\\n\\n---\\n\\n## 3. Events\\n\\nEvents are circles. Stroke weight encodes lifecycle role:\\n\\n| Kind | Stroke | DSL |\\n|---|---|---|\\n| Start | thin (1px) | `start` |\\n| Intermediate | thin double ring | `intermediate` |\\n| End | thick (3px) | `end` |\\n\\nThe optional **trigger** keyword adds an inner glyph. v0.1 supports the three most common triggers — `none` (no glyph), `message` (envelope), and `timer` (clock face):\\n\\n```\\nA: start // none-trigger\\nA: start message \\\"Inbound\\\" // message-catch (unfilled envelope)\\nT: intermediate timer \\\"60 min\\\" // timer (clock face)\\nF: end \\\"Done\\\"\\n```\\n\\nFilled glyph = **throw**, unfilled glyph = **catch**. Schematex picks the right fill automatically based on event kind.\\n\\n---\\n\\n## 4. Activities — tasks and subprocesses\\n\\nActivities are rounded rectangles. The two v0.1 forms are `task` and a **collapsed** subprocess (the `+` marker indicates expandable detail):\\n\\n```\\nB: task \\\"Generic abstract task\\\"\\nU: task user \\\"User decides\\\"\\nS: task service \\\"API call\\\"\\nSE: task send \\\"Send email\\\"\\nRE: task receive \\\"Wait for reply\\\"\\nM: task manual \\\"Hand-stamp the form\\\"\\nSC: task script \\\"Run rule engine\\\"\\n\\nX: subprocess \\\"Verify identity\\\" collapsed\\n```\\n\\nThe **task marker** (small icon top-left) communicates *who or what* performs the work — a person (`user`), a software service (`service`), an outbound message (`send`), an inbound wait (`receive`), an out-of-system manual step (`manual`), or an automated script (`script`). When in doubt, omit the marker — it defaults to abstract.\\n\\n---\\n\\n## 5. Gateways\\n\\nGateways are diamonds. The inner glyph encodes branching semantics:\\n\\n| Kind | Glyph | Meaning | DSL |\\n|---|---|---|---|\\n| Exclusive (XOR) | **X** | Take exactly one outgoing branch (data-based) | `gateway xor` |\\n| Inclusive (OR) | **O** | Take one or more outgoing branches | `gateway or` |\\n| Parallel (AND) | **+** | Take all outgoing branches concurrently | `gateway and` |\\n| Event-based | pentagon-in-circle | Pick the branch whose event fires first | `gateway event` |\\n\\nSchematex uses Bruce Silver's **X glyph** for XOR by default — that's the convention real BPMN audits expect. Most diagrams use XOR (data branch) and AND (parallel split / join); OR is rare and event-based mostly appears in race-condition models.\\n\\n---\\n\\n## 6. Connectors\\n\\nFour connector types. Three of them stay inside a pool; only message flow is allowed to cross pool boundaries.\\n\\n```\\nA --> B // sequence flow (default)\\nG --? \\\"yes\\\" --> C // conditional sequence (label is a guard)\\nG --* \\\"default\\\" --> D // default flow (one per gateway, max)\\n\\\"Customer\\\" ~~> A : \\\"Submit application\\\" // message flow\\nE ~~> \\\"Customer\\\" : \\\"Notify approval\\\" // message flow back\\n```\\n\\n- `-->` is the workhorse — solid line + filled triangle arrowhead.\\n- `--? \\\"label\\\" -->` adds a small unfilled diamond at the source. Use it when leaving an activity *directly* on a guarded outcome. (At a gateway, the conditional label suffices; the diamond glyph isn't drawn.)\\n- `--* \\\"label\\\" -->` adds a slash mark at the source. **One default flow maximum per gateway** — Schematex enforces this.\\n- `~~>` is dashed with an open arrowhead and a small unfilled circle at the source. Source or target may be a quoted **pool name** (for black-box participants) or an object id.\\n\\n---\\n\\n## 7. Validation\\n\\nThe parser refuses diagrams that violate BPMN semantics, with line-numbered errors:\\n\\n| Rule | Error |\\n|---|---|\\n| Sequence flow crosses pool | `sequence flow 'A --> B' crosses pool boundary — use message flow (~~>)` |\\n| Message flow inside one pool | `message flow 'A ~~> B' must cross pool boundaries` |\\n| Black-box pool has internals | `black-box pool \\\"X\\\" cannot contain lanes` |\\n| Two default flows from same gateway | `gateway 'G' has 2 default flows (max 1)` |\\n| Duplicate id within a pool | `duplicate id 'A'` |\\n| Unknown source / target | `unknown source 'X' in sequence flow` |\\n\\nThese checks fire during parse, so an LLM gets a usable signal before the layout pass.\\n\\n---\\n\\n## 8. Larger example — pizza order with black-box customer\\n\\nA canonical BPMN tutorial: an external customer (whose process we don't model) places an order with a pizzeria split into Clerk / Chef / Delivery lanes, with a rework loop on the chef's quality check.\\n\\n```\\nbpmn\\ndirection: LR\\ntitle: \\\"Pizza order\\\"\\n\\npool \\\"Customer\\\" blackbox\\n\\npool \\\"Pizzeria\\\" {\\n lane \\\"Clerk\\\" {\\n A: start message \\\"Order received\\\"\\n B: task user \\\"Take order\\\"\\n }\\n lane \\\"Chef\\\" {\\n C: task manual \\\"Make pizza\\\"\\n G1: gateway xor \\\"Pizza ok?\\\"\\n D: task manual \\\"Rework\\\"\\n }\\n lane \\\"Delivery\\\" {\\n E: task send \\\"Deliver\\\"\\n F: end \\\"Done\\\"\\n }\\n}\\n\\nflows\\nA --> B\\nB --> C\\nC --> G1\\nG1 --? \\\"yes\\\" --> E\\nG1 --* \\\"no\\\" --> D\\nD --> C\\nE --> F\\n\\\"Customer\\\" ~~> A : \\\"Place order\\\"\\nE ~~> \\\"Customer\\\" : \\\"Pizza delivered\\\"\\n```\\n\\nThe rework loop (`D --> C`) creates a back-edge that the layout's cycle-break detects via DFS — the longest-path layering then proceeds on the forward DAG so columns stay sensible.\\n\\n---\\n\\n## 9. Limitations of v0.1\\n\\nThese are deferred to a later release. If your diagram needs one, file an issue with the use case:\\n\\n- **Boundary events** — events attached to an activity edge (timer, error, escalation, compensation). Currently you have to model the boundary as a free-floating intermediate event with manual flows.\\n- **Expanded subprocesses** — collapsed `subprocess` works; expanded inline blocks (`subprocess \\\"X\\\" { … }`) are deferred.\\n- **Rare event triggers** — error / escalation / cancel / compensation / signal / link / conditional / multiple / parallel-multiple. Use `none` or `message` as a placeholder.\\n- **Transaction / call activities** — render as normal tasks for v0.1.\\n- **Loop and multi-instance markers** — bottom-center activity glyphs; deferred.\\n- **Artifacts** — data object / data store / group / text annotation. Deferred.\\n- **BPMN 2.0 XML import / export** — out of scope. Schematex computes layout from DSL; no DI layer to round-trip.\"\n },\n \"fbd\": {\n \"title\": \"Function Block Diagram (FBD)\",\n \"content\": \"## 1. Your first FBD network\\n\\nThe smallest useful FBD network: one block, two inputs, one output.\\n\\n```\\nfbd\\nnetwork 0:\\n Out = AND(A, B)\\n```\\n\\n`A` and `B` are auto-declared as BOOL inputs (left-side terminals), and `Out` is auto-wired to the AND block's `OUT` port (right-side terminal). The block sits between them with port stubs and labels.\\n\\n---\\n\\n## 2. Variables\\n\\nDeclare variables before any networks. Each variable has a name, an IEC data type, and an optional initial value.\\n\\n```\\nfbd \\\"Tank Control\\\"\\n\\nvar StartBtn: bool\\nvar TankLevel: real\\nvar SetPoint: real = 80.0\\nvar DwellTimer: timer\\nvar Pulse: counter\\n```\\n\\nSupported types: `bool`, `int`, `dint`, `uint`, `udint`, `real`, `lreal`, `time`, `date`, `tod`, `string`, `wstring`, `byte`, `word`, `dword`, `timer`, `counter`. Any other identifier is treated as a user-defined function-block type.\\n\\nOptional scope prefixes (default = local): `var_input`, `var_output`, `var_in_out`, `var_global`, `var_external`.\\n\\n---\\n\\n## 3. Networks\\n\\nA **network** is one independent piece of data flow, evaluated left-to-right within itself; networks evaluate top-to-bottom across the program per scan.\\n\\n```\\nnetwork 0 \\\"Start latch\\\":\\n ...\\n\\nnetwork 1:\\n ...\\n```\\n\\nThe number is optional — networks are auto-numbered if absent. The title (in quotes) renders at the top-left of the network frame.\\n\\n---\\n\\n## 4. Block calls — inline expression notation\\n\\nThe clearest way to write a combinational network is as a single nested expression:\\n\\n```\\nnetwork 0:\\n Out = OR(A, AND(B, ~C))\\n```\\n\\nThe parser builds the call tree: outer OR with `A` on input 1 and the AND result on input 2; the AND has `B` and the negated `C`. The renderer lays them out left-to-right (inputs at layer 0, AND at layer 1, OR at layer 2, output at layer 3).\\n\\n`~C` adds a **negation bubble** (small open circle) at the input port — equivalent to inserting a NOT block on that wire, but cleaner.\\n\\n---\\n\\n## 5. Block calls — instance-named notation\\n\\nWhen you need to reference a block's outputs from elsewhere, give it an instance tag:\\n\\n```\\nnetwork 0:\\n Pulse = R_TRIG(CLK: Sensor)\\n Count = CTU(CU: Pulse.Q, R: Reset, PV: 100)\\n Done = GE(IN1: Count.CV, IN2: 100)\\n```\\n\\n`Pulse.Q`, `Count.CV` reference output ports of named instances. The instance tag renders italicized above the block header.\\n\\nArgument lists accept **named ports** (`CU: Pulse.Q`) or **positional** (`CTU(Pulse.Q, Reset, 100)`) — named is recommended for readability and required when you skip a port.\\n\\n---\\n\\n## 6. Inline constants\\n\\nInput ports can take literals directly — no wire needed:\\n\\n```\\nnetwork 0:\\n Dwell = TON(IN: BottleSensor, PT: T#50ms)\\n Cap = LIMIT(MN: 0.0, IN: Setpoint, MX: 95.0)\\n Mode = SEL(G: ManualSwitch, IN0: AutoMode, IN1: ManualMode)\\n```\\n\\n`T#50ms`, `0.0`, `95.0` render as small yellow boxed text to the left of their port. Time literals follow IEC 61131-3: `T#10ms`, `T#5s`, `T#3m20s`, `T#1h`. Booleans are `true` / `false` (case-insensitive).\\n\\n---\\n\\n## 7. Standard block library\\n\\n| Category | Blocks |\\n|---|---|\\n| Boolean | `AND`, `OR`, `NOT`, `NAND`, `NOR`, `XOR`, `XNOR`, `BUF` |\\n| Edge detect | `R_TRIG`, `F_TRIG` |\\n| Bistable | `SR`, `RS` |\\n| Timer | `TON`, `TOF`, `TP` |\\n| Counter | `CTU`, `CTD` |\\n| Math | `ADD`, `SUB`, `MUL`, `DIV`, `MOD`, `ABS`, `NEG`, `MOVE` |\\n| Comparison | `EQ`, `NE`, `GT`, `GE`, `LT`, `LE` |\\n| Selection | `SEL`, `MUX`, `MAX`, `MIN`, `LIMIT` |\\n\\nAND, OR, NAND, NOR, ADD, MUL, MAX, MIN accept any number of inputs (default 2). Pass extra positional args or use `[inputs: N]` to extend.\\n\\n```\\nnetwork 0:\\n All4 = AND(A, B, C, D)\\n Sum = ADD(X, Y, Z)\\n```\\n\\n---\\n\\n## 8. Larger example — bottle counter\\n\\n```\\nfbd \\\"Bottle Counter\\\"\\n\\nvar ConveyorRunning: bool\\nvar BottleSensor: bool\\nvar BatchDone: bool\\nvar BatchSize: counter\\nvar DwellTimer: timer\\n\\nnetwork 0 \\\"Debounce sensor with 50ms dwell\\\":\\n Dwell = TON(IN: BottleSensor, PT: T#50ms)\\n\\nnetwork 1 \\\"Count one bottle on rising edge of debounced signal\\\":\\n Pulse = R_TRIG(CLK: Dwell.Q)\\n BatchSize = CTU(CU: Pulse.Q, R: BatchDone, PV: 24)\\n\\nnetwork 2 \\\"Batch done\\\":\\n BatchDone = MOVE(BatchSize.Q)\\n```\\n\\nThree networks: debounce → edge-detect → count → flag. Each network is one DAG; the renderer routes wires through Manhattan paths.\\n\\n---\\n\\n## 9. v0.1 limitations\\n\\nThe current engine implements the standard-block subset most teams use day-to-day. The following are deferred and will be added in a follow-up:\\n\\n- **EN/ENO power-flow rails** (`[en]` block attribute, `[rail: on]` header) — adds a top-of-network enable rail, vendor convention from Studio 5000 / TIA Portal.\\n- **User-defined function blocks** with `pins_in:` / `pins_out:` declarations — for custom motor controllers, PID instances, etc.\\n- **Page connectors** (`connector_out` / `connector_in`) for wires that span multiple pages.\\n- **Bit-string blocks** (SHL, SHR, ROL, ROR, AND_BIT, OR_BIT, etc.).\\n- **Extended math** (SQRT, LN, LOG, EXP, SIN, COS, TAN, ASIN, ACOS, ATAN).\\n- **ANSI distinctive shape mode** (`[shape: ansi]`) — the `logic` engine already provides this for pure-Boolean diagrams.\\n- **CTUD** bidirectional counter, **TP** retentive timer (RTO).\\n\\nIf you need any of these now, open an issue or use the `ladder` engine for the full IEC 61131-3 LD subset.\"\n },\n \"sfc\": {\n \"title\": \"Sequential Function Chart (SFC)\",\n \"content\": \"## 1. Your first chart\\n\\nTwo steps, one transition, an initial marker:\\n\\n```\\nsfc\\nstep S0 [initial]\\nstep S1\\ntransition from: S0 to: S1: Trigger\\n```\\n\\n`S0` renders as a **double-bordered rectangle** (the IEC initial-step convention); `S1` as a single-border rectangle. Between them is a horizontal **transition bar** with the condition text `Trigger` to its right.\\n\\nIf you forget `[initial]`, the first declared step is auto-promoted to initial.\\n\\n---\\n\\n## 2. Steps\\n\\n```\\nstep S_Filling [label: \\\"Filling tank\\\"]\\n N FillValve_Open\\n D Mixer_Run T#30s\\n P StartChime\\n```\\n\\nA step has:\\n\\n- An **id** (unique across the chart) — used in transitions and jumps.\\n- An optional `[label: \\\"...\\\"]` for display.\\n- An optional `[initial]` (one allowed) or `[final]` (vendor stop step, three borders).\\n- Zero or more **action blocks**, indented one level, each with a qualifier letter.\\n\\n---\\n\\n## 3. Transitions\\n\\nA **transition** declares a directed link between two steps with a boolean condition:\\n\\n```\\ntransition from: S0 to: S1: StartBtn\\ntransition from: S1 to: S2: TankLevel >= 80.0 AND NOT EmergencyStop\\ntransition T_Reset from: S5 to: S0: ResetBtn\\n```\\n\\nThe condition text is opaque — Schematex stores it verbatim and renders it next to the bar. Every transition must have a non-empty condition; use `TRUE` for unconditional links.\\n\\nTransitions whose `from` and `to` are linearly adjacent in the body render as inline bars between the steps. Transitions whose pair is *not* linearly adjacent (e.g. a jump back to an earlier step) render as **margin arrows** on the left or right side of the chart.\\n\\n---\\n\\n## 4. Action qualifiers\\n\\nActions attach to the right side of a step and run according to their qualifier letter:\\n\\n| Qualifier | Behavior |\\n|---|---|\\n| `N` | Active while step is active (most common) |\\n| `S` | Stored — set true on entry, stays until matching `R` |\\n| `R` | Reset — clears a previously-stored action |\\n| `L` | Time-Limited — active up to T after step entry |\\n| `D` | Time-Delayed — activates T after entry |\\n| `P` | Pulse — true for one PLC scan only |\\n| `P0` | Pulse on deactivate (Siemens) |\\n| `P1` | Synonym for `P` (Siemens) |\\n| `SD` | Stored & Delayed |\\n| `DS` | Delayed & Stored |\\n| `SL` | Stored & Time-Limited |\\n\\nTime-parameterized qualifiers (L, D, SD, DS, SL) take a duration literal:\\n\\n```\\nstep S1\\n L LimitedRun T#5s\\n D DelayedRun T#2s\\n```\\n\\n---\\n\\n## 5. Alternative branches (single bar — OR)\\n\\nOnly **one** branch fires per scan, picked by transition condition:\\n\\n```\\nstep S0 [initial]\\nstep S_Pick\\n\\nalt from: S_Pick:\\n branch [priority: 1]:\\n transition: IsExpressShipping\\n step S_Express\\n N PrepExpressBox\\n transition: TRUE\\n branch [priority: 2]:\\n transition: IsStandardShipping\\n step S_Standard\\n N PrepStandardBox\\n transition: TRUE\\nmerge_to: S_Ship\\n\\nstep S_Ship\\n\\ntransition from: S0 to: S_Pick: ProductOrdered\\ntransition from: S_Ship to: S0: Shipped\\n```\\n\\nThe single horizontal lines above and below the branches are the divergence and convergence bars. Each branch starts with its **entry transition** (between div bar and first step) and ends with an **exit transition** (between last step and conv bar).\\n\\n---\\n\\n## 6. Simultaneous branches (double bar — AND)\\n\\n**All** branches run concurrently; the chart waits at the convergence until every branch finishes:\\n\\n```\\nsim from: S_Heat: TRUE\\n branch:\\n step S_Bake\\n D Oven_Run T#15m\\n branch:\\n step S_Cool\\n L Cooler_On T#5m\\nmerge_to: S_Done: Bake_Done AND Cool_Done\\n```\\n\\nThe two parallel horizontal lines (gap 4px) above and below the branches are the simultaneous bars. The shared **transition above** (`TRUE` here) triggers the divergence; the shared **transition below** (`Bake_Done AND Cool_Done`) is checked before convergence fires.\\n\\n---\\n\\n## 7. Jumps (loops)\\n\\nA transition whose target is an earlier step renders as a margin arrow:\\n\\n```\\nstep S0 [initial]\\nstep S1\\nstep S2\\ntransition from: S0 to: S1: A\\ntransition from: S1 to: S2: B\\ntransition T_Reset from: S2 to: S0: ResetBtn\\ntransition from: S2 to: S1: NOT ResetBtn\\n```\\n\\nThe forward `S2 → S1` (back-edge) gets the margin arrow on the right; the `T_Reset` jump back to `S0` goes on the left. Each margin arrow shows its target id and condition.\\n\\n---\\n\\n## 8. Variables\\n\\nReused from `ladder` and `fbd`:\\n\\n```\\nvar StartBtn: bool\\nvar TankLevel: real\\nvar BakeReady: bool\\nvar Counter: counter\\nvar T1: timer\\n```\\n\\nVariables declared in conditions and actions are not validated — Schematex treats condition / action body text as opaque strings, matching how `state` handles guards and actions.\\n\\n---\\n\\n## 9. v0.1 limitations\\n\\n- **Nested branches** (alt-in-sim, sim-in-alt) parse but layout collapse heuristics are basic; deep nests may overlap.\\n- **S/R action-pair dashed connectors** (visually link an `S` action with its matching `R` elsewhere) are deferred.\\n- **Active-step runtime indicator** (yellow fill on the currently active step) is deferred — useful for debugging integrations that surface PLC runtime state.\\n- **GRAFCET forcing orders** (out-of-scope per IEC 60848-only feature).\\n- **Final step** parses with `[final]` but renders with the same double border as initial; the IEC triple-border convention is deferred.\"\n },\n \"usecase\": {\n \"title\": \"UML Use Case Diagram\",\n \"content\": \"## 1. Your first diagram\\n\\nEvery document starts with the `usecase` keyword, then a header, then declarations and relationships:\\n\\n```\\nusecase\\nsystem: \\\"Library\\\"\\n\\nactor: Member\\nusecase: \\\"Borrow Book\\\" as Borrow\\n\\nMember -- Borrow\\n```\\n\\n`actor:` declares an actor (a stick figure), `usecase:` declares a use case (an ellipse), and `Member -- Borrow` draws a plain association between them. `system:` wraps the use cases in a labelled **subject** rectangle. The primary actor is placed on the left; supporting actors on the right.\\n\\nThe header accepts:\\n\\n- `title: \\\"…\\\"` — a heading drawn above the diagram.\\n- `system: \\\"…\\\"` — the subject (system boundary) name. Omit it to let the use cases float free (Schematex warns if you omit it with ≥3 use cases).\\n- `direction: LR | TB` — default `LR` (actors flank the subject horizontally).\\n- `generalization: tree | individual` — whether to merge sibling generalization arrows (default `tree`).\\n\\n---\\n\\n## 2. Actors\\n\\n```\\nactor: Customer\\nactor: \\\"Payment Gateway\\\" as PG (external)\\nactor: \\\"Warehouse Staff\\\" as WH\\nactor: Admin (left)\\n```\\n\\n- A bare or `\\\"quoted\\\"` name becomes the label; `as ID` gives it a short identifier for relationships.\\n- `(external)` (or `(system)`) renders the actor as a **rectangle with an `«actor»` stereotype** instead of a stick figure — use it for other software systems (payment gateways, third-party APIs).\\n- `(business)` adds the Bittner & Spence diagonal slash across the stick figure.\\n- `(left)` / `(right)` pins the actor to a side. By default the first actor goes left and the rest go right.\\n\\nCustom stereotypes go in guillemets after the declaration: `actor: \\\"Audit Service\\\" as Audit (external) «system»`. The parser also accepts ASCII `<<system>>` and normalises it to `«system»`.\\n\\n---\\n\\n## 3. Use cases\\n\\n```\\nusecase: \\\"Checkout\\\" as Checkout {\\n extension point: payment failed\\n extension point: stock depleted\\n}\\n```\\n\\nA use case is an ellipse sized to fit its text. The optional `{ … }` block lists **extension points** in a compartment below the name, separated by a divider line. Stereotypes work here too: `usecase: \\\"Validate Card\\\" as ValidateCard «secured»`.\\n\\n---\\n\\n## 4. Relationships\\n\\nFour line types connect actors and use cases:\\n\\n| DSL | Meaning | Rendering |\\n|-----|---------|-----------|\\n| `A -- B` | association | solid line, no arrow |\\n| `A --> B` | directed association | solid line, open arrow at B |\\n| `A ..> B` | `A` **includes** `B` | dashed line, open arrow → B, `«include»` pill |\\n| `A <.. B` | `A` **extends** `B` | dashed line, open arrow → B (the base), `«extend»` pill |\\n| `A --\\\\|> B` | `A` is a specialisation of `B` | solid line, hollow triangle → parent B |\\n\\n```\\nCustomer -- Checkout\\nCheckout ..> Pay : «include»\\nPay ..> ValidateCard : «include»\\nCancel <.. Checkout : «extend» [payment failed] (extension point: payment failed)\\n```\\n\\n- **`«include»`** points *toward the included use case* — the reusable behavior `A` always runs. Source includes target.\\n- **`«extend»`** points *toward the base* — `A` is the optional behavior, `B` is the base it extends. You can attach a `[condition]` and reference one of the base's `(extension point: …)` entries. (The arrowhead is always drawn toward the base regardless of how you order the endpoints.)\\n- An `«extend»` line is drawn in the theme **accent color** so the rarer, more surprising relationship stands out.\\n\\nThe parser rejects the high-confidence mistakes humans and LLMs both make, with the offending line number: association between two actors or two use cases, `include`/`extend` touching an actor, generalization across metaclasses (actor → use case), reused identifiers, and extension-point references that don't exist on the base.\\n\\n---\\n\\n## 5. Generalization\\n\\n`--|>` works between two actors **or** between two use cases (never across the two — that's a hard error):\\n\\n```\\nactor: User as U\\nactor: \\\"Premium User\\\" as PU\\nPU --|> U\\n\\nusecase: \\\"Pay by Card\\\" as PayCard\\nusecase: \\\"Pay by PayPal\\\" as PayPaypal\\nusecase: \\\"Pay\\\" as Pay\\nPayCard --|> Pay\\nPayPaypal --|> Pay\\n```\\n\\nThe arrow carries a **hollow triangle** pointing at the parent. When three or more siblings share one parent, the arrows merge into a single shared head (UML 2.5 Figure 18.5 convention); set `generalization: individual` in the header to keep them separate. Actor hierarchies route as a clean bus on the outer edge of the actor stack.\\n\\n---\\n\\n## 6. Multiplicity\\n\\nQuote a multiplicity string immediately beside the endpoint it belongs to:\\n\\n```\\nCustomer \\\"1\\\" -- \\\"*\\\" Checkout\\nCashier \\\"1..*\\\" -- \\\"1\\\" Register\\n```\\n\\n---\\n\\n## 7. PlantUML-style inline form\\n\\nComing from PlantUML? The inline declaration form works and mixes freely with the declarative form:\\n\\n```\\nusecase\\n:Customer: as C\\n(Browse Catalog) as Browse\\n(Add to Cart) as AddCart\\n\\nC -- Browse\\nC -- AddCart\\nBrowse ..> AddCart : «include»\\n```\\n\\n`:Name:` declares an actor, `(Name)` declares a use case, and `as ID` aliases either. Schematex is *inspired by* PlantUML, not a 1:1 transpiler — multiplicity and relationship syntax differ slightly.\\n\\n---\\n\\n## 8. Grammar (EBNF)\\n\\n```text\\ndocument = \\\"usecase\\\" NEWLINE header_prop* statement*\\nheader_prop = (\\\"title:\\\" | \\\"system:\\\") quoted_string\\n | \\\"direction:\\\" (\\\"LR\\\" | \\\"TB\\\")\\n | \\\"generalization:\\\" (\\\"tree\\\" | \\\"individual\\\")\\n\\nstatement = actor_decl | usecase_decl | plantuml_inline | relation | note\\n\\nactor_decl = \\\"actor\\\" \\\":\\\" name (\\\"as\\\" IDENT)? actor_kind? stereotype?\\nactor_kind = \\\"(\\\" (\\\"external\\\" | \\\"system\\\" | \\\"business\\\" | \\\"left\\\" | \\\"right\\\") \\\")\\\"\\nusecase_decl = \\\"usecase\\\" \\\":\\\" quoted (\\\"as\\\" IDENT)? stereotype? extpoints?\\nextpoints = \\\"{\\\" (\\\"extension point:\\\" TEXT)+ \\\"}\\\"\\nplantuml_inline = \\\":\\\" name \\\":\\\" (\\\"as\\\" IDENT)? ; actor\\n | \\\"(\\\" name \\\")\\\" (\\\"as\\\" IDENT)? ; use case\\n\\nrelation = endpoint relop endpoint label_clause?\\nendpoint = (IDENT | quoted) multiplicity? | multiplicity? (IDENT | quoted)\\nrelop = \\\"--\\\" | \\\"-->\\\" | \\\"..>\\\" | \\\"<..\\\" | \\\"--|>\\\"\\nlabel_clause = \\\":\\\" stereotype? condition? extpoint_ref?\\nstereotype = \\\"«\\\" TEXT \\\"»\\\" | \\\"<<\\\" TEXT \\\">>\\\"\\ncondition = \\\"[\\\" TEXT \\\"]\\\"\\nextpoint_ref = \\\"(extension point:\\\" TEXT \\\")\\\"\\nmultiplicity = quoted_string ; \\\"1\\\", \\\"*\\\", \\\"0..1\\\", \\\"1..*\\\"\\n```\\n\\n---\"\n },\n \"prisma\": {\n \"title\": \"PRISMA 2020 flow diagram\",\n \"content\": \"## 1. Your first diagram\\n\\nThe minimum is the four stage blocks. Counts are mandatory; the parser refuses to lay out a diagram with a missing total.\\n\\n```\\nprisma\\n\\nidentification:\\n databases:\\n n: 1000\\n\\nscreening:\\n records-screened: 900\\n excluded:\\n n: 600\\n\\neligibility:\\n full-text-assessed: 300\\n excluded:\\n n: 250\\n\\nincluded:\\n studies: 50\\n```\\n\\nIndentation is significant — **two spaces per level**, like genogram and SLD. The first non-blank line must be `prisma`. Comments use `#` or `//`.\\n\\n---\\n\\n## 2. Meta lines\\n\\nTop-level `key: value` lines, written before the stage blocks:\\n\\n```\\nprisma\\nmode: 2020-single\\nkind: systematic-review\\ntitle: My review\\nvalidate-counts: warn\\n```\\n\\n| Key | Values | Default | Meaning |\\n|---|---|---|---|\\n| `mode` | `2020-single` · `2020-dual` · `2009` | `2020-single` | Single column, or dual (\\\"other methods\\\") column. |\\n| `kind` | `systematic-review` · `scoping-review` · `ipd` · `nma` | `systematic-review` | Swaps stage vocabulary (see §6). |\\n| `title` | string | — | Rendered above the diagram. |\\n| `validate-counts` | `warn` · `strict` · `off` | `warn` | Arithmetic checking (see §7). |\\n| `direction` | `TB` / `TD` | `TB` | PRISMA is vertical by standard; horizontal is rejected. |\\n\\n---\\n\\n## 3. Identification\\n\\nThe `identification:` block holds a `databases:` sub-block (always) and an optional `other:` sub-block (dual mode).\\n\\n```\\nidentification:\\n databases:\\n n: 1418\\n sources: PubMed=600, Embase=450, Cochrane=184\\n duplicates-removed: 318\\n ineligible-automation: 0\\n other-removed: 0\\n```\\n\\n- `n:` — total records identified (**mandatory**).\\n- `sources:` — `name=count` pairs, comma-separated. Rendered as an indented breakdown. Names with spaces or punctuation can be quoted: `\\\"Web of Science\\\"=184`.\\n- `duplicates-removed:`, `ineligible-automation:`, `other-removed:` — optional removal counts. When any are present they render as a separate **\\\"Records removed before screening\\\"** box in the right column, connected by a horizontal arrow.\\n\\nLarge numbers may use commas: `n: 1,418` is the same as `n: 1418`.\\n\\n---\\n\\n## 4. Screening & Eligibility\\n\\nBoth stages carry a main count plus an `excluded:` block. The excluded block has its own `n:` and an optional `reasons:` breakdown.\\n\\n```\\nscreening:\\n records-screened: 1100\\n excluded:\\n n: 870\\n reasons: irrelevant title=750, non-English=120\\n reports-sought: 226 # optional\\n reports-not-retrieved: 12 # optional\\n\\neligibility:\\n full-text-assessed: 230\\n excluded:\\n n: 195\\n reasons: wrong population=80, wrong intervention=60, wrong outcome=55\\n```\\n\\n`reasons:` are `name=count` pairs. If you list more than 8, the renderer sorts them descending and aggregates the tail as `Other (n = …)` so the side-box stays readable.\\n\\n---\\n\\n## 5. Included\\n\\n```\\nincluded:\\n studies: 35\\n reports: 38 # one study may yield several reports\\n participants: 28741 # PRISMA-IPD only\\n```\\n\\n`studies:` is mandatory. `reports:` and `participants:` are optional extra count lines.\\n\\n---\\n\\n## 6. Dual pipeline & review kinds\\n\\n**Dual pipeline** — the PRISMA 2020 update added a second \\\"Identification via other methods\\\" column (citation searching, hand searches, expert recommendations). Add an `other:` block; the two columns merge into Screening via a Y-junction.\\n\\n```\\nprisma\\nmode: 2020-dual\\n\\nidentification:\\n databases:\\n n: 1234\\n duplicates-removed: 254\\n other:\\n n: 56\\n sources: citation-search=30, hand-search=20, expert-recommendation=6\\n\\nscreening:\\n records-screened: 1036\\n excluded:\\n n: 810\\n\\neligibility:\\n full-text-assessed: 226\\n excluded:\\n n: 195\\n\\nincluded:\\n studies: 31\\n```\\n\\n**Scoping review** — `kind: scoping-review` swaps \\\"studies\\\" → \\\"sources of evidence\\\" and re-labels the stages per Tricco et al. 2018, without changing geometry.\\n\\n**Updated review** — an optional `previous-studies:` block draws a dashed box on top that feeds into the identification section:\\n\\n```\\nprevious-studies:\\n n: 19\\n sources: previous review=19\\n```\\n\\n---\\n\\n## 7. Count arithmetic validation\\n\\nWith `validate-counts: warn` (default) the engine checks that the counts reconcile across stages — e.g. `databases.n + other.n − duplicates-removed = records-screened`, and that source/reason breakdowns sum to their totals. Mismatches render a small warning under the diagram (also surfaced in the SVG `<desc>` for screen readers).\\n\\n`validate-counts: strict` turns a mismatch into a parse error with an \\\"off by N\\\" message. `off` skips checking entirely.\\n\\n---\\n\\n## 8. Grammar (EBNF)\\n\\n```\\nprisma-document = \\\"prisma\\\", { meta-line }, stage-block, { stage-block } ;\\nmeta-line = (\\\"mode:\\\" | \\\"kind:\\\" | \\\"title:\\\" | \\\"review-id:\\\" | \\\"validate-counts:\\\" | \\\"direction:\\\") value ;\\n\\nstage-block = previous-block | identification-block | screening-block | eligibility-block | included-block ;\\n\\nprevious-block = \\\"previous-studies:\\\" , indent, \\\"n:\\\" int, [ \\\"reports:\\\" int ], { \\\"sources:\\\" pairs } ;\\nidentification-block = \\\"identification:\\\" , indent,\\n \\\"databases:\\\" , indent, \\\"n:\\\" int, { \\\"sources:\\\" pairs },\\n [ \\\"duplicates-removed:\\\" int ], [ \\\"ineligible-automation:\\\" int ], [ \\\"other-removed:\\\" int ],\\n [ \\\"other:\\\" , indent, \\\"n:\\\" int, { \\\"sources:\\\" pairs } ] ;\\nscreening-block = \\\"screening:\\\" , indent, \\\"records-screened:\\\" int,\\n \\\"excluded:\\\" , indent, \\\"n:\\\" int, { \\\"reasons:\\\" pairs },\\n [ \\\"reports-sought:\\\" int ], [ \\\"reports-not-retrieved:\\\" int ] ;\\neligibility-block = \\\"eligibility:\\\" , indent, \\\"full-text-assessed:\\\" int,\\n \\\"excluded:\\\" , indent, \\\"n:\\\" int, { \\\"reasons:\\\" pairs } ;\\nincluded-block = \\\"included:\\\" , indent, \\\"studies:\\\" int, [ \\\"reports:\\\" int ], [ \\\"participants:\\\" int ] ;\\n\\npairs = pair, { \\\",\\\" pair } ;\\npair = (string | quoted) \\\"=\\\" int ;\\nint = digit, { digit | \\\",\\\" } ; (* commas stripped: 1,234 == 1234 *)\\n```\\n\\nIndentation is two spaces per level. Unknown keys inside a stage block are a parse error, keeping each stage well-defined.\\n\\n---\"\n },\n \"pert\": {\n \"title\": \"PERT / CPM Network\",\n \"content\": \"## 1. Your first diagram\\n\\nEvery document starts with the `pert` keyword, an optional header, then one `task` line per activity:\\n\\n```\\npert\\nunit: days\\n\\ntask A \\\"Market research\\\" duration: 5\\ntask B \\\"Design mockups\\\" duration: 8 after: A\\ntask C \\\"Backend API\\\" duration: 15 after: A\\ntask D \\\"Frontend build\\\" duration: 10 after: B, C\\n```\\n\\nEach task carries an `<id>`, a quoted `<label>`, a `duration:`, and an optional `after:` list of predecessors. The engine adds an invisible Start and Finish, runs the schedule, and draws the six-field activity box for every task. You never type ES/EF/LS/LF — they are computed.\\n\\nThe header accepts:\\n\\n- `title: \\\"…\\\"` — a heading drawn above the diagram.\\n- `unit: days | weeks | hours | abstract` — the time unit (default `days`; purely a label, the engine is calendar-agnostic).\\n- `direction: LR | TB` — left-to-right (default) or top-to-bottom.\\n- `layout: network | timescaled` — the layered network (default) or a time-proportional view (§6).\\n- `critical-tolerance: <n>` — slack ≤ this counts as critical (default `0`; set `0.001` for three-point projects).\\n- `show-sentinels: true` — draw the synthetic Start / Finish nodes (hidden by default).\\n\\n---\\n\\n## 2. The six-field activity box\\n\\nEvery task renders as the canonical 3×2 PERT/CPM rectangle:\\n\\n```\\n┌──────────┬────────────┬──────────┐\\n│ ES │ Duration │ EF │\\n├──────────┴────────────┴──────────┤\\n│ Task Name (ID) │\\n├──────────┬────────────┬──────────┤\\n│ LS │ Slack │ LF │\\n└──────────┴────────────┴──────────┘\\n```\\n\\n- **ES / EF** — Early Start / Early Finish, from the forward pass.\\n- **LS / LF** — Late Start / Late Finish, from the backward pass.\\n- **Slack** (total float) = LS − ES = LF − EF. Zero slack means the activity is on the critical path.\\n\\nCritical activities get a **red border** and a bold `0` slack; the project's critical chain reads as an unbroken red line. Every field is also mirrored onto `data-*` attributes (`data-es`, `data-slack`, `data-critical`, …) so you can query the SVG without re-running the math.\\n\\n**On the two-colour palette.** A PERT chart is deliberately drawn in just two colours. Red for the critical path is a genuine industry convention — MS Project, Oracle Primavera P6, and PMBOK figures all use it, because \\\"critical vs. not\\\" is the one distinction the diagram exists to surface. Everything else stays a neutral house-blue. Adding more colours would imply categories that PERT's semantics don't have; if you need to group by team or phase, use [swimlanes](#7-swimlanes-tags-classes-and-comments) (`lane:`) instead of colour.\\n\\n---\\n\\n## 3. Dependencies (FS / SS / FF / SF + lag/lead)\\n\\n`after:` takes a comma-separated list of predecessor references. The default relationship is **Finish-to-Start (FS)** — a task starts once its predecessor finishes:\\n\\n```\\ntask D \\\"Frontend build\\\" duration: 10 after: B, C\\n```\\n\\nModern scheduling needs the other three Precedence-Diagramming relationships and **lag** (delay) or **lead** (negative lag):\\n\\n```\\ntask B \\\"Stakeholder interviews\\\" duration: 6 after: A SS+1 # start 1d after A starts\\ntask I \\\"Documentation\\\" duration: 4 after: D+3 # FS with a 3-day lag\\ntask J \\\"Translation\\\" duration: 3 after: I SS-1 # start 1d before I starts (lead)\\ntask K \\\"Sign-off\\\" duration: 1 after: I FF # finishes when I finishes\\ntask L \\\"Press release\\\" duration: 2 after: G SF+1 # start-to-finish (rare)\\n```\\n\\n| Form | Meaning |\\n|------|---------|\\n| `after: A` | Finish-to-Start, no lag (the common case) |\\n| `after: A+2` or `after: A+2d` | FS with a 2-unit lag |\\n| `after: A SS` / `A FF` / `A SF` | Start-to-Start / Finish-to-Finish / Start-to-Finish |\\n| `after: A SS+2d` / `A FF-1d` | any type with lag (`+`) or lead (`-`) |\\n\\nA lag unit suffix (`d` / `w` / `h`) must match the diagram's `unit:` or be omitted; mixed units are rejected. FS with zero lag is unlabelled; SS/FF/SF always show their type on the edge.\\n\\n---\\n\\n## 4. Three-point (PERT) estimation\\n\\nWrite a duration as `O/M/P` (optimistic / most-likely / pessimistic) and the engine computes the beta-distribution expected duration **te = (O + 4M + P) / 6** and the variance **σ² = ((P − O)/6)²**:\\n\\n```\\npert\\ncritical-tolerance: 0.01\\ntask A \\\"Spec\\\" duration: 2/3/5 # te = 3.17, σ² = 0.25\\ntask B \\\"Build\\\" duration: 5/8/14 after: A # te = 8.50, σ² = 2.25\\ntask C \\\"Test\\\" duration: 3/4/6 after: B\\ntask D \\\"Deploy\\\" duration: 1/2/3 after: C\\n```\\n\\nThe Duration field shows `te`; a small `σ=…` annotation appears under the name; and the project-level standard deviation (√ of the summed critical-path variances) is reported in the footer. Use `critical-tolerance: 0.01` so floating-point `te` values don't displace the visible critical path.\\n\\n---\\n\\n## 5. Milestones\\n\\nA milestone is a zero-duration checkpoint, drawn as a diamond. Use the `milestone` flag or `duration: 0`:\\n\\n```\\ntask P \\\"Cutover weekend\\\" milestone after: O\\ntask Q \\\"Go-live\\\" duration: 0 after: P\\n```\\n\\n---\\n\\n## 6. Time-scaled layout\\n\\n`layout: timescaled` switches from the layered network to a network-Gantt hybrid: each activity's **x-position is proportional to its ES** and its **width is proportional to its duration**, with a unit time axis along the bottom. Activities are packed into lanes so nothing overlaps.\\n\\n```\\npert\\nlayout: timescaled\\nunit: days\\n\\ntask A \\\"Inventory\\\" duration: 5\\ntask B \\\"Interviews\\\" duration: 6 after: A SS+1\\ntask C \\\"Design\\\" duration: 10 after: A, B\\ntask D \\\"Build\\\" duration: 15 after: C\\ntask E \\\"Pilot\\\" duration: 7 after: D\\n```\\n\\nThis is a bridge to a Gantt view, not a replacement: there's no calendar, working-time mask, or resource swimlane.\\n\\n### Activity-on-arrow (`layout: aoa`)\\n\\n`layout: aoa` renders the older **activity-on-arrow** (ADM) notation you'll find in textbooks and on Investopedia: numbered **event** circles, **activities as labelled arrows**, and dotted **dummy activities** auto-inserted wherever an activity has two or more predecessors. You write the same activity-on-node DSL — Schematex builds the event graph and numbers the events for you.\\n\\n```\\npert\\nlayout: aoa\\nunit: days\\n\\ntask A \\\"create schedule\\\" duration: 10\\ntask B \\\"buy hardware\\\" duration: 5\\ntask C \\\"programming\\\" duration: 20 after: A\\ntask D \\\"installation\\\" duration: 5 after: B\\ntask E \\\"conversion\\\" duration: 15 after: D\\ntask F \\\"test code\\\" duration: 20 after: C, E\\ntask G \\\"write manual\\\" duration: 15 after: E\\n```\\n\\nAOA is a **legacy** notation (PMBOK 7 dropped it; AON is the modern standard) and it can only express **finish-to-start** logic — SS/FF/SF and lag/lead are flattened to FS with a warning. Use it for teaching, exam prep, or matching an existing textbook figure; use the default `network` (AON) layout for real scheduling.\\n\\n---\\n\\n## 7. Swimlanes, tags, classes, and comments\\n\\nAdd `lane: \\\"…\\\"` to any task and the network re-groups into horizontal swimlanes — by responsible team, phase, or owner — while still computing the schedule exactly as before:\\n\\n```\\ntask T1 \\\"Support Account Deletion\\\" duration: 3 lane: \\\"Customer Account\\\"\\ntask T2 \\\"Design a New Theme\\\" duration: 8 lane: \\\"Shopping Site\\\"\\ntask T3 \\\"Apply New Theme\\\" duration: 15 after: T2 lane: \\\"Shopping Site\\\"\\n```\\n\\nLanes appear in first-declared order, each as a banded row with a label gutter on the left. Swimlanes are a presentation of the same AON network, not a different layout mode — they activate automatically when any task declares a lane.\\n\\n```\\ntask X \\\"External vendor work\\\" duration: 10 after: A tags: vendor, external class: secondary\\n# this is a comment\\n```\\n\\n`tags:` emit `data-tag=\\\"…\\\"` on the node group and `class:` adds a CSS class for downstream theming. `#` and `//` start a comment to end of line.\\n\\n---\\n\\n## 8. Grammar (EBNF)\\n\\n```text\\ndocument = \\\"pert\\\" NEWLINE header* task+\\nheader = \\\"title:\\\" quoted\\n | \\\"unit:\\\" (\\\"days\\\" | \\\"weeks\\\" | \\\"hours\\\" | \\\"abstract\\\")\\n | \\\"direction:\\\" (\\\"LR\\\" | \\\"TB\\\")\\n | \\\"layout:\\\" (\\\"network\\\" | \\\"timescaled\\\" | \\\"aoa\\\")\\n | \\\"critical-tolerance:\\\" number\\n | \\\"show-sentinels:\\\" boolean\\n\\ntask = \\\"task\\\" IDENT quoted \\\"duration:\\\" duration (\\\"after:\\\" reflist)? \\\"milestone\\\"? attrs?\\n | \\\"task\\\" IDENT quoted \\\"milestone\\\" (\\\"after:\\\" reflist)? attrs?\\nduration = number | number \\\"/\\\" number \\\"/\\\" number ; deterministic or O/M/P\\nreflist = ref (\\\",\\\" ref)*\\nref = IDENT (WS deptype)? laglead?\\n | IDENT \\\"+\\\" number unit? ; attached FS lag sugar\\ndeptype = \\\"FS\\\" | \\\"SS\\\" | \\\"FF\\\" | \\\"SF\\\"\\nlaglead = (\\\"+\\\" | \\\"-\\\") number unit?\\nunit = \\\"d\\\" | \\\"w\\\" | \\\"h\\\"\\nattrs = (\\\"tags:\\\" idlist)? (\\\"class:\\\" IDENT)? (\\\"lane:\\\" quoted)?\\n```\\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"]}