-
Notifications
You must be signed in to change notification settings - Fork 56
Expand file tree
/
Copy pathlayout-patterns-guide.html
More file actions
407 lines (390 loc) · 51.4 KB
/
Copy pathlayout-patterns-guide.html
File metadata and controls
407 lines (390 loc) · 51.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
<!--
GUIDE FOR THE AI — 4 flow-diagram layout patterns (a companion to SKILL.md, not a replacement).
Purpose: SKILL.md states the RULES (color, spacing, layer order…); this file is for VISUALLY
JUDGING 4 common TOPOLOGY patterns — when a new diagram request comes in, read the "When to use
this" section of each pattern below to pick the RIGHT skeleton before placing any coordinates,
instead of inventing a new layout from scratch.
4 patterns:
1. Linear multi-phase flow — 1 main flow, split by PHASE (live / planned).
2. Multiple sources converging — 2+ independent sources merge into 1 shared processing step.
3. Central hub (orchestrator) — 1 central box fans out to N subsystems, each branch independent.
4. Conditional branching — 1 decision point splits the flow into 2+ different outcomes.
This is a scrollable READING page, it does NOT use the 2-pane app shell with zoom/pan/sidebar/
drag-to-rearrange that a real operational diagram uses — the purpose is different: a real
operational diagram needs to cover a large system at a glance, while this page only needs to be
read top to bottom to be understood. When building a REAL diagram for a project, always copy
assets/template.html (the full app shell), never copy this page's shell.
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>4 Flow-Diagram Layout Patterns — AI Guide</title>
<script>try{const t=localStorage.getItem('theme-guide');if(t==='light'||t==='dark')document.documentElement.dataset.theme=t;}catch(e){}</script>
<style>
:root {
color-scheme: light dark;
--bg: light-dark(#f6f8fa, #0f141c); --panel: light-dark(#ffffff, #151c29);
--line: light-dark(#d1d5db, #394456); --line-soft: light-dark(#e5e7eb, #273143);
--text: light-dark(#1f2937, #e2e8f0); --text2: light-dark(#374151, #cbd5e1);
--muted: light-dark(#6b7280, #94a3b8); --dim: light-dark(#9ca3af, #64748b);
--di: light-dark(#2563eb, #60a5fa); --ve: light-dark(#059669, #34d399);
--sec: light-dark(#dc2626, #f87171); --script: light-dark(#7c3aed, #a78bfa);
--pipe: light-dark(#d97706, #fbbf24); --sky: light-dark(#0284c7, #38bdf8);
--fx: light-dark(#c026d3, #e879f9);
--tint-di: light-dark(#eff6ff, #1a2740); --hl: light-dark(#fef9c3, #423c14);
}
:root[data-theme=light] { color-scheme: light; }
:root[data-theme=dark] { color-scheme: dark; }
* { box-sizing: border-box; }
body { margin: 0; background: var(--bg); color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, "Helvetica Neue", Arial, sans-serif; }
.wrap { max-width: 980px; margin: 0 auto; padding: 28px 20px 80px; }
.top { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; margin-bottom: 8px; }
h1 { font-size: 21px; font-weight: 650; margin: 0 0 6px; }
.lead { color: var(--muted); font-size: 13px; line-height: 1.6; max-width: 680px; }
.theme-btn { flex: none; display: inline-flex; align-items: center; justify-content: center;
width: 34px; height: 34px; padding: 0; border-radius: 10px; color: var(--text);
background: var(--panel); border: 1px solid var(--line-soft); cursor: pointer; }
.theme-btn svg { width: 16px; height: 16px; }
.choose { background: var(--panel); border: 1px solid var(--line-soft); border-radius: 12px;
padding: 16px 18px; margin: 24px 0 40px; font-size: 12.5px; }
.choose h2 { font-size: 13.5px; margin: 0 0 10px; }
.choose table { width: 100%; border-collapse: collapse; }
.choose td { padding: 7px 8px; border-top: 1px solid var(--line-soft); vertical-align: top; }
.choose tr:first-child td { border-top: none; }
.choose td:first-child { white-space: nowrap; font-weight: 650; color: var(--text2); width: 190px; }
section.pattern { margin: 44px 0; }
section.pattern h2 { font-size: 16px; font-weight: 650; margin: 0 0 4px; display: flex; align-items: center; gap: 8px; }
section.pattern .num { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px;
border-radius: 50%; background: var(--tint-di); color: var(--di); font-size: 12px; font-weight: 700; }
.when { color: var(--muted); font-size: 12.5px; line-height: 1.6; margin: 0 0 14px; }
.when b { color: var(--text2); }
.board { background: var(--panel); border: 1px solid var(--line-soft); border-radius: 14px;
box-shadow: 0 1px 3px rgba(16,24,40,.06); padding: 6px; }
.board svg { width: 100%; height: auto; display: block; }
.chips { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 12px 0; }
.chip { display: inline-flex; align-items: center; gap: 7px; padding: 6px 12px; font-size: 12px;
background: var(--panel); color: var(--text); border: 1px solid var(--line-soft);
border-radius: 999px; cursor: pointer; font-family: inherit; transition: all .15s; }
.chip:hover { border-color: var(--line); }
.chip.on { border-color: currentColor; box-shadow: 0 0 0 1px currentColor inset; }
.chip .dot { width: 8px; height: 8px; border-radius: 50%; background: currentColor; }
.cards { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 10px; }
@media (max-width: 700px) { .cards { grid-template-columns: 1fr; } }
.card { background: var(--panel); border: 1px solid var(--line-soft); border-radius: 10px;
padding: 12px 14px; font-size: 12px; line-height: 1.65; }
.card.hidden { display: none; }
.card h3 { margin: 0 0 6px; font-size: 12.5px; font-weight: 650; display: flex; align-items: center; gap: 7px; }
.card .dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex: none; }
.card ul, .card ol { margin: 0; padding-left: 16px; color: var(--muted); }
.card b { color: var(--text); font-weight: 600; }
.card .why { margin-top: 6px; padding-top: 6px; border-top: 1px dashed var(--line-soft); color: var(--text2); }
svg text { paint-order: stroke; stroke: var(--panel); stroke-width: 3px; stroke-linejoin: round; }
svg [stroke="#2563eb"] { stroke: var(--di); } svg [fill="#2563eb"] { fill: var(--di); }
svg [stroke="#059669"] { stroke: var(--ve); } svg [fill="#059669"] { fill: var(--ve); }
svg [stroke="#dc2626"] { stroke: var(--sec); } svg [fill="#dc2626"] { fill: var(--sec); }
svg [stroke="#7c3aed"] { stroke: var(--script); } svg [fill="#7c3aed"] { fill: var(--script); }
svg [stroke="#d97706"] { stroke: var(--pipe); } svg [fill="#d97706"] { fill: var(--pipe); }
svg [stroke="#0284c7"] { stroke: var(--sky); } svg [fill="#0284c7"] { fill: var(--sky); }
svg [stroke="#c026d3"] { stroke: var(--fx); } svg [fill="#c026d3"] { fill: var(--fx); }
svg [stroke="#d1d5db"] { stroke: var(--line); } svg [stroke="#e5e7eb"] { stroke: var(--line-soft); }
svg [fill="#ffffff"] { fill: var(--panel); } svg [fill="#eff6ff"] { fill: var(--tint-di); }
svg [fill="#1f2937"] { fill: var(--text); } svg [fill="#374151"] { fill: var(--text2); }
svg [fill="#6b7280"] { fill: var(--muted); } svg [fill="#9ca3af"] { fill: var(--dim); }
svg [data-flow] { cursor: pointer; }
svg .dim { opacity: .1; transition: opacity .25s; }
svg .flow-dot { opacity: 0; transition: opacity .25s; }
svg .flow-dot.flowing { opacity: 1; }
@keyframes flow-ping { 0% { transform: scale(1); opacity: .35; } 65% { transform: scale(1.05); opacity: 0; } 100% { transform: scale(1.05); opacity: 0; } }
svg .flow-ping-ring { transform-box: fill-box; transform-origin: center; animation: flow-ping 2.6s ease-out infinite; pointer-events: none; }
svg .flow-ping { opacity: 0; }
svg .flow-ping.flowing { opacity: 1; }
@media (prefers-reduced-motion: reduce) { svg .flow-dot { display: none; }
svg .flow-ping { display: none; } }
</style>
</head>
<body>
<div class="wrap">
<div class="top">
<div>
<h1>4 Flow-Diagram Layout Patterns</h1>
<div class="lead">A guide that accompanies <code>SKILL.md</code> — pick the right skeleton pattern before placing any coordinates. Each pattern: when to use it, a minimal worked example (click a chip to watch the flow run), and a <b>"Why this layout"</b> section — the single most important part for applying it correctly to a different case, not just for copying the raw coordinates.</div>
</div>
<button id="theme-toggle" class="theme-btn" aria-label="Toggle light/dark mode"></button>
</div>
<div class="choose">
<h2>Which pattern to choose?</h2>
<table>
<tr><td>1 flow, with phases</td><td>A single process running from A to Z, but part of it is "live", the rest is "planned / not yet built" → <b>Pattern 1</b>.</td></tr>
<tr><td>2+ sources, 1 shared destination</td><td>Multiple methods/channels produce the same kind of result, then merge into 1 shared processing/review step → <b>Pattern 2</b>.</td></tr>
<tr><td>1 orchestrator, N subsystems</td><td>A central component calls back and forth with multiple independent systems, and the subsystems aren't directly related to each other → <b>Pattern 3</b>.</td></tr>
<tr><td>Has a pass/fail branch point</td><td>A condition decides which direction the flow continues in, each direction leading to a different outcome → <b>Pattern 4</b>.</td></tr>
<tr><td>Combining several patterns</td><td>Real diagrams usually NEST patterns — e.g. 2 sources converging (Pattern 2) into a step that then has a pass/fail branch (Pattern 4). Identify each piece and combine them, don't try to force everything into 1 mold.</td></tr>
</table>
</div>
<!-- ============================================================ -->
<!-- PATTERN 1 — LINEAR MULTI-PHASE FLOW -->
<!-- ============================================================ -->
<section class="pattern" id="p1">
<h2><span class="num">1</span>Linear multi-phase flow</h2>
<p class="when">Use this when there's <b>1 main flow</b> but only part of it has been built — the rest is planned. Lay it out by <b>row = phase</b> (not by box type), each phase gets its own boxed-off region, region borders in a different color so "live" vs "planned" is distinguishable from a distance without reading any text. Real example: a data pipeline where phase 1 (ingest & storage) is live and phases 2–3 (enrichment, serving) are still planned.</p>
<div class="board">
<svg viewBox="0 0 690 300" xmlns="http://www.w3.org/2000/svg" font-family="inherit">
<!-- LAYOUT PLAN: row 1 (live) y=50-110, row 2 (planned) y=200-260 — SAME x columns so the eye
reading vertically immediately sees "B continues down into D" as a phase transition, not
a separate flow. Vertical corridor B→D at x=330 (between columns B/D). No box crosses a
region border (margin ≥20). -->
<defs>
<marker id="p1-di" markerWidth="9" markerHeight="9" refX="7.5" refY="4.5" orient="auto"><path d="M1,1 L8,4.5 L1,8" fill="none" stroke="#2563eb" stroke-width="1.6"/></marker>
<marker id="p1-ve" markerWidth="9" markerHeight="9" refX="7.5" refY="4.5" orient="auto"><path d="M1,1 L8,4.5 L1,8" fill="none" stroke="#059669" stroke-width="1.6"/></marker>
</defs>
<!-- arrows -->
<line data-flow="main" x1="190" y1="80" x2="242" y2="80" stroke="#2563eb" stroke-width="1.8" marker-end="url(#p1-di)"/><g class="flow-dot" data-flow="main"><circle r="3.4" fill="#2563eb"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M190,80 L242,80"/></circle><circle r="3.4" fill="#2563eb"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M190,80 L242,80"/></circle></g>
<line data-flow="main" x1="250" y1="95" x2="198" y2="95" stroke="#059669" stroke-width="1.6" stroke-dasharray="6 4" marker-end="url(#p1-ve)"/><g class="flow-dot" data-flow="main"><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M250,95 L198,95"/></circle><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M250,95 L198,95"/></circle></g>
<line data-flow="main" x1="410" y1="80" x2="462" y2="80" stroke="#2563eb" stroke-width="1.8" marker-end="url(#p1-di)"/><g class="flow-dot" data-flow="main"><circle r="3.4" fill="#2563eb"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M410,80 L462,80"/></circle><circle r="3.4" fill="#2563eb"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M410,80 L462,80"/></circle></g>
<line data-flow="main" x1="470" y1="95" x2="418" y2="95" stroke="#059669" stroke-width="1.6" stroke-dasharray="6 4" marker-end="url(#p1-ve)"/><g class="flow-dot" data-flow="main"><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M470,95 L418,95"/></circle><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M470,95 L418,95"/></circle></g>
<line data-flow="chuyen" x1="330" y1="110" x2="330" y2="192" stroke="#7c3aed" stroke-width="1.6" marker-end="url(#p1-di)"/><g class="flow-dot" data-flow="chuyen"><circle r="3.4" fill="#7c3aed"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M330,110 L330,192"/></circle><circle r="3.4" fill="#7c3aed"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M330,110 L330,192"/></circle></g>
<line data-flow="plan" x1="410" y1="230" x2="462" y2="230" stroke="#2563eb" stroke-width="1.8" marker-end="url(#p1-di)"/><g class="flow-dot" data-flow="plan"><circle r="3.4" fill="#2563eb"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M410,230 L462,230"/></circle><circle r="3.4" fill="#2563eb"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M410,230 L462,230"/></circle></g>
<line data-flow="plan" x1="470" y1="245" x2="418" y2="245" stroke="#059669" stroke-width="1.6" stroke-dasharray="6 4" marker-end="url(#p1-ve)"/><g class="flow-dot" data-flow="plan"><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M470,245 L418,245"/></circle><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M470,245 L418,245"/></circle></g>
<!-- region -->
<rect x="15" y="20" width="640" height="115" rx="10" fill="none" stroke="#0284c7" stroke-width="1.2" stroke-dasharray="7 5" opacity="0.55"/>
<text x="30" y="40" fill="#0284c7" font-size="10.5" font-weight="700">PHASE 1 · LIVE</text>
<rect x="15" y="170" width="640" height="115" rx="10" fill="none" stroke="#9ca3af" stroke-width="1.2" stroke-dasharray="7 5" opacity="0.55"/>
<text x="30" y="190" fill="#9ca3af" font-size="10.5" font-weight="700">PHASE 2 · PLANNED</text>
<!-- boxes -->
<g data-flows="main"><rect x="40" y="50" width="150" height="60" rx="10" fill="#ffffff" stroke="#d1d5db" stroke-width="1.3"/><text x="115" y="76" fill="#1f2937" font-size="11.5" font-weight="650" text-anchor="middle">Data source</text><text x="115" y="94" fill="#6b7280" font-size="9" text-anchor="middle">where it originates</text></g>
<g data-flows="main chuyen"><rect x="250" y="50" width="160" height="60" rx="10" fill="#eff6ff" stroke="#2563eb" stroke-width="1.6"/><text x="330" y="76" fill="#1f2937" font-size="11.5" font-weight="650" text-anchor="middle">Processing</text><text x="330" y="94" fill="#6b7280" font-size="9" text-anchor="middle">live</text><g class="flow-ping" data-flow="main"><rect class="flow-ping-ring" x="250" y="50" width="160" height="60" rx="10" fill="none" stroke="#2563eb" stroke-width="1.5"/></g><g class="flow-ping" data-flow="top"><rect class="flow-ping-ring" x="250" y="50" width="160" height="60" rx="10" fill="none" stroke="#2563eb" stroke-width="1.5"/></g></g>
<g data-flows="main"><rect x="470" y="50" width="160" height="60" rx="10" fill="#ffffff" stroke="#d1d5db" stroke-width="1.3"/><text x="550" y="76" fill="#1f2937" font-size="11.5" font-weight="650" text-anchor="middle">Storage</text><text x="550" y="94" fill="#6b7280" font-size="9" text-anchor="middle">live</text><g class="flow-ping" data-flow="main"><rect class="flow-ping-ring" x="470" y="50" width="160" height="60" rx="10" fill="none" stroke="#d1d5db" stroke-width="1.5"/></g><g class="flow-ping" data-flow="yes"><rect class="flow-ping-ring" x="470" y="50" width="160" height="60" rx="10" fill="none" stroke="#d1d5db" stroke-width="1.5"/></g></g>
<g data-flows="chuyen plan"><rect x="250" y="200" width="160" height="60" rx="10" fill="#ffffff" stroke="#e5e7eb" stroke-width="1.2" stroke-dasharray="5 4"/><text x="330" y="226" fill="#6b7280" font-size="11" font-weight="600" text-anchor="middle">Advanced analytics</text><text x="330" y="244" fill="#9ca3af" font-size="9" text-anchor="middle">planned</text><g class="flow-ping" data-flow="top"><rect class="flow-ping-ring" x="250" y="200" width="160" height="60" rx="10" fill="none" stroke="#e5e7eb" stroke-width="1.5"/></g><g class="flow-ping" data-flow="left"><rect class="flow-ping-ring" x="250" y="200" width="160" height="60" rx="10" fill="none" stroke="#e5e7eb" stroke-width="1.5"/></g></g>
<g data-flows="plan"><rect x="470" y="200" width="160" height="60" rx="10" fill="#ffffff" stroke="#e5e7eb" stroke-width="1.2" stroke-dasharray="5 4"/><text x="550" y="226" fill="#6b7280" font-size="11" font-weight="600" text-anchor="middle">Result</text><text x="550" y="244" fill="#9ca3af" font-size="9" text-anchor="middle">planned</text><g class="flow-ping" data-flow="plan"><rect class="flow-ping-ring" x="470" y="200" width="160" height="60" rx="10" fill="none" stroke="#e5e7eb" stroke-width="1.5"/></g><g class="flow-ping" data-flow="right"><rect class="flow-ping-ring" x="470" y="200" width="160" height="60" rx="10" fill="none" stroke="#e5e7eb" stroke-width="1.5"/></g><g class="flow-ping" data-flow="no"><rect class="flow-ping-ring" x="470" y="200" width="160" height="60" rx="10" fill="none" stroke="#e5e7eb" stroke-width="1.5"/></g></g>
<!-- badges -->
<g font-size="8.5" font-weight="700">
<g data-flow="main" data-step="1"><circle cx="216" cy="80" r="8" fill="#ffffff" stroke="#2563eb"/><text x="216" y="83" fill="#2563eb" text-anchor="middle">1</text></g>
<g data-flow="main" data-step="2"><circle cx="436" cy="80" r="8" fill="#ffffff" stroke="#2563eb"/><text x="436" y="83" fill="#2563eb" text-anchor="middle">2</text></g>
<g data-flow="chuyen" data-step="3"><circle cx="330" cy="151" r="8" fill="#ffffff" stroke="#7c3aed"/><text x="330" y="154" fill="#7c3aed" text-anchor="middle">3</text></g>
<g data-flow="plan" data-step="4"><circle cx="436" cy="230" r="8" fill="#ffffff" stroke="#2563eb"/><text x="436" y="233" fill="#2563eb" text-anchor="middle">4</text></g>
</g>
</svg>
</div>
<div class="chips">
<button class="chip" data-flow="main" style="color: var(--di)"><i class="dot"></i><span>Main flow (1→2)</span></button>
<button class="chip" data-flow="chuyen" style="color: var(--script)"><i class="dot"></i><span>Phase transition (3)</span></button>
<button class="chip" data-flow="plan" style="color: var(--di)"><i class="dot"></i><span>Planned branch (4)</span></button>
</div>
<div class="cards">
<div class="card" data-flow="main"><h3><span class="dot" style="background:var(--di)"></span>Main flow</h3><ul><li>1 Source → Processing, 2 Processing → Storage, each pair has a parallel return direction</li></ul></div>
<div class="card" data-flow="chuyen"><h3><span class="dot" style="background:var(--script)"></span>Phase transition</h3><ul><li>3 Only 1 direction — this is a progress dependency ("phase 2 waits for phase 1 to stabilize"), not a request/response, so it doesn't need a return direction</li></ul></div>
<div class="card" data-flow="plan"><h3><span class="dot" style="background:var(--di)"></span>Planned branch</h3><ul><li>4 Drawn identically to the main flow but with dashed boxes + a "planned" label — don't use a different color for "not yet built", use the STROKE (dashed) instead so it doesn't stray from the semantic color palette</li></ul></div>
<div class="card">
<h3>Why this layout</h3>
<div class="why">Stack phases by <b>row</b> (top/bottom), not by column — readers are used to reading "top before bottom", the same way they read text, so "phase 1 on top, phase 2 below" feels more natural than "left/right". Columns B/D line up so the phase-transition arrow is <b>1 straight vertical segment</b>, not a detour.</div>
</div>
</div>
</section>
<!-- ============================================================ -->
<!-- PATTERN 2 — MULTIPLE SOURCES CONVERGING -->
<!-- ============================================================ -->
<section class="pattern" id="p2">
<h2><span class="num">2</span>Multiple sources converging to one point</h2>
<p class="when">Use this when there are <b>2+ sources/methods producing the same kind of result</b>, and they all merge into 1 shared processing/review step — e.g. 2 content-generation methods but only 1 moderation process. Stack the sources <b>offset vertically</b> (not lined up in the same row) so the converging lines form a natural funnel shape, instead of forcing 2 lines lined up into the same point. Real example: an AI auto-draft flow and a manual staff-authored flow, both merging into a shared "Finalize & publish" review step.</p>
<div class="board">
<svg viewBox="0 0 700 280" xmlns="http://www.w3.org/2000/svg" font-family="inherit">
<!-- LAYOUT PLAN: source A (top, y=40) and source B (bottom, y=180) use OFFSET vertical
corridors (x=250 vs x=260) so the 2 converging lines don't overlap mid-route — they
only MEET at DIFFERENT y values on the same left edge of the Hub (y=130 and y=160), per
the "offset anchor points" rule. -->
<defs>
<marker id="p2-di" markerWidth="9" markerHeight="9" refX="7.5" refY="4.5" orient="auto"><path d="M1,1 L8,4.5 L1,8" fill="none" stroke="#2563eb" stroke-width="1.6"/></marker>
<marker id="p2-ve" markerWidth="9" markerHeight="9" refX="7.5" refY="4.5" orient="auto"><path d="M1,1 L8,4.5 L1,8" fill="none" stroke="#059669" stroke-width="1.6"/></marker>
</defs>
<path data-flow="a" d="M200,70 L250,70 L250,130 L282,130" fill="none" stroke="#0284c7" stroke-width="1.8" marker-end="url(#p2-di)"/><g class="flow-dot" data-flow="a"><circle r="3.4" fill="#0284c7"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M200,70 L250,70 L250,130 L282,130"/></circle><circle r="3.4" fill="#0284c7"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M200,70 L250,70 L250,130 L282,130"/></circle></g>
<path data-flow="a" d="M290,140 L260,140 L260,85 L208,85" fill="none" stroke="#059669" stroke-width="1.6" stroke-dasharray="6 4" marker-end="url(#p2-ve)"/><g class="flow-dot" data-flow="a"><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M290,140 L260,140 L260,85 L208,85"/></circle><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M290,140 L260,140 L260,85 L208,85"/></circle></g>
<path data-flow="b" d="M200,210 L262,210 L262,160 L282,160" fill="none" stroke="#c026d3" stroke-width="1.8" marker-end="url(#p2-di)"/><g class="flow-dot" data-flow="b"><circle r="3.4" fill="#c026d3"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M200,210 L262,210 L262,160 L282,160"/></circle><circle r="3.4" fill="#c026d3"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M200,210 L262,210 L262,160 L282,160"/></circle></g>
<path data-flow="b" d="M290,170 L254,170 L254,195 L208,195" fill="none" stroke="#059669" stroke-width="1.6" stroke-dasharray="6 4" marker-end="url(#p2-ve)"/><g class="flow-dot" data-flow="b"><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M290,170 L254,170 L254,195 L208,195"/></circle><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M290,170 L254,170 L254,195 L208,195"/></circle></g>
<line data-flow="out" x1="460" y1="145" x2="512" y2="145" stroke="#2563eb" stroke-width="1.8" marker-end="url(#p2-di)"/><g class="flow-dot" data-flow="out"><circle r="3.4" fill="#2563eb"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M460,145 L512,145"/></circle><circle r="3.4" fill="#2563eb"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M460,145 L512,145"/></circle></g>
<line data-flow="out" x1="520" y1="160" x2="468" y2="160" stroke="#059669" stroke-width="1.6" stroke-dasharray="6 4" marker-end="url(#p2-ve)"/><g class="flow-dot" data-flow="out"><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M520,160 L468,160"/></circle><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M520,160 L468,160"/></circle></g>
<g data-flows="a"><rect x="40" y="40" width="160" height="60" rx="10" fill="#ffffff" stroke="#0284c7" stroke-width="1.4"/><text x="120" y="66" fill="#1f2937" font-size="11.5" font-weight="650" text-anchor="middle">Source A</text><text x="120" y="84" fill="#6b7280" font-size="9" text-anchor="middle">generation method 1</text><g class="flow-ping" data-flow="main"><rect class="flow-ping-ring" x="40" y="40" width="160" height="60" rx="10" fill="none" stroke="#0284c7" stroke-width="1.5"/></g><g class="flow-ping" data-flow="a"><rect class="flow-ping-ring" x="40" y="40" width="160" height="60" rx="10" fill="none" stroke="#0284c7" stroke-width="1.5"/></g></g>
<g data-flows="b"><rect x="40" y="180" width="160" height="60" rx="10" fill="#ffffff" stroke="#c026d3" stroke-width="1.4"/><text x="120" y="206" fill="#1f2937" font-size="11.5" font-weight="650" text-anchor="middle">Source B</text><text x="120" y="224" fill="#6b7280" font-size="9" text-anchor="middle">generation method 2</text><g class="flow-ping" data-flow="left"><rect class="flow-ping-ring" x="40" y="180" width="160" height="60" rx="10" fill="none" stroke="#c026d3" stroke-width="1.5"/></g><g class="flow-ping" data-flow="in"><rect class="flow-ping-ring" x="40" y="180" width="160" height="60" rx="10" fill="none" stroke="#c026d3" stroke-width="1.5"/></g><g class="flow-ping" data-flow="b"><rect class="flow-ping-ring" x="40" y="180" width="160" height="60" rx="10" fill="none" stroke="#c026d3" stroke-width="1.5"/></g></g>
<g data-flows="a b out"><rect x="290" y="110" width="170" height="70" rx="10" fill="#eff6ff" stroke="#2563eb" stroke-width="1.7"/><text x="375" y="140" fill="#1f2937" font-size="11.5" font-weight="700" text-anchor="middle">Shared processing</text><text x="375" y="158" fill="#6b7280" font-size="9" text-anchor="middle">merges both sources</text><g class="flow-ping" data-flow="out"><rect class="flow-ping-ring" x="290" y="110" width="170" height="70" rx="10" fill="none" stroke="#2563eb" stroke-width="1.5"/></g></g>
<g data-flows="out"><rect x="520" y="110" width="150" height="70" rx="10" fill="#ffffff" stroke="#d1d5db" stroke-width="1.3"/><text x="595" y="140" fill="#1f2937" font-size="11.5" font-weight="650" text-anchor="middle">Result</text><text x="595" y="158" fill="#6b7280" font-size="9" text-anchor="middle">shared output</text><g class="flow-ping" data-flow="out"><rect class="flow-ping-ring" x="520" y="110" width="150" height="70" rx="10" fill="none" stroke="#d1d5db" stroke-width="1.5"/></g></g>
<g font-size="8.5" font-weight="700">
<g data-flow="a" data-step="1"><circle cx="266" cy="120" r="8" fill="#ffffff" stroke="#0284c7"/><text x="266" y="123" fill="#0284c7" text-anchor="middle">1</text></g>
<g data-flow="b" data-step="2"><circle cx="272" cy="171" r="8" fill="#ffffff" stroke="#c026d3"/><text x="272" y="174" fill="#c026d3" text-anchor="middle">2</text></g>
<g data-flow="out" data-step="3"><circle cx="486" cy="145" r="8" fill="#ffffff" stroke="#2563eb"/><text x="486" y="148" fill="#2563eb" text-anchor="middle">3</text></g>
</g>
</svg>
</div>
<div class="chips">
<button class="chip" data-flow="a" style="color: var(--sky)"><i class="dot"></i><span>Source A (1)</span></button>
<button class="chip" data-flow="b" style="color: var(--fx)"><i class="dot"></i><span>Source B (2)</span></button>
<button class="chip" data-flow="out" style="color: var(--di)"><i class="dot"></i><span>Shared processing → Result (3)</span></button>
</div>
<div class="cards">
<div class="card" data-flow="a"><h3><span class="dot" style="background:var(--sky)"></span>Source A</h3><ul><li>1 Enters Shared processing at the TOP anchor point on the left edge (y=130)</li></ul></div>
<div class="card" data-flow="b"><h3><span class="dot" style="background:var(--fx)"></span>Source B</h3><ul><li>2 Enters the same left edge but at the BOTTOM anchor point (y=160) — doesn't coincide with Source A</li></ul></div>
<div class="card" data-flow="out"><h3><span class="dot" style="background:var(--di)"></span>Shared processing → Result</h3><ul><li>3 After merging, there's only 1 outgoing flow — no need to split by origin anymore</li></ul></div>
<div class="card">
<h3>Why this layout</h3>
<div class="why">The 2 sources are <b>offset top/bottom</b> (not in the same row) so each converging line is a simple bent segment (L-shape), instead of 2 lines stacking on top of each other as they both approach the same point. Both enter the <b>left edge</b> of Shared processing (not the top edge) because the left edge makes use of the vertical gap between the 2 sources — the top edge would force one of the two lines to loop over the other box's roof.</div>
</div>
</div>
</section>
<!-- ============================================================ -->
<!-- PATTERN 3 — CENTRAL HUB -->
<!-- ============================================================ -->
<section class="pattern" id="p3">
<h2><span class="num">3</span>Central hub (orchestrator)</h2>
<p class="when">Use this when there's <b>1 central component calling back and forth with N independent systems</b>, and the subsystems are NOT directly related to each other (unlike Pattern 2 — there, sources all flow into the same kind of result; here, each branch is a separate relationship with the hub). Place the hub at the <b>dead center</b>, each subsystem at exactly 1 of the 4 directions (top/bottom/left/right) so every branch is a straight axis-aligned line, with no branch needing to route around another.</p>
<div class="board">
<svg viewBox="0 0 700 500" xmlns="http://www.w3.org/2000/svg" font-family="inherit">
<!-- LAYOUT PLAN: hub at the center (275-425, 220-290). 4 satellites in exactly the 4
directions, each axis offset by 10-20 units between the OUT/RETURN direction so they
aren't concentric. NO region used — the hub is naturally already 1 system, no extra
boxed-off region needed (a region is only for GROUPING multiple boxes together —
redundant here). -->
<defs>
<marker id="p3-sky" markerWidth="9" markerHeight="9" refX="7.5" refY="4.5" orient="auto"><path d="M1,1 L8,4.5 L1,8" fill="none" stroke="#0284c7" stroke-width="1.6"/></marker>
<marker id="p3-pipe" markerWidth="9" markerHeight="9" refX="7.5" refY="4.5" orient="auto"><path d="M1,1 L8,4.5 L1,8" fill="none" stroke="#d97706" stroke-width="1.6"/></marker>
<marker id="p3-sc" markerWidth="9" markerHeight="9" refX="7.5" refY="4.5" orient="auto"><path d="M1,1 L8,4.5 L1,8" fill="none" stroke="#7c3aed" stroke-width="1.6"/></marker>
<marker id="p3-fx" markerWidth="9" markerHeight="9" refX="7.5" refY="4.5" orient="auto"><path d="M1,1 L8,4.5 L1,8" fill="none" stroke="#c026d3" stroke-width="1.6"/></marker>
<marker id="p3-ve" markerWidth="9" markerHeight="9" refX="7.5" refY="4.5" orient="auto"><path d="M1,1 L8,4.5 L1,8" fill="none" stroke="#059669" stroke-width="1.6"/></marker>
</defs>
<!-- Top -->
<line data-flow="top" x1="340" y1="220" x2="340" y2="108" stroke="#0284c7" stroke-width="1.8" marker-end="url(#p3-sky)"/><g class="flow-dot" data-flow="top"><circle r="3.4" fill="#0284c7"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M340,220 L340,108"/></circle><circle r="3.4" fill="#0284c7"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M340,220 L340,108"/></circle></g>
<line data-flow="top" x1="360" y1="100" x2="360" y2="212" stroke="#059669" stroke-width="1.6" stroke-dasharray="6 4" marker-end="url(#p3-ve)"/><g class="flow-dot" data-flow="top"><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M360,100 L360,212"/></circle><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M360,100 L360,212"/></circle></g>
<!-- Bottom -->
<line data-flow="bottom" x1="340" y1="290" x2="340" y2="402" stroke="#d97706" stroke-width="1.8" marker-end="url(#p3-pipe)"/><g class="flow-dot" data-flow="bottom"><circle r="3.4" fill="#d97706"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M340,290 L340,402"/></circle><circle r="3.4" fill="#d97706"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M340,290 L340,402"/></circle></g>
<line data-flow="bottom" x1="360" y1="410" x2="360" y2="298" stroke="#059669" stroke-width="1.6" stroke-dasharray="6 4" marker-end="url(#p3-ve)"/><g class="flow-dot" data-flow="bottom"><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M360,410 L360,298"/></circle><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M360,410 L360,298"/></circle></g>
<!-- Left -->
<line data-flow="left" x1="275" y1="245" x2="198" y2="245" stroke="#7c3aed" stroke-width="1.8" marker-end="url(#p3-sc)"/><g class="flow-dot" data-flow="left"><circle r="3.4" fill="#7c3aed"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M275,245 L198,245"/></circle><circle r="3.4" fill="#7c3aed"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M275,245 L198,245"/></circle></g>
<line data-flow="left" x1="190" y1="265" x2="267" y2="265" stroke="#059669" stroke-width="1.6" stroke-dasharray="6 4" marker-end="url(#p3-ve)"/><g class="flow-dot" data-flow="left"><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M190,265 L267,265"/></circle><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M190,265 L267,265"/></circle></g>
<!-- Right -->
<line data-flow="right" x1="425" y1="245" x2="502" y2="245" stroke="#c026d3" stroke-width="1.8" marker-end="url(#p3-fx)"/><g class="flow-dot" data-flow="right"><circle r="3.4" fill="#c026d3"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M425,245 L502,245"/></circle><circle r="3.4" fill="#c026d3"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M425,245 L502,245"/></circle></g>
<line data-flow="right" x1="510" y1="265" x2="433" y2="265" stroke="#059669" stroke-width="1.6" stroke-dasharray="6 4" marker-end="url(#p3-ve)"/><g class="flow-dot" data-flow="right"><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M510,265 L433,265"/></circle><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M510,265 L433,265"/></circle></g>
<!-- box -->
<g data-flows="top bottom left right"><rect x="275" y="220" width="150" height="70" rx="10" fill="#eff6ff" stroke="#2563eb" stroke-width="1.7"/><text x="350" y="250" fill="#1f2937" font-size="11.5" font-weight="700" text-anchor="middle">Orchestrator</text><text x="350" y="268" fill="#6b7280" font-size="9" text-anchor="middle">central hub</text><g class="flow-ping" data-flow="plan"><rect class="flow-ping-ring" x="275" y="220" width="150" height="70" rx="10" fill="none" stroke="#2563eb" stroke-width="1.5"/></g><g class="flow-ping" data-flow="bottom"><rect class="flow-ping-ring" x="275" y="220" width="150" height="70" rx="10" fill="none" stroke="#2563eb" stroke-width="1.5"/></g><g class="flow-ping" data-flow="right"><rect class="flow-ping-ring" x="275" y="220" width="150" height="70" rx="10" fill="none" stroke="#2563eb" stroke-width="1.5"/></g></g>
<g data-flows="top"><rect x="275" y="40" width="150" height="60" rx="10" fill="#ffffff" stroke="#0284c7" stroke-width="1.3"/><text x="350" y="66" fill="#1f2937" font-size="11" font-weight="650" text-anchor="middle">System 1</text><text x="350" y="84" fill="#6b7280" font-size="9" text-anchor="middle">independent</text><g class="flow-ping" data-flow="main"><rect class="flow-ping-ring" x="275" y="40" width="150" height="60" rx="10" fill="none" stroke="#0284c7" stroke-width="1.5"/></g></g>
<g data-flows="bottom"><rect x="275" y="410" width="150" height="60" rx="10" fill="#ffffff" stroke="#d97706" stroke-width="1.3"/><text x="350" y="436" fill="#1f2937" font-size="11" font-weight="650" text-anchor="middle">System 2</text><text x="350" y="454" fill="#6b7280" font-size="9" text-anchor="middle">independent</text><g class="flow-ping" data-flow="bottom"><rect class="flow-ping-ring" x="275" y="410" width="150" height="60" rx="10" fill="none" stroke="#d97706" stroke-width="1.5"/></g></g>
<g data-flows="left"><rect x="40" y="215" width="150" height="60" rx="10" fill="#ffffff" stroke="#7c3aed" stroke-width="1.3"/><text x="115" y="241" fill="#1f2937" font-size="11" font-weight="650" text-anchor="middle">System 3</text><text x="115" y="259" fill="#6b7280" font-size="9" text-anchor="middle">independent</text></g>
<g data-flows="right"><rect x="510" y="215" width="150" height="60" rx="10" fill="#ffffff" stroke="#c026d3" stroke-width="1.3"/><text x="585" y="241" fill="#1f2937" font-size="11" font-weight="650" text-anchor="middle">System 4</text><text x="585" y="259" fill="#6b7280" font-size="9" text-anchor="middle">independent</text></g>
<g font-size="8.5" font-weight="700">
<g data-flow="top" data-step="1"><circle cx="340" cy="164" r="8" fill="#ffffff" stroke="#0284c7"/><text x="340" y="167" fill="#0284c7" text-anchor="middle">1</text></g>
<g data-flow="bottom" data-step="2"><circle cx="340" cy="346" r="8" fill="#ffffff" stroke="#d97706"/><text x="340" y="349" fill="#d97706" text-anchor="middle">2</text></g>
<g data-flow="left" data-step="3"><circle cx="236" cy="245" r="8" fill="#ffffff" stroke="#7c3aed"/><text x="236" y="248" fill="#7c3aed" text-anchor="middle">3</text></g>
<g data-flow="right" data-step="4"><circle cx="464" cy="245" r="8" fill="#ffffff" stroke="#c026d3"/><text x="464" y="248" fill="#c026d3" text-anchor="middle">4</text></g>
</g>
</svg>
</div>
<div class="chips">
<button class="chip" data-flow="top" style="color: var(--sky)"><i class="dot"></i><span>Hub ↔ System 1</span></button>
<button class="chip" data-flow="bottom" style="color: var(--pipe)"><i class="dot"></i><span>Hub ↔ System 2</span></button>
<button class="chip" data-flow="left" style="color: var(--script)"><i class="dot"></i><span>Hub ↔ System 3</span></button>
<button class="chip" data-flow="right" style="color: var(--fx)"><i class="dot"></i><span>Hub ↔ System 4</span></button>
</div>
<div class="cards">
<div class="card" data-flow="top"><h3><span class="dot" style="background:var(--sky)"></span>Hub ↔ System 1</h3><ul><li>1 Each branch has its own color — don't reuse 1 color for all 4 branches even though they're all "hub calling out"</li></ul></div>
<div class="card" data-flow="bottom"><h3><span class="dot" style="background:var(--pipe)"></span>Hub ↔ System 2</h3><ul><li>2 Symmetric with the top branch through the hub's center</li></ul></div>
<div class="card" data-flow="left"><h3><span class="dot" style="background:var(--script)"></span>Hub ↔ System 3</h3><ul><li>3 Horizontal axis, doesn't cross the vertical axis because the hub sits at the center of both axes</li></ul></div>
<div class="card" data-flow="right"><h3><span class="dot" style="background:var(--fx)"></span>Hub ↔ System 4</h3><ul><li>4 Symmetric with the left branch through the hub's center</li></ul></div>
<div class="card">
<h3>Why this layout</h3>
<div class="why">Placing the hub at the center of all 4 axes is the ONLY way to make all 4 branches straight lines — any off-center position forces at least 1 branch to bend/route around. Beyond 4 subsystems, <b>don't force in extra diagonal directions</b> (gets messy) — switch to placing the hub near the top, with the subsystems arranged in a grid below it; each branch can still be a short bent line.</div>
</div>
</div>
</section>
<!-- ============================================================ -->
<!-- PATTERN 4 — CONDITIONAL BRANCHING -->
<!-- ============================================================ -->
<section class="pattern" id="p4">
<h2><span class="num">4</span>Conditional branching</h2>
<p class="when">Use this when there's <b>1 decision point</b> splitting the flow into 2+ different outcomes. No need to draw a diamond symbol (classic flowchart style) — just use a REGULAR BOX for the decision point, attach a "Pass"/"Fail" label (≤4 words) on each branching arrow to distinguish direction — keep the same shape vocabulary consistent with the rest of the diagram, don't mix in a new symbol.</p>
<div class="board">
<svg viewBox="0 0 700 320" xmlns="http://www.w3.org/2000/svg" font-family="inherit">
<!-- LAYOUT PLAN: Pass goes up (y=70), Fail goes down (y=250) — symmetric across the decision
point's horizontal axis (y=160) so the 2 branches look balanced, not skewed to one side. -->
<defs>
<marker id="p4-di" markerWidth="9" markerHeight="9" refX="7.5" refY="4.5" orient="auto"><path d="M1,1 L8,4.5 L1,8" fill="none" stroke="#2563eb" stroke-width="1.6"/></marker>
<marker id="p4-ve" markerWidth="9" markerHeight="9" refX="7.5" refY="4.5" orient="auto"><path d="M1,1 L8,4.5 L1,8" fill="none" stroke="#059669" stroke-width="1.6"/></marker>
<marker id="p4-sec" markerWidth="9" markerHeight="9" refX="7.5" refY="4.5" orient="auto"><path d="M1,1 L8,4.5 L1,8" fill="none" stroke="#dc2626" stroke-width="1.6"/></marker>
</defs>
<line data-flow="in" x1="190" y1="160" x2="262" y2="160" stroke="#2563eb" stroke-width="1.8" marker-end="url(#p4-di)"/><g class="flow-dot" data-flow="in"><circle r="3.4" fill="#2563eb"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M190,160 L262,160"/></circle><circle r="3.4" fill="#2563eb"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M190,160 L262,160"/></circle></g>
<line data-flow="in" x1="270" y1="175" x2="198" y2="175" stroke="#059669" stroke-width="1.6" stroke-dasharray="6 4" marker-end="url(#p4-ve)"/><g class="flow-dot" data-flow="in"><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M270,175 L198,175"/></circle><circle r="3.4" fill="#059669"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M270,175 L198,175"/></circle></g>
<path data-flow="yes" d="M440,145 L480,145 L480,70 L512,70" fill="none" stroke="#2563eb" stroke-width="1.8" marker-end="url(#p4-di)"/><g class="flow-dot" data-flow="yes"><circle r="3.4" fill="#2563eb"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M440,145 L480,145 L480,70 L512,70"/></circle><circle r="3.4" fill="#2563eb"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M440,145 L480,145 L480,70 L512,70"/></circle></g>
<path data-flow="no" d="M440,175 L480,175 L480,250 L512,250" fill="none" stroke="#dc2626" stroke-width="1.8" marker-end="url(#p4-sec)"/><g class="flow-dot" data-flow="no"><circle r="3.4" fill="#dc2626"><animateMotion dur="1.20s" begin="0.00s" repeatCount="indefinite" path="M440,175 L480,175 L480,250 L512,250"/></circle><circle r="3.4" fill="#dc2626"><animateMotion dur="1.20s" begin="0.60s" repeatCount="indefinite" path="M440,175 L480,175 L480,250 L512,250"/></circle></g>
<g data-flows="in"><rect x="40" y="130" width="150" height="60" rx="10" fill="#ffffff" stroke="#d1d5db" stroke-width="1.3"/><text x="115" y="156" fill="#1f2937" font-size="11.5" font-weight="650" text-anchor="middle">Incoming request</text><text x="115" y="174" fill="#6b7280" font-size="9" text-anchor="middle">starting point</text></g>
<g data-flows="in yes no"><rect x="270" y="130" width="170" height="60" rx="10" fill="#eff6ff" stroke="#2563eb" stroke-width="1.7"/><text x="355" y="156" fill="#1f2937" font-size="11.5" font-weight="700" text-anchor="middle">Meets criteria?</text><text x="355" y="174" fill="#6b7280" font-size="9" text-anchor="middle">decision point</text><g class="flow-ping" data-flow="chuyen"><rect class="flow-ping-ring" x="270" y="130" width="170" height="60" rx="10" fill="none" stroke="#2563eb" stroke-width="1.5"/></g><g class="flow-ping" data-flow="in"><rect class="flow-ping-ring" x="270" y="130" width="170" height="60" rx="10" fill="none" stroke="#2563eb" stroke-width="1.5"/></g><g class="flow-ping" data-flow="a"><rect class="flow-ping-ring" x="270" y="130" width="170" height="60" rx="10" fill="none" stroke="#2563eb" stroke-width="1.5"/></g><g class="flow-ping" data-flow="b"><rect class="flow-ping-ring" x="270" y="130" width="170" height="60" rx="10" fill="none" stroke="#2563eb" stroke-width="1.5"/></g></g>
<g data-flows="yes"><rect x="520" y="40" width="150" height="60" rx="10" fill="#ffffff" stroke="#2563eb" stroke-width="1.4"/><text x="595" y="66" fill="#1f2937" font-size="11" font-weight="650" text-anchor="middle">Automated processing</text><text x="595" y="84" fill="#6b7280" font-size="9" text-anchor="middle">pass branch</text></g>
<g data-flows="no"><rect x="520" y="220" width="150" height="60" rx="10" fill="#ffffff" stroke="#dc2626" stroke-width="1.4"/><text x="595" y="246" fill="#1f2937" font-size="11" font-weight="650" text-anchor="middle">Route to reviewer</text><text x="595" y="264" fill="#6b7280" font-size="9" text-anchor="middle">fail branch</text></g>
<text data-flow="yes" x="490" y="105" fill="#2563eb" font-size="8.5" text-anchor="middle" font-weight="400">Pass</text>
<text data-flow="no" x="505" y="188" fill="#dc2626" font-size="8.5" text-anchor="middle" font-weight="400">Fail</text>
<g font-size="8.5" font-weight="700">
<g data-flow="in" data-step="1"><circle cx="230" cy="160" r="8" fill="#ffffff" stroke="#2563eb"/><text x="230" y="163" fill="#2563eb" text-anchor="middle">1</text></g>
<g data-flow="yes" data-step="2"><circle cx="480" cy="115" r="8" fill="#ffffff" stroke="#2563eb"/><text x="480" y="118" fill="#2563eb" text-anchor="middle">2</text></g>
<g data-flow="no" data-step="3"><circle cx="480" cy="205" r="8" fill="#ffffff" stroke="#dc2626"/><text x="480" y="208" fill="#dc2626" text-anchor="middle">3</text></g>
</g>
</svg>
</div>
<div class="chips">
<button class="chip" data-flow="in" style="color: var(--di)"><i class="dot"></i><span>Into the decision point (1)</span></button>
<button class="chip" data-flow="yes" style="color: var(--di)"><i class="dot"></i><span>Pass branch (2)</span></button>
<button class="chip" data-flow="no" style="color: var(--sec)"><i class="dot"></i><span>Fail branch (3)</span></button>
</div>
<div class="cards">
<div class="card" data-flow="in"><h3><span class="dot" style="background:var(--di)"></span>Into the decision point</h3><ul><li>1 Has a return direction — the decision point may need to ask for more information</li></ul></div>
<div class="card" data-flow="yes"><h3><span class="dot" style="background:var(--di)"></span>Pass branch</h3><ul><li>2 Has NO return direction — this is the END point of the flow, not a loop, so it doesn't need a return direction (state the reason explicitly, as the rule requires)</li></ul></div>
<div class="card" data-flow="no"><h3><span class="dot" style="background:var(--sec)"></span>Fail branch</h3><ul><li>3 Also an end point, colored red because this branch needs a human to step in — red is used per its correct semantic meaning "checkpoint needing attention", not "error"</li></ul></div>
<div class="card">
<h3>Why this layout</h3>
<div class="why">The 2 branches are symmetric across the decision point's horizontal axis — every branch has just 1 bend (horizontal→vertical→horizontal), neither branch is longer/more complex than the other despite different content, keeping a "2 equal choices" feel instead of accidentally implying one branch is "primary" and the other "secondary".</div>
</div>
</div>
</section>
</div>
<script>
(() => {
// Theme (thin-line SVG icon — see SKILL.md's "Theme icon button" section)
const tbtn = document.getElementById('theme-toggle');
const sysDark = matchMedia('(prefers-color-scheme: dark)');
const effTheme = () => document.documentElement.dataset.theme || (sysDark.matches ? 'dark' : 'light');
const ICON_SUN = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/></svg>';
const ICON_MOON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79Z"/></svg>';
const paintBtn = () => { tbtn.innerHTML = effTheme() === 'dark' ? ICON_SUN : ICON_MOON;
tbtn.title = effTheme() === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'; };
tbtn.addEventListener('click', () => { const n = effTheme() === 'dark' ? 'light' : 'dark';
document.documentElement.dataset.theme = n; try { localStorage.setItem('theme-guide', n); } catch (e) {} paintBtn(); });
sysDark.addEventListener('change', paintBtn);
paintBtn();
// Flow-select interaction — scoped to EACH section.pattern independently (this page has 4
// separate diagrams, unlike a real operational diagram which has only 1 <svg> inside the
// template.html app shell).
document.querySelectorAll('section.pattern').forEach(sec => {
const svg = sec.querySelector('svg');
const chips = [...sec.querySelectorAll('.chip[data-flow]')];
const flowEls = [...svg.querySelectorAll('[data-flow]')];
const nodeEls = [...svg.querySelectorAll('[data-flows]')];
const cards = [...sec.querySelectorAll('.card[data-flow]')];
let cur = null;
function focus(f) {
cur = f;
flowEls.forEach(e => {
const mine = e.dataset.flow === f;
e.classList.toggle('dim', !!f && !mine);
if (e.matches('line, path') || e.classList.contains('flow-dot') || e.classList.contains('flow-ping')) e.classList.toggle('flowing', !!f && mine);
});
nodeEls.forEach(n => n.classList.toggle('dim', !!f && !n.dataset.flows.split(' ').includes(f)));
chips.forEach(c => c.classList.toggle('on', c.dataset.flow === f));
cards.forEach(c => c.classList.toggle('hidden', !!f && c.dataset.flow !== f));
}
chips.forEach(c => c.addEventListener('click', () => focus(cur === c.dataset.flow ? null : c.dataset.flow)));
svg.addEventListener('click', e => {
const el = e.target.closest('[data-flow]');
focus(el ? el.dataset.flow : null);
});
document.addEventListener('keydown', e => { if (e.key === 'Escape') focus(null); });
});
})();
</script>
</body>
</html>