Merge curated fork work: 12 new diagram types, icons, export, onboarding v2

Imports the substantive work from the aristide/diagram-design fork (PR #9)
as a curated set:

- 12 new diagram types with reference docs: radar, bar, line, gantt,
  scatter, it-state, high-level, process, medallion, data-flow,
  dp-integration, dp-security-matrix (26 types total)
- 86-icon monochrome IT/cloud primitive, build script, and gallery
- /diagram-design:export command with PNG/SVG export reference
- Onboarding v2: URL, skill, and folder design-token sources
- Mandatory connector rules (orthogonal elbows, label gaps, fanned
  attach points, bridge crossings) + architecture/ER connector upgrades
- Series palette tokens for multi-series chart types
- THIRD_PARTY_LICENSES.md covering Tabler, Simple Icons, log-z, Devicon

Not taken from the fork: plugin-manifest identity changes, the client-brand
takeover of style-guide.md, and client-branded example files. The six
affected type examples were regenerated in the neutral editorial skin with
generic content, and their README screenshots recaptured. Reference docs
for those types were rewritten to semantic tokens and typography roles.

Version: 1.0.0 -> 2.0.0.

Co-authored-by: Aristide <mendoo.aristide@gmail.com>
This commit is contained in:
Cathryn Lavery
2026-07-14 17:09:25 -07:00
co-authored by Aristide
parent 0ab077f229
commit 2e9cefb9f3
175 changed files with 13368 additions and 261 deletions
@@ -71,24 +71,39 @@
<rect width="100%" height="100%" fill="#2d3142"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- Zone: Content services (drawn before arrows and nodes) -->
<rect x="616" y="128" width="164" height="272" rx="8"
fill="rgba(245,245,245,0.03)" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<rect x="672" y="132" width="52" height="12" rx="2" fill="#2d3142"/>
<text x="698" y="141" fill="rgba(245,245,245,0.35)" font-size="7" font-family="'Geist Mono', monospace"
text-anchor="middle" letter-spacing="0.14em">CONTENT</text>
<!-- Arrows first (behind boxes) -->
<line x1="168" y1="272" x2="220" y2="272" stroke="#6a95d8" stroke-width="1.2" marker-end="url(#arrow-link)"/>
<line x1="364" y1="272" x2="416" y2="272" stroke="#f08a59" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
<line x1="576" y1="256" x2="628" y2="212" stroke="#bfc0c0" stroke-width="1.2" marker-end="url(#arrow)"/>
<line x1="576" y1="288" x2="628" y2="332" stroke="#bfc0c0" stroke-width="1.2" marker-end="url(#arrow)"/>
<path d="M 496,240 H 692 Q 700,240 700,232 V 224"
fill="none" stroke="#bfc0c0" stroke-width="1.2" marker-end="url(#arrow)"/>
<path d="M 496,304 H 692 Q 700,304 700,312 V 320"
fill="none" stroke="#bfc0c0" stroke-width="1.2" marker-end="url(#arrow)"/>
<!-- Dashed return: Cloudflare→Reader (cached HTML) -->
<path d="M 220,288 H 168" fill="none" stroke="#bfc0c0" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
<!-- Arrow labels -->
<rect x="172" y="252" width="48" height="12" rx="2" fill="#2d3142"/>
<text x="196" y="262" fill="#6a95d8" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">HTTPS</text>
<rect x="172" y="278" width="32" height="12" rx="2" fill="#2d3142"/>
<text x="188" y="287" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">RESP</text>
<rect x="368" y="252" width="48" height="12" rx="2" fill="#2d3142"/>
<text x="392" y="262" fill="#f08a59" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">SSR</text>
<rect x="560" y="212" width="56" height="12" rx="2" fill="#2d3142"/>
<text x="588" y="222" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">READ MDX</text>
<rect x="564" y="234" width="60" height="12" rx="2" fill="#2d3142"/>
<text x="594" y="243" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">READ MDX</text>
<rect x="560" y="320" width="52" height="12" rx="2" fill="#2d3142"/>
<text x="586" y="330" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">QUERY</text>
<rect x="568" y="298" width="44" height="12" rx="2" fill="#2d3142"/>
<text x="590" y="307" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">QUERY</text>
<!-- Node: Reader -->
<rect x="40" y="240" width="128" height="64" rx="6" fill="#2d3142"/>
@@ -156,6 +171,9 @@
<line x1="784" y1="442" x2="812" y2="442" stroke="#f08a59" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
<text x="820" y="444" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Primary flow</text>
<line x1="900" y1="442" x2="928" y2="442" stroke="#bfc0c0" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
<text x="936" y="444" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Return / async</text>
</svg>
</div>
</body>
@@ -61,24 +61,39 @@
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- Zone: Content services (drawn before arrows and nodes) -->
<rect x="616" y="128" width="164" height="272" rx="8"
fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<rect x="672" y="132" width="52" height="12" rx="2" fill="#f5f5f5"/>
<text x="698" y="141" fill="rgba(45,49,66,0.40)" font-size="7" font-family="'Geist Mono', monospace"
text-anchor="middle" letter-spacing="0.14em">CONTENT</text>
<!-- Arrows first (behind boxes) -->
<line x1="168" y1="272" x2="220" y2="272" stroke="#2e5aa8" stroke-width="1.2" marker-end="url(#arrow-link)"/>
<line x1="364" y1="272" x2="416" y2="272" stroke="#eb6c36" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
<line x1="576" y1="256" x2="628" y2="212" stroke="#4f5d75" stroke-width="1.2" marker-end="url(#arrow)"/>
<line x1="576" y1="288" x2="628" y2="332" stroke="#4f5d75" stroke-width="1.2" marker-end="url(#arrow)"/>
<path d="M 496,240 H 692 Q 700,240 700,232 V 224"
fill="none" stroke="#4f5d75" stroke-width="1.2" marker-end="url(#arrow)"/>
<path d="M 496,304 H 692 Q 700,304 700,312 V 320"
fill="none" stroke="#4f5d75" stroke-width="1.2" marker-end="url(#arrow)"/>
<!-- Dashed return: Cloudflare→Reader (cached HTML) -->
<path d="M 220,288 H 168" fill="none" stroke="#4f5d75" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
<!-- Arrow labels -->
<rect x="172" y="252" width="48" height="12" rx="2" fill="#f5f5f5"/>
<text x="196" y="262" fill="#2e5aa8" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">HTTPS</text>
<rect x="172" y="278" width="32" height="12" rx="2" fill="#f5f5f5"/>
<text x="188" y="287" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">RESP</text>
<rect x="368" y="252" width="48" height="12" rx="2" fill="#f5f5f5"/>
<text x="392" y="262" fill="#eb6c36" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">SSR</text>
<rect x="560" y="212" width="56" height="12" rx="2" fill="#f5f5f5"/>
<text x="588" y="222" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">READ MDX</text>
<rect x="564" y="234" width="60" height="12" rx="2" fill="#f5f5f5"/>
<text x="594" y="243" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">READ MDX</text>
<rect x="560" y="320" width="52" height="12" rx="2" fill="#f5f5f5"/>
<text x="586" y="330" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">QUERY</text>
<rect x="568" y="298" width="44" height="12" rx="2" fill="#f5f5f5"/>
<text x="590" y="307" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">QUERY</text>
<!-- Node: Reader -->
<rect x="40" y="240" width="128" height="64" rx="6" fill="#f5f5f5"/>
@@ -146,6 +161,9 @@
<line x1="784" y1="442" x2="812" y2="442" stroke="#eb6c36" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
<text x="820" y="444" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Primary flow</text>
<line x1="900" y1="442" x2="928" y2="442" stroke="#4f5d75" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
<text x="936" y="444" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Return / async</text>
</svg>
</div>
@@ -71,24 +71,40 @@
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- Zone: Content services (drawn before arrows and nodes) -->
<rect x="616" y="128" width="164" height="272" rx="8"
fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<rect x="672" y="132" width="52" height="12" rx="2" fill="#f5f5f5"/>
<text x="698" y="141" fill="rgba(45,49,66,0.40)" font-size="7" font-family="'Geist Mono', monospace"
text-anchor="middle" letter-spacing="0.14em">CONTENT</text>
<!-- Arrows first (behind boxes) -->
<line x1="168" y1="272" x2="220" y2="272" stroke="#2e5aa8" stroke-width="1.2" marker-end="url(#arrow-link)"/>
<line x1="364" y1="272" x2="416" y2="272" stroke="#eb6c36" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
<line x1="576" y1="256" x2="628" y2="212" stroke="#4f5d75" stroke-width="1.2" marker-end="url(#arrow)"/>
<line x1="576" y1="288" x2="628" y2="332" stroke="#4f5d75" stroke-width="1.2" marker-end="url(#arrow)"/>
<!-- Exit Astro top→enter MDX bottom; exit Astro bottom→enter CMS top -->
<path d="M 496,240 H 692 Q 700,240 700,232 V 224"
fill="none" stroke="#4f5d75" stroke-width="1.2" marker-end="url(#arrow)"/>
<path d="M 496,304 H 692 Q 700,304 700,312 V 320"
fill="none" stroke="#4f5d75" stroke-width="1.2" marker-end="url(#arrow)"/>
<!-- Dashed return: Cloudflare→Reader (cached HTML — same orthogonal rules as solid) -->
<path d="M 220,288 H 168" fill="none" stroke="#4f5d75" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
<!-- Arrow labels -->
<rect x="172" y="252" width="48" height="12" rx="2" fill="#f5f5f5"/>
<text x="196" y="262" fill="#2e5aa8" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">HTTPS</text>
<rect x="172" y="278" width="32" height="12" rx="2" fill="#f5f5f5"/>
<text x="188" y="287" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">RESP</text>
<rect x="368" y="252" width="48" height="12" rx="2" fill="#f5f5f5"/>
<text x="392" y="262" fill="#eb6c36" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">SSR</text>
<rect x="560" y="212" width="56" height="12" rx="2" fill="#f5f5f5"/>
<text x="588" y="222" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">READ MDX</text>
<rect x="564" y="234" width="60" height="12" rx="2" fill="#f5f5f5"/>
<text x="594" y="243" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">READ MDX</text>
<rect x="560" y="320" width="52" height="12" rx="2" fill="#f5f5f5"/>
<text x="586" y="330" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">QUERY</text>
<rect x="568" y="298" width="44" height="12" rx="2" fill="#f5f5f5"/>
<text x="590" y="307" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">QUERY</text>
<!-- Node: Reader -->
<rect x="40" y="240" width="128" height="64" rx="6" fill="#f5f5f5"/>
@@ -156,6 +172,9 @@
<line x1="784" y1="442" x2="812" y2="442" stroke="#eb6c36" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
<text x="820" y="444" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Primary flow</text>
<line x1="900" y1="442" x2="928" y2="442" stroke="#4f5d75" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
<text x="936" y="444" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Return / async</text>
</svg>
</div>
</body>
@@ -0,0 +1,127 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sprint velocity · Bar chart · dark</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--color-paper: #2d3142;
--color-ink: #f5f5f5;
--color-muted: #bfc0c0;
--color-accent: #f08a59;
--font-sans: 'Geist', system-ui, sans-serif;
--font-serif: 'Instrument Serif', serif;
--font-mono: 'Geist Mono', ui-monospace, monospace;
}
body { font-family: var(--font-sans); background: var(--color-paper); color: var(--color-ink); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 3rem 2rem; }
.frame { max-width: 1200px; width: 100%; }
.eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.15; margin-bottom: 1.5rem; }
svg { width: 100%; min-width: 760px; display: block; }
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Bar · Diagram Design</p>
<h1>Sprint velocity · 8-sprint view</h1>
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(245,245,245,0.10)"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- Y-axis rotated label -->
<text transform="rotate(-90 24 230)" x="24" y="230" fill="#bfc0c0" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">STORY POINTS</text>
<!-- Horizontal gridlines (drawn before bars) -->
<line x1="80" y1="357" x2="960" y2="357" stroke="rgba(245,245,245,0.08)" stroke-width="0.8"/>
<line x1="80" y1="293" x2="960" y2="293" stroke="rgba(245,245,245,0.08)" stroke-width="0.8"/>
<line x1="80" y1="230" x2="960" y2="230" stroke="rgba(245,245,245,0.08)" stroke-width="0.8"/>
<line x1="80" y1="167" x2="960" y2="167" stroke="rgba(245,245,245,0.08)" stroke-width="0.8"/>
<line x1="80" y1="103" x2="960" y2="103" stroke="rgba(245,245,245,0.08)" stroke-width="0.8"/>
<line x1="80" y1="40" x2="960" y2="40" stroke="rgba(245,245,245,0.06)" stroke-width="0.8"/>
<!-- Y-axis line and X-axis baseline -->
<line x1="80" y1="40" x2="80" y2="420" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<line x1="80" y1="420" x2="960" y2="420" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<!-- Y-axis tick labels -->
<text x="72" y="361" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">20</text>
<text x="72" y="297" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">40</text>
<text x="72" y="234" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">60</text>
<text x="72" y="171" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">80</text>
<text x="72" y="107" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">100</text>
<text x="72" y="44" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">120</text>
<!-- ─── BARS (pitch=110, width=72, pad=19) ─── -->
<!-- S1: v=72, h=228, y=192 -->
<rect x="99" y="192" width="72" height="228" fill="#f5f5f5"/>
<rect x="99" y="192" width="72" height="228" fill="rgba(191,192,192,0.15)" stroke="#bfc0c0" stroke-width="1"/>
<text x="135" y="184" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">72</text>
<!-- S2: v=88, h=280, y=140 -->
<rect x="209" y="140" width="72" height="280" fill="#f5f5f5"/>
<rect x="209" y="140" width="72" height="280" fill="rgba(191,192,192,0.15)" stroke="#bfc0c0" stroke-width="1"/>
<text x="245" y="132" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">88</text>
<!-- S3: v=95, h=300, y=120 -->
<rect x="319" y="120" width="72" height="300" fill="#f5f5f5"/>
<rect x="319" y="120" width="72" height="300" fill="rgba(191,192,192,0.15)" stroke="#bfc0c0" stroke-width="1"/>
<text x="355" y="112" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">95</text>
<!-- S4: v=78, h=248, y=172 -->
<rect x="429" y="172" width="72" height="248" fill="#f5f5f5"/>
<rect x="429" y="172" width="72" height="248" fill="rgba(191,192,192,0.15)" stroke="#bfc0c0" stroke-width="1"/>
<text x="465" y="164" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">78</text>
<!-- S5 FOCAL: v=110, h=348, y=72 -->
<rect x="539" y="72" width="72" height="348" fill="#f5f5f5"/>
<rect x="539" y="72" width="72" height="348" fill="rgba(240,138,89,0.14)" stroke="#f08a59" stroke-width="1"/>
<text x="575" y="64" fill="#f08a59" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">110</text>
<!-- S6: v=102, h=324, y=96 -->
<rect x="649" y="96" width="72" height="324" fill="#f5f5f5"/>
<rect x="649" y="96" width="72" height="324" fill="rgba(191,192,192,0.15)" stroke="#bfc0c0" stroke-width="1"/>
<text x="685" y="88" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">102</text>
<!-- S7: v=85, h=268, y=152 -->
<rect x="759" y="152" width="72" height="268" fill="#f5f5f5"/>
<rect x="759" y="152" width="72" height="268" fill="rgba(191,192,192,0.15)" stroke="#bfc0c0" stroke-width="1"/>
<text x="795" y="144" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">85</text>
<!-- S8: v=93, h=296, y=124 -->
<rect x="869" y="124" width="72" height="296" fill="#f5f5f5"/>
<rect x="869" y="124" width="72" height="296" fill="rgba(191,192,192,0.15)" stroke="#bfc0c0" stroke-width="1"/>
<text x="905" y="116" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">93</text>
<!-- X-axis category labels -->
<text x="135" y="440" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S1</text>
<text x="245" y="440" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S2</text>
<text x="355" y="440" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S3</text>
<text x="465" y="440" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S4</text>
<text x="575" y="440" fill="#f08a59" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S5</text>
<text x="685" y="440" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S6</text>
<text x="795" y="440" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S7</text>
<text x="905" y="440" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S8</text>
<!-- Legend -->
<line x1="40" y1="462" x2="960" y2="462" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<text x="40" y="478" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<rect x="40" y="488" width="16" height="10" rx="2" fill="rgba(240,138,89,0.14)" stroke="#f08a59" stroke-width="1"/>
<text x="64" y="497" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Sprint 5 · record high</text>
<rect x="220" y="488" width="16" height="10" rx="2" fill="rgba(191,192,192,0.15)" stroke="#bfc0c0" stroke-width="1"/>
<text x="244" y="497" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Other sprints</text>
</svg>
</div>
</body>
</html>
@@ -0,0 +1,103 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sprint velocity · Bar chart</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root { --color-paper:#f5f5f5; --color-paper-2:#ececec; --color-ink:#2d3142; --color-muted:#4f5d75; --color-soft:#7a8399; --color-rule:rgba(45,49,66,0.12); --color-accent:#eb6c36; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
.container { max-width: 1200px; margin: 0 auto; }
.header { margin-bottom: 2.5rem; }
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 58ch; }
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
svg { width: 100%; min-width: 760px; display: block; }
.cards { display: grid; grid-template-columns: 1.1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
@media (max-width: 720px) { .cards { grid-template-columns: 1fr; } }
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(45,49,66,0.08); }
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
.card-dot.coral { background: var(--color-accent); } .card-dot.ink { background: var(--color-ink); } .card-dot.muted { background: var(--color-muted); }
.card h3 { font-size: 0.875rem; font-weight: 600; }
.card p { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; }
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(45,49,66,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<p class="header-eyebrow">Bar · Diagram Design</p>
<h1>Sprint velocity · 8-sprint view</h1>
<p class="subtitle">Story points delivered per two-week sprint over one product cycle. Sprint 5 is the standout — a focused scope and no mid-sprint scope additions.</p>
</div>
<div class="diagram-container">
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
<defs><pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse"><circle cx="1" cy="1" r="0.9" fill="rgba(45,49,66,0.10)"/></pattern></defs>
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<text transform="rotate(-90 24 230)" x="24" y="230" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">STORY POINTS</text>
<line x1="80" y1="357" x2="960" y2="357" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="293" x2="960" y2="293" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="230" x2="960" y2="230" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="167" x2="960" y2="167" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="103" x2="960" y2="103" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="40" x2="960" y2="40" stroke="rgba(45,49,66,0.06)" stroke-width="0.8"/>
<line x1="80" y1="40" x2="80" y2="420" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<line x1="80" y1="420" x2="960" y2="420" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<text x="72" y="361" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">20</text>
<text x="72" y="297" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">40</text>
<text x="72" y="234" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">60</text>
<text x="72" y="171" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">80</text>
<text x="72" y="107" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">100</text>
<text x="72" y="44" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">120</text>
<rect x="99" y="192" width="72" height="228" fill="#f5f5f5"/><rect x="99" y="192" width="72" height="228" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/><text x="135" y="184" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">72</text>
<rect x="209" y="140" width="72" height="280" fill="#f5f5f5"/><rect x="209" y="140" width="72" height="280" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/><text x="245" y="132" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">88</text>
<rect x="319" y="120" width="72" height="300" fill="#f5f5f5"/><rect x="319" y="120" width="72" height="300" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/><text x="355" y="112" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">95</text>
<rect x="429" y="172" width="72" height="248" fill="#f5f5f5"/><rect x="429" y="172" width="72" height="248" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/><text x="465" y="164" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">78</text>
<rect x="539" y="72" width="72" height="348" fill="#f5f5f5"/><rect x="539" y="72" width="72" height="348" fill="rgba(235,108,54,0.12)" stroke="#eb6c36" stroke-width="1"/><text x="575" y="64" fill="#eb6c36" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">110</text>
<rect x="649" y="96" width="72" height="324" fill="#f5f5f5"/><rect x="649" y="96" width="72" height="324" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/><text x="685" y="88" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">102</text>
<rect x="759" y="152" width="72" height="268" fill="#f5f5f5"/><rect x="759" y="152" width="72" height="268" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/><text x="795" y="144" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">85</text>
<rect x="869" y="124" width="72" height="296" fill="#f5f5f5"/><rect x="869" y="124" width="72" height="296" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/><text x="905" y="116" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">93</text>
<text x="135" y="440" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S1</text>
<text x="245" y="440" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S2</text>
<text x="355" y="440" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S3</text>
<text x="465" y="440" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S4</text>
<text x="575" y="440" fill="#eb6c36" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S5</text>
<text x="685" y="440" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S6</text>
<text x="795" y="440" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S7</text>
<text x="905" y="440" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S8</text>
<line x1="40" y1="462" x2="960" y2="462" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="40" y="478" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<rect x="40" y="488" width="16" height="10" rx="2" fill="rgba(235,108,54,0.12)" stroke="#eb6c36" stroke-width="1"/>
<text x="64" y="497" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Sprint 5 · record high</text>
<rect x="220" y="488" width="16" height="10" rx="2" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<text x="244" y="497" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Other sprints</text>
</svg>
</div>
<div class="cards">
<div class="card">
<p class="eyebrow">RECORD SPRINT</p>
<div class="card-header"><span class="card-dot coral"></span><h3>Sprint 5 · 110 points</h3></div>
<p>No mid-sprint additions, zero carry-over from S4, and the team's highest-complexity stories were completed two days early. The bar is not the goal — the conditions are.</p>
</div>
<div class="card">
<div class="card-header"><span class="card-dot ink"></span><h3>Trend: rising with one dip</h3></div>
<p>Average across 8 sprints: 89 points. S4 dipped to 78 — a dependency on an external API that shipped late. Excluding S4, the underlying trajectory is +4 pts/sprint.</p>
</div>
<div class="card">
<div class="card-header"><span class="card-dot muted"></span><h3>What the chart doesn't show</h3></div>
<p>Story points measure scope delivered, not value shipped. S3 at 95 points included three stories that were deprioritised before the sprint ended — they don't appear in the count.</p>
</div>
</div>
<div class="footer">
<span>sprint velocity · 8-sprint view</span>
<span>example · diagram design</span>
</div>
</div>
</body>
</html>
@@ -0,0 +1,127 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sprint velocity · Bar chart</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--color-paper: #f5f5f5;
--color-ink: #2d3142;
--color-muted: #4f5d75;
--color-accent: #eb6c36;
--font-sans: 'Geist', system-ui, sans-serif;
--font-serif: 'Instrument Serif', serif;
--font-mono: 'Geist Mono', ui-monospace, monospace;
}
body { font-family: var(--font-sans); background: var(--color-paper); color: var(--color-ink); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 3rem 2rem; }
.frame { max-width: 1200px; width: 100%; }
.eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.15; margin-bottom: 1.5rem; }
svg { width: 100%; min-width: 760px; display: block; }
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Bar · Diagram Design</p>
<h1>Sprint velocity · 8-sprint view</h1>
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(45,49,66,0.10)"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- Y-axis rotated label -->
<text transform="rotate(-90 24 230)" x="24" y="230" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">STORY POINTS</text>
<!-- Horizontal gridlines (drawn before bars) -->
<line x1="80" y1="357" x2="960" y2="357" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="293" x2="960" y2="293" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="230" x2="960" y2="230" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="167" x2="960" y2="167" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="103" x2="960" y2="103" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="40" x2="960" y2="40" stroke="rgba(45,49,66,0.06)" stroke-width="0.8"/>
<!-- Y-axis line and X-axis baseline -->
<line x1="80" y1="40" x2="80" y2="420" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<line x1="80" y1="420" x2="960" y2="420" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<!-- Y-axis tick labels -->
<text x="72" y="361" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">20</text>
<text x="72" y="297" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">40</text>
<text x="72" y="234" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">60</text>
<text x="72" y="171" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">80</text>
<text x="72" y="107" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">100</text>
<text x="72" y="44" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">120</text>
<!-- ─── BARS (pitch=110, width=72, pad=19) ─── -->
<!-- S1: v=72, h=228, y=192 -->
<rect x="99" y="192" width="72" height="228" fill="#f5f5f5"/>
<rect x="99" y="192" width="72" height="228" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<text x="135" y="184" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">72</text>
<!-- S2: v=88, h=280, y=140 -->
<rect x="209" y="140" width="72" height="280" fill="#f5f5f5"/>
<rect x="209" y="140" width="72" height="280" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<text x="245" y="132" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">88</text>
<!-- S3: v=95, h=300, y=120 -->
<rect x="319" y="120" width="72" height="300" fill="#f5f5f5"/>
<rect x="319" y="120" width="72" height="300" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<text x="355" y="112" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">95</text>
<!-- S4: v=78, h=248, y=172 -->
<rect x="429" y="172" width="72" height="248" fill="#f5f5f5"/>
<rect x="429" y="172" width="72" height="248" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<text x="465" y="164" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">78</text>
<!-- S5 FOCAL: v=110, h=348, y=72 -->
<rect x="539" y="72" width="72" height="348" fill="#f5f5f5"/>
<rect x="539" y="72" width="72" height="348" fill="rgba(235,108,54,0.12)" stroke="#eb6c36" stroke-width="1"/>
<text x="575" y="64" fill="#eb6c36" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" font-weight="600">110</text>
<!-- S6: v=102, h=324, y=96 -->
<rect x="649" y="96" width="72" height="324" fill="#f5f5f5"/>
<rect x="649" y="96" width="72" height="324" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<text x="685" y="88" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">102</text>
<!-- S7: v=85, h=268, y=152 -->
<rect x="759" y="152" width="72" height="268" fill="#f5f5f5"/>
<rect x="759" y="152" width="72" height="268" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<text x="795" y="144" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">85</text>
<!-- S8: v=93, h=296, y=124 -->
<rect x="869" y="124" width="72" height="296" fill="#f5f5f5"/>
<rect x="869" y="124" width="72" height="296" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<text x="905" y="116" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">93</text>
<!-- X-axis category labels -->
<text x="135" y="440" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S1</text>
<text x="245" y="440" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S2</text>
<text x="355" y="440" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S3</text>
<text x="465" y="440" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S4</text>
<text x="575" y="440" fill="#eb6c36" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S5</text>
<text x="685" y="440" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S6</text>
<text x="795" y="440" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S7</text>
<text x="905" y="440" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">S8</text>
<!-- Legend -->
<line x1="40" y1="462" x2="960" y2="462" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="40" y="478" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<rect x="40" y="488" width="16" height="10" rx="2" fill="rgba(235,108,54,0.12)" stroke="#eb6c36" stroke-width="1"/>
<text x="64" y="497" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Sprint 5 · record high</text>
<rect x="220" y="488" width="16" height="10" rx="2" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<text x="244" y="497" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Other sprints</text>
</svg>
</div>
</body>
</html>
@@ -0,0 +1,143 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Analytics pipeline · Data flow · Dark</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&amp;family=Geist:wght@400;500;600&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--paper: #2d3142; --paper-2: #393e53; --ink: #f5f5f5; --muted: #bfc0c0;
--soft: #8e98ac; --rule: rgba(245,245,245,0.12); --accent: #f08a59;
--accent-tint: rgba(240,138,89,0.10); --link: #6a95d8;
--sans: 'Geist', system-ui, sans-serif; --serif: 'Instrument Serif', serif;
--mono: 'Geist Mono', ui-monospace, monospace;
}
body { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 3rem 2rem; background: var(--paper); color: var(--ink); font-family: var(--sans); }
.frame { width: 100%; max-width: 1200px; }
.eyebrow { margin-bottom: 0.5rem; color: var(--muted); font: 500 0.66rem var(--mono); letter-spacing: 0.18em; text-transform: uppercase; }
h1 { margin-bottom: 1.5rem; color: var(--ink); font: 400 clamp(1.5rem, 2.4vw + 0.75rem, 2rem)/1.15 var(--serif); letter-spacing: -0.02em; }
svg { display: block; width: 100%; min-width: 728px; }
.step-number, .step-label, .lane-label, .role-text, .chip-text, .legend-label { font-family: var(--mono); text-anchor: middle; }
.step-number { fill: var(--ink); font-size: 7px; font-weight: 600; }
.step-label { fill: var(--muted); font-size: 7px; font-weight: 500; letter-spacing: 0.12em; }
.lane-label { fill: var(--muted); font-size: 8px; font-weight: 500; letter-spacing: 0.14em; }
.role-text { fill: var(--ink); font-size: 6px; font-weight: 600; }
.node-title { fill: var(--ink); font: 600 9px var(--sans); text-anchor: middle; }
.node-sub { fill: var(--muted); font: 400 6.5px var(--mono); text-anchor: middle; }
.node-tool { fill: var(--soft); font: 400 6.5px var(--mono); text-anchor: middle; }
.chip-text { fill: #fff; font-size: 5px; font-weight: 700; }
.focal-text { fill: var(--accent); }
.legend-label { fill: var(--muted); font-size: 7px; font-weight: 500; letter-spacing: 0.12em; text-anchor: end; }
.legend-text { fill: var(--muted); font: 400 7px var(--sans); }
</style>
</head>
<body>
<main class="frame">
<p class="eyebrow">Data flow · Diagram Design</p>
<h1>Role-scoped analytics pipeline</h1>
<svg viewBox="0 0 728 356" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="flow-title flow-desc">
<title id="flow-title">Role-scoped analytics data flow</title>
<desc id="flow-desc">A Data Engineer ingests and stores commerce data, a Data Scientist transforms and analyzes it, and an Analyst publishes a dashboard.</desc>
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse"><circle cx="11" cy="11" r="0.8" fill="rgba(245,245,245,0.10)"/></pattern>
<marker id="arr-muted" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"><path d="M0 0 L6 3 L0 6 Z" fill="#bfc0c0"/></marker>
<marker id="arr-accent" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"><path d="M0 0 L6 3 L0 6 Z" fill="#f08a59"/></marker>
<marker id="arr-link" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"><path d="M0 0 L6 3 L0 6 Z" fill="#6a95d8"/></marker>
</defs>
<rect width="728" height="356" fill="#2d3142"/>
<rect width="728" height="356" fill="url(#dots)"/>
<rect x="0" y="36" width="728" height="80" fill="rgba(245,245,245,0.025)"/>
<rect x="0" y="196" width="728" height="80" fill="rgba(245,245,245,0.025)"/>
<!-- Header and lane structure -->
<line x1="0" y1="36" x2="728" y2="36" stroke="rgba(245,245,245,0.12)" stroke-width="0.8"/>
<line x1="0" y1="116" x2="728" y2="116" stroke="rgba(245,245,245,0.12)" stroke-width="0.8"/>
<line x1="0" y1="196" x2="728" y2="196" stroke="rgba(245,245,245,0.12)" stroke-width="0.8"/>
<line x1="0" y1="276" x2="728" y2="276" stroke="rgba(245,245,245,0.12)" stroke-width="0.8"/>
<line x1="140" y1="36" x2="140" y2="276" stroke="rgba(245,245,245,0.12)" stroke-width="0.8"/>
<g aria-label="Pipeline steps">
<rect x="180" y="6" width="32" height="16" rx="8" fill="rgba(245,245,245,0.12)"/><text x="196" y="14" class="step-number">01</text><text x="196" y="29" class="step-label">INGEST</text>
<rect x="292" y="6" width="32" height="16" rx="8" fill="rgba(245,245,245,0.12)"/><text x="308" y="14" class="step-number">02</text><text x="308" y="29" class="step-label">STORE</text>
<rect x="404" y="6" width="32" height="16" rx="8" fill="rgba(240,138,89,0.22)"/><text x="420" y="14" class="step-number focal-text">03</text><text x="420" y="29" class="step-label focal-text">TRANSFORM</text>
<rect x="516" y="6" width="32" height="16" rx="8" fill="rgba(245,245,245,0.12)"/><text x="532" y="14" class="step-number">04</text><text x="532" y="29" class="step-label">ANALYZE</text>
<rect x="628" y="6" width="32" height="16" rx="8" fill="rgba(245,245,245,0.12)"/><text x="644" y="14" class="step-number">05</text><text x="644" y="29" class="step-label">PUBLISH</text>
</g>
<g aria-label="Role lanes">
<text x="70" y="72" class="lane-label">DATA</text><text x="70" y="84" class="lane-label">ENGINEER</text>
<text x="70" y="152" class="lane-label">DATA</text><text x="70" y="164" class="lane-label">SCIENTIST</text>
<text x="70" y="232" class="lane-label">ANALYTICS</text><text x="70" y="244" class="lane-label">ANALYST</text>
</g>
<!-- Connectors first. Cross-lane paths use r=8 orthogonal elbows. -->
<path d="M246 76 H258" fill="none" stroke="#bfc0c0" marker-end="url(#arr-muted)"/>
<path d="M358 76 H412 Q420 76 420 84 V124" fill="none" stroke="#f08a59" stroke-width="1.2" marker-end="url(#arr-accent)"/>
<path d="M470 156 H482" fill="none" stroke="#bfc0c0" marker-end="url(#arr-muted)"/>
<path d="M582 156 H636 Q644 156 644 164 V204" fill="none" stroke="#6a95d8" marker-end="url(#arr-link)"/>
<rect x="368" y="56" width="48" height="12" rx="2" fill="#2d3142"/>
<text x="392" y="65" fill="#f08a59" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.06em">RAW TABLE</text>
<!-- Data Engineer: Capture Events -->
<rect x="146" y="44" width="100" height="64" rx="6" fill="rgba(245,245,245,0.04)" stroke="rgba(245,245,245,0.20)"/>
<rect x="150" y="48" width="18" height="10" rx="3" fill="rgba(245,245,245,0.12)"/><text x="159" y="53" class="role-text">ENG</text>
<text x="196" y="67" class="node-title">Capture Events</text><text x="196" y="79" class="node-sub">shop events → batch</text><text x="196" y="91" class="node-tool">NiFi ingest</text>
<rect x="150" y="98" width="16" height="8" rx="3" fill="#4a7c59"/><text x="158" y="104" class="chip-text">LS</text>
<rect x="226" y="98" width="16" height="8" rx="3" fill="#5e7a9b"/><text x="234" y="104" class="chip-text">DB</text>
<!-- Data Engineer: Land Records -->
<rect x="258" y="44" width="100" height="64" rx="6" fill="rgba(245,245,245,0.04)" stroke="rgba(245,245,245,0.20)"/>
<rect x="262" y="48" width="18" height="10" rx="3" fill="rgba(245,245,245,0.12)"/><text x="271" y="53" class="role-text">ENG</text>
<text x="308" y="67" class="node-title">Land Records</text><text x="308" y="79" class="node-sub">events · orders</text><text x="308" y="91" class="node-tool">Object storage</text>
<rect x="262" y="98" width="16" height="8" rx="3" fill="#5e7a9b"/><text x="270" y="104" class="chip-text">DB</text>
<rect x="338" y="98" width="16" height="8" rx="3" fill="#5e7a9b"/><text x="346" y="104" class="chip-text">DB</text>
<!-- Data Scientist: Clean and Model (focal) -->
<rect x="370" y="124" width="100" height="64" rx="6" fill="rgba(240,138,89,0.12)" stroke="#f08a59" stroke-width="1.2"/>
<rect x="374" y="128" width="18" height="10" rx="3" fill="rgba(240,138,89,0.22)"/><text x="383" y="133" class="role-text focal-text">SCI</text>
<text x="420" y="147" class="node-title">Clean &amp; Model</text><text x="420" y="159" class="node-sub">raw → trusted table</text><text x="420" y="171" class="node-tool">Trino · notebooks</text>
<rect x="374" y="178" width="16" height="8" rx="3" fill="#5e7a9b"/><text x="382" y="184" class="chip-text">DB</text>
<rect x="450" y="178" width="16" height="8" rx="3" fill="#b8915a"/><text x="458" y="184" class="chip-text">TB</text>
<!-- Data Scientist: Curate Metrics -->
<rect x="482" y="124" width="100" height="64" rx="6" fill="rgba(245,245,245,0.04)" stroke="rgba(245,245,245,0.20)"/>
<rect x="486" y="128" width="18" height="10" rx="3" fill="rgba(245,245,245,0.12)"/><text x="495" y="133" class="role-text">SCI</text>
<text x="532" y="147" class="node-title">Curate Metrics</text><text x="532" y="159" class="node-sub">conversion · revenue</text><text x="532" y="171" class="node-tool">Trino SQL</text>
<rect x="486" y="178" width="16" height="8" rx="3" fill="#b8915a"/><text x="494" y="184" class="chip-text">TB</text>
<rect x="562" y="178" width="16" height="8" rx="3" fill="#b8915a"/><text x="570" y="184" class="chip-text">TB</text>
<!-- Analyst: Publish Dashboard -->
<rect x="594" y="204" width="100" height="64" rx="6" fill="rgba(245,245,245,0.04)" stroke="rgba(245,245,245,0.20)"/>
<rect x="598" y="208" width="18" height="10" rx="3" fill="rgba(245,245,245,0.12)"/><text x="607" y="213" class="role-text">ANL</text>
<text x="644" y="227" class="node-title">Publish Dashboard</text><text x="644" y="239" class="node-sub">metrics → decisions</text><text x="644" y="251" class="node-tool">BI workspace</text>
<rect x="598" y="258" width="16" height="8" rx="3" fill="#b8915a"/><text x="606" y="264" class="chip-text">TB</text>
<rect x="674" y="258" width="16" height="8" rx="3" fill="#9c6b50"/><text x="682" y="264" class="chip-text">FL</text>
<!-- Legend: steps -->
<text x="164" y="293" class="legend-label">STEPS</text>
<rect x="180" y="284" width="24" height="12" rx="6" fill="rgba(245,245,245,0.12)"/><text x="192" y="292" class="step-number">01</text><text x="212" y="293" class="legend-text">Ingest</text>
<rect x="272" y="284" width="24" height="12" rx="6" fill="rgba(245,245,245,0.12)"/><text x="284" y="292" class="step-number">02</text><text x="304" y="293" class="legend-text">Store</text>
<rect x="360" y="284" width="24" height="12" rx="6" fill="rgba(240,138,89,0.22)"/><text x="372" y="292" class="step-number focal-text">03</text><text x="392" y="293" class="legend-text">Transform</text>
<rect x="468" y="284" width="24" height="12" rx="6" fill="rgba(245,245,245,0.12)"/><text x="480" y="292" class="step-number">04</text><text x="500" y="293" class="legend-text">Analyze</text>
<rect x="568" y="284" width="24" height="12" rx="6" fill="rgba(245,245,245,0.12)"/><text x="580" y="292" class="step-number">05</text><text x="600" y="293" class="legend-text">Publish</text>
<!-- Legend: data types -->
<text x="164" y="314" class="legend-label">DATA TYPE</text>
<rect x="180" y="306" width="16" height="8" rx="3" fill="#4a7c59"/><text x="188" y="312" class="chip-text">LS</text><text x="202" y="314" class="legend-text">Stream</text>
<rect x="252" y="306" width="16" height="8" rx="3" fill="#5e7a9b"/><text x="260" y="312" class="chip-text">DB</text><text x="274" y="314" class="legend-text">Dataset</text>
<rect x="332" y="306" width="16" height="8" rx="3" fill="#b8915a"/><text x="340" y="312" class="chip-text">TB</text><text x="354" y="314" class="legend-text">Table</text>
<rect x="404" y="306" width="16" height="8" rx="3" fill="#9c6b50"/><text x="412" y="312" class="chip-text">FL</text><text x="426" y="314" class="legend-text">Dashboard</text>
<text x="500" y="314" class="legend-text">left chip = input · right chip = output</text>
<!-- Legend: flow -->
<text x="164" y="336" class="legend-label">FLOW</text>
<line x1="180" y1="333" x2="204" y2="333" stroke="#bfc0c0" marker-end="url(#arr-muted)"/><text x="212" y="336" class="legend-text">Standard handoff</text>
<line x1="324" y1="333" x2="348" y2="333" stroke="#f08a59" stroke-width="1.2" marker-end="url(#arr-accent)"/><text x="356" y="336" class="legend-text">Focal handoff</text>
<line x1="460" y1="333" x2="484" y2="333" stroke="#6a95d8" marker-end="url(#arr-link)"/><text x="492" y="336" class="legend-text">Published output</text>
</svg>
</main>
</body>
</html>
@@ -0,0 +1,185 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Analytics pipeline · Data flow · Full</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&amp;family=Geist:wght@400;500;600&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--paper: #f5f5f5; --paper-2: #ececec; --ink: #2d3142; --muted: #4f5d75;
--soft: #7a8399; --rule: rgba(45,49,66,0.12); --accent: #eb6c36;
--accent-tint: rgba(235,108,54,0.08); --link: #2e5aa8;
--sans: 'Geist', system-ui, sans-serif; --serif: 'Instrument Serif', serif;
--mono: 'Geist Mono', ui-monospace, monospace;
}
body { min-height: 100vh; padding: 3rem 2rem; background: var(--paper); color: var(--ink); font-family: var(--sans); }
.container { width: 100%; max-width: 1200px; margin: 0 auto; }
.eyebrow { margin-bottom: 0.5rem; color: var(--muted); font: 500 0.66rem var(--mono); letter-spacing: 0.18em; text-transform: uppercase; }
h1 { margin-bottom: 1.5rem; color: var(--ink); font: 400 clamp(1.5rem, 2.4vw + 0.75rem, 2rem)/1.15 var(--serif); letter-spacing: -0.02em; }
svg { display: block; width: 100%; min-width: 728px; }
.step-number, .step-label, .lane-label, .role-text, .chip-text, .legend-label { font-family: var(--mono); text-anchor: middle; }
.step-number { fill: var(--ink); font-size: 7px; font-weight: 600; }
.step-label { fill: var(--muted); font-size: 7px; font-weight: 500; letter-spacing: 0.12em; }
.lane-label { fill: var(--muted); font-size: 8px; font-weight: 500; letter-spacing: 0.14em; }
.role-text { fill: var(--ink); font-size: 6px; font-weight: 600; }
.node-title { fill: var(--ink); font: 600 9px var(--sans); text-anchor: middle; }
.node-sub { fill: var(--muted); font: 400 6.5px var(--mono); text-anchor: middle; }
.node-tool { fill: var(--soft); font: 400 6.5px var(--mono); text-anchor: middle; }
.chip-text { fill: #fff; font-size: 5px; font-weight: 700; }
.focal-text { fill: var(--accent); }
.legend-label { fill: var(--muted); font-size: 7px; font-weight: 500; letter-spacing: 0.12em; text-anchor: end; }
.legend-text { fill: var(--muted); font: 400 7px var(--sans); }
.header { margin-bottom: 2.5rem; }
.header h1 { margin-bottom: 0.5rem; }
.subtitle { max-width: 62ch; color: var(--muted); font-size: 1rem; line-height: 1.55; }
.diagram-container { overflow-x: auto; padding: 1.5rem; background: var(--paper-2); border: 1px solid var(--rule); border-radius: 8px; }
.cards { display: grid; grid-template-columns: 1.1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
.card { padding: 1.25rem; background: #fff; border: 1px solid var(--rule); border-radius: 6px; }
.card .card-eyebrow { margin-bottom: 0.5rem; color: var(--muted); font: 500 0.5rem var(--mono); letter-spacing: 0.18em; text-transform: uppercase; }
.card-header { display: flex; gap: 0.6rem; align-items: center; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(45,49,66,0.08); }
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
.card-dot.coral { background: var(--accent); } .card-dot.ink { background: var(--ink); } .card-dot.muted { background: var(--muted); }
.card h2 { font-size: 0.875rem; font-weight: 600; }
.card p, .card ul { color: var(--muted); font-size: 0.8125rem; line-height: 1.55; list-style: none; }
.card li { position: relative; margin-bottom: 0.3rem; padding-left: 0.875rem; }
.card li::before { position: absolute; left: 0; color: rgba(45,49,66,0.25); content: '—'; }
.footer { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.5rem; margin-top: 2rem; padding-top: 1.5rem; color: var(--soft); border-top: 1px solid rgba(45,49,66,0.10); font: 400 0.72rem var(--mono); letter-spacing: 0.06em; }
@media (max-width: 820px) { .cards { grid-template-columns: 1fr; } }
</style>
</head>
<body>
<main class="container">
<header class="header">
<p class="eyebrow">Data flow · Diagram Design</p>
<h1>Role-scoped analytics pipeline</h1>
<p class="subtitle">Typed commerce data crosses clear ownership boundaries: engineering lands it, data science makes it trustworthy, and analytics publishes it.</p>
</header>
<div class="diagram-container">
<svg viewBox="0 0 728 356" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="flow-title flow-desc">
<title id="flow-title">Role-scoped analytics data flow</title>
<desc id="flow-desc">A Data Engineer ingests and stores commerce data, a Data Scientist transforms and analyzes it, and an Analyst publishes a dashboard.</desc>
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse"><circle cx="11" cy="11" r="0.8" fill="rgba(45,49,66,0.10)"/></pattern>
<marker id="arr-muted" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"><path d="M0 0 L6 3 L0 6 Z" fill="#4f5d75"/></marker>
<marker id="arr-accent" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"><path d="M0 0 L6 3 L0 6 Z" fill="#eb6c36"/></marker>
<marker id="arr-link" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"><path d="M0 0 L6 3 L0 6 Z" fill="#2e5aa8"/></marker>
</defs>
<rect width="728" height="356" fill="#f5f5f5"/>
<rect width="728" height="356" fill="url(#dots)"/>
<rect x="0" y="36" width="728" height="80" fill="rgba(45,49,66,0.018)"/>
<rect x="0" y="196" width="728" height="80" fill="rgba(45,49,66,0.018)"/>
<!-- Header and lane structure -->
<line x1="0" y1="36" x2="728" y2="36" stroke="rgba(45,49,66,0.12)" stroke-width="0.8"/>
<line x1="0" y1="116" x2="728" y2="116" stroke="rgba(45,49,66,0.12)" stroke-width="0.8"/>
<line x1="0" y1="196" x2="728" y2="196" stroke="rgba(45,49,66,0.12)" stroke-width="0.8"/>
<line x1="0" y1="276" x2="728" y2="276" stroke="rgba(45,49,66,0.12)" stroke-width="0.8"/>
<line x1="140" y1="36" x2="140" y2="276" stroke="rgba(45,49,66,0.12)" stroke-width="0.8"/>
<g aria-label="Pipeline steps">
<rect x="180" y="6" width="32" height="16" rx="8" fill="rgba(45,49,66,0.12)"/><text x="196" y="14" class="step-number">01</text><text x="196" y="29" class="step-label">INGEST</text>
<rect x="292" y="6" width="32" height="16" rx="8" fill="rgba(45,49,66,0.12)"/><text x="308" y="14" class="step-number">02</text><text x="308" y="29" class="step-label">STORE</text>
<rect x="404" y="6" width="32" height="16" rx="8" fill="rgba(235,108,54,0.20)"/><text x="420" y="14" class="step-number focal-text">03</text><text x="420" y="29" class="step-label focal-text">TRANSFORM</text>
<rect x="516" y="6" width="32" height="16" rx="8" fill="rgba(45,49,66,0.12)"/><text x="532" y="14" class="step-number">04</text><text x="532" y="29" class="step-label">ANALYZE</text>
<rect x="628" y="6" width="32" height="16" rx="8" fill="rgba(45,49,66,0.12)"/><text x="644" y="14" class="step-number">05</text><text x="644" y="29" class="step-label">PUBLISH</text>
</g>
<g aria-label="Role lanes">
<text x="70" y="72" class="lane-label">DATA</text><text x="70" y="84" class="lane-label">ENGINEER</text>
<text x="70" y="152" class="lane-label">DATA</text><text x="70" y="164" class="lane-label">SCIENTIST</text>
<text x="70" y="232" class="lane-label">ANALYTICS</text><text x="70" y="244" class="lane-label">ANALYST</text>
</g>
<!-- Connectors first. Cross-lane paths use r=8 orthogonal elbows. -->
<path d="M246 76 H258" fill="none" stroke="#4f5d75" marker-end="url(#arr-muted)"/>
<path d="M358 76 H412 Q420 76 420 84 V124" fill="none" stroke="#eb6c36" stroke-width="1.2" marker-end="url(#arr-accent)"/>
<path d="M470 156 H482" fill="none" stroke="#4f5d75" marker-end="url(#arr-muted)"/>
<path d="M582 156 H636 Q644 156 644 164 V204" fill="none" stroke="#2e5aa8" marker-end="url(#arr-link)"/>
<rect x="368" y="56" width="48" height="12" rx="2" fill="#f5f5f5"/>
<text x="392" y="65" fill="#eb6c36" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.06em">RAW TABLE</text>
<!-- Data Engineer: Capture Events -->
<rect x="146" y="44" width="100" height="64" rx="6" fill="#f5f5f5" stroke="rgba(45,49,66,0.25)"/>
<rect x="150" y="48" width="18" height="10" rx="3" fill="rgba(45,49,66,0.12)"/><text x="159" y="53" class="role-text">ENG</text>
<text x="196" y="67" class="node-title">Capture Events</text><text x="196" y="79" class="node-sub">shop events → batch</text><text x="196" y="91" class="node-tool">NiFi ingest</text>
<rect x="150" y="98" width="16" height="8" rx="3" fill="#4a7c59"/><text x="158" y="104" class="chip-text">LS</text>
<rect x="226" y="98" width="16" height="8" rx="3" fill="#5e7a9b"/><text x="234" y="104" class="chip-text">DB</text>
<!-- Data Engineer: Land Records -->
<rect x="258" y="44" width="100" height="64" rx="6" fill="#f5f5f5" stroke="rgba(45,49,66,0.25)"/>
<rect x="262" y="48" width="18" height="10" rx="3" fill="rgba(45,49,66,0.12)"/><text x="271" y="53" class="role-text">ENG</text>
<text x="308" y="67" class="node-title">Land Records</text><text x="308" y="79" class="node-sub">events · orders</text><text x="308" y="91" class="node-tool">Object storage</text>
<rect x="262" y="98" width="16" height="8" rx="3" fill="#5e7a9b"/><text x="270" y="104" class="chip-text">DB</text>
<rect x="338" y="98" width="16" height="8" rx="3" fill="#5e7a9b"/><text x="346" y="104" class="chip-text">DB</text>
<!-- Data Scientist: Clean and Model (focal) -->
<rect x="370" y="124" width="100" height="64" rx="6" fill="rgba(235,108,54,0.07)" stroke="#eb6c36" stroke-width="1.2"/>
<rect x="374" y="128" width="18" height="10" rx="3" fill="rgba(235,108,54,0.20)"/><text x="383" y="133" class="role-text focal-text">SCI</text>
<text x="420" y="147" class="node-title">Clean &amp; Model</text><text x="420" y="159" class="node-sub">raw → trusted table</text><text x="420" y="171" class="node-tool">Trino · notebooks</text>
<rect x="374" y="178" width="16" height="8" rx="3" fill="#5e7a9b"/><text x="382" y="184" class="chip-text">DB</text>
<rect x="450" y="178" width="16" height="8" rx="3" fill="#b8915a"/><text x="458" y="184" class="chip-text">TB</text>
<!-- Data Scientist: Curate Metrics -->
<rect x="482" y="124" width="100" height="64" rx="6" fill="#f5f5f5" stroke="rgba(45,49,66,0.25)"/>
<rect x="486" y="128" width="18" height="10" rx="3" fill="rgba(45,49,66,0.12)"/><text x="495" y="133" class="role-text">SCI</text>
<text x="532" y="147" class="node-title">Curate Metrics</text><text x="532" y="159" class="node-sub">conversion · revenue</text><text x="532" y="171" class="node-tool">Trino SQL</text>
<rect x="486" y="178" width="16" height="8" rx="3" fill="#b8915a"/><text x="494" y="184" class="chip-text">TB</text>
<rect x="562" y="178" width="16" height="8" rx="3" fill="#b8915a"/><text x="570" y="184" class="chip-text">TB</text>
<!-- Analyst: Publish Dashboard -->
<rect x="594" y="204" width="100" height="64" rx="6" fill="#f5f5f5" stroke="rgba(45,49,66,0.25)"/>
<rect x="598" y="208" width="18" height="10" rx="3" fill="rgba(45,49,66,0.12)"/><text x="607" y="213" class="role-text">ANL</text>
<text x="644" y="227" class="node-title">Publish Dashboard</text><text x="644" y="239" class="node-sub">metrics → decisions</text><text x="644" y="251" class="node-tool">BI workspace</text>
<rect x="598" y="258" width="16" height="8" rx="3" fill="#b8915a"/><text x="606" y="264" class="chip-text">TB</text>
<rect x="674" y="258" width="16" height="8" rx="3" fill="#9c6b50"/><text x="682" y="264" class="chip-text">FL</text>
<!-- Legend: steps -->
<text x="164" y="293" class="legend-label">STEPS</text>
<rect x="180" y="284" width="24" height="12" rx="6" fill="rgba(45,49,66,0.12)"/><text x="192" y="292" class="step-number">01</text><text x="212" y="293" class="legend-text">Ingest</text>
<rect x="272" y="284" width="24" height="12" rx="6" fill="rgba(45,49,66,0.12)"/><text x="284" y="292" class="step-number">02</text><text x="304" y="293" class="legend-text">Store</text>
<rect x="360" y="284" width="24" height="12" rx="6" fill="rgba(235,108,54,0.20)"/><text x="372" y="292" class="step-number focal-text">03</text><text x="392" y="293" class="legend-text">Transform</text>
<rect x="468" y="284" width="24" height="12" rx="6" fill="rgba(45,49,66,0.12)"/><text x="480" y="292" class="step-number">04</text><text x="500" y="293" class="legend-text">Analyze</text>
<rect x="568" y="284" width="24" height="12" rx="6" fill="rgba(45,49,66,0.12)"/><text x="580" y="292" class="step-number">05</text><text x="600" y="293" class="legend-text">Publish</text>
<!-- Legend: data types -->
<text x="164" y="314" class="legend-label">DATA TYPE</text>
<rect x="180" y="306" width="16" height="8" rx="3" fill="#4a7c59"/><text x="188" y="312" class="chip-text">LS</text><text x="202" y="314" class="legend-text">Stream</text>
<rect x="252" y="306" width="16" height="8" rx="3" fill="#5e7a9b"/><text x="260" y="312" class="chip-text">DB</text><text x="274" y="314" class="legend-text">Dataset</text>
<rect x="332" y="306" width="16" height="8" rx="3" fill="#b8915a"/><text x="340" y="312" class="chip-text">TB</text><text x="354" y="314" class="legend-text">Table</text>
<rect x="404" y="306" width="16" height="8" rx="3" fill="#9c6b50"/><text x="412" y="312" class="chip-text">FL</text><text x="426" y="314" class="legend-text">Dashboard</text>
<text x="500" y="314" class="legend-text">left chip = input · right chip = output</text>
<!-- Legend: flow -->
<text x="164" y="336" class="legend-label">FLOW</text>
<line x1="180" y1="333" x2="204" y2="333" stroke="#4f5d75" marker-end="url(#arr-muted)"/><text x="212" y="336" class="legend-text">Standard handoff</text>
<line x1="324" y1="333" x2="348" y2="333" stroke="#eb6c36" stroke-width="1.2" marker-end="url(#arr-accent)"/><text x="356" y="336" class="legend-text">Focal handoff</text>
<line x1="460" y1="333" x2="484" y2="333" stroke="#2e5aa8" marker-end="url(#arr-link)"/><text x="492" y="336" class="legend-text">Published output</text>
</svg>
</div>
<section class="cards" aria-label="Diagram summary">
<article class="card">
<p class="card-eyebrow">THE HANDOFF</p>
<div class="card-header"><span class="card-dot coral"></span><h2>Raw becomes analysis-ready</h2></div>
<p>The focal handoff marks the moment stored records become a trusted table owned by data science.</p>
</article>
<article class="card">
<div class="card-header"><span class="card-dot ink"></span><h2>Roles stay explicit</h2></div>
<ul><li>Data Engineer ingests and lands</li><li>Data Scientist transforms and models</li><li>Analyst publishes the dashboard</li></ul>
</article>
<article class="card">
<div class="card-header"><span class="card-dot muted"></span><h2>Payloads explain change</h2></div>
<p>Left and right chips show the input and output type at every active pipeline step.</p>
</article>
</section>
<footer class="footer">
<span>commerce analytics · data flow</span>
<span>example · schematic skill</span>
</footer>
</main>
</body>
</html>
@@ -0,0 +1,143 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Analytics pipeline · Data flow</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&amp;family=Geist:wght@400;500;600&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--paper: #f5f5f5; --paper-2: #ececec; --ink: #2d3142; --muted: #4f5d75;
--soft: #7a8399; --rule: rgba(45,49,66,0.12); --accent: #eb6c36;
--accent-tint: rgba(235,108,54,0.08); --link: #2e5aa8;
--sans: 'Geist', system-ui, sans-serif; --serif: 'Instrument Serif', serif;
--mono: 'Geist Mono', ui-monospace, monospace;
}
body { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 3rem 2rem; background: var(--paper); color: var(--ink); font-family: var(--sans); }
.frame { width: 100%; max-width: 1200px; }
.eyebrow { margin-bottom: 0.5rem; color: var(--muted); font: 500 0.66rem var(--mono); letter-spacing: 0.18em; text-transform: uppercase; }
h1 { margin-bottom: 1.5rem; color: var(--ink); font: 400 clamp(1.5rem, 2.4vw + 0.75rem, 2rem)/1.15 var(--serif); letter-spacing: -0.02em; }
svg { display: block; width: 100%; min-width: 728px; }
.step-number, .step-label, .lane-label, .role-text, .chip-text, .legend-label { font-family: var(--mono); text-anchor: middle; }
.step-number { fill: var(--ink); font-size: 7px; font-weight: 600; }
.step-label { fill: var(--muted); font-size: 7px; font-weight: 500; letter-spacing: 0.12em; }
.lane-label { fill: var(--muted); font-size: 8px; font-weight: 500; letter-spacing: 0.14em; }
.role-text { fill: var(--ink); font-size: 6px; font-weight: 600; }
.node-title { fill: var(--ink); font: 600 9px var(--sans); text-anchor: middle; }
.node-sub { fill: var(--muted); font: 400 6.5px var(--mono); text-anchor: middle; }
.node-tool { fill: var(--soft); font: 400 6.5px var(--mono); text-anchor: middle; }
.chip-text { fill: #fff; font-size: 5px; font-weight: 700; }
.focal-text { fill: var(--accent); }
.legend-label { fill: var(--muted); font-size: 7px; font-weight: 500; letter-spacing: 0.12em; text-anchor: end; }
.legend-text { fill: var(--muted); font: 400 7px var(--sans); }
</style>
</head>
<body>
<main class="frame">
<p class="eyebrow">Data flow · Diagram Design</p>
<h1>Role-scoped analytics pipeline</h1>
<svg viewBox="0 0 728 356" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="flow-title flow-desc">
<title id="flow-title">Role-scoped analytics data flow</title>
<desc id="flow-desc">A Data Engineer ingests and stores commerce data, a Data Scientist transforms and analyzes it, and an Analyst publishes a dashboard.</desc>
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse"><circle cx="11" cy="11" r="0.8" fill="rgba(45,49,66,0.10)"/></pattern>
<marker id="arr-muted" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"><path d="M0 0 L6 3 L0 6 Z" fill="#4f5d75"/></marker>
<marker id="arr-accent" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"><path d="M0 0 L6 3 L0 6 Z" fill="#eb6c36"/></marker>
<marker id="arr-link" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"><path d="M0 0 L6 3 L0 6 Z" fill="#2e5aa8"/></marker>
</defs>
<rect width="728" height="356" fill="#f5f5f5"/>
<rect width="728" height="356" fill="url(#dots)"/>
<rect x="0" y="36" width="728" height="80" fill="rgba(45,49,66,0.018)"/>
<rect x="0" y="196" width="728" height="80" fill="rgba(45,49,66,0.018)"/>
<!-- Header and lane structure -->
<line x1="0" y1="36" x2="728" y2="36" stroke="rgba(45,49,66,0.12)" stroke-width="0.8"/>
<line x1="0" y1="116" x2="728" y2="116" stroke="rgba(45,49,66,0.12)" stroke-width="0.8"/>
<line x1="0" y1="196" x2="728" y2="196" stroke="rgba(45,49,66,0.12)" stroke-width="0.8"/>
<line x1="0" y1="276" x2="728" y2="276" stroke="rgba(45,49,66,0.12)" stroke-width="0.8"/>
<line x1="140" y1="36" x2="140" y2="276" stroke="rgba(45,49,66,0.12)" stroke-width="0.8"/>
<g aria-label="Pipeline steps">
<rect x="180" y="6" width="32" height="16" rx="8" fill="rgba(45,49,66,0.12)"/><text x="196" y="14" class="step-number">01</text><text x="196" y="29" class="step-label">INGEST</text>
<rect x="292" y="6" width="32" height="16" rx="8" fill="rgba(45,49,66,0.12)"/><text x="308" y="14" class="step-number">02</text><text x="308" y="29" class="step-label">STORE</text>
<rect x="404" y="6" width="32" height="16" rx="8" fill="rgba(235,108,54,0.20)"/><text x="420" y="14" class="step-number focal-text">03</text><text x="420" y="29" class="step-label focal-text">TRANSFORM</text>
<rect x="516" y="6" width="32" height="16" rx="8" fill="rgba(45,49,66,0.12)"/><text x="532" y="14" class="step-number">04</text><text x="532" y="29" class="step-label">ANALYZE</text>
<rect x="628" y="6" width="32" height="16" rx="8" fill="rgba(45,49,66,0.12)"/><text x="644" y="14" class="step-number">05</text><text x="644" y="29" class="step-label">PUBLISH</text>
</g>
<g aria-label="Role lanes">
<text x="70" y="72" class="lane-label">DATA</text><text x="70" y="84" class="lane-label">ENGINEER</text>
<text x="70" y="152" class="lane-label">DATA</text><text x="70" y="164" class="lane-label">SCIENTIST</text>
<text x="70" y="232" class="lane-label">ANALYTICS</text><text x="70" y="244" class="lane-label">ANALYST</text>
</g>
<!-- Connectors first. Cross-lane paths use r=8 orthogonal elbows. -->
<path d="M246 76 H258" fill="none" stroke="#4f5d75" marker-end="url(#arr-muted)"/>
<path d="M358 76 H412 Q420 76 420 84 V124" fill="none" stroke="#eb6c36" stroke-width="1.2" marker-end="url(#arr-accent)"/>
<path d="M470 156 H482" fill="none" stroke="#4f5d75" marker-end="url(#arr-muted)"/>
<path d="M582 156 H636 Q644 156 644 164 V204" fill="none" stroke="#2e5aa8" marker-end="url(#arr-link)"/>
<rect x="368" y="56" width="48" height="12" rx="2" fill="#f5f5f5"/>
<text x="392" y="65" fill="#eb6c36" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.06em">RAW TABLE</text>
<!-- Data Engineer: Capture Events -->
<rect x="146" y="44" width="100" height="64" rx="6" fill="#f5f5f5" stroke="rgba(45,49,66,0.25)"/>
<rect x="150" y="48" width="18" height="10" rx="3" fill="rgba(45,49,66,0.12)"/><text x="159" y="53" class="role-text">ENG</text>
<text x="196" y="67" class="node-title">Capture Events</text><text x="196" y="79" class="node-sub">shop events → batch</text><text x="196" y="91" class="node-tool">NiFi ingest</text>
<rect x="150" y="98" width="16" height="8" rx="3" fill="#4a7c59"/><text x="158" y="104" class="chip-text">LS</text>
<rect x="226" y="98" width="16" height="8" rx="3" fill="#5e7a9b"/><text x="234" y="104" class="chip-text">DB</text>
<!-- Data Engineer: Land Records -->
<rect x="258" y="44" width="100" height="64" rx="6" fill="#f5f5f5" stroke="rgba(45,49,66,0.25)"/>
<rect x="262" y="48" width="18" height="10" rx="3" fill="rgba(45,49,66,0.12)"/><text x="271" y="53" class="role-text">ENG</text>
<text x="308" y="67" class="node-title">Land Records</text><text x="308" y="79" class="node-sub">events · orders</text><text x="308" y="91" class="node-tool">Object storage</text>
<rect x="262" y="98" width="16" height="8" rx="3" fill="#5e7a9b"/><text x="270" y="104" class="chip-text">DB</text>
<rect x="338" y="98" width="16" height="8" rx="3" fill="#5e7a9b"/><text x="346" y="104" class="chip-text">DB</text>
<!-- Data Scientist: Clean and Model (focal) -->
<rect x="370" y="124" width="100" height="64" rx="6" fill="rgba(235,108,54,0.07)" stroke="#eb6c36" stroke-width="1.2"/>
<rect x="374" y="128" width="18" height="10" rx="3" fill="rgba(235,108,54,0.20)"/><text x="383" y="133" class="role-text focal-text">SCI</text>
<text x="420" y="147" class="node-title">Clean &amp; Model</text><text x="420" y="159" class="node-sub">raw → trusted table</text><text x="420" y="171" class="node-tool">Trino · notebooks</text>
<rect x="374" y="178" width="16" height="8" rx="3" fill="#5e7a9b"/><text x="382" y="184" class="chip-text">DB</text>
<rect x="450" y="178" width="16" height="8" rx="3" fill="#b8915a"/><text x="458" y="184" class="chip-text">TB</text>
<!-- Data Scientist: Curate Metrics -->
<rect x="482" y="124" width="100" height="64" rx="6" fill="#f5f5f5" stroke="rgba(45,49,66,0.25)"/>
<rect x="486" y="128" width="18" height="10" rx="3" fill="rgba(45,49,66,0.12)"/><text x="495" y="133" class="role-text">SCI</text>
<text x="532" y="147" class="node-title">Curate Metrics</text><text x="532" y="159" class="node-sub">conversion · revenue</text><text x="532" y="171" class="node-tool">Trino SQL</text>
<rect x="486" y="178" width="16" height="8" rx="3" fill="#b8915a"/><text x="494" y="184" class="chip-text">TB</text>
<rect x="562" y="178" width="16" height="8" rx="3" fill="#b8915a"/><text x="570" y="184" class="chip-text">TB</text>
<!-- Analyst: Publish Dashboard -->
<rect x="594" y="204" width="100" height="64" rx="6" fill="#f5f5f5" stroke="rgba(45,49,66,0.25)"/>
<rect x="598" y="208" width="18" height="10" rx="3" fill="rgba(45,49,66,0.12)"/><text x="607" y="213" class="role-text">ANL</text>
<text x="644" y="227" class="node-title">Publish Dashboard</text><text x="644" y="239" class="node-sub">metrics → decisions</text><text x="644" y="251" class="node-tool">BI workspace</text>
<rect x="598" y="258" width="16" height="8" rx="3" fill="#b8915a"/><text x="606" y="264" class="chip-text">TB</text>
<rect x="674" y="258" width="16" height="8" rx="3" fill="#9c6b50"/><text x="682" y="264" class="chip-text">FL</text>
<!-- Legend: steps -->
<text x="164" y="293" class="legend-label">STEPS</text>
<rect x="180" y="284" width="24" height="12" rx="6" fill="rgba(45,49,66,0.12)"/><text x="192" y="292" class="step-number">01</text><text x="212" y="293" class="legend-text">Ingest</text>
<rect x="272" y="284" width="24" height="12" rx="6" fill="rgba(45,49,66,0.12)"/><text x="284" y="292" class="step-number">02</text><text x="304" y="293" class="legend-text">Store</text>
<rect x="360" y="284" width="24" height="12" rx="6" fill="rgba(235,108,54,0.20)"/><text x="372" y="292" class="step-number focal-text">03</text><text x="392" y="293" class="legend-text">Transform</text>
<rect x="468" y="284" width="24" height="12" rx="6" fill="rgba(45,49,66,0.12)"/><text x="480" y="292" class="step-number">04</text><text x="500" y="293" class="legend-text">Analyze</text>
<rect x="568" y="284" width="24" height="12" rx="6" fill="rgba(45,49,66,0.12)"/><text x="580" y="292" class="step-number">05</text><text x="600" y="293" class="legend-text">Publish</text>
<!-- Legend: data types -->
<text x="164" y="314" class="legend-label">DATA TYPE</text>
<rect x="180" y="306" width="16" height="8" rx="3" fill="#4a7c59"/><text x="188" y="312" class="chip-text">LS</text><text x="202" y="314" class="legend-text">Stream</text>
<rect x="252" y="306" width="16" height="8" rx="3" fill="#5e7a9b"/><text x="260" y="312" class="chip-text">DB</text><text x="274" y="314" class="legend-text">Dataset</text>
<rect x="332" y="306" width="16" height="8" rx="3" fill="#b8915a"/><text x="340" y="312" class="chip-text">TB</text><text x="354" y="314" class="legend-text">Table</text>
<rect x="404" y="306" width="16" height="8" rx="3" fill="#9c6b50"/><text x="412" y="312" class="chip-text">FL</text><text x="426" y="314" class="legend-text">Dashboard</text>
<text x="500" y="314" class="legend-text">left chip = input · right chip = output</text>
<!-- Legend: flow -->
<text x="164" y="336" class="legend-label">FLOW</text>
<line x1="180" y1="333" x2="204" y2="333" stroke="#4f5d75" marker-end="url(#arr-muted)"/><text x="212" y="336" class="legend-text">Standard handoff</text>
<line x1="324" y1="333" x2="348" y2="333" stroke="#eb6c36" stroke-width="1.2" marker-end="url(#arr-accent)"/><text x="356" y="336" class="legend-text">Focal handoff</text>
<line x1="460" y1="333" x2="484" y2="333" stroke="#2e5aa8" marker-end="url(#arr-link)"/><text x="492" y="336" class="legend-text">Published output</text>
</svg>
</main>
</body>
</html>
@@ -0,0 +1,241 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Open data lake · Architecture</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--color-paper: #2d3142;
--color-ink: #f5f5f5;
--color-muted: #bfc0c0;
--color-accent: #f08a59;
--font-sans: 'Geist', system-ui, sans-serif;
--font-serif: 'Instrument Serif', serif;
--font-mono: 'Geist Mono', ui-monospace, monospace;
}
body {
font-family: var(--font-sans);
background: var(--color-paper);
color: var(--color-ink);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 3rem 2rem;
}
.frame { max-width: 1200px; width: 100%; }
.eyebrow {
font-family: var(--font-mono);
font-size: 0.66rem;
font-weight: 500;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--color-muted);
margin-bottom: 0.5rem;
}
h1 {
font-family: var(--font-serif);
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
font-weight: 400;
letter-spacing: -0.02em;
line-height: 1.15;
margin-bottom: 1.5rem;
}
svg { width: 100%; min-width: 960px; display: block; }
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Architecture · Diagram Design</p>
<h1>Open data lake · End-to-end stack</h1>
<svg viewBox="0 0 1000 488" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(245,245,245,0.10)"/>
</pattern>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="#bfc0c0"/>
</marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="#f08a59"/>
</marker>
</defs>
<rect width="100%" height="100%" fill="#2d3142"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<rect x="4" y="32" width="160" height="200" rx="8" fill="rgba(245,245,245,0.03)" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<rect x="176" y="32" width="160" height="200" rx="8" fill="rgba(245,245,245,0.03)" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<rect x="380" y="32" width="184" height="160" rx="8" fill="rgba(240,138,89,0.06)" stroke="rgba(240,138,89,0.22)" stroke-width="0.8"/>
<rect x="600" y="32" width="168" height="200" rx="8" fill="rgba(245,245,245,0.03)" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<rect x="804" y="32" width="164" height="280" rx="8" fill="rgba(245,245,245,0.03)" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<text x="84" y="44" fill="#bfc0c0" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.16em" text-anchor="middle">SOURCES</text>
<text x="256" y="44" fill="#bfc0c0" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.16em" text-anchor="middle">INGEST</text>
<text x="472" y="44" fill="#f08a59" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.16em" text-anchor="middle">DATA LAKE</text>
<text x="688" y="44" fill="#bfc0c0" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.16em" text-anchor="middle">QUERY</text>
<text x="876" y="44" fill="#bfc0c0" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.16em" text-anchor="middle">CONSUME</text>
<line x1="152" y1="88" x2="187" y2="88" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<line x1="152" y1="192" x2="187" y2="192" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 324,80 H 350 Q 358,80 358,88 V 112 Q 358,120 366,120 H 391" fill="none" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 324,200 H 350 Q 358,200 358,192 V 168 Q 358,160 366,160 H 391" fill="none" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 553,128 H 576 Q 584,128 584,120 V 96 Q 584,88 592,88 H 615" fill="none" stroke="#f08a59" stroke-width="1.2" marker-end="url(#arrow-accent)"/>
<path d="M 553,152 H 576 Q 584,152 584,160 V 180 Q 584,188 592,188 H 615" fill="none" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<rect x="586" y="102" width="28" height="12" rx="2" fill="#2d3142"/>
<text x="600" y="111" fill="#f08a59" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">SQL</text>
<path d="M 760,76 H 784 Q 788,76 788,68 H 815" fill="none" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 760,100 H 780 Q 788,100 788,108 V 148 Q 788,156 796,156 H 815" fill="none" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 760,180 H 782 Q 788,180 788,168 H 815" fill="none" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 760,204 H 780 Q 788,204 788,212 V 232 Q 788,240 796,240 H 815" fill="none" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 884,300 V 312 Q 884,320 876,320 H 480 Q 472,320 472,312 V 180" fill="none" stroke="rgba(245,245,245,0.30)" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
<rect x="660" y="314" width="36" height="12" rx="2" fill="#2d3142"/>
<text x="678" y="323" fill="#8e98ac" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">WRITE</text>
<!-- App servers -->
<rect x="16" y="60" width="136" height="56" rx="6" fill="#2d3142"/>
<rect x="16" y="60" width="136" height="56" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="24" y="66" width="26" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.25)" stroke-width="0.8"/>
<text x="37" y="75" fill="rgba(245,245,245,0.45)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="72" y="66" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#f5f5f5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 7a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3"/>
<path d="M3 15a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3l0 -2"/>
<path d="M7 8l0 .01"/><path d="M7 16l0 .01"/>
</svg>
<text x="84" y="102" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">App servers</text>
<text x="84" y="114" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Events · logs</text>
<!-- Databases -->
<rect x="16" y="164" width="136" height="56" rx="6" fill="#2d3142"/>
<rect x="16" y="164" width="136" height="56" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="24" y="170" width="26" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.25)" stroke-width="0.8"/>
<text x="37" y="179" fill="rgba(245,245,245,0.45)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="72" y="170" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#f5f5f5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 6a8 3 0 1 0 16 0a8 3 0 1 0 -16 0"/>
<path d="M4 6v6a8 3 0 0 0 16 0v-6"/>
<path d="M4 12v6a8 3 0 0 0 16 0v-6"/>
</svg>
<text x="84" y="206" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Databases</text>
<text x="84" y="218" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">CDC · exports</text>
<!-- Apache NiFi -->
<rect x="188" y="60" width="136" height="56" rx="6" fill="#2d3142"/>
<rect x="188" y="60" width="136" height="56" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="196" y="66" width="28" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.25)" stroke-width="0.8"/>
<text x="210" y="75" fill="rgba(245,245,245,0.45)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">FLOW</text>
<svg x="244" y="66" width="24" height="24" viewBox="0 0 24 24" fill="#f5f5f5">
<path d="M11.648 0a.093.093 0 0 0-.084.053A30.71 30.71 0 0 1 8.592 4.73c-2.09 2.728-5.145 6.466-5.145 10.364a8.201 8.201 0 0 0 8.201 8.2v-5.003c0-.106.087-.193.194-.193h2.81v-2.813c0-.106.087-.191.194-.191h5.004c0-3.9-3.056-7.636-5.145-10.364A30.712 30.712 0 0 1 11.732.053.094.094 0 0 0 11.648 0zm-1.632 3.867c.05 0 .08.034.037.112-.11.197-.218.397-.328.593-.396.702-.819 1.389-1.23 2.08-.196-.032-.39-.06-.585-.088.495-.651 1-1.296 1.48-1.959.153-.209.302-.423.454-.634a.24.24 0 0 1 .172-.104zM7.44 7.186c.221.035.444.076.666.119-.073.129-.15.256-.223.383a29.073 29.073 0 0 0-1.625 3.261c-.874 2.123-1.383 4.444-.77 6.707a8.222 8.222 0 0 0 2.217 3.74c.083.083-.02.216-.119.155a7.568 7.568 0 0 1-.93-.686A7.674 7.674 0 0 1 4.1 16.248c-.329-2.156.387-4.246 1.418-6.115a27.44 27.44 0 0 1 1.92-2.947zm7.931 8.435a.193.193 0 0 0-.191.191V18.3h2.677V15.62zm3.299 0V18.3h1.348a7.975 7.975 0 0 0 .515-2.678zm-6.303 3.004a.193.193 0 0 0-.191.193v2.485h2.678v-2.678Zm3.295.484v2.68h2.115a.562.562 0 0 0 .399-.162v-.004a.562.562 0 0 0 .16-.397V19.11zm3.674.182v1.98a7.999 7.999 0 0 0 1.217-1.98zm-6.969 2.824a.193.193 0 0 0-.191.192v1.672a7.997 7.997 0 0 0 2.678-.516v-1.348zm3.48.668V24a8.008 8.008 0 0 0 1.98-1.217z"/>
</svg>
<text x="256" y="102" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Apache NiFi</text>
<text x="256" y="114" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Route · transform</text>
<!-- Airflow -->
<rect x="188" y="164" width="136" height="56" rx="6" fill="#2d3142"/>
<rect x="188" y="164" width="136" height="56" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="196" y="170" width="28" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.25)" stroke-width="0.8"/>
<text x="210" y="179" fill="rgba(245,245,245,0.45)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ORCH</text>
<svg x="244" y="170" width="24" height="24" viewBox="0 0 24 24" fill="#f5f5f5">
<path d="M17.195 16.822l4.002-4.102C23.55 10.308 23.934 5.154 24 .43a.396.396 0 0 0-.246-.373.392.392 0 0 0-.437.09l-6.495 6.658-4.102-4.003C10.309.45 5.154.066.43 0H.423a.397.397 0 0 0-.277.683l6.658 6.494-4.003 4.103C.45 13.692.065 18.846 0 23.57a.398.398 0 0 0 .683.282l6.494-6.657 3.934 3.837.17.165c2.41 2.353 7.565 2.737 12.288 2.803h.006a.397.397 0 0 0 .277-.683l-6.657-6.495z"/>
</svg>
<text x="256" y="206" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Airflow</text>
<text x="256" y="218" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">DAG scheduling</text>
<!-- MinIO (FOCAL) -->
<rect x="392" y="100" width="160" height="80" rx="8" fill="#2d3142"/>
<rect x="392" y="100" width="160" height="80" rx="8" fill="rgba(240,138,89,0.10)" stroke="#f08a59" stroke-width="1.2"/>
<rect x="400" y="106" width="36" height="12" rx="2" fill="transparent" stroke="rgba(240,138,89,0.50)" stroke-width="0.8"/>
<text x="418" y="115" fill="rgba(240,138,89,0.80)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STORE</text>
<svg x="460" y="108" width="24" height="24" viewBox="0 0 24 24" fill="#f08a59">
<path d="M13.2072.006c-.6216-.0478-1.2.1943-1.6211.582a2.15 2.15 0 0 0-.0938 3.0352l3.4082 3.5507a3.042 3.042 0 0 1-.664 4.6875l-.463.2383V7.2853a15.4198 15.4198 0 0 0-8.0174 10.4862v.0176l6.5487-3.3281v7.621L13.7794 24V13.6817l.8965-.4629a4.4432 4.4432 0 0 0 1.2207-7.0292l-3.371-3.5254a.7489.7489 0 0 1 .037-1.0547.7522.7522 0 0 1 1.0567.0371l.4668.4863-.006.0059 4.0704 4.2441a.0566.0566 0 0 0 .082 0 .06.06 0 0 0 0-.0703l-3.1406-5.1425-.1484.1425.1484-.1445C14.4945.3926 13.8287.0538 13.2072.006Zm-.9024 9.8652v2.9941l-4.1523 2.1484a13.9787 13.9787 0 0 1 2.7676-3.9277 14.1784 14.1784 0 0 1 1.3847-1.2148z"/>
</svg>
<text x="472" y="148" fill="#f08a59" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">MinIO</text>
<text x="472" y="164" fill="#f08a59" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" opacity="0.75">Object store · S3-API</text>
<!-- Trino -->
<rect x="616" y="60" width="144" height="56" rx="6" fill="#2d3142"/>
<rect x="616" y="60" width="144" height="56" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="624" y="66" width="36" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.25)" stroke-width="0.8"/>
<text x="642" y="75" fill="rgba(245,245,245,0.45)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">QUERY</text>
<svg x="676" y="66" width="24" height="24" viewBox="0 0 24 24" fill="#f5f5f5">
<path d="M14.124 16.8529a.1615.1615 0 1 1 .1576.1614.1577.1577 0 0 1-.1576-.1614zm-5.607-.1576a.1614.1614 0 1 0 0 .3228.1614.1614 0 0 0 0-.3228zm10.1341-.6648v1.9869c-.031.5788-.524 1.0237-1.1029.9954h-.3843a5.0596 5.0596 0 0 1-1.1298 1.7178.3192.3192 0 0 0 0 .465l.2382.2191a.3036.3036 0 0 1 .0385.4304c-1.126 1.3835-2.9669 2.1521-5.0498 2.1521a6.575 6.575 0 0 1-4.8192-1.8985c-.0029-.0032-.0059-.0063-.0087-.0096a.6302.6302 0 0 1 .0548-.8896c.137-.1265.1371-.3462 0-.4727a4.944 4.944 0 0 1-1.126-1.714h-.3497c-.5797.0284-1.0737-.416-1.1068-.9954v-1.9869c.0351-.5779.5286-1.02 1.1068-.9915h.2728a5.7648 5.7648 0 0 1 2.0791-3.0936c-.4227-1.0991-1.1529-3.2551-1.226-5.0075C6.0229 4.4705 6.2189.078 7.8253.001c1.6064-.0768 1.3719 4.0275 1.0991 6.6946a32.732 32.732 0 0 0-.123 4.4503 6.994 6.994 0 0 1 2.4826-.4304 7.2414 7.2414 0 0 1 1.7371.2075c.2614-1.2682.8762-3.574 2.0292-5.1958 1.6717-2.352 3.4357-4.7808 4.6116-4.1006 1.176.6802-.3074 3.1398-1.3297 4.4272-1.0222 1.2874-2.7862 3.2089-3.3742 4.2274-.2114.3843-.4304.8032-.5956 1.1529a5.7375 5.7375 0 0 1 2.9169 3.6125h.073v-2.3058a.3075.3075 0 0 0-.1806-.2844.9148.9148 0 0 1-.5573-.8148 1.0184 1.0184 0 0 1 .9045-.9044c.5593-.0598 1.061.3452 1.1208.9044a.9187.9187 0 0 1-.5534.8148.3074.3074 0 0 0-.1691.2844v2.1522a.3113.3113 0 0 0 .1691.2805.9724.9724 0 0 1 .5648.857z"/>
</svg>
<text x="688" y="102" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Trino</text>
<text x="688" y="114" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">SQL · any format</text>
<!-- StarRocks -->
<rect x="616" y="164" width="144" height="56" rx="6" fill="#2d3142"/>
<rect x="616" y="164" width="144" height="56" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="624" y="170" width="28" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.25)" stroke-width="0.8"/>
<text x="638" y="179" fill="rgba(245,245,245,0.45)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">OLAP</text>
<svg x="676" y="170" width="24" height="24" viewBox="0 0 100 100" fill="#f5f5f5">
<path d="M11.8,26.4c-0.1,2.2,0.9,3.4,2.3,4.5c9,7.4,18,14.8,27,22.2c2.5,2.1,2.7,3.5,1,6.2c-4.4,6.7-8.8,13.4-13.2,20.1c-1.7,2.5-3.2,2.9-5.9,1.4c-2.8-1.6-5.6-3.2-8.4-4.8c-3.2-1.8-4.8-4.6-4.8-8.3c0-11.8,0-23.6,0-35.5C9.8,30.2,10.3,28.4,11.8,26.4z"/>
<path d="M87.9,73.8c0.6-2.3-0.5-3.5-1.9-4.6c-9-7.4-17.9-14.7-26.8-22.1c-2.9-2.4-3.1-3.6-1.1-6.7c4.3-6.6,8.7-13.2,13-19.8c1.7-2.6,3.3-2.9,6-1.4c2.8,1.6,5.6,3.2,8.3,4.8c3.2,1.8,4.7,4.5,4.7,8.2c0,11.9,0,23.7,0,35.6C90.2,69.9,89.6,71.9,87.9,73.8z"/>
<path d="M67.1,56.8c0.6,0.4,17.3,14.1,17.5,14.3c2.4,2.2,2.2,4.1-0.6,5.8C76.8,81,57.3,92.1,54.8,93.6c-3.2,1.9-6.4,1.9-9.6,0c-4.6-2.7-9.2-5.3-13.8-8c-2.2-1.3-2.2-2.7,0-3.9C42.3,75.5,53.1,69.2,64,63C66.5,61.6,68.2,60.1,67.1,56.8z"/>
<path d="M32.9,43.2c-0.6-0.4-17.3-14.1-17.5-14.3c-2.4-2.2-2.2-4.1,0.6-5.8C23.2,19,42.7,7.9,45.2,6.4c3.2-1.9,6.4-1.9,9.6,0c4.6,2.7,9.2,5.3,13.8,8c2.2,1.3,2.2,2.7,0,3.9C57.7,24.5,46.9,30.8,36,37C33.5,38.4,31.8,39.9,32.9,43.2z"/>
</svg>
<text x="688" y="206" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">StarRocks</text>
<text x="688" y="218" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">MPP · hot layer</text>
<!-- Superset -->
<rect x="816" y="60" width="136" height="56" rx="6" fill="#2d3142"/>
<rect x="816" y="60" width="136" height="56" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="824" y="66" width="18" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.25)" stroke-width="0.8"/>
<text x="833" y="75" fill="rgba(245,245,245,0.45)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">BI</text>
<svg x="872" y="66" width="24" height="24" viewBox="0 0 24 24" fill="#f5f5f5">
<path d="M6.168 6.045C2.603 6.045 0 8.579 0 12.014c0 3.434 2.603 5.941 6.168 5.941 2.184 0 3.888-1.026 5.775-3.078 1.53 2.033 4.037 3.136 5.89 3.078 3.566 0 6.167-2.503 6.167-5.941 0-3.438-2.601-5.97-6.168-5.97-2.864 0-5.138 2.425-5.771 3.173-.76-.9-1.674-1.665-2.682-2.274-1.019-.588-2.084-.898-3.211-.898z"/>
</svg>
<text x="884" y="102" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Superset</text>
<text x="884" y="114" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Dashboards</text>
<!-- JupyterLab -->
<rect x="816" y="152" width="136" height="56" rx="6" fill="#2d3142"/>
<rect x="816" y="152" width="136" height="56" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="824" y="158" width="22" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.25)" stroke-width="0.8"/>
<text x="835" y="167" fill="rgba(245,245,245,0.45)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">NB</text>
<svg x="872" y="158" width="24" height="24" viewBox="0 0 24 24" fill="#f5f5f5">
<path d="M7.157 22.201A1.784 1.799 0 0 1 5.374 24a1.784 1.799 0 0 1-1.784-1.799 1.784 1.799 0 0 1 1.784-1.799 1.784 1.799 0 0 1 1.783 1.799zM20.582 1.427a1.415 1.427 0 0 1-1.415 1.428 1.415 1.427 0 0 1-1.416-1.428A1.415 1.427 0 0 1 19.167 0a1.415 1.427 0 0 1 1.415 1.427zM4.992 3.336A1.047 1.056 0 0 1 3.946 4.39a1.047 1.056 0 0 1-1.047-1.055A1.047 1.056 0 0 1 3.946 2.28a1.047 1.056 0 0 1 1.046 1.056zm7.336 1.517c3.769 0 7.06 1.38 8.768 3.424a9.363 9.363 0 0 0-3.393-4.547 9.238 9.238 0 0 0-5.377-1.728A9.238 9.238 0 0 0 6.95 3.73a9.363 9.363 0 0 0-3.394 4.547c1.713-2.04 5.004-3.424 8.772-3.424zm.001 13.295c-3.768 0-7.06-1.381-8.768-3.425a9.363 9.363 0 0 0 3.394 4.547A9.238 9.238 0 0 0 12.33 21a9.238 9.238 0 0 0 5.377-1.729 9.363 9.363 0 0 0 3.393-4.547c-1.712 2.044-5.003 3.425-8.772 3.425z"/>
</svg>
<text x="884" y="194" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">JupyterLab</text>
<text x="884" y="206" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Exploration</text>
<!-- Python -->
<rect x="816" y="244" width="136" height="56" rx="6" fill="#2d3142"/>
<rect x="816" y="244" width="136" height="56" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="824" y="250" width="32" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.25)" stroke-width="0.8"/>
<text x="840" y="259" fill="rgba(245,245,245,0.45)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">PROC</text>
<svg x="872" y="250" width="24" height="24" viewBox="0 0 24 24" fill="#f5f5f5">
<path d="M14.25.18l.9.2.73.26.59.3.45.32.34.34.25.34.16.33.1.3.04.26.02.2-.01.13V8.5l-.05.63-.13.55-.21.46-.26.38-.3.31-.33.25-.35.19-.35.14-.33.1-.3.07-.26.04-.21.02H8.77l-.69.05-.59.14-.5.22-.41.27-.33.32-.27.35-.2.36-.15.37-.1.35-.07.32-.04.27-.02.21v3.06H3.17l-.21-.03-.28-.07-.32-.12-.35-.18-.36-.26-.36-.36-.35-.46-.32-.59-.28-.73-.21-.88-.14-1.05-.05-1.23.06-1.22.16-1.04.24-.87.32-.71.36-.57.4-.44.42-.33.42-.24.4-.16.36-.1.32-.05.24-.01h.16l.06.01h8.16v-.83H6.18l-.01-2.75-.02-.37.05-.34.11-.31.17-.28.25-.26.31-.23.38-.2.44-.18.51-.15.58-.12.64-.1.71-.06.77-.04.84-.02 1.27.05zm-6.3 1.98l-.23.33-.08.41.08.41.23.34.33.22.41.09.41-.09.33-.22.23-.34.08-.41-.08-.41-.23-.33-.33-.22-.41-.09-.41.09zm13.09 3.95l.28.06.32.12.35.18.36.27.36.35.35.47.32.59.28.73.21.88.14 1.04.05 1.23-.06 1.23-.16 1.04-.24.86-.32.71-.36.57-.4.45-.42.33-.42.24-.4.16-.36.09-.32.05-.24.02-.16-.01h-8.22v.82h5.84l.01 2.76.02.36-.05.34-.11.31-.17.29-.25.25-.31.24-.38.2-.44.17-.51.15-.58.13-.64.09-.71.07-.77.04-.84.01-1.27-.04-1.07-.14-.9-.2-.73-.25-.59-.3-.45-.33-.34-.34-.25-.34-.16-.33-.1-.3-.04-.25-.02-.2.01-.13v-5.34l.05-.64.13-.54.21-.46.26-.38.3-.32.33-.24.35-.2.35-.14.33-.1.3-.06.26-.04.21-.02.13-.01h5.84l.69-.05.59-.14.5-.21.41-.28.33-.32.27-.35.2-.36.15-.36.1-.35.07-.32.04-.28.02-.21V6.07h2.09l.14.01zm-6.47 14.25l-.23.33-.08.41.08.41.23.33.33.23.41.08.41-.08.33-.23.23-.33.08-.41-.08-.41-.23-.33-.33-.23-.41-.08-.41.08z"/>
</svg>
<text x="884" y="286" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Python</text>
<text x="884" y="298" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Batch · ML</text>
<line x1="40" y1="432" x2="960" y2="432" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<text x="40" y="448" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<rect x="40" y="460" width="20" height="12" rx="3" fill="rgba(240,138,89,0.12)" stroke="#f08a59" stroke-width="1.2"/>
<text x="68" y="471" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">MinIO data lake (focal)</text>
<rect x="220" y="460" width="20" height="12" rx="3" fill="rgba(245,245,245,0.08)" stroke="rgba(245,245,245,0.25)" stroke-width="1"/>
<text x="248" y="471" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Ingest · query tools</text>
<rect x="400" y="460" width="20" height="12" rx="3" fill="rgba(245,245,245,0.08)" stroke="rgba(245,245,245,0.25)" stroke-width="1"/>
<text x="428" y="471" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">BI · notebooks · pipelines</text>
<line x1="648" y1="462" x2="668" y2="462" stroke="rgba(245,245,245,0.30)" stroke-width="1" stroke-dasharray="4,3"/>
<text x="676" y="468" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Write-back path (batch)</text>
<line x1="820" y1="462" x2="840" y2="462" stroke="#f08a59" stroke-width="1.2"/>
<text x="848" y="468" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Primary query path</text>
</svg>
</div>
</body>
</html>
@@ -0,0 +1,246 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Open data lake · Architecture</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root { --color-paper:#f5f5f5; --color-paper-2:#ececec; --color-ink:#2d3142; --color-muted:#4f5d75; --color-soft:#7a8399; --color-rule:rgba(45,49,66,0.12); --color-accent:#eb6c36; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
.container { max-width: 1200px; margin: 0 auto; }
.header { margin-bottom: 2.5rem; }
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 64ch; }
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
svg { width: 100%; min-width: 960px; display: block; }
.cards { display: grid; grid-template-columns: 1.1fr 1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
@media (max-width: 980px) { .cards { grid-template-columns: 1fr 1fr; } }
@media (max-width: 600px) { .cards { grid-template-columns: 1fr; } }
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(45,49,66,0.08); }
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
.card-dot.coral { background: var(--color-accent); }
.card-dot.ink { background: var(--color-ink); }
.card-dot.muted { background: var(--color-muted); }
.card-dot.soft { background: var(--color-soft); }
.card h3 { font-size: 0.875rem; font-weight: 600; }
.card p, .card ul { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; list-style: none; }
.card li { margin-bottom: 0.3rem; padding-left: 0.875rem; position: relative; }
.card li::before { content: '—'; position: absolute; left: 0; color: rgba(45,49,66,0.25); font-size: 0.75rem; }
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(45,49,66,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<p class="header-eyebrow">Architecture · Diagram Design</p>
<h1>Open data lake · End-to-end stack</h1>
<p class="subtitle">Five functional zones from raw event ingestion to analytics consumption. MinIO is the focal centre — every other tool either lands data into it or reads out of it.</p>
</div>
<div class="diagram-container">
<svg viewBox="0 0 1000 488" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(45,49,66,0.10)"/>
</pattern>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="#4f5d75"/>
</marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="#eb6c36"/>
</marker>
</defs>
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<rect x="4" y="32" width="160" height="200" rx="8" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<rect x="176" y="32" width="160" height="200" rx="8" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<rect x="380" y="32" width="184" height="160" rx="8" fill="rgba(235,108,54,0.04)" stroke="rgba(235,108,54,0.20)" stroke-width="0.8"/>
<rect x="600" y="32" width="168" height="200" rx="8" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<rect x="804" y="32" width="164" height="280" rx="8" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="84" y="44" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.16em" text-anchor="middle">SOURCES</text>
<text x="256" y="44" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.16em" text-anchor="middle">INGEST</text>
<text x="472" y="44" fill="#eb6c36" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.16em" text-anchor="middle">DATA LAKE</text>
<text x="688" y="44" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.16em" text-anchor="middle">QUERY</text>
<text x="876" y="44" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.16em" text-anchor="middle">CONSUME</text>
<line x1="152" y1="88" x2="187" y2="88" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<line x1="152" y1="192" x2="187" y2="192" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 324,80 H 350 Q 358,80 358,88 V 112 Q 358,120 366,120 H 391" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 324,200 H 350 Q 358,200 358,192 V 168 Q 358,160 366,160 H 391" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 553,128 H 576 Q 584,128 584,120 V 96 Q 584,88 592,88 H 615" fill="none" stroke="#eb6c36" stroke-width="1.2" marker-end="url(#arrow-accent)"/>
<path d="M 553,152 H 576 Q 584,152 584,160 V 180 Q 584,188 592,188 H 615" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<rect x="586" y="102" width="28" height="12" rx="2" fill="#f5f5f5"/>
<text x="600" y="111" fill="#eb6c36" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">SQL</text>
<path d="M 760,76 H 784 Q 788,76 788,68 H 815" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 760,100 H 780 Q 788,100 788,108 V 148 Q 788,156 796,156 H 815" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 760,180 H 782 Q 788,180 788,168 H 815" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 760,204 H 780 Q 788,204 788,212 V 232 Q 788,240 796,240 H 815" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 884,300 V 312 Q 884,320 876,320 H 480 Q 472,320 472,312 V 180" fill="none" stroke="rgba(45,49,66,0.30)" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
<rect x="660" y="314" width="36" height="12" rx="2" fill="#f5f5f5"/>
<text x="678" y="323" fill="#7a8399" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">WRITE</text>
<rect x="16" y="60" width="136" height="56" rx="6" fill="#f5f5f5"/>
<rect x="16" y="60" width="136" height="56" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="24" y="66" width="26" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="37" y="75" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="72" y="66" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#2d3142" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 7a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3"/>
<path d="M3 15a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3l0 -2"/>
<path d="M7 8l0 .01"/><path d="M7 16l0 .01"/>
</svg>
<text x="84" y="102" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">App servers</text>
<text x="84" y="114" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Events · logs</text>
<rect x="16" y="164" width="136" height="56" rx="6" fill="#f5f5f5"/>
<rect x="16" y="164" width="136" height="56" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="24" y="170" width="26" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="37" y="179" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="72" y="170" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#2d3142" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 6a8 3 0 1 0 16 0a8 3 0 1 0 -16 0"/>
<path d="M4 6v6a8 3 0 0 0 16 0v-6"/>
<path d="M4 12v6a8 3 0 0 0 16 0v-6"/>
</svg>
<text x="84" y="206" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Databases</text>
<text x="84" y="218" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">CDC · exports</text>
<rect x="188" y="60" width="136" height="56" rx="6" fill="#f5f5f5"/>
<rect x="188" y="60" width="136" height="56" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="196" y="66" width="28" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="210" y="75" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">FLOW</text>
<svg x="244" y="66" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142">
<path d="M11.648 0a.093.093 0 0 0-.084.053A30.71 30.71 0 0 1 8.592 4.73c-2.09 2.728-5.145 6.466-5.145 10.364a8.201 8.201 0 0 0 8.201 8.2v-5.003c0-.106.087-.193.194-.193h2.81v-2.813c0-.106.087-.191.194-.191h5.004c0-3.9-3.056-7.636-5.145-10.364A30.712 30.712 0 0 1 11.732.053.094.094 0 0 0 11.648 0zm-1.632 3.867c.05 0 .08.034.037.112-.11.197-.218.397-.328.593-.396.702-.819 1.389-1.23 2.08-.196-.032-.39-.06-.585-.088.495-.651 1-1.296 1.48-1.959.153-.209.302-.423.454-.634a.24.24 0 0 1 .172-.104zM7.44 7.186c.221.035.444.076.666.119-.073.129-.15.256-.223.383a29.073 29.073 0 0 0-1.625 3.261c-.874 2.123-1.383 4.444-.77 6.707a8.222 8.222 0 0 0 2.217 3.74c.083.083-.02.216-.119.155a7.568 7.568 0 0 1-.93-.686A7.674 7.674 0 0 1 4.1 16.248c-.329-2.156.387-4.246 1.418-6.115a27.44 27.44 0 0 1 1.92-2.947zm7.931 8.435a.193.193 0 0 0-.191.191V18.3h2.677V15.62zm3.299 0V18.3h1.348a7.975 7.975 0 0 0 .515-2.678zm-6.303 3.004a.193.193 0 0 0-.191.193v2.485h2.678v-2.678Zm3.295.484v2.68h2.115a.562.562 0 0 0 .399-.162v-.004a.562.562 0 0 0 .16-.397V19.11zm3.674.182v1.98a7.999 7.999 0 0 0 1.217-1.98zm-6.969 2.824a.193.193 0 0 0-.191.192v1.672a7.997 7.997 0 0 0 2.678-.516v-1.348zm3.48.668V24a8.008 8.008 0 0 0 1.98-1.217z"/>
</svg>
<text x="256" y="102" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Apache NiFi</text>
<text x="256" y="114" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Route · transform</text>
<rect x="188" y="164" width="136" height="56" rx="6" fill="#f5f5f5"/>
<rect x="188" y="164" width="136" height="56" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="196" y="170" width="28" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="210" y="179" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ORCH</text>
<svg x="244" y="170" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142">
<path d="M17.195 16.822l4.002-4.102C23.55 10.308 23.934 5.154 24 .43a.396.396 0 0 0-.246-.373.392.392 0 0 0-.437.09l-6.495 6.658-4.102-4.003C10.309.45 5.154.066.43 0H.423a.397.397 0 0 0-.277.683l6.658 6.494-4.003 4.103C.45 13.692.065 18.846 0 23.57a.398.398 0 0 0 .683.282l6.494-6.657 3.934 3.837.17.165c2.41 2.353 7.565 2.737 12.288 2.803h.006a.397.397 0 0 0 .277-.683l-6.657-6.495z"/>
</svg>
<text x="256" y="206" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Airflow</text>
<text x="256" y="218" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">DAG scheduling</text>
<rect x="392" y="100" width="160" height="80" rx="8" fill="#f5f5f5"/>
<rect x="392" y="100" width="160" height="80" rx="8" fill="rgba(235,108,54,0.08)" stroke="#eb6c36" stroke-width="1.2"/>
<rect x="400" y="106" width="36" height="12" rx="2" fill="transparent" stroke="rgba(235,108,54,0.50)" stroke-width="0.8"/>
<text x="418" y="115" fill="rgba(235,108,54,0.80)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STORE</text>
<svg x="460" y="108" width="24" height="24" viewBox="0 0 24 24" fill="#eb6c36">
<path d="M13.2072.006c-.6216-.0478-1.2.1943-1.6211.582a2.15 2.15 0 0 0-.0938 3.0352l3.4082 3.5507a3.042 3.042 0 0 1-.664 4.6875l-.463.2383V7.2853a15.4198 15.4198 0 0 0-8.0174 10.4862v.0176l6.5487-3.3281v7.621L13.7794 24V13.6817l.8965-.4629a4.4432 4.4432 0 0 0 1.2207-7.0292l-3.371-3.5254a.7489.7489 0 0 1 .037-1.0547.7522.7522 0 0 1 1.0567.0371l.4668.4863-.006.0059 4.0704 4.2441a.0566.0566 0 0 0 .082 0 .06.06 0 0 0 0-.0703l-3.1406-5.1425-.1484.1425.1484-.1445C14.4945.3926 13.8287.0538 13.2072.006Zm-.9024 9.8652v2.9941l-4.1523 2.1484a13.9787 13.9787 0 0 1 2.7676-3.9277 14.1784 14.1784 0 0 1 1.3847-1.2148z"/>
</svg>
<text x="472" y="148" fill="#eb6c36" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">MinIO</text>
<text x="472" y="164" fill="#eb6c36" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" opacity="0.75">Object store · S3-API</text>
<rect x="616" y="60" width="144" height="56" rx="6" fill="#f5f5f5"/>
<rect x="616" y="60" width="144" height="56" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="624" y="66" width="36" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="642" y="75" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">QUERY</text>
<svg x="676" y="66" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142">
<path d="M14.124 16.8529a.1615.1615 0 1 1 .1576.1614.1577.1577 0 0 1-.1576-.1614zm-5.607-.1576a.1614.1614 0 1 0 0 .3228.1614.1614 0 0 0 0-.3228zm10.1341-.6648v1.9869c-.031.5788-.524 1.0237-1.1029.9954h-.3843a5.0596 5.0596 0 0 1-1.1298 1.7178.3192.3192 0 0 0 0 .465l.2382.2191a.3036.3036 0 0 1 .0385.4304c-1.126 1.3835-2.9669 2.1521-5.0498 2.1521a6.575 6.575 0 0 1-4.8192-1.8985c-.0029-.0032-.0059-.0063-.0087-.0096a.6302.6302 0 0 1 .0548-.8896c.137-.1265.1371-.3462 0-.4727a4.944 4.944 0 0 1-1.126-1.714h-.3497c-.5797.0284-1.0737-.416-1.1068-.9954v-1.9869c.0351-.5779.5286-1.02 1.1068-.9915h.2728a5.7648 5.7648 0 0 1 2.0791-3.0936c-.4227-1.0991-1.1529-3.2551-1.226-5.0075C6.0229 4.4705 6.2189.078 7.8253.001c1.6064-.0768 1.3719 4.0275 1.0991 6.6946a32.732 32.732 0 0 0-.123 4.4503 6.994 6.994 0 0 1 2.4826-.4304 7.2414 7.2414 0 0 1 1.7371.2075c.2614-1.2682.8762-3.574 2.0292-5.1958 1.6717-2.352 3.4357-4.7808 4.6116-4.1006 1.176.6802-.3074 3.1398-1.3297 4.4272-1.0222 1.2874-2.7862 3.2089-3.3742 4.2274-.2114.3843-.4304.8032-.5956 1.1529a5.7375 5.7375 0 0 1 2.9169 3.6125h.073v-2.3058a.3075.3075 0 0 0-.1806-.2844.9148.9148 0 0 1-.5573-.8148 1.0184 1.0184 0 0 1 .9045-.9044c.5593-.0598 1.061.3452 1.1208.9044a.9187.9187 0 0 1-.5534.8148.3074.3074 0 0 0-.1691.2844v2.1522a.3113.3113 0 0 0 .1691.2805.9724.9724 0 0 1 .5648.857z"/>
</svg>
<text x="688" y="102" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Trino</text>
<text x="688" y="114" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">SQL · any format</text>
<rect x="616" y="164" width="144" height="56" rx="6" fill="#f5f5f5"/>
<rect x="616" y="164" width="144" height="56" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="624" y="170" width="28" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="638" y="179" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">OLAP</text>
<svg x="676" y="170" width="24" height="24" viewBox="0 0 100 100" fill="#2d3142">
<path d="M11.8,26.4c-0.1,2.2,0.9,3.4,2.3,4.5c9,7.4,18,14.8,27,22.2c2.5,2.1,2.7,3.5,1,6.2c-4.4,6.7-8.8,13.4-13.2,20.1c-1.7,2.5-3.2,2.9-5.9,1.4c-2.8-1.6-5.6-3.2-8.4-4.8c-3.2-1.8-4.8-4.6-4.8-8.3c0-11.8,0-23.6,0-35.5C9.8,30.2,10.3,28.4,11.8,26.4z"/>
<path d="M87.9,73.8c0.6-2.3-0.5-3.5-1.9-4.6c-9-7.4-17.9-14.7-26.8-22.1c-2.9-2.4-3.1-3.6-1.1-6.7c4.3-6.6,8.7-13.2,13-19.8c1.7-2.6,3.3-2.9,6-1.4c2.8,1.6,5.6,3.2,8.3,4.8c3.2,1.8,4.7,4.5,4.7,8.2c0,11.9,0,23.7,0,35.6C90.2,69.9,89.6,71.9,87.9,73.8z"/>
<path d="M67.1,56.8c0.6,0.4,17.3,14.1,17.5,14.3c2.4,2.2,2.2,4.1-0.6,5.8C76.8,81,57.3,92.1,54.8,93.6c-3.2,1.9-6.4,1.9-9.6,0c-4.6-2.7-9.2-5.3-13.8-8c-2.2-1.3-2.2-2.7,0-3.9C42.3,75.5,53.1,69.2,64,63C66.5,61.6,68.2,60.1,67.1,56.8z"/>
<path d="M32.9,43.2c-0.6-0.4-17.3-14.1-17.5-14.3c-2.4-2.2-2.2-4.1,0.6-5.8C23.2,19,42.7,7.9,45.2,6.4c3.2-1.9,6.4-1.9,9.6,0c4.6,2.7,9.2,5.3,13.8,8c2.2,1.3,2.2,2.7,0,3.9C57.7,24.5,46.9,30.8,36,37C33.5,38.4,31.8,39.9,32.9,43.2z"/>
</svg>
<text x="688" y="206" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">StarRocks</text>
<text x="688" y="218" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">MPP · hot layer</text>
<rect x="816" y="60" width="136" height="56" rx="6" fill="#f5f5f5"/>
<rect x="816" y="60" width="136" height="56" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="824" y="66" width="18" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="833" y="75" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">BI</text>
<svg x="872" y="66" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142">
<path d="M6.168 6.045C2.603 6.045 0 8.579 0 12.014c0 3.434 2.603 5.941 6.168 5.941 2.184 0 3.888-1.026 5.775-3.078 1.53 2.033 4.037 3.136 5.89 3.078 3.566 0 6.167-2.503 6.167-5.941 0-3.438-2.601-5.97-6.168-5.97-2.864 0-5.138 2.425-5.771 3.173-.76-.9-1.674-1.665-2.682-2.274-1.019-.588-2.084-.898-3.211-.898z"/>
</svg>
<text x="884" y="102" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Superset</text>
<text x="884" y="114" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Dashboards</text>
<rect x="816" y="152" width="136" height="56" rx="6" fill="#f5f5f5"/>
<rect x="816" y="152" width="136" height="56" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="824" y="158" width="22" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="835" y="167" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">NB</text>
<svg x="872" y="158" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142">
<path d="M7.157 22.201A1.784 1.799 0 0 1 5.374 24a1.784 1.799 0 0 1-1.784-1.799 1.784 1.799 0 0 1 1.784-1.799 1.784 1.799 0 0 1 1.783 1.799zM20.582 1.427a1.415 1.427 0 0 1-1.415 1.428 1.415 1.427 0 0 1-1.416-1.428A1.415 1.427 0 0 1 19.167 0a1.415 1.427 0 0 1 1.415 1.427zM4.992 3.336A1.047 1.056 0 0 1 3.946 4.39a1.047 1.056 0 0 1-1.047-1.055A1.047 1.056 0 0 1 3.946 2.28a1.047 1.056 0 0 1 1.046 1.056zm7.336 1.517c3.769 0 7.06 1.38 8.768 3.424a9.363 9.363 0 0 0-3.393-4.547 9.238 9.238 0 0 0-5.377-1.728A9.238 9.238 0 0 0 6.95 3.73a9.363 9.363 0 0 0-3.394 4.547c1.713-2.04 5.004-3.424 8.772-3.424zm.001 13.295c-3.768 0-7.06-1.381-8.768-3.425a9.363 9.363 0 0 0 3.394 4.547A9.238 9.238 0 0 0 12.33 21a9.238 9.238 0 0 0 5.377-1.729 9.363 9.363 0 0 0 3.393-4.547c-1.712 2.044-5.003 3.425-8.772 3.425z"/>
</svg>
<text x="884" y="194" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">JupyterLab</text>
<text x="884" y="206" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Exploration</text>
<rect x="816" y="244" width="136" height="56" rx="6" fill="#f5f5f5"/>
<rect x="816" y="244" width="136" height="56" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="824" y="250" width="32" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="840" y="259" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">PROC</text>
<svg x="872" y="250" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142">
<path d="M14.25.18l.9.2.73.26.59.3.45.32.34.34.25.34.16.33.1.3.04.26.02.2-.01.13V8.5l-.05.63-.13.55-.21.46-.26.38-.3.31-.33.25-.35.19-.35.14-.33.1-.3.07-.26.04-.21.02H8.77l-.69.05-.59.14-.5.22-.41.27-.33.32-.27.35-.2.36-.15.37-.1.35-.07.32-.04.27-.02.21v3.06H3.17l-.21-.03-.28-.07-.32-.12-.35-.18-.36-.26-.36-.36-.35-.46-.32-.59-.28-.73-.21-.88-.14-1.05-.05-1.23.06-1.22.16-1.04.24-.87.32-.71.36-.57.4-.44.42-.33.42-.24.4-.16.36-.1.32-.05.24-.01h.16l.06.01h8.16v-.83H6.18l-.01-2.75-.02-.37.05-.34.11-.31.17-.28.25-.26.31-.23.38-.2.44-.18.51-.15.58-.12.64-.1.71-.06.77-.04.84-.02 1.27.05zm-6.3 1.98l-.23.33-.08.41.08.41.23.34.33.22.41.09.41-.09.33-.22.23-.34.08-.41-.08-.41-.23-.33-.33-.22-.41-.09-.41.09zm13.09 3.95l.28.06.32.12.35.18.36.27.36.35.35.47.32.59.28.73.21.88.14 1.04.05 1.23-.06 1.23-.16 1.04-.24.86-.32.71-.36.57-.4.45-.42.33-.42.24-.4.16-.36.09-.32.05-.24.02-.16-.01h-8.22v.82h5.84l.01 2.76.02.36-.05.34-.11.31-.17.29-.25.25-.31.24-.38.2-.44.17-.51.15-.58.13-.64.09-.71.07-.77.04-.84.01-1.27-.04-1.07-.14-.9-.2-.73-.25-.59-.3-.45-.33-.34-.34-.25-.34-.16-.33-.1-.3-.04-.25-.02-.2.01-.13v-5.34l.05-.64.13-.54.21-.46.26-.38.3-.32.33-.24.35-.2.35-.14.33-.1.3-.06.26-.04.21-.02.13-.01h5.84l.69-.05.59-.14.5-.21.41-.28.33-.32.27-.35.2-.36.15-.36.1-.35.07-.32.04-.28.02-.21V6.07h2.09l.14.01zm-6.47 14.25l-.23.33-.08.41.08.41.23.33.33.23.41.08.41-.08.33-.23.23-.33.08-.41-.08-.41-.23-.33-.33-.23-.41-.08-.41.08z"/>
</svg>
<text x="884" y="286" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Python</text>
<text x="884" y="298" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Batch · ML</text>
<line x1="40" y1="432" x2="960" y2="432" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="40" y="448" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<rect x="40" y="460" width="20" height="12" rx="3" fill="rgba(235,108,54,0.10)" stroke="#eb6c36" stroke-width="1.2"/>
<text x="68" y="471" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">MinIO data lake (focal)</text>
<rect x="220" y="460" width="20" height="12" rx="3" fill="white" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text x="248" y="471" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Ingest · query tools</text>
<rect x="400" y="460" width="20" height="12" rx="3" fill="white" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text x="428" y="471" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">BI · notebooks · pipelines</text>
<line x1="648" y1="462" x2="668" y2="462" stroke="rgba(45,49,66,0.30)" stroke-width="1" stroke-dasharray="4,3"/>
<text x="676" y="468" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Write-back path (batch)</text>
<line x1="820" y1="462" x2="840" y2="462" stroke="#eb6c36" stroke-width="1.2"/>
<text x="848" y="468" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Primary query path</text>
</svg>
</div>
<div class="cards">
<div class="card">
<p class="eyebrow">FOCAL · STORAGE</p>
<div class="card-header"><span class="card-dot coral"></span><h3>MinIO — data lake core</h3></div>
<p>S3-compatible object storage. Every tool in the stack either lands data here or reads from it. All formats land as raw files; Iceberg table metadata sits alongside them. When in doubt, write to MinIO.</p>
</div>
<div class="card">
<div class="card-header"><span class="card-dot ink"></span><h3>Ingest — NiFi + Airflow</h3></div>
<ul>
<li>NiFi: real-time routing, record-level transformation, CDC fan-out</li>
<li>Airflow: batch scheduling, cross-system DAGs, retry semantics</li>
<li>Two tools, two tempos — they complement rather than replace each other</li>
</ul>
</div>
<div class="card">
<div class="card-header"><span class="card-dot muted"></span><h3>Query — Trino + StarRocks</h3></div>
<ul>
<li>Trino: federated ad-hoc SQL across any file format, any catalog</li>
<li>StarRocks: sub-second MPP for hot partitions and dashboard queries</li>
<li>Different latency budgets, same underlying lake</li>
</ul>
</div>
<div class="card">
<div class="card-header"><span class="card-dot soft"></span><h3>Consume</h3></div>
<p>Superset for dashboards, JupyterLab for exploration, Python for batch ML. All three can write processed results back to MinIO via the dashed write-back path.</p>
</div>
</div>
<div class="footer">
<span>open data lake · end-to-end stack</span>
<span>example · diagram design</span>
</div>
</div>
</body>
</html>
@@ -0,0 +1,267 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Open data lake · Architecture</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--color-paper: #f5f5f5;
--color-ink: #2d3142;
--color-muted: #4f5d75;
--color-accent: #eb6c36;
--font-sans: 'Geist', system-ui, sans-serif;
--font-serif: 'Instrument Serif', serif;
--font-mono: 'Geist Mono', ui-monospace, monospace;
}
body {
font-family: var(--font-sans);
background: var(--color-paper);
color: var(--color-ink);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 3rem 2rem;
}
.frame { max-width: 1200px; width: 100%; }
.eyebrow {
font-family: var(--font-mono);
font-size: 0.66rem;
font-weight: 500;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--color-muted);
margin-bottom: 0.5rem;
}
h1 {
font-family: var(--font-serif);
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
font-weight: 400;
letter-spacing: -0.02em;
line-height: 1.15;
margin-bottom: 1.5rem;
}
svg { width: 100%; min-width: 960px; display: block; }
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Architecture · Diagram Design</p>
<h1>Open data lake · End-to-end stack</h1>
<svg viewBox="0 0 1000 488" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(45,49,66,0.10)"/>
</pattern>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="#4f5d75"/>
</marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="#eb6c36"/>
</marker>
</defs>
<!-- Background -->
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- Zone rects (drawn before labels, arrows, nodes) -->
<rect x="4" y="32" width="160" height="200" rx="8" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<rect x="176" y="32" width="160" height="200" rx="8" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<rect x="380" y="32" width="184" height="160" rx="8" fill="rgba(235,108,54,0.04)" stroke="rgba(235,108,54,0.20)" stroke-width="0.8"/>
<rect x="600" y="32" width="168" height="200" rx="8" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<rect x="804" y="32" width="164" height="280" rx="8" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<!-- Zone labels -->
<text x="84" y="44" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.16em" text-anchor="middle">SOURCES</text>
<text x="256" y="44" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.16em" text-anchor="middle">INGEST</text>
<text x="472" y="44" fill="#eb6c36" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.16em" text-anchor="middle">DATA LAKE</text>
<text x="688" y="44" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.16em" text-anchor="middle">QUERY</text>
<text x="876" y="44" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.16em" text-anchor="middle">CONSUME</text>
<!-- ═══════════════════════════════════════════
ARROWS — drawn before boxes (z-order rule)
═══════════════════════════════════════════ -->
<!-- AppServer → NiFi -->
<line x1="152" y1="88" x2="187" y2="88" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<!-- DBSource → Airflow -->
<line x1="152" y1="192" x2="187" y2="192" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<!-- NiFi → MinIO -->
<path d="M 324,80 H 350 Q 358,80 358,88 V 112 Q 358,120 366,120 H 391" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<!-- Airflow → MinIO -->
<path d="M 324,200 H 350 Q 358,200 358,192 V 168 Q 358,160 366,160 H 391" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<!-- MinIO → Trino (accent — primary query path) -->
<path d="M 553,128 H 576 Q 584,128 584,120 V 96 Q 584,88 592,88 H 615" fill="none" stroke="#eb6c36" stroke-width="1.2" marker-end="url(#arrow-accent)"/>
<!-- MinIO → StarRocks -->
<path d="M 553,152 H 576 Q 584,152 584,160 V 180 Q 584,188 592,188 H 615" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<!-- Arrow label on MinIO→Trino (on vertical segment at x=584) -->
<rect x="586" y="102" width="28" height="12" rx="2" fill="#f5f5f5"/>
<text x="600" y="111" fill="#eb6c36" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">SQL</text>
<!-- Trino → Superset (same row → left/right port) -->
<line x1="760" y1="88" x2="815" y2="88" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<!-- Trino → Jupyter (vertical-primary → exit Trino bottom, enter Jupyter top) -->
<path d="M 688,116 V 132 Q 688,140 696,140 H 876 Q 884,140 884,148 V 152" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<!-- StarRocks → Jupyter (nearly horizontal → left/right port) -->
<line x1="760" y1="180" x2="815" y2="180" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<!-- StarRocks → Python (vertical-primary → exit StarRocks bottom, enter Python top) -->
<path d="M 688,220 V 232 Q 688,240 696,240 H 876 Q 884,240 884,244" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<!-- Python → MinIO (write-back, dashed — orthogonal via bottom gutter at y=320) -->
<path d="M 884,300 V 312 Q 884,320 876,320 H 480 Q 472,320 472,312 V 180" fill="none" stroke="rgba(45,49,66,0.30)" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
<rect x="660" y="314" width="36" height="12" rx="2" fill="#f5f5f5"/>
<text x="678" y="323" fill="#7a8399" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">WRITE</text>
<!-- ═══════════════════════════════════════════
NODES
═══════════════════════════════════════════ -->
<!-- ── App servers ── -->
<rect x="16" y="60" width="136" height="56" rx="6" fill="#f5f5f5"/>
<rect x="16" y="60" width="136" height="56" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="24" y="66" width="26" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="37" y="75" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="72" y="66" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#2d3142" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 7a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3"/>
<path d="M3 15a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3l0 -2"/>
<path d="M7 8l0 .01"/><path d="M7 16l0 .01"/>
</svg>
<text x="84" y="102" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">App servers</text>
<text x="84" y="114" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Events · logs</text>
<!-- ── Databases ── -->
<rect x="16" y="164" width="136" height="56" rx="6" fill="#f5f5f5"/>
<rect x="16" y="164" width="136" height="56" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="24" y="170" width="26" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="37" y="179" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="72" y="170" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#2d3142" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 6a8 3 0 1 0 16 0a8 3 0 1 0 -16 0"/>
<path d="M4 6v6a8 3 0 0 0 16 0v-6"/>
<path d="M4 12v6a8 3 0 0 0 16 0v-6"/>
</svg>
<text x="84" y="206" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Databases</text>
<text x="84" y="218" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">CDC · exports</text>
<!-- ── Apache NiFi ── -->
<rect x="188" y="60" width="136" height="56" rx="6" fill="#f5f5f5"/>
<rect x="188" y="60" width="136" height="56" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="196" y="66" width="28" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="210" y="75" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">FLOW</text>
<svg x="244" y="66" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142">
<path d="M11.648 0a.093.093 0 0 0-.084.053A30.71 30.71 0 0 1 8.592 4.73c-2.09 2.728-5.145 6.466-5.145 10.364a8.201 8.201 0 0 0 8.201 8.2v-5.003c0-.106.087-.193.194-.193h2.81v-2.813c0-.106.087-.191.194-.191h5.004c0-3.9-3.056-7.636-5.145-10.364A30.712 30.712 0 0 1 11.732.053.094.094 0 0 0 11.648 0zm-1.632 3.867c.05 0 .08.034.037.112-.11.197-.218.397-.328.593-.396.702-.819 1.389-1.23 2.08-.196-.032-.39-.06-.585-.088.495-.651 1-1.296 1.48-1.959.153-.209.302-.423.454-.634a.24.24 0 0 1 .172-.104zM7.44 7.186c.221.035.444.076.666.119-.073.129-.15.256-.223.383a29.073 29.073 0 0 0-1.625 3.261c-.874 2.123-1.383 4.444-.77 6.707a8.222 8.222 0 0 0 2.217 3.74c.083.083-.02.216-.119.155a7.568 7.568 0 0 1-.93-.686A7.674 7.674 0 0 1 4.1 16.248c-.329-2.156.387-4.246 1.418-6.115a27.44 27.44 0 0 1 1.92-2.947zm7.931 8.435a.193.193 0 0 0-.191.191V18.3h2.677V15.62zm3.299 0V18.3h1.348a7.975 7.975 0 0 0 .515-2.678zm-6.303 3.004a.193.193 0 0 0-.191.193v2.485h2.678v-2.678Zm3.295.484v2.68h2.115a.562.562 0 0 0 .399-.162v-.004a.562.562 0 0 0 .16-.397V19.11zm3.674.182v1.98a7.999 7.999 0 0 0 1.217-1.98zm-6.969 2.824a.193.193 0 0 0-.191.192v1.672a7.997 7.997 0 0 0 2.678-.516v-1.348zm3.48.668V24a8.008 8.008 0 0 0 1.98-1.217z"/>
</svg>
<text x="256" y="102" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Apache NiFi</text>
<text x="256" y="114" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Route · transform</text>
<!-- ── Airflow ── -->
<rect x="188" y="164" width="136" height="56" rx="6" fill="#f5f5f5"/>
<rect x="188" y="164" width="136" height="56" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="196" y="170" width="28" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="210" y="179" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ORCH</text>
<svg x="244" y="170" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142">
<path d="M17.195 16.822l4.002-4.102C23.55 10.308 23.934 5.154 24 .43a.396.396 0 0 0-.246-.373.392.392 0 0 0-.437.09l-6.495 6.658-4.102-4.003C10.309.45 5.154.066.43 0H.423a.397.397 0 0 0-.277.683l6.658 6.494-4.003 4.103C.45 13.692.065 18.846 0 23.57a.398.398 0 0 0 .683.282l6.494-6.657 3.934 3.837.17.165c2.41 2.353 7.565 2.737 12.288 2.803h.006a.397.397 0 0 0 .277-.683l-6.657-6.495z"/>
</svg>
<text x="256" y="206" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Airflow</text>
<text x="256" y="218" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">DAG scheduling</text>
<!-- ── MinIO (FOCAL) ── -->
<rect x="392" y="100" width="160" height="80" rx="8" fill="#f5f5f5"/>
<rect x="392" y="100" width="160" height="80" rx="8" fill="rgba(235,108,54,0.08)" stroke="#eb6c36" stroke-width="1.2"/>
<rect x="400" y="106" width="36" height="12" rx="2" fill="transparent" stroke="rgba(235,108,54,0.50)" stroke-width="0.8"/>
<text x="418" y="115" fill="rgba(235,108,54,0.80)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STORE</text>
<svg x="460" y="108" width="24" height="24" viewBox="0 0 24 24" fill="#eb6c36">
<path d="M13.2072.006c-.6216-.0478-1.2.1943-1.6211.582a2.15 2.15 0 0 0-.0938 3.0352l3.4082 3.5507a3.042 3.042 0 0 1-.664 4.6875l-.463.2383V7.2853a15.4198 15.4198 0 0 0-8.0174 10.4862v.0176l6.5487-3.3281v7.621L13.7794 24V13.6817l.8965-.4629a4.4432 4.4432 0 0 0 1.2207-7.0292l-3.371-3.5254a.7489.7489 0 0 1 .037-1.0547.7522.7522 0 0 1 1.0567.0371l.4668.4863-.006.0059 4.0704 4.2441a.0566.0566 0 0 0 .082 0 .06.06 0 0 0 0-.0703l-3.1406-5.1425-.1484.1425.1484-.1445C14.4945.3926 13.8287.0538 13.2072.006Zm-.9024 9.8652v2.9941l-4.1523 2.1484a13.9787 13.9787 0 0 1 2.7676-3.9277 14.1784 14.1784 0 0 1 1.3847-1.2148z"/>
</svg>
<text x="472" y="148" fill="#eb6c36" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">MinIO</text>
<text x="472" y="164" fill="#eb6c36" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" opacity="0.75">Object store · S3-API</text>
<!-- ── Trino ── -->
<rect x="616" y="60" width="144" height="56" rx="6" fill="#f5f5f5"/>
<rect x="616" y="60" width="144" height="56" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="624" y="66" width="36" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="642" y="75" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">QUERY</text>
<svg x="676" y="66" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142">
<path d="M14.124 16.8529a.1615.1615 0 1 1 .1576.1614.1577.1577 0 0 1-.1576-.1614zm-5.607-.1576a.1614.1614 0 1 0 0 .3228.1614.1614 0 0 0 0-.3228zm10.1341-.6648v1.9869c-.031.5788-.524 1.0237-1.1029.9954h-.3843a5.0596 5.0596 0 0 1-1.1298 1.7178.3192.3192 0 0 0 0 .465l.2382.2191a.3036.3036 0 0 1 .0385.4304c-1.126 1.3835-2.9669 2.1521-5.0498 2.1521a6.575 6.575 0 0 1-4.8192-1.8985c-.0029-.0032-.0059-.0063-.0087-.0096a.6302.6302 0 0 1 .0548-.8896c.137-.1265.1371-.3462 0-.4727a4.944 4.944 0 0 1-1.126-1.714h-.3497c-.5797.0284-1.0737-.416-1.1068-.9954v-1.9869c.0351-.5779.5286-1.02 1.1068-.9915h.2728a5.7648 5.7648 0 0 1 2.0791-3.0936c-.4227-1.0991-1.1529-3.2551-1.226-5.0075C6.0229 4.4705 6.2189.078 7.8253.001c1.6064-.0768 1.3719 4.0275 1.0991 6.6946a32.732 32.732 0 0 0-.123 4.4503 6.994 6.994 0 0 1 2.4826-.4304 7.2414 7.2414 0 0 1 1.7371.2075c.2614-1.2682.8762-3.574 2.0292-5.1958 1.6717-2.352 3.4357-4.7808 4.6116-4.1006 1.176.6802-.3074 3.1398-1.3297 4.4272-1.0222 1.2874-2.7862 3.2089-3.3742 4.2274-.2114.3843-.4304.8032-.5956 1.1529a5.7375 5.7375 0 0 1 2.9169 3.6125h.073v-2.3058a.3075.3075 0 0 0-.1806-.2844.9148.9148 0 0 1-.5573-.8148 1.0184 1.0184 0 0 1 .9045-.9044c.5593-.0598 1.061.3452 1.1208.9044a.9187.9187 0 0 1-.5534.8148.3074.3074 0 0 0-.1691.2844v2.1522a.3113.3113 0 0 0 .1691.2805.9724.9724 0 0 1 .5648.857z"/>
</svg>
<text x="688" y="102" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Trino</text>
<text x="688" y="114" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">SQL · any format</text>
<!-- ── StarRocks ── -->
<rect x="616" y="164" width="144" height="56" rx="6" fill="#f5f5f5"/>
<rect x="616" y="164" width="144" height="56" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="624" y="170" width="28" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="638" y="179" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">OLAP</text>
<svg x="676" y="170" width="24" height="24" viewBox="0 0 100 100" fill="#2d3142">
<path d="M11.8,26.4c-0.1,2.2,0.9,3.4,2.3,4.5c9,7.4,18,14.8,27,22.2c2.5,2.1,2.7,3.5,1,6.2c-4.4,6.7-8.8,13.4-13.2,20.1c-1.7,2.5-3.2,2.9-5.9,1.4c-2.8-1.6-5.6-3.2-8.4-4.8c-3.2-1.8-4.8-4.6-4.8-8.3c0-11.8,0-23.6,0-35.5C9.8,30.2,10.3,28.4,11.8,26.4z"/>
<path d="M87.9,73.8c0.6-2.3-0.5-3.5-1.9-4.6c-9-7.4-17.9-14.7-26.8-22.1c-2.9-2.4-3.1-3.6-1.1-6.7c4.3-6.6,8.7-13.2,13-19.8c1.7-2.6,3.3-2.9,6-1.4c2.8,1.6,5.6,3.2,8.3,4.8c3.2,1.8,4.7,4.5,4.7,8.2c0,11.9,0,23.7,0,35.6C90.2,69.9,89.6,71.9,87.9,73.8z"/>
<path d="M67.1,56.8c0.6,0.4,17.3,14.1,17.5,14.3c2.4,2.2,2.2,4.1-0.6,5.8C76.8,81,57.3,92.1,54.8,93.6c-3.2,1.9-6.4,1.9-9.6,0c-4.6-2.7-9.2-5.3-13.8-8c-2.2-1.3-2.2-2.7,0-3.9C42.3,75.5,53.1,69.2,64,63C66.5,61.6,68.2,60.1,67.1,56.8z"/>
<path d="M32.9,43.2c-0.6-0.4-17.3-14.1-17.5-14.3c-2.4-2.2-2.2-4.1,0.6-5.8C23.2,19,42.7,7.9,45.2,6.4c3.2-1.9,6.4-1.9,9.6,0c4.6,2.7,9.2,5.3,13.8,8c2.2,1.3,2.2,2.7,0,3.9C57.7,24.5,46.9,30.8,36,37C33.5,38.4,31.8,39.9,32.9,43.2z"/>
</svg>
<text x="688" y="206" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">StarRocks</text>
<text x="688" y="218" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">MPP · hot layer</text>
<!-- ── Superset ── -->
<rect x="816" y="60" width="136" height="56" rx="6" fill="#f5f5f5"/>
<rect x="816" y="60" width="136" height="56" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="824" y="66" width="18" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="833" y="75" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">BI</text>
<svg x="872" y="66" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142">
<path d="M6.168 6.045C2.603 6.045 0 8.579 0 12.014c0 3.434 2.603 5.941 6.168 5.941 2.184 0 3.888-1.026 5.775-3.078 1.53 2.033 4.037 3.136 5.89 3.078 3.566 0 6.167-2.503 6.167-5.941 0-3.438-2.601-5.97-6.168-5.97-2.864 0-5.138 2.425-5.771 3.173-.76-.9-1.674-1.665-2.682-2.274-1.019-.588-2.084-.898-3.211-.898z"/>
</svg>
<text x="884" y="102" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Superset</text>
<text x="884" y="114" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Dashboards</text>
<!-- ── JupyterLab ── -->
<rect x="816" y="152" width="136" height="56" rx="6" fill="#f5f5f5"/>
<rect x="816" y="152" width="136" height="56" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="824" y="158" width="22" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="835" y="167" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">NB</text>
<svg x="872" y="158" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142">
<path d="M7.157 22.201A1.784 1.799 0 0 1 5.374 24a1.784 1.799 0 0 1-1.784-1.799 1.784 1.799 0 0 1 1.784-1.799 1.784 1.799 0 0 1 1.783 1.799zM20.582 1.427a1.415 1.427 0 0 1-1.415 1.428 1.415 1.427 0 0 1-1.416-1.428A1.415 1.427 0 0 1 19.167 0a1.415 1.427 0 0 1 1.415 1.427zM4.992 3.336A1.047 1.056 0 0 1 3.946 4.39a1.047 1.056 0 0 1-1.047-1.055A1.047 1.056 0 0 1 3.946 2.28a1.047 1.056 0 0 1 1.046 1.056zm7.336 1.517c3.769 0 7.06 1.38 8.768 3.424a9.363 9.363 0 0 0-3.393-4.547 9.238 9.238 0 0 0-5.377-1.728A9.238 9.238 0 0 0 6.95 3.73a9.363 9.363 0 0 0-3.394 4.547c1.713-2.04 5.004-3.424 8.772-3.424zm.001 13.295c-3.768 0-7.06-1.381-8.768-3.425a9.363 9.363 0 0 0 3.394 4.547A9.238 9.238 0 0 0 12.33 21a9.238 9.238 0 0 0 5.377-1.729 9.363 9.363 0 0 0 3.393-4.547c-1.712 2.044-5.003 3.425-8.772 3.425z"/>
</svg>
<text x="884" y="194" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">JupyterLab</text>
<text x="884" y="206" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Exploration</text>
<!-- ── Python ── -->
<rect x="816" y="244" width="136" height="56" rx="6" fill="#f5f5f5"/>
<rect x="816" y="244" width="136" height="56" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="824" y="250" width="32" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="840" y="259" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">PROC</text>
<svg x="872" y="250" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142">
<path d="M14.25.18l.9.2.73.26.59.3.45.32.34.34.25.34.16.33.1.3.04.26.02.2-.01.13V8.5l-.05.63-.13.55-.21.46-.26.38-.3.31-.33.25-.35.19-.35.14-.33.1-.3.07-.26.04-.21.02H8.77l-.69.05-.59.14-.5.22-.41.27-.33.32-.27.35-.2.36-.15.37-.1.35-.07.32-.04.27-.02.21v3.06H3.17l-.21-.03-.28-.07-.32-.12-.35-.18-.36-.26-.36-.36-.35-.46-.32-.59-.28-.73-.21-.88-.14-1.05-.05-1.23.06-1.22.16-1.04.24-.87.32-.71.36-.57.4-.44.42-.33.42-.24.4-.16.36-.1.32-.05.24-.01h.16l.06.01h8.16v-.83H6.18l-.01-2.75-.02-.37.05-.34.11-.31.17-.28.25-.26.31-.23.38-.2.44-.18.51-.15.58-.12.64-.1.71-.06.77-.04.84-.02 1.27.05zm-6.3 1.98l-.23.33-.08.41.08.41.23.34.33.22.41.09.41-.09.33-.22.23-.34.08-.41-.08-.41-.23-.33-.33-.22-.41-.09-.41.09zm13.09 3.95l.28.06.32.12.35.18.36.27.36.35.35.47.32.59.28.73.21.88.14 1.04.05 1.23-.06 1.23-.16 1.04-.24.86-.32.71-.36.57-.4.45-.42.33-.42.24-.4.16-.36.09-.32.05-.24.02-.16-.01h-8.22v.82h5.84l.01 2.76.02.36-.05.34-.11.31-.17.29-.25.25-.31.24-.38.2-.44.17-.51.15-.58.13-.64.09-.71.07-.77.04-.84.01-1.27-.04-1.07-.14-.9-.2-.73-.25-.59-.3-.45-.33-.34-.34-.25-.34-.16-.33-.1-.3-.04-.25-.02-.2.01-.13v-5.34l.05-.64.13-.54.21-.46.26-.38.3-.32.33-.24.35-.2.35-.14.33-.1.3-.06.26-.04.21-.02.13-.01h5.84l.69-.05.59-.14.5-.21.41-.28.33-.32.27-.35.2-.36.15-.36.1-.35.07-.32.04-.28.02-.21V6.07h2.09l.14.01zm-6.47 14.25l-.23.33-.08.41.08.41.23.33.33.23.41.08.41-.08.33-.23.23-.33.08-.41-.08-.41-.23-.33-.33-.23-.41-.08-.41.08z"/>
</svg>
<text x="884" y="286" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Python</text>
<text x="884" y="298" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Batch · ML</text>
<!-- ═══════════════════════════════════
LEGEND
═══════════════════════════════════ -->
<line x1="40" y1="432" x2="960" y2="432" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="40" y="448" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<rect x="40" y="460" width="20" height="12" rx="3" fill="rgba(235,108,54,0.10)" stroke="#eb6c36" stroke-width="1.2"/>
<text x="68" y="471" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">MinIO data lake (focal)</text>
<rect x="220" y="460" width="20" height="12" rx="3" fill="white" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text x="248" y="471" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Ingest · query tools</text>
<rect x="400" y="460" width="20" height="12" rx="3" fill="white" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text x="428" y="471" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">BI · notebooks · pipelines</text>
<line x1="648" y1="462" x2="668" y2="462" stroke="rgba(45,49,66,0.30)" stroke-width="1" stroke-dasharray="4,3"/>
<text x="676" y="468" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Write-back path (batch)</text>
<line x1="820" y1="462" x2="840" y2="462" stroke="#eb6c36" stroke-width="1.2"/>
<text x="848" y="468" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Primary query path</text>
</svg>
</div>
</body>
</html>
@@ -0,0 +1,78 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Generic data platform · Integration topology (dark)</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&amp;family=Geist:wght@400;500;600&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet">
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{--paper:#f5f5f5;--paper-2:#ececec;--ink:#2d3142;--muted:#4f5d75;--soft:#7a8399;--accent:#eb6c36;--accent-tint:rgba(235,108,54,.08);--link:#2e5aa8;--rule:rgba(45,49,66,.12);--dot:rgba(45,49,66,.10);--side-fill:rgba(79,93,117,.06);--side-stroke:#7a8399;--zone-fill:rgba(45,49,66,.025);--zone-stroke:rgba(45,49,66,.32);--bar-fill:rgba(45,49,66,.05);--node-fill:#fff;--custom-red:#b85450;--custom-red-fill:rgba(184,84,80,.06);--custom-red-stroke:rgba(184,84,80,.35);--custom-blue:#5a7d9a;--custom-blue-fill:rgba(90,125,154,.06);--custom-blue-stroke:rgba(90,125,154,.35);--sans:'Geist',system-ui,sans-serif;--serif:'Instrument Serif',serif;--mono:'Geist Mono',ui-monospace,monospace}
body{font-family:var(--sans);background:var(--paper);color:var(--ink);min-height:100vh;display:flex;align-items:center;justify-content:center;padding:3rem 2rem}.frame{max-width:1200px;width:100%}.eyebrow{font:500 .66rem var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--muted);margin-bottom:.5rem}h1{font:400 clamp(1.5rem,2.4vw + .75rem,2rem)/1.15 var(--serif);letter-spacing:-.02em;margin-bottom:1.5rem}svg{width:100%;min-width:1100px;display:block}svg text{font-family:var(--sans)}.mono{font-family:var(--mono)}.connector{fill:none;stroke:var(--muted);stroke-width:1.2}.connector.primary{stroke:var(--accent);stroke-width:1.4}.connector.trigger{stroke:var(--muted);stroke-width:1;stroke-dasharray:4 3}.connector.auth{stroke:var(--accent);stroke-width:1.2;stroke-dasharray:5 4}.label-mask{fill:var(--paper)}.edge-label{fill:var(--accent);font:8px var(--mono);letter-spacing:.06em}.zone{fill:var(--zone-fill);stroke:var(--zone-stroke)}.zone-mask,.node-mask{fill:var(--paper)}.zone-label{fill:var(--soft);font:500 8px var(--mono);letter-spacing:.18em}.side-node{fill:var(--side-fill);stroke:var(--side-stroke)}.core-node{fill:var(--accent-tint);stroke:var(--accent)}.bar{fill:var(--bar-fill);stroke:var(--zone-stroke)}.role-box{fill:transparent;stroke:var(--side-stroke);stroke-opacity:.5}.role-box.focal{stroke:var(--accent)}.role-text{fill:var(--side-stroke);font:500 7px var(--mono);letter-spacing:.08em}.role-text.focal{fill:var(--accent)}.node-name{fill:var(--ink);font:600 12px var(--sans)}.node-name.focal{fill:var(--ink)}.node-sub{fill:var(--muted);font:9px var(--mono)}.footer-red{fill:var(--custom-red-fill);stroke:var(--custom-red-stroke)}.footer-blue{fill:var(--custom-blue-fill);stroke:var(--custom-blue-stroke)}.footer-red-text{fill:var(--custom-red)}.footer-blue-text{fill:var(--custom-blue)}.legend-key{fill:var(--muted);font:500 8px var(--mono);letter-spacing:.14em}.legend-text{fill:var(--muted);font:8px var(--sans)}
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">DP Integration · Diagram Design</p>
<h1>Generic data platform integration topology</h1>
<svg viewBox="0 0 1200 664" xmlns="http://www.w3.org/2000/svg" aria-label="Sources connect to a core data platform and three consumer surfaces">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse"><circle cx="1" cy="1" r="0.9" fill="var(--dot)"/></pattern>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="var(--muted)"/></marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="var(--accent)"/></marker>
<marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="var(--link)"/></marker>
<marker id="arrow-sm" markerWidth="6" markerHeight="5" refX="5" refY="2.5" orient="auto"><polygon points="0 0,6 2.5,0 5" fill="var(--muted)"/></marker>
<marker id="arrow-dim" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="var(--soft)"/></marker>
<g id="ico-db" fill="none" stroke="currentColor" stroke-width="1.2"><ellipse cx="0" cy="-5" rx="7" ry="3"/><path d="M-7-5V5c0 2 14 2 14 0V-5M-7 0c0 2 14 2 14 0"/></g>
<g id="ico-file" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M-6-8H2l4 4V8H-6ZM2-8v4h4"/></g>
<g id="ico-stream" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M-8-5H2M-4 0H8M-8 5H2"/><path d="m0-8 3 3-3 3M6-3l3 3-3 3M0 2l3 3-3 3"/></g>
<g id="ico-chart" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M-8 8H8M-6 6V0H-2V6M0 6V-7H4V6M6 6V-3H9"/></g>
<g id="ico-notebook" fill="none" stroke="currentColor" stroke-width="1.2"><rect x="-7" y="-8" width="14" height="16" rx="2"/><path d="M-3-8V8M0-3h4M0 1h4"/></g>
<g id="ico-api" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M-3-8c-3 0-3 2-3 4v2c0 2-1 2-3 2 2 0 3 0 3 2v2c0 2 0 4 3 4M3-8c3 0 3 2 3 4v2c0 2 1 2 3 2-2 0-3 0-3 2v2c0 2 0 4-3 4"/></g>
<g id="ico-key" fill="none" stroke="currentColor" stroke-width="1.2"><circle cx="-4" cy="0" r="4"/><path d="M0 0H9M6 0v3M3 0v2"/></g>
<g id="ico-log" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M-8 6h16M-6 3l3-4 3 2 4-7 3 2"/></g>
</defs>
<rect width="1200" height="664" fill="var(--paper)"/><rect width="1200" height="664" fill="url(#dots)" opacity=".55"/>
<rect class="zone" x="260" y="72" width="696" height="336" rx="8" stroke-width="1"/>
<!-- Connectors are emitted before all node rectangles. -->
<path class="connector primary" d="M200 124 H360 Q368 124 368 132 V180 Q368 188 376 188 H400" marker-end="url(#arrow-accent)"/>
<line class="connector primary" x1="200" y1="212" x2="400" y2="212" marker-end="url(#arrow-accent)"/>
<path class="connector primary" d="M200 300 H360 Q368 300 368 292 V244 Q368 236 376 236 H400" marker-end="url(#arrow-accent)"/>
<line class="connector primary" x1="560" y1="212" x2="656" y2="212" marker-end="url(#arrow-accent)"/>
<path class="connector primary" d="M816 188 H960 Q968 188 968 180 V132 Q968 124 976 124 H1000" marker-end="url(#arrow-accent)"/>
<line class="connector primary" x1="816" y1="212" x2="1000" y2="212" marker-end="url(#arrow-accent)"/>
<path class="connector primary" d="M816 236 H960 Q968 236 968 244 V292 Q968 300 976 300 H1000" marker-end="url(#arrow-accent)"/>
<line class="connector trigger" x1="480" y1="160" x2="480" y2="176" marker-end="url(#arrow)"/><line class="connector trigger" x1="736" y1="160" x2="736" y2="176" marker-end="url(#arrow)"/>
<line class="connector auth" x1="592" y1="460" x2="592" y2="408" marker-end="url(#arrow-accent)"/>
<!-- Dashed transit crosses the identity footer without treating it as an endpoint. -->
<line class="connector auth" x1="624" y1="524" x2="624" y2="408" marker-end="url(#arrow-accent)"/>
<!-- Protocol labels retain an 8px gap from their connector segment. -->
<g text-anchor="middle"><rect class="label-mask" x="252" y="104" width="44" height="12" rx="2"/><text class="edge-label" x="274" y="113">REST</text><rect class="label-mask" x="272" y="192" width="48" height="12" rx="2"/><text class="edge-label" x="296" y="201">CSV</text><rect class="label-mask" x="248" y="280" width="56" height="12" rx="2"/><text class="edge-label" x="276" y="289">EVENTS</text><rect class="label-mask" x="584" y="192" width="48" height="12" rx="2"/><text class="edge-label" x="608" y="201">READ</text><rect class="label-mask" x="856" y="168" width="48" height="12" rx="2"/><text class="edge-label" x="880" y="177">JDBC</text><rect class="label-mask" x="872" y="192" width="56" height="12" rx="2"/><text class="edge-label" x="900" y="201">KERNEL</text><rect class="label-mask" x="856" y="216" width="56" height="12" rx="2"/><text class="edge-label" x="884" y="225">HTTPS</text></g>
<g><rect class="label-mask" x="548" y="420" width="32" height="12" rx="2"/><text class="edge-label" x="564" y="429" text-anchor="middle">AUTH</text><rect class="label-mask" x="636" y="420" width="32" height="12" rx="2"/><text class="edge-label" x="652" y="429" text-anchor="middle">AUTH</text></g>
<!-- Zone label -->
<rect class="zone-mask" x="548" y="68" width="120" height="16" rx="2"/><text class="zone-label" x="608" y="79" text-anchor="middle">DATA PLATFORM</text>
<!-- Sources -->
<g text-anchor="middle">
<g><rect class="node-mask" x="40" y="92" width="160" height="64" rx="6"/><rect class="side-node" x="40" y="92" width="160" height="64" rx="6"/><use href="#ico-db" transform="translate(64 124)" style="color:var(--muted)"/><text class="node-name" x="120" y="120">CRM</text><text class="node-sub" x="120" y="140">customer records</text></g>
<g><rect class="node-mask" x="40" y="180" width="160" height="64" rx="6"/><rect class="side-node" x="40" y="180" width="160" height="64" rx="6"/><use href="#ico-file" transform="translate(64 212)" style="color:var(--muted)"/><text class="node-name" x="120" y="208">POS exports</text><text class="node-sub" x="120" y="228">daily CSV batches</text></g>
<g><rect class="node-mask" x="40" y="268" width="160" height="64" rx="6"/><rect class="side-node" x="40" y="268" width="160" height="64" rx="6"/><use href="#ico-stream" transform="translate(64 300)" style="color:var(--muted)"/><text class="node-name" x="120" y="296">Event stream</text><text class="node-sub" x="120" y="316">near-real-time events</text></g>
</g>
<!-- Core: fixed 160px nodes keep clean connector corridors; centers remain evenly spaced. -->
<g text-anchor="middle"><rect class="node-mask" x="276" y="116" width="664" height="44" rx="6"/><rect class="bar" x="276" y="116" width="664" height="44" rx="6"/><rect class="role-box" x="288" y="128" width="32" height="12" rx="2"/><text class="role-text" x="304" y="137">DAG</text><text class="node-name" x="608" y="136">Orchestrator</text><text class="node-sub" x="608" y="151">schedules · retries · lineage</text>
<rect class="node-mask" x="400" y="176" width="160" height="72" rx="6"/><rect class="core-node" x="400" y="176" width="160" height="72" rx="6"/><rect class="role-box focal" x="408" y="184" width="44" height="12" rx="2"/><text class="role-text focal" x="430" y="193">STORE</text><text class="node-name focal" x="480" y="214">Object storage</text><text class="node-sub" x="480" y="232">versioned data objects</text>
<rect class="node-mask" x="656" y="176" width="160" height="72" rx="6"/><rect class="core-node" x="656" y="176" width="160" height="72" rx="6"/><rect class="role-box focal" x="664" y="184" width="32" height="12" rx="2"/><text class="role-text focal" x="680" y="193">SQL</text><text class="node-name focal" x="736" y="214">Query engine</text><text class="node-sub" x="736" y="232">federated SQL access</text></g>
<!-- Consumers -->
<g text-anchor="middle">
<g><rect class="node-mask" x="1000" y="92" width="160" height="64" rx="6"/><rect class="side-node" x="1000" y="92" width="160" height="64" rx="6"/><use href="#ico-chart" transform="translate(1024 124)" style="color:var(--muted)"/><text class="node-name" x="1080" y="120">BI tool</text><text class="node-sub" x="1080" y="140">dashboards · reports</text></g>
<g><rect class="node-mask" x="1000" y="180" width="160" height="64" rx="6"/><rect class="side-node" x="1000" y="180" width="160" height="64" rx="6"/><use href="#ico-notebook" transform="translate(1024 212)" style="color:var(--muted)"/><text class="node-name" x="1080" y="208">Notebooks</text><text class="node-sub" x="1080" y="228">Python · exploration</text></g>
<g><rect class="node-mask" x="1000" y="268" width="160" height="64" rx="6"/><rect class="side-node" x="1000" y="268" width="160" height="64" rx="6"/><use href="#ico-api" transform="translate(1024 300)" style="color:var(--muted)"/><text class="node-name" x="1080" y="296">Partner API</text><text class="node-sub" x="1080" y="316">scoped data products</text></g>
</g>
<!-- Layer-wide footer services -->
<g><rect class="node-mask" x="40" y="460" width="1120" height="56" rx="6"/><rect class="footer-red" x="40" y="460" width="1120" height="56" rx="6"/><use href="#ico-key" transform="translate(72 488)" style="color:var(--custom-red)"/><text class="node-name footer-red-text" x="96" y="486">Identity provider</text><text class="node-sub" x="96" y="502">SSO · service identities · policy groups</text>
<rect class="node-mask" x="40" y="524" width="1120" height="56" rx="6"/><rect class="footer-blue" x="40" y="524" width="1120" height="56" rx="6"/><use href="#ico-log" transform="translate(72 552)" style="color:var(--custom-blue)"/><text class="node-name footer-blue-text" x="96" y="550">Centralized logging</text><text class="node-sub" x="96" y="566">platform events · audit trail · retention</text></g>
<!-- Legend -->
<line x1="40" y1="608" x2="1160" y2="608" stroke="var(--rule)" stroke-width=".8"/><text class="legend-key" x="40" y="628">TYPE KEY</text><rect x="128" y="620" width="16" height="12" rx="2" fill="var(--side-fill)" stroke="var(--side-stroke)"/><text class="legend-text" x="152" y="630">Source / consumer</text><rect x="296" y="620" width="16" height="12" rx="2" fill="var(--accent-tint)" stroke="var(--accent)"/><text class="legend-text" x="320" y="630">Focal platform surface</text><rect x="492" y="620" width="16" height="12" rx="2" fill="var(--bar-fill)" stroke="var(--zone-stroke)"/><text class="legend-text" x="516" y="630">Orchestration</text><line x1="640" y1="626" x2="672" y2="626" stroke="var(--accent)" stroke-width="1.4" marker-end="url(#arrow-accent)"/><text class="legend-text" x="684" y="630">Primary data path</text><line x1="824" y1="626" x2="856" y2="626" stroke="var(--accent)" stroke-dasharray="5 4" marker-end="url(#arrow-accent)"/><text class="legend-text" x="868" y="630">Layer-wide service</text>
</svg>
</div>
</body>
</html>
@@ -0,0 +1,88 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Generic data platform · Integration overview</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&amp;family=Geist:wght@400;500;600&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet">
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{--paper:#f5f5f5;--paper-2:#ececec;--ink:#2d3142;--muted:#4f5d75;--soft:#7a8399;--accent:#eb6c36;--accent-tint:rgba(235,108,54,.08);--link:#2e5aa8;--rule:rgba(45,49,66,.12);--dot:rgba(45,49,66,.10);--side-fill:rgba(79,93,117,.06);--side-stroke:#7a8399;--zone-fill:rgba(45,49,66,.025);--zone-stroke:rgba(45,49,66,.32);--bar-fill:rgba(45,49,66,.05);--node-fill:#fff;--custom-red:#b85450;--custom-red-fill:rgba(184,84,80,.06);--custom-red-stroke:rgba(184,84,80,.35);--custom-blue:#5a7d9a;--custom-blue-fill:rgba(90,125,154,.06);--custom-blue-stroke:rgba(90,125,154,.35);--sans:'Geist',system-ui,sans-serif;--serif:'Instrument Serif',serif;--mono:'Geist Mono',ui-monospace,monospace}
body{font-family:var(--sans);background:var(--paper);color:var(--ink);min-height:100vh;padding:3rem 2rem}.frame{max-width:1200px;width:100%;margin:0 auto}.eyebrow{font:500 .66rem var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--muted);margin-bottom:.5rem}h1{font:400 clamp(1.5rem,2.4vw + .75rem,2rem)/1.15 var(--serif);letter-spacing:-.02em;margin-bottom:1.5rem}svg{width:100%;min-width:1100px;display:block}svg text{font-family:var(--sans)}.mono{font-family:var(--mono)}.connector{fill:none;stroke:var(--muted);stroke-width:1.2}.connector.primary{stroke:var(--accent);stroke-width:1.4}.connector.trigger{stroke:var(--muted);stroke-width:1;stroke-dasharray:4 3}.connector.auth{stroke:var(--accent);stroke-width:1.2;stroke-dasharray:5 4}.label-mask{fill:var(--paper)}.edge-label{fill:var(--accent);font:8px var(--mono);letter-spacing:.06em}.zone{fill:var(--zone-fill);stroke:var(--zone-stroke)}.zone-mask,.node-mask{fill:var(--paper)}.zone-label{fill:var(--soft);font:500 8px var(--mono);letter-spacing:.18em}.side-node{fill:var(--side-fill);stroke:var(--side-stroke)}.core-node{fill:var(--accent-tint);stroke:var(--accent)}.bar{fill:var(--bar-fill);stroke:var(--zone-stroke)}.role-box{fill:transparent;stroke:var(--side-stroke);stroke-opacity:.5}.role-box.focal{stroke:var(--accent)}.role-text{fill:var(--side-stroke);font:500 7px var(--mono);letter-spacing:.08em}.role-text.focal{fill:var(--accent)}.node-name{fill:var(--ink);font:600 12px var(--sans)}.node-name.focal{fill:var(--ink)}.node-sub{fill:var(--muted);font:9px var(--mono)}.footer-red{fill:var(--custom-red-fill);stroke:var(--custom-red-stroke)}.footer-blue{fill:var(--custom-blue-fill);stroke:var(--custom-blue-stroke)}.footer-red-text{fill:var(--custom-red)}.footer-blue-text{fill:var(--custom-blue)}.legend-key{fill:var(--muted);font:500 8px var(--mono);letter-spacing:.14em}.legend-text{fill:var(--muted);font:8px var(--sans)}
.subtitle{max-width:62ch;color:var(--muted);font-size:1rem;line-height:1.55;margin:-.75rem 0 2rem}.diagram-container{background:var(--paper-2);border:1px solid var(--rule);border-radius:8px;padding:1.5rem;overflow-x:auto}.cards{display:grid;grid-template-columns:1.1fr 1fr .9fr;gap:1rem;margin-top:1.5rem}.card{background:#fff;border:1px solid var(--rule);border-radius:6px;padding:1.25rem}.card .card-label{font:500 .5rem var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--muted);margin-bottom:.5rem}.card-header{display:flex;align-items:center;gap:.6rem;margin-bottom:.875rem;padding-bottom:.875rem;border-bottom:1px solid rgba(45,49,66,.08)}.card-dot{width:7px;height:7px;border-radius:50%;background:var(--muted)}.card-dot.coral{background:var(--accent)}.card-dot.ink{background:var(--ink)}.card h3{font-size:.875rem;font-weight:600}.card p,.card li{color:var(--muted);font-size:.8125rem;line-height:1.55}.card ul{list-style:none}.card li{padding-left:.875rem;position:relative}.card li::before{content:'—';position:absolute;left:0;color:rgba(45,49,66,.25)}.footer{display:flex;justify-content:space-between;gap:.5rem;flex-wrap:wrap;margin-top:2rem;padding-top:1.5rem;border-top:1px solid var(--rule);color:var(--soft);font:.72rem var(--mono);letter-spacing:.06em}@media(max-width:820px){.cards{grid-template-columns:1fr}}
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">DP Integration · Diagram Design</p>
<h1>Generic data platform integration topology</h1>
<p class="subtitle">Three source patterns land in shared object storage, a query surface serves three consumer modes, and platform-wide services remain visibly cross-cutting.</p>
<div class="diagram-container">
<svg viewBox="0 0 1200 664" xmlns="http://www.w3.org/2000/svg" aria-label="Sources connect to a core data platform and three consumer surfaces">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse"><circle cx="1" cy="1" r="0.9" fill="var(--dot)"/></pattern>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="var(--muted)"/></marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="var(--accent)"/></marker>
<marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="var(--link)"/></marker>
<marker id="arrow-sm" markerWidth="6" markerHeight="5" refX="5" refY="2.5" orient="auto"><polygon points="0 0,6 2.5,0 5" fill="var(--muted)"/></marker>
<marker id="arrow-dim" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="var(--soft)"/></marker>
<g id="ico-db" fill="none" stroke="currentColor" stroke-width="1.2"><ellipse cx="0" cy="-5" rx="7" ry="3"/><path d="M-7-5V5c0 2 14 2 14 0V-5M-7 0c0 2 14 2 14 0"/></g>
<g id="ico-file" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M-6-8H2l4 4V8H-6ZM2-8v4h4"/></g>
<g id="ico-stream" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M-8-5H2M-4 0H8M-8 5H2"/><path d="m0-8 3 3-3 3M6-3l3 3-3 3M0 2l3 3-3 3"/></g>
<g id="ico-chart" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M-8 8H8M-6 6V0H-2V6M0 6V-7H4V6M6 6V-3H9"/></g>
<g id="ico-notebook" fill="none" stroke="currentColor" stroke-width="1.2"><rect x="-7" y="-8" width="14" height="16" rx="2"/><path d="M-3-8V8M0-3h4M0 1h4"/></g>
<g id="ico-api" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M-3-8c-3 0-3 2-3 4v2c0 2-1 2-3 2 2 0 3 0 3 2v2c0 2 0 4 3 4M3-8c3 0 3 2 3 4v2c0 2 1 2 3 2-2 0-3 0-3 2v2c0 2 0 4-3 4"/></g>
<g id="ico-key" fill="none" stroke="currentColor" stroke-width="1.2"><circle cx="-4" cy="0" r="4"/><path d="M0 0H9M6 0v3M3 0v2"/></g>
<g id="ico-log" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M-8 6h16M-6 3l3-4 3 2 4-7 3 2"/></g>
</defs>
<rect width="1200" height="664" fill="var(--paper)"/><rect width="1200" height="664" fill="url(#dots)" opacity=".55"/>
<rect class="zone" x="260" y="72" width="696" height="336" rx="8" stroke-width="1"/>
<!-- Connectors are emitted before all node rectangles. -->
<path class="connector primary" d="M200 124 H360 Q368 124 368 132 V180 Q368 188 376 188 H400" marker-end="url(#arrow-accent)"/>
<line class="connector primary" x1="200" y1="212" x2="400" y2="212" marker-end="url(#arrow-accent)"/>
<path class="connector primary" d="M200 300 H360 Q368 300 368 292 V244 Q368 236 376 236 H400" marker-end="url(#arrow-accent)"/>
<line class="connector primary" x1="560" y1="212" x2="656" y2="212" marker-end="url(#arrow-accent)"/>
<path class="connector primary" d="M816 188 H960 Q968 188 968 180 V132 Q968 124 976 124 H1000" marker-end="url(#arrow-accent)"/>
<line class="connector primary" x1="816" y1="212" x2="1000" y2="212" marker-end="url(#arrow-accent)"/>
<path class="connector primary" d="M816 236 H960 Q968 236 968 244 V292 Q968 300 976 300 H1000" marker-end="url(#arrow-accent)"/>
<line class="connector trigger" x1="480" y1="160" x2="480" y2="176" marker-end="url(#arrow)"/><line class="connector trigger" x1="736" y1="160" x2="736" y2="176" marker-end="url(#arrow)"/>
<line class="connector auth" x1="592" y1="460" x2="592" y2="408" marker-end="url(#arrow-accent)"/>
<!-- Dashed transit crosses the identity footer without treating it as an endpoint. -->
<line class="connector auth" x1="624" y1="524" x2="624" y2="408" marker-end="url(#arrow-accent)"/>
<!-- Protocol labels retain an 8px gap from their connector segment. -->
<g text-anchor="middle"><rect class="label-mask" x="252" y="104" width="44" height="12" rx="2"/><text class="edge-label" x="274" y="113">REST</text><rect class="label-mask" x="272" y="192" width="48" height="12" rx="2"/><text class="edge-label" x="296" y="201">CSV</text><rect class="label-mask" x="248" y="280" width="56" height="12" rx="2"/><text class="edge-label" x="276" y="289">EVENTS</text><rect class="label-mask" x="584" y="192" width="48" height="12" rx="2"/><text class="edge-label" x="608" y="201">READ</text><rect class="label-mask" x="856" y="168" width="48" height="12" rx="2"/><text class="edge-label" x="880" y="177">JDBC</text><rect class="label-mask" x="872" y="192" width="56" height="12" rx="2"/><text class="edge-label" x="900" y="201">KERNEL</text><rect class="label-mask" x="856" y="216" width="56" height="12" rx="2"/><text class="edge-label" x="884" y="225">HTTPS</text></g>
<g><rect class="label-mask" x="548" y="420" width="32" height="12" rx="2"/><text class="edge-label" x="564" y="429" text-anchor="middle">AUTH</text><rect class="label-mask" x="636" y="420" width="32" height="12" rx="2"/><text class="edge-label" x="652" y="429" text-anchor="middle">AUTH</text></g>
<!-- Zone label -->
<rect class="zone-mask" x="548" y="68" width="120" height="16" rx="2"/><text class="zone-label" x="608" y="79" text-anchor="middle">DATA PLATFORM</text>
<!-- Sources -->
<g text-anchor="middle">
<g><rect class="node-mask" x="40" y="92" width="160" height="64" rx="6"/><rect class="side-node" x="40" y="92" width="160" height="64" rx="6"/><use href="#ico-db" transform="translate(64 124)" style="color:var(--muted)"/><text class="node-name" x="120" y="120">CRM</text><text class="node-sub" x="120" y="140">customer records</text></g>
<g><rect class="node-mask" x="40" y="180" width="160" height="64" rx="6"/><rect class="side-node" x="40" y="180" width="160" height="64" rx="6"/><use href="#ico-file" transform="translate(64 212)" style="color:var(--muted)"/><text class="node-name" x="120" y="208">POS exports</text><text class="node-sub" x="120" y="228">daily CSV batches</text></g>
<g><rect class="node-mask" x="40" y="268" width="160" height="64" rx="6"/><rect class="side-node" x="40" y="268" width="160" height="64" rx="6"/><use href="#ico-stream" transform="translate(64 300)" style="color:var(--muted)"/><text class="node-name" x="120" y="296">Event stream</text><text class="node-sub" x="120" y="316">near-real-time events</text></g>
</g>
<!-- Core: fixed 160px nodes keep clean connector corridors; centers remain evenly spaced. -->
<g text-anchor="middle"><rect class="node-mask" x="276" y="116" width="664" height="44" rx="6"/><rect class="bar" x="276" y="116" width="664" height="44" rx="6"/><rect class="role-box" x="288" y="128" width="32" height="12" rx="2"/><text class="role-text" x="304" y="137">DAG</text><text class="node-name" x="608" y="136">Orchestrator</text><text class="node-sub" x="608" y="151">schedules · retries · lineage</text>
<rect class="node-mask" x="400" y="176" width="160" height="72" rx="6"/><rect class="core-node" x="400" y="176" width="160" height="72" rx="6"/><rect class="role-box focal" x="408" y="184" width="44" height="12" rx="2"/><text class="role-text focal" x="430" y="193">STORE</text><text class="node-name focal" x="480" y="214">Object storage</text><text class="node-sub" x="480" y="232">versioned data objects</text>
<rect class="node-mask" x="656" y="176" width="160" height="72" rx="6"/><rect class="core-node" x="656" y="176" width="160" height="72" rx="6"/><rect class="role-box focal" x="664" y="184" width="32" height="12" rx="2"/><text class="role-text focal" x="680" y="193">SQL</text><text class="node-name focal" x="736" y="214">Query engine</text><text class="node-sub" x="736" y="232">federated SQL access</text></g>
<!-- Consumers -->
<g text-anchor="middle">
<g><rect class="node-mask" x="1000" y="92" width="160" height="64" rx="6"/><rect class="side-node" x="1000" y="92" width="160" height="64" rx="6"/><use href="#ico-chart" transform="translate(1024 124)" style="color:var(--muted)"/><text class="node-name" x="1080" y="120">BI tool</text><text class="node-sub" x="1080" y="140">dashboards · reports</text></g>
<g><rect class="node-mask" x="1000" y="180" width="160" height="64" rx="6"/><rect class="side-node" x="1000" y="180" width="160" height="64" rx="6"/><use href="#ico-notebook" transform="translate(1024 212)" style="color:var(--muted)"/><text class="node-name" x="1080" y="208">Notebooks</text><text class="node-sub" x="1080" y="228">Python · exploration</text></g>
<g><rect class="node-mask" x="1000" y="268" width="160" height="64" rx="6"/><rect class="side-node" x="1000" y="268" width="160" height="64" rx="6"/><use href="#ico-api" transform="translate(1024 300)" style="color:var(--muted)"/><text class="node-name" x="1080" y="296">Partner API</text><text class="node-sub" x="1080" y="316">scoped data products</text></g>
</g>
<!-- Layer-wide footer services -->
<g><rect class="node-mask" x="40" y="460" width="1120" height="56" rx="6"/><rect class="footer-red" x="40" y="460" width="1120" height="56" rx="6"/><use href="#ico-key" transform="translate(72 488)" style="color:var(--custom-red)"/><text class="node-name footer-red-text" x="96" y="486">Identity provider</text><text class="node-sub" x="96" y="502">SSO · service identities · policy groups</text>
<rect class="node-mask" x="40" y="524" width="1120" height="56" rx="6"/><rect class="footer-blue" x="40" y="524" width="1120" height="56" rx="6"/><use href="#ico-log" transform="translate(72 552)" style="color:var(--custom-blue)"/><text class="node-name footer-blue-text" x="96" y="550">Centralized logging</text><text class="node-sub" x="96" y="566">platform events · audit trail · retention</text></g>
<!-- Legend -->
<line x1="40" y1="608" x2="1160" y2="608" stroke="var(--rule)" stroke-width=".8"/><text class="legend-key" x="40" y="628">TYPE KEY</text><rect x="128" y="620" width="16" height="12" rx="2" fill="var(--side-fill)" stroke="var(--side-stroke)"/><text class="legend-text" x="152" y="630">Source / consumer</text><rect x="296" y="620" width="16" height="12" rx="2" fill="var(--accent-tint)" stroke="var(--accent)"/><text class="legend-text" x="320" y="630">Focal platform surface</text><rect x="492" y="620" width="16" height="12" rx="2" fill="var(--bar-fill)" stroke="var(--zone-stroke)"/><text class="legend-text" x="516" y="630">Orchestration</text><line x1="640" y1="626" x2="672" y2="626" stroke="var(--accent)" stroke-width="1.4" marker-end="url(#arrow-accent)"/><text class="legend-text" x="684" y="630">Primary data path</text><line x1="824" y1="626" x2="856" y2="626" stroke="var(--accent)" stroke-dasharray="5 4" marker-end="url(#arrow-accent)"/><text class="legend-text" x="868" y="630">Layer-wide service</text>
</svg>
</div>
<div class="cards">
<article class="card"><p class="card-label">THE HEADLINE</p><div class="card-header"><span class="card-dot coral"></span><h3>Storage and query are the platform core</h3></div><p>The two focal surfaces separate durable ingestion from governed access without hiding the wire protocols.</p></article>
<article class="card"><div class="card-header"><span class="card-dot ink"></span><h3>Every integration stays explicit</h3></div><ul><li>CRM arrives over REST</li><li>Point-of-sale data arrives as CSV</li><li>Consumers use JDBC, kernels, or HTTPS</li></ul></article>
<article class="card"><div class="card-header"><span class="card-dot"></span><h3>Services apply to the whole layer</h3></div><p>Identity and centralized logging terminate at the platform boundary, not at an individual component.</p></article>
</div>
<footer class="footer"><span>data platform · integration topology</span><span>example · diagram design</span></footer>
</div>
</body>
</html>
@@ -0,0 +1,78 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Generic data platform · Integration topology</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&amp;family=Geist:wght@400;500;600&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet">
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{--paper:#f5f5f5;--paper-2:#ececec;--ink:#2d3142;--muted:#4f5d75;--soft:#7a8399;--accent:#eb6c36;--accent-tint:rgba(235,108,54,.08);--link:#2e5aa8;--rule:rgba(45,49,66,.12);--dot:rgba(45,49,66,.10);--side-fill:rgba(79,93,117,.06);--side-stroke:#7a8399;--zone-fill:rgba(45,49,66,.025);--zone-stroke:rgba(45,49,66,.32);--bar-fill:rgba(45,49,66,.05);--node-fill:#fff;--custom-red:#b85450;--custom-red-fill:rgba(184,84,80,.06);--custom-red-stroke:rgba(184,84,80,.35);--custom-blue:#5a7d9a;--custom-blue-fill:rgba(90,125,154,.06);--custom-blue-stroke:rgba(90,125,154,.35);--sans:'Geist',system-ui,sans-serif;--serif:'Instrument Serif',serif;--mono:'Geist Mono',ui-monospace,monospace}
body{font-family:var(--sans);background:var(--paper);color:var(--ink);min-height:100vh;display:flex;align-items:center;justify-content:center;padding:3rem 2rem}.frame{max-width:1200px;width:100%}.eyebrow{font:500 .66rem var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--muted);margin-bottom:.5rem}h1{font:400 clamp(1.5rem,2.4vw + .75rem,2rem)/1.15 var(--serif);letter-spacing:-.02em;margin-bottom:1.5rem}svg{width:100%;min-width:1100px;display:block}svg text{font-family:var(--sans)}.mono{font-family:var(--mono)}.connector{fill:none;stroke:var(--muted);stroke-width:1.2}.connector.primary{stroke:var(--accent);stroke-width:1.4}.connector.trigger{stroke:var(--muted);stroke-width:1;stroke-dasharray:4 3}.connector.auth{stroke:var(--accent);stroke-width:1.2;stroke-dasharray:5 4}.label-mask{fill:var(--paper)}.edge-label{fill:var(--accent);font:8px var(--mono);letter-spacing:.06em}.zone{fill:var(--zone-fill);stroke:var(--zone-stroke)}.zone-mask,.node-mask{fill:var(--paper)}.zone-label{fill:var(--soft);font:500 8px var(--mono);letter-spacing:.18em}.side-node{fill:var(--side-fill);stroke:var(--side-stroke)}.core-node{fill:var(--accent-tint);stroke:var(--accent)}.bar{fill:var(--bar-fill);stroke:var(--zone-stroke)}.role-box{fill:transparent;stroke:var(--side-stroke);stroke-opacity:.5}.role-box.focal{stroke:var(--accent)}.role-text{fill:var(--side-stroke);font:500 7px var(--mono);letter-spacing:.08em}.role-text.focal{fill:var(--accent)}.node-name{fill:var(--ink);font:600 12px var(--sans)}.node-name.focal{fill:var(--ink)}.node-sub{fill:var(--muted);font:9px var(--mono)}.footer-red{fill:var(--custom-red-fill);stroke:var(--custom-red-stroke)}.footer-blue{fill:var(--custom-blue-fill);stroke:var(--custom-blue-stroke)}.footer-red-text{fill:var(--custom-red)}.footer-blue-text{fill:var(--custom-blue)}.legend-key{fill:var(--muted);font:500 8px var(--mono);letter-spacing:.14em}.legend-text{fill:var(--muted);font:8px var(--sans)}
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">DP Integration · Diagram Design</p>
<h1>Generic data platform integration topology</h1>
<svg viewBox="0 0 1200 664" xmlns="http://www.w3.org/2000/svg" aria-label="Sources connect to a core data platform and three consumer surfaces">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse"><circle cx="1" cy="1" r="0.9" fill="var(--dot)"/></pattern>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="var(--muted)"/></marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="var(--accent)"/></marker>
<marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="var(--link)"/></marker>
<marker id="arrow-sm" markerWidth="6" markerHeight="5" refX="5" refY="2.5" orient="auto"><polygon points="0 0,6 2.5,0 5" fill="var(--muted)"/></marker>
<marker id="arrow-dim" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="var(--soft)"/></marker>
<g id="ico-db" fill="none" stroke="currentColor" stroke-width="1.2"><ellipse cx="0" cy="-5" rx="7" ry="3"/><path d="M-7-5V5c0 2 14 2 14 0V-5M-7 0c0 2 14 2 14 0"/></g>
<g id="ico-file" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M-6-8H2l4 4V8H-6ZM2-8v4h4"/></g>
<g id="ico-stream" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M-8-5H2M-4 0H8M-8 5H2"/><path d="m0-8 3 3-3 3M6-3l3 3-3 3M0 2l3 3-3 3"/></g>
<g id="ico-chart" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M-8 8H8M-6 6V0H-2V6M0 6V-7H4V6M6 6V-3H9"/></g>
<g id="ico-notebook" fill="none" stroke="currentColor" stroke-width="1.2"><rect x="-7" y="-8" width="14" height="16" rx="2"/><path d="M-3-8V8M0-3h4M0 1h4"/></g>
<g id="ico-api" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M-3-8c-3 0-3 2-3 4v2c0 2-1 2-3 2 2 0 3 0 3 2v2c0 2 0 4 3 4M3-8c3 0 3 2 3 4v2c0 2 1 2 3 2-2 0-3 0-3 2v2c0 2 0 4-3 4"/></g>
<g id="ico-key" fill="none" stroke="currentColor" stroke-width="1.2"><circle cx="-4" cy="0" r="4"/><path d="M0 0H9M6 0v3M3 0v2"/></g>
<g id="ico-log" fill="none" stroke="currentColor" stroke-width="1.2"><path d="M-8 6h16M-6 3l3-4 3 2 4-7 3 2"/></g>
</defs>
<rect width="1200" height="664" fill="var(--paper)"/><rect width="1200" height="664" fill="url(#dots)" opacity=".55"/>
<rect class="zone" x="260" y="72" width="696" height="336" rx="8" stroke-width="1"/>
<!-- Connectors are emitted before all node rectangles. -->
<path class="connector primary" d="M200 124 H360 Q368 124 368 132 V180 Q368 188 376 188 H400" marker-end="url(#arrow-accent)"/>
<line class="connector primary" x1="200" y1="212" x2="400" y2="212" marker-end="url(#arrow-accent)"/>
<path class="connector primary" d="M200 300 H360 Q368 300 368 292 V244 Q368 236 376 236 H400" marker-end="url(#arrow-accent)"/>
<line class="connector primary" x1="560" y1="212" x2="656" y2="212" marker-end="url(#arrow-accent)"/>
<path class="connector primary" d="M816 188 H960 Q968 188 968 180 V132 Q968 124 976 124 H1000" marker-end="url(#arrow-accent)"/>
<line class="connector primary" x1="816" y1="212" x2="1000" y2="212" marker-end="url(#arrow-accent)"/>
<path class="connector primary" d="M816 236 H960 Q968 236 968 244 V292 Q968 300 976 300 H1000" marker-end="url(#arrow-accent)"/>
<line class="connector trigger" x1="480" y1="160" x2="480" y2="176" marker-end="url(#arrow)"/><line class="connector trigger" x1="736" y1="160" x2="736" y2="176" marker-end="url(#arrow)"/>
<line class="connector auth" x1="592" y1="460" x2="592" y2="408" marker-end="url(#arrow-accent)"/>
<!-- Dashed transit crosses the identity footer without treating it as an endpoint. -->
<line class="connector auth" x1="624" y1="524" x2="624" y2="408" marker-end="url(#arrow-accent)"/>
<!-- Protocol labels retain an 8px gap from their connector segment. -->
<g text-anchor="middle"><rect class="label-mask" x="252" y="104" width="44" height="12" rx="2"/><text class="edge-label" x="274" y="113">REST</text><rect class="label-mask" x="272" y="192" width="48" height="12" rx="2"/><text class="edge-label" x="296" y="201">CSV</text><rect class="label-mask" x="248" y="280" width="56" height="12" rx="2"/><text class="edge-label" x="276" y="289">EVENTS</text><rect class="label-mask" x="584" y="192" width="48" height="12" rx="2"/><text class="edge-label" x="608" y="201">READ</text><rect class="label-mask" x="856" y="168" width="48" height="12" rx="2"/><text class="edge-label" x="880" y="177">JDBC</text><rect class="label-mask" x="872" y="192" width="56" height="12" rx="2"/><text class="edge-label" x="900" y="201">KERNEL</text><rect class="label-mask" x="856" y="216" width="56" height="12" rx="2"/><text class="edge-label" x="884" y="225">HTTPS</text></g>
<g><rect class="label-mask" x="548" y="420" width="32" height="12" rx="2"/><text class="edge-label" x="564" y="429" text-anchor="middle">AUTH</text><rect class="label-mask" x="636" y="420" width="32" height="12" rx="2"/><text class="edge-label" x="652" y="429" text-anchor="middle">AUTH</text></g>
<!-- Zone label -->
<rect class="zone-mask" x="548" y="68" width="120" height="16" rx="2"/><text class="zone-label" x="608" y="79" text-anchor="middle">DATA PLATFORM</text>
<!-- Sources -->
<g text-anchor="middle">
<g><rect class="node-mask" x="40" y="92" width="160" height="64" rx="6"/><rect class="side-node" x="40" y="92" width="160" height="64" rx="6"/><use href="#ico-db" transform="translate(64 124)" style="color:var(--muted)"/><text class="node-name" x="120" y="120">CRM</text><text class="node-sub" x="120" y="140">customer records</text></g>
<g><rect class="node-mask" x="40" y="180" width="160" height="64" rx="6"/><rect class="side-node" x="40" y="180" width="160" height="64" rx="6"/><use href="#ico-file" transform="translate(64 212)" style="color:var(--muted)"/><text class="node-name" x="120" y="208">POS exports</text><text class="node-sub" x="120" y="228">daily CSV batches</text></g>
<g><rect class="node-mask" x="40" y="268" width="160" height="64" rx="6"/><rect class="side-node" x="40" y="268" width="160" height="64" rx="6"/><use href="#ico-stream" transform="translate(64 300)" style="color:var(--muted)"/><text class="node-name" x="120" y="296">Event stream</text><text class="node-sub" x="120" y="316">near-real-time events</text></g>
</g>
<!-- Core: fixed 160px nodes keep clean connector corridors; centers remain evenly spaced. -->
<g text-anchor="middle"><rect class="node-mask" x="276" y="116" width="664" height="44" rx="6"/><rect class="bar" x="276" y="116" width="664" height="44" rx="6"/><rect class="role-box" x="288" y="128" width="32" height="12" rx="2"/><text class="role-text" x="304" y="137">DAG</text><text class="node-name" x="608" y="136">Orchestrator</text><text class="node-sub" x="608" y="151">schedules · retries · lineage</text>
<rect class="node-mask" x="400" y="176" width="160" height="72" rx="6"/><rect class="core-node" x="400" y="176" width="160" height="72" rx="6"/><rect class="role-box focal" x="408" y="184" width="44" height="12" rx="2"/><text class="role-text focal" x="430" y="193">STORE</text><text class="node-name focal" x="480" y="214">Object storage</text><text class="node-sub" x="480" y="232">versioned data objects</text>
<rect class="node-mask" x="656" y="176" width="160" height="72" rx="6"/><rect class="core-node" x="656" y="176" width="160" height="72" rx="6"/><rect class="role-box focal" x="664" y="184" width="32" height="12" rx="2"/><text class="role-text focal" x="680" y="193">SQL</text><text class="node-name focal" x="736" y="214">Query engine</text><text class="node-sub" x="736" y="232">federated SQL access</text></g>
<!-- Consumers -->
<g text-anchor="middle">
<g><rect class="node-mask" x="1000" y="92" width="160" height="64" rx="6"/><rect class="side-node" x="1000" y="92" width="160" height="64" rx="6"/><use href="#ico-chart" transform="translate(1024 124)" style="color:var(--muted)"/><text class="node-name" x="1080" y="120">BI tool</text><text class="node-sub" x="1080" y="140">dashboards · reports</text></g>
<g><rect class="node-mask" x="1000" y="180" width="160" height="64" rx="6"/><rect class="side-node" x="1000" y="180" width="160" height="64" rx="6"/><use href="#ico-notebook" transform="translate(1024 212)" style="color:var(--muted)"/><text class="node-name" x="1080" y="208">Notebooks</text><text class="node-sub" x="1080" y="228">Python · exploration</text></g>
<g><rect class="node-mask" x="1000" y="268" width="160" height="64" rx="6"/><rect class="side-node" x="1000" y="268" width="160" height="64" rx="6"/><use href="#ico-api" transform="translate(1024 300)" style="color:var(--muted)"/><text class="node-name" x="1080" y="296">Partner API</text><text class="node-sub" x="1080" y="316">scoped data products</text></g>
</g>
<!-- Layer-wide footer services -->
<g><rect class="node-mask" x="40" y="460" width="1120" height="56" rx="6"/><rect class="footer-red" x="40" y="460" width="1120" height="56" rx="6"/><use href="#ico-key" transform="translate(72 488)" style="color:var(--custom-red)"/><text class="node-name footer-red-text" x="96" y="486">Identity provider</text><text class="node-sub" x="96" y="502">SSO · service identities · policy groups</text>
<rect class="node-mask" x="40" y="524" width="1120" height="56" rx="6"/><rect class="footer-blue" x="40" y="524" width="1120" height="56" rx="6"/><use href="#ico-log" transform="translate(72 552)" style="color:var(--custom-blue)"/><text class="node-name footer-blue-text" x="96" y="550">Centralized logging</text><text class="node-sub" x="96" y="566">platform events · audit trail · retention</text></g>
<!-- Legend -->
<line x1="40" y1="608" x2="1160" y2="608" stroke="var(--rule)" stroke-width=".8"/><text class="legend-key" x="40" y="628">TYPE KEY</text><rect x="128" y="620" width="16" height="12" rx="2" fill="var(--side-fill)" stroke="var(--side-stroke)"/><text class="legend-text" x="152" y="630">Source / consumer</text><rect x="296" y="620" width="16" height="12" rx="2" fill="var(--accent-tint)" stroke="var(--accent)"/><text class="legend-text" x="320" y="630">Focal platform surface</text><rect x="492" y="620" width="16" height="12" rx="2" fill="var(--bar-fill)" stroke="var(--zone-stroke)"/><text class="legend-text" x="516" y="630">Orchestration</text><line x1="640" y1="626" x2="672" y2="626" stroke="var(--accent)" stroke-width="1.4" marker-end="url(#arrow-accent)"/><text class="legend-text" x="684" y="630">Primary data path</text><line x1="824" y1="626" x2="856" y2="626" stroke="var(--accent)" stroke-dasharray="5 4" marker-end="url(#arrow-accent)"/><text class="legend-text" x="868" y="630">Layer-wide service</text>
</svg>
</div>
</body>
</html>
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Data Platform · Access Matrix · Dark</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&amp;family=Geist:wght@400;500;600&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet">
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}:root{--paper:#2d3142;--paper-2:#393e53;--ink:#f5f5f5;--muted:#bfc0c0;--soft:#8e98ac;--rule:rgba(245,245,245,.12);--accent:#f08a59;--sans:'Geist',system-ui,sans-serif;--serif:'Instrument Serif',serif;--mono:'Geist Mono',ui-monospace,monospace}
body{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:3rem 2rem;background:var(--paper);color:var(--ink);font-family:var(--sans)}.frame{width:100%;max-width:1200px}
.eyebrow{margin-bottom:.5rem;color:var(--muted);font:500 .66rem/1 var(--mono);letter-spacing:.18em;text-transform:uppercase}h1{margin-bottom:.35rem;font:400 clamp(1.5rem,2.4vw + .75rem,2rem)/1.15 var(--serif);letter-spacing:-.02em}.subtitle{margin-bottom:1.5rem;color:var(--muted);font-size:.9rem;line-height:1.5}.diagram{overflow-x:auto}svg{display:block;width:100%;min-width:1040px}
svg .component-head,svg .component{fill:rgba(245,245,245,.06);stroke:rgba(245,245,245,.18);stroke-width:.8}svg .role{fill:#f5f5f5}
svg .head-name,svg .component-name,svg .role-name,svg .value{font-family:'Geist',sans-serif}svg .head-name,svg .component-name,svg .role-name{font-size:11px;font-weight:600}svg .head-name,svg .component-name{fill:#f5f5f5}svg .role-name,svg .role-code{fill:#2d3142}
svg .head-sub,svg .role-code,svg .hint,svg .legend-text,svg .focal-sub{font-family:'Geist Mono',monospace}svg .head-sub,svg .hint,svg .legend-text{fill:#8e98ac}svg .head-sub,svg .role-code,svg .hint{font-size:9px}svg .role-code{opacity:.85}
svg .cell{stroke:rgba(245,245,245,.18);stroke-width:.6}svg .full{fill:rgba(245,245,245,.10)}svg .write{fill:rgba(245,245,245,.06)}svg .read{fill:rgba(142,152,172,.12)}svg .none{fill:rgba(245,245,245,.02)}svg .value{font-size:10px}svg .value.full-text{fill:#f5f5f5;font-weight:600}svg .value.write-text{fill:#f5f5f5}svg .value.read-text{fill:#8e98ac}svg .value.none-text{fill:#bfc0c0}
svg .focal{fill:rgba(240,138,89,.12);stroke:#f08a59;stroke-width:1.4}svg .focal-text{fill:#f08a59;font-weight:600}svg .focal-sub{fill:#f08a59;font-size:8px;opacity:.85}svg .legend-text{font-size:9px}
</style>
</head>
<body><main class="frame"><p class="eyebrow">DP security matrix · Diagram Design</p><h1>Data platform access matrix</h1><p class="subtitle">Five operating roles × five platform components, expressed as admin, write, read, or none.</p><div class="diagram">
<svg viewBox="0 0 1084 400" role="img" aria-labelledby="matrix-dark-title matrix-dark-desc" xmlns="http://www.w3.org/2000/svg">
<title id="matrix-dark-title">Data platform access matrix</title><desc id="matrix-dark-desc">A five by five permission matrix for platform roles and components.</desc><rect width="1084" height="400" fill="#2d3142"/>
<rect class="component-head" x="12" y="72" width="208" height="52" rx="6"/><text class="head-name" x="116" y="96" text-anchor="middle">Component</text><text class="head-sub" x="116" y="112" text-anchor="middle">vs. AD group</text>
<rect class="role" x="232" y="72" width="148" height="52" rx="6"/><text class="role-name" x="306" y="92" text-anchor="middle">Data Engineer</text><text class="role-code" x="306" y="108" text-anchor="middle">GRP-DATA-ENG</text><rect class="role" x="396" y="72" width="148" height="52" rx="6"/><text class="role-name" x="470" y="92" text-anchor="middle">Data Scientist</text><text class="role-code" x="470" y="108" text-anchor="middle">GRP-DATA-SCI</text><rect class="role" x="560" y="72" width="148" height="52" rx="6"/><text class="role-name" x="634" y="92" text-anchor="middle">Analyst</text><text class="role-code" x="634" y="108" text-anchor="middle">GRP-ANALYST</text><rect class="role" x="724" y="72" width="148" height="52" rx="6"/><text class="role-name" x="798" y="92" text-anchor="middle">Administrator</text><text class="role-code" x="798" y="108" text-anchor="middle">GRP-ADMIN</text><rect class="role" x="888" y="72" width="148" height="52" rx="6"/><text class="role-name" x="962" y="92" text-anchor="middle">External Partner</text><text class="role-code" x="962" y="108" text-anchor="middle">GRP-PARTNER</text>
<rect class="component" x="12" y="140" width="208" height="36" rx="4"/><text class="component-name" x="24" y="162">Object storage</text><text class="hint" x="208" y="162" text-anchor="end">S3</text><rect class="cell write" x="232" y="140" width="148" height="36" rx="4"/><text class="value write-text" x="306" y="162" text-anchor="middle">Write</text><rect class="cell read" x="396" y="140" width="148" height="36" rx="4"/><text class="value read-text" x="470" y="162" text-anchor="middle">Read</text><rect class="cell none" x="560" y="140" width="148" height="36" rx="4"/><text class="value none-text" x="634" y="162" text-anchor="middle">None</text><rect class="cell full" x="724" y="140" width="148" height="36" rx="4"/><text class="value full-text" x="798" y="162" text-anchor="middle">Admin</text><rect class="cell none" x="888" y="140" width="148" height="36" rx="4"/><text class="value none-text" x="962" y="162" text-anchor="middle">None</text>
<rect class="component" x="12" y="180" width="208" height="36" rx="4"/><text class="component-name" x="24" y="202">Query engine</text><text class="hint" x="208" y="202" text-anchor="end">SQL</text><rect class="cell write" x="232" y="180" width="148" height="36" rx="4"/><text class="value write-text" x="306" y="202" text-anchor="middle">Write</text><rect class="cell read" x="396" y="180" width="148" height="36" rx="4"/><text class="value read-text" x="470" y="202" text-anchor="middle">Read</text><rect class="cell read" x="560" y="180" width="148" height="36" rx="4"/><text class="value read-text" x="634" y="202" text-anchor="middle">Read</text><rect class="cell full" x="724" y="180" width="148" height="36" rx="4"/><text class="value full-text" x="798" y="202" text-anchor="middle">Admin</text><rect class="cell read" x="888" y="180" width="148" height="36" rx="4"/><text class="value read-text" x="962" y="202" text-anchor="middle">Read</text>
<rect class="component" x="12" y="220" width="208" height="36" rx="4"/><text class="component-name" x="24" y="242">Notebooks</text><text class="hint" x="208" y="242" text-anchor="end">PY</text><rect class="cell write" x="232" y="220" width="148" height="36" rx="4"/><text class="value write-text" x="306" y="242" text-anchor="middle">Write</text><rect class="cell write" x="396" y="220" width="148" height="36" rx="4"/><text class="value write-text" x="470" y="242" text-anchor="middle">Write</text><rect class="cell none" x="560" y="220" width="148" height="36" rx="4"/><text class="value none-text" x="634" y="242" text-anchor="middle">None</text><rect class="cell full" x="724" y="220" width="148" height="36" rx="4"/><text class="value full-text" x="798" y="242" text-anchor="middle">Admin</text><rect class="cell none" x="888" y="220" width="148" height="36" rx="4"/><text class="value none-text" x="962" y="242" text-anchor="middle">None</text>
<rect class="component" x="12" y="260" width="208" height="36" rx="4"/><text class="component-name" x="24" y="282">BI tool</text><text class="hint" x="208" y="282" text-anchor="end">DASH</text><rect class="cell write" x="232" y="260" width="148" height="36" rx="4"/><text class="value write-text" x="306" y="282" text-anchor="middle">Write</text><rect class="cell read" x="396" y="260" width="148" height="36" rx="4"/><text class="value read-text" x="470" y="282" text-anchor="middle">Read</text><rect class="cell write" x="560" y="260" width="148" height="36" rx="4"/><text class="value write-text" x="634" y="282" text-anchor="middle">Write</text><rect class="cell full" x="724" y="260" width="148" height="36" rx="4"/><text class="value full-text" x="798" y="282" text-anchor="middle">Admin</text><rect class="focal" x="888" y="260" width="148" height="36" rx="4"/><text class="value focal-text" x="962" y="278" text-anchor="middle">Read</text><text class="focal-sub" x="962" y="290" text-anchor="middle">shared dashboards</text>
<rect class="component" x="12" y="300" width="208" height="36" rx="4"/><text class="component-name" x="24" y="322">Orchestrator</text><text class="hint" x="208" y="322" text-anchor="end">DAG</text><rect class="cell write" x="232" y="300" width="148" height="36" rx="4"/><text class="value write-text" x="306" y="322" text-anchor="middle">Write</text><rect class="cell read" x="396" y="300" width="148" height="36" rx="4"/><text class="value read-text" x="470" y="322" text-anchor="middle">Read</text><rect class="cell none" x="560" y="300" width="148" height="36" rx="4"/><text class="value none-text" x="634" y="322" text-anchor="middle">None</text><rect class="cell full" x="724" y="300" width="148" height="36" rx="4"/><text class="value full-text" x="798" y="322" text-anchor="middle">Admin</text><rect class="cell none" x="888" y="300" width="148" height="36" rx="4"/><text class="value none-text" x="962" y="322" text-anchor="middle">None</text>
<line x1="12" y1="356" x2="1072" y2="356" stroke="rgba(245,245,245,.12)" stroke-width=".8"/><text class="legend-text" x="12" y="380" letter-spacing=".14em">LEGEND</text><rect class="cell full" x="96" y="368" width="16" height="12" rx="2"/><text class="legend-text" x="120" y="380">Admin</text><rect class="cell write" x="200" y="368" width="16" height="12" rx="2"/><text class="legend-text" x="224" y="380">Write</text><rect class="cell read" x="304" y="368" width="16" height="12" rx="2"/><text class="legend-text" x="328" y="380">Read</text><rect class="cell none" x="408" y="368" width="16" height="12" rx="2"/><text class="legend-text" x="432" y="380">None</text><rect class="focal" x="504" y="368" width="16" height="12" rx="2"/><text class="legend-text" x="528" y="380">partner read boundary</text>
</svg>
</div></main></body>
</html>
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Data Platform · Access Matrix · Editorial</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&amp;family=Geist:wght@400;500;600&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet">
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}:root{--paper:#f5f5f5;--paper-2:#ececec;--ink:#2d3142;--muted:#4f5d75;--soft:#7a8399;--rule:rgba(45,49,66,.12);--accent:#eb6c36;--sans:'Geist',system-ui,sans-serif;--serif:'Instrument Serif',serif;--mono:'Geist Mono',ui-monospace,monospace}
body{min-height:100vh;padding:3rem 2rem;background:var(--paper);color:var(--ink);font-family:var(--sans)}.container{max-width:1200px;margin:0 auto}.header{margin-bottom:2.5rem}.header-eyebrow,.card .eyebrow{color:var(--muted);font-family:var(--mono);font-weight:500;letter-spacing:.18em;text-transform:uppercase}.header-eyebrow{margin-bottom:.75rem;font-size:.66rem}h1{margin-bottom:.5rem;font:400 clamp(1.75rem,3vw + 1rem,2.5rem)/1.1 var(--serif);letter-spacing:-.02em}.subtitle{max-width:68ch;color:var(--muted);font-size:1rem;line-height:1.55}
.diagram-container{overflow-x:auto;padding:1.5rem;background:var(--paper-2);border:1px solid var(--rule);border-radius:8px}svg{display:block;width:100%;min-width:1040px}
svg .component-head,svg .component{fill:#fff;stroke:rgba(45,49,66,.12);stroke-width:.8}svg .role{fill:#2d3142}svg .head-name,svg .component-name,svg .role-name,svg .value{font-family:'Geist',sans-serif}svg .head-name,svg .component-name,svg .role-name{font-size:11px;font-weight:600}svg .head-name,svg .component-name{fill:#2d3142}svg .role-name,svg .role-code{fill:#f5f5f5}svg .head-sub,svg .role-code,svg .hint,svg .legend-text,svg .focal-sub{font-family:'Geist Mono',monospace}svg .head-sub,svg .hint,svg .legend-text{fill:#4f5d75}svg .head-sub,svg .role-code,svg .hint{font-size:9px}svg .role-code{opacity:.85}
svg .cell{stroke:rgba(45,49,66,.12);stroke-width:.6}svg .full{fill:rgba(45,49,66,.08)}svg .write{fill:#fff}svg .read{fill:rgba(79,93,117,.08)}svg .none{fill:#f5f5f5}svg .value{font-size:10px}svg .value.full-text{fill:#2d3142;font-weight:600}svg .value.write-text{fill:#2d3142}svg .value.read-text{fill:#4f5d75}svg .value.none-text{fill:#7a8399}svg .focal{fill:rgba(235,108,54,.07);stroke:#eb6c36;stroke-width:1.4}svg .focal-text{fill:#eb6c36;font-weight:600}svg .focal-sub{fill:#eb6c36;font-size:8px;opacity:.85}svg .legend-text{font-size:9px}
.cards{display:grid;grid-template-columns:1.1fr 1fr .9fr;gap:1rem;margin-top:1.5rem}@media(max-width:820px){.cards{grid-template-columns:1fr}}.card{padding:1.25rem;background:#fff;border:1px solid var(--rule);border-radius:6px}.card .eyebrow{margin-bottom:.5rem;font-size:.5rem}.card-header{display:flex;align-items:center;gap:.6rem;margin-bottom:.875rem;padding-bottom:.875rem;border-bottom:1px solid rgba(45,49,66,.08)}.card-dot{width:7px;height:7px;flex:0 0 auto;border-radius:50%}.card-dot.coral{background:var(--accent)}.card-dot.ink{background:var(--ink)}.card-dot.muted{background:var(--muted)}.card h2{font-size:.875rem;font-weight:600}.card p,.card ul{color:var(--muted);font-size:.8125rem;line-height:1.55;list-style:none}.card li{position:relative;margin-bottom:.3rem;padding-left:.875rem}.card li::before{content:'—';position:absolute;left:0;color:rgba(45,49,66,.25)}.footer{display:flex;justify-content:space-between;flex-wrap:wrap;gap:.5rem;margin-top:2rem;padding-top:1.5rem;color:var(--soft);border-top:1px solid rgba(45,49,66,.10);font:400 .72rem/1.4 var(--mono);letter-spacing:.06em}
</style>
</head>
<body><main class="container"><header class="header"><p class="header-eyebrow">DP security matrix · Diagram Design</p><h1>Data platform access matrix</h1><p class="subtitle">A compact policy view for five human roles across storage, query, analysis, reporting, and orchestration. Each intersection resolves to one explicit permission level.</p></header><div class="diagram-container">
<svg viewBox="0 0 1084 400" role="img" aria-labelledby="matrix-full-title matrix-full-desc" xmlns="http://www.w3.org/2000/svg">
<title id="matrix-full-title">Data platform access matrix</title><desc id="matrix-full-desc">A five by five permission matrix for platform roles and components.</desc><rect width="1084" height="400" fill="#f5f5f5"/>
<rect class="component-head" x="12" y="72" width="208" height="52" rx="6"/><text class="head-name" x="116" y="96" text-anchor="middle">Component</text><text class="head-sub" x="116" y="112" text-anchor="middle">vs. AD group</text>
<rect class="role" x="232" y="72" width="148" height="52" rx="6"/><text class="role-name" x="306" y="92" text-anchor="middle">Data Engineer</text><text class="role-code" x="306" y="108" text-anchor="middle">GRP-DATA-ENG</text><rect class="role" x="396" y="72" width="148" height="52" rx="6"/><text class="role-name" x="470" y="92" text-anchor="middle">Data Scientist</text><text class="role-code" x="470" y="108" text-anchor="middle">GRP-DATA-SCI</text><rect class="role" x="560" y="72" width="148" height="52" rx="6"/><text class="role-name" x="634" y="92" text-anchor="middle">Analyst</text><text class="role-code" x="634" y="108" text-anchor="middle">GRP-ANALYST</text><rect class="role" x="724" y="72" width="148" height="52" rx="6"/><text class="role-name" x="798" y="92" text-anchor="middle">Administrator</text><text class="role-code" x="798" y="108" text-anchor="middle">GRP-ADMIN</text><rect class="role" x="888" y="72" width="148" height="52" rx="6"/><text class="role-name" x="962" y="92" text-anchor="middle">External Partner</text><text class="role-code" x="962" y="108" text-anchor="middle">GRP-PARTNER</text>
<rect class="component" x="12" y="140" width="208" height="36" rx="4"/><text class="component-name" x="24" y="162">Object storage</text><text class="hint" x="208" y="162" text-anchor="end">S3</text><rect class="cell write" x="232" y="140" width="148" height="36" rx="4"/><text class="value write-text" x="306" y="162" text-anchor="middle">Write</text><rect class="cell read" x="396" y="140" width="148" height="36" rx="4"/><text class="value read-text" x="470" y="162" text-anchor="middle">Read</text><rect class="cell none" x="560" y="140" width="148" height="36" rx="4"/><text class="value none-text" x="634" y="162" text-anchor="middle">None</text><rect class="cell full" x="724" y="140" width="148" height="36" rx="4"/><text class="value full-text" x="798" y="162" text-anchor="middle">Admin</text><rect class="cell none" x="888" y="140" width="148" height="36" rx="4"/><text class="value none-text" x="962" y="162" text-anchor="middle">None</text>
<rect class="component" x="12" y="180" width="208" height="36" rx="4"/><text class="component-name" x="24" y="202">Query engine</text><text class="hint" x="208" y="202" text-anchor="end">SQL</text><rect class="cell write" x="232" y="180" width="148" height="36" rx="4"/><text class="value write-text" x="306" y="202" text-anchor="middle">Write</text><rect class="cell read" x="396" y="180" width="148" height="36" rx="4"/><text class="value read-text" x="470" y="202" text-anchor="middle">Read</text><rect class="cell read" x="560" y="180" width="148" height="36" rx="4"/><text class="value read-text" x="634" y="202" text-anchor="middle">Read</text><rect class="cell full" x="724" y="180" width="148" height="36" rx="4"/><text class="value full-text" x="798" y="202" text-anchor="middle">Admin</text><rect class="cell read" x="888" y="180" width="148" height="36" rx="4"/><text class="value read-text" x="962" y="202" text-anchor="middle">Read</text>
<rect class="component" x="12" y="220" width="208" height="36" rx="4"/><text class="component-name" x="24" y="242">Notebooks</text><text class="hint" x="208" y="242" text-anchor="end">PY</text><rect class="cell write" x="232" y="220" width="148" height="36" rx="4"/><text class="value write-text" x="306" y="242" text-anchor="middle">Write</text><rect class="cell write" x="396" y="220" width="148" height="36" rx="4"/><text class="value write-text" x="470" y="242" text-anchor="middle">Write</text><rect class="cell none" x="560" y="220" width="148" height="36" rx="4"/><text class="value none-text" x="634" y="242" text-anchor="middle">None</text><rect class="cell full" x="724" y="220" width="148" height="36" rx="4"/><text class="value full-text" x="798" y="242" text-anchor="middle">Admin</text><rect class="cell none" x="888" y="220" width="148" height="36" rx="4"/><text class="value none-text" x="962" y="242" text-anchor="middle">None</text>
<rect class="component" x="12" y="260" width="208" height="36" rx="4"/><text class="component-name" x="24" y="282">BI tool</text><text class="hint" x="208" y="282" text-anchor="end">DASH</text><rect class="cell write" x="232" y="260" width="148" height="36" rx="4"/><text class="value write-text" x="306" y="282" text-anchor="middle">Write</text><rect class="cell read" x="396" y="260" width="148" height="36" rx="4"/><text class="value read-text" x="470" y="282" text-anchor="middle">Read</text><rect class="cell write" x="560" y="260" width="148" height="36" rx="4"/><text class="value write-text" x="634" y="282" text-anchor="middle">Write</text><rect class="cell full" x="724" y="260" width="148" height="36" rx="4"/><text class="value full-text" x="798" y="282" text-anchor="middle">Admin</text><rect class="focal" x="888" y="260" width="148" height="36" rx="4"/><text class="value focal-text" x="962" y="278" text-anchor="middle">Read</text><text class="focal-sub" x="962" y="290" text-anchor="middle">shared dashboards</text>
<rect class="component" x="12" y="300" width="208" height="36" rx="4"/><text class="component-name" x="24" y="322">Orchestrator</text><text class="hint" x="208" y="322" text-anchor="end">DAG</text><rect class="cell write" x="232" y="300" width="148" height="36" rx="4"/><text class="value write-text" x="306" y="322" text-anchor="middle">Write</text><rect class="cell read" x="396" y="300" width="148" height="36" rx="4"/><text class="value read-text" x="470" y="322" text-anchor="middle">Read</text><rect class="cell none" x="560" y="300" width="148" height="36" rx="4"/><text class="value none-text" x="634" y="322" text-anchor="middle">None</text><rect class="cell full" x="724" y="300" width="148" height="36" rx="4"/><text class="value full-text" x="798" y="322" text-anchor="middle">Admin</text><rect class="cell none" x="888" y="300" width="148" height="36" rx="4"/><text class="value none-text" x="962" y="322" text-anchor="middle">None</text>
<line x1="12" y1="356" x2="1072" y2="356" stroke="rgba(45,49,66,.12)" stroke-width=".8"/><text class="legend-text" x="12" y="380" letter-spacing=".14em">LEGEND</text><rect class="cell full" x="96" y="368" width="16" height="12" rx="2"/><text class="legend-text" x="120" y="380">Admin</text><rect class="cell write" x="200" y="368" width="16" height="12" rx="2"/><text class="legend-text" x="224" y="380">Write</text><rect class="cell read" x="304" y="368" width="16" height="12" rx="2"/><text class="legend-text" x="328" y="380">Read</text><rect class="cell none" x="408" y="368" width="16" height="12" rx="2"/><text class="legend-text" x="432" y="380">None</text><rect class="focal" x="504" y="368" width="16" height="12" rx="2"/><text class="legend-text" x="528" y="380">partner read boundary</text>
</svg>
</div>
<section class="cards" aria-label="Access policy summary"><article class="card"><p class="eyebrow">THE HEADLINE</p><div class="card-header"><span class="card-dot coral"></span><h2>Partner access stops at BI</h2></div><p>External partners receive read-only shared dashboards. They cannot enter storage, notebooks, or orchestration.</p></article><article class="card"><div class="card-header"><span class="card-dot ink"></span><h2>Administration stays separate</h2></div><ul><li>Administrators hold admin rights everywhere.</li><li>Engineers write across the platform.</li><li>Scientists write only in notebooks.</li></ul></article><article class="card"><div class="card-header"><span class="card-dot muted"></span><h2>Analyst scope is narrow</h2></div><p>Analysts query governed data and author BI content, while object storage, notebooks, and workflow controls remain unavailable.</p></article></section>
<footer class="footer"><span>data platform · access policy</span><span>example · diagram design</span></footer>
</main></body>
</html>
@@ -0,0 +1,88 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Data Platform · Access Matrix</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&amp;family=Geist:wght@400;500;600&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet">
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{--paper:#f5f5f5;--ink:#2d3142;--muted:#4f5d75;--soft:#7a8399;--rule:rgba(45,49,66,.12);--accent:#eb6c36;--sans:'Geist',system-ui,sans-serif;--serif:'Instrument Serif',serif;--mono:'Geist Mono',ui-monospace,monospace}
body{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:3rem 2rem;background:var(--paper);color:var(--ink);font-family:var(--sans)}
.frame{width:100%;max-width:1200px}.eyebrow{margin-bottom:.5rem;color:var(--muted);font:500 .66rem/1 var(--mono);letter-spacing:.18em;text-transform:uppercase}
h1{margin-bottom:.35rem;font:400 clamp(1.5rem,2.4vw + .75rem,2rem)/1.15 var(--serif);letter-spacing:-.02em}.subtitle{margin-bottom:1.5rem;color:var(--muted);font-size:.9rem;line-height:1.5}
.diagram{overflow-x:auto}svg{display:block;width:100%;min-width:1040px}
svg .component-head,svg .component{fill:#fff;stroke:rgba(45,49,66,.12);stroke-width:.8}svg .role{fill:#2d3142}
svg .head-name,svg .component-name,svg .role-name,svg .value{font-family:'Geist',sans-serif}svg .head-name,svg .component-name,svg .role-name{font-size:11px;font-weight:600}
svg .head-name,svg .component-name{fill:#2d3142}svg .role-name,svg .role-code{fill:#f5f5f5}svg .head-sub,svg .role-code,svg .hint,svg .legend-text,svg .focal-sub{font-family:'Geist Mono',monospace}
svg .head-sub,svg .hint,svg .legend-text{fill:#4f5d75}svg .head-sub,svg .role-code,svg .hint{font-size:9px}svg .role-code{opacity:.85}
svg .cell{stroke:rgba(45,49,66,.12);stroke-width:.6}svg .full{fill:rgba(45,49,66,.08)}svg .write{fill:#fff}svg .read{fill:rgba(79,93,117,.08)}svg .none{fill:#f5f5f5}
svg .value{font-size:10px}svg .value.full-text{fill:#2d3142;font-weight:600}svg .value.write-text{fill:#2d3142}svg .value.read-text{fill:#4f5d75}svg .value.none-text{fill:#7a8399}
svg .focal{fill:rgba(235,108,54,.07);stroke:#eb6c36;stroke-width:1.4}svg .focal-text{fill:#eb6c36;font-weight:600}svg .focal-sub{fill:#eb6c36;font-size:8px;opacity:.85}
svg .legend-text{font-size:9px}
</style>
</head>
<body>
<main class="frame">
<p class="eyebrow">DP security matrix · Diagram Design</p><h1>Data platform access matrix</h1><p class="subtitle">Five operating roles × five platform components, expressed as admin, write, read, or none.</p>
<div class="diagram">
<svg viewBox="0 0 1084 400" role="img" aria-labelledby="matrix-title matrix-desc" xmlns="http://www.w3.org/2000/svg">
<title id="matrix-title">Data platform access matrix</title><desc id="matrix-desc">A five by five permission matrix for platform roles and components.</desc>
<rect width="1084" height="400" fill="#f5f5f5"/>
<!-- Header: y=72, h=52; role x positions derive from the 148px column formula -->
<rect class="component-head" x="12" y="72" width="208" height="52" rx="6"/><text class="head-name" x="116" y="96" text-anchor="middle">Component</text><text class="head-sub" x="116" y="112" text-anchor="middle">vs. AD group</text>
<rect class="role" x="232" y="72" width="148" height="52" rx="6"/><text class="role-name" x="306" y="92" text-anchor="middle">Data Engineer</text><text class="role-code" x="306" y="108" text-anchor="middle">GRP-DATA-ENG</text>
<rect class="role" x="396" y="72" width="148" height="52" rx="6"/><text class="role-name" x="470" y="92" text-anchor="middle">Data Scientist</text><text class="role-code" x="470" y="108" text-anchor="middle">GRP-DATA-SCI</text>
<rect class="role" x="560" y="72" width="148" height="52" rx="6"/><text class="role-name" x="634" y="92" text-anchor="middle">Analyst</text><text class="role-code" x="634" y="108" text-anchor="middle">GRP-ANALYST</text>
<rect class="role" x="724" y="72" width="148" height="52" rx="6"/><text class="role-name" x="798" y="92" text-anchor="middle">Administrator</text><text class="role-code" x="798" y="108" text-anchor="middle">GRP-ADMIN</text>
<rect class="role" x="888" y="72" width="148" height="52" rx="6"/><text class="role-name" x="962" y="92" text-anchor="middle">External Partner</text><text class="role-code" x="962" y="108" text-anchor="middle">GRP-PARTNER</text>
<!-- Object storage, y=140 -->
<rect class="component" x="12" y="140" width="208" height="36" rx="4"/><text class="component-name" x="24" y="162">Object storage</text><text class="hint" x="208" y="162" text-anchor="end">S3</text>
<rect class="cell write" x="232" y="140" width="148" height="36" rx="4"/><text class="value write-text" x="306" y="162" text-anchor="middle">Write</text>
<rect class="cell read" x="396" y="140" width="148" height="36" rx="4"/><text class="value read-text" x="470" y="162" text-anchor="middle">Read</text>
<rect class="cell none" x="560" y="140" width="148" height="36" rx="4"/><text class="value none-text" x="634" y="162" text-anchor="middle">None</text>
<rect class="cell full" x="724" y="140" width="148" height="36" rx="4"/><text class="value full-text" x="798" y="162" text-anchor="middle">Admin</text>
<rect class="cell none" x="888" y="140" width="148" height="36" rx="4"/><text class="value none-text" x="962" y="162" text-anchor="middle">None</text>
<!-- Query engine, y=180 -->
<rect class="component" x="12" y="180" width="208" height="36" rx="4"/><text class="component-name" x="24" y="202">Query engine</text><text class="hint" x="208" y="202" text-anchor="end">SQL</text>
<rect class="cell write" x="232" y="180" width="148" height="36" rx="4"/><text class="value write-text" x="306" y="202" text-anchor="middle">Write</text>
<rect class="cell read" x="396" y="180" width="148" height="36" rx="4"/><text class="value read-text" x="470" y="202" text-anchor="middle">Read</text>
<rect class="cell read" x="560" y="180" width="148" height="36" rx="4"/><text class="value read-text" x="634" y="202" text-anchor="middle">Read</text>
<rect class="cell full" x="724" y="180" width="148" height="36" rx="4"/><text class="value full-text" x="798" y="202" text-anchor="middle">Admin</text>
<rect class="cell read" x="888" y="180" width="148" height="36" rx="4"/><text class="value read-text" x="962" y="202" text-anchor="middle">Read</text>
<!-- Notebooks, y=220 -->
<rect class="component" x="12" y="220" width="208" height="36" rx="4"/><text class="component-name" x="24" y="242">Notebooks</text><text class="hint" x="208" y="242" text-anchor="end">PY</text>
<rect class="cell write" x="232" y="220" width="148" height="36" rx="4"/><text class="value write-text" x="306" y="242" text-anchor="middle">Write</text>
<rect class="cell write" x="396" y="220" width="148" height="36" rx="4"/><text class="value write-text" x="470" y="242" text-anchor="middle">Write</text>
<rect class="cell none" x="560" y="220" width="148" height="36" rx="4"/><text class="value none-text" x="634" y="242" text-anchor="middle">None</text>
<rect class="cell full" x="724" y="220" width="148" height="36" rx="4"/><text class="value full-text" x="798" y="242" text-anchor="middle">Admin</text>
<rect class="cell none" x="888" y="220" width="148" height="36" rx="4"/><text class="value none-text" x="962" y="242" text-anchor="middle">None</text>
<!-- BI tool, y=260; one focal access boundary -->
<rect class="component" x="12" y="260" width="208" height="36" rx="4"/><text class="component-name" x="24" y="282">BI tool</text><text class="hint" x="208" y="282" text-anchor="end">DASH</text>
<rect class="cell write" x="232" y="260" width="148" height="36" rx="4"/><text class="value write-text" x="306" y="282" text-anchor="middle">Write</text>
<rect class="cell read" x="396" y="260" width="148" height="36" rx="4"/><text class="value read-text" x="470" y="282" text-anchor="middle">Read</text>
<rect class="cell write" x="560" y="260" width="148" height="36" rx="4"/><text class="value write-text" x="634" y="282" text-anchor="middle">Write</text>
<rect class="cell full" x="724" y="260" width="148" height="36" rx="4"/><text class="value full-text" x="798" y="282" text-anchor="middle">Admin</text>
<rect class="focal" x="888" y="260" width="148" height="36" rx="4"/><text class="value focal-text" x="962" y="278" text-anchor="middle">Read</text><text class="focal-sub" x="962" y="290" text-anchor="middle">shared dashboards</text>
<!-- Orchestrator, y=300 -->
<rect class="component" x="12" y="300" width="208" height="36" rx="4"/><text class="component-name" x="24" y="322">Orchestrator</text><text class="hint" x="208" y="322" text-anchor="end">DAG</text>
<rect class="cell write" x="232" y="300" width="148" height="36" rx="4"/><text class="value write-text" x="306" y="322" text-anchor="middle">Write</text>
<rect class="cell read" x="396" y="300" width="148" height="36" rx="4"/><text class="value read-text" x="470" y="322" text-anchor="middle">Read</text>
<rect class="cell none" x="560" y="300" width="148" height="36" rx="4"/><text class="value none-text" x="634" y="322" text-anchor="middle">None</text>
<rect class="cell full" x="724" y="300" width="148" height="36" rx="4"/><text class="value full-text" x="798" y="322" text-anchor="middle">Admin</text>
<rect class="cell none" x="888" y="300" width="148" height="36" rx="4"/><text class="value none-text" x="962" y="322" text-anchor="middle">None</text>
<!-- Legend: rows_bottom=336, legend_y_top=356 -->
<line x1="12" y1="356" x2="1072" y2="356" stroke="rgba(45,49,66,.12)" stroke-width=".8"/><text class="legend-text" x="12" y="380" letter-spacing=".14em">LEGEND</text>
<rect class="cell full" x="96" y="368" width="16" height="12" rx="2"/><text class="legend-text" x="120" y="380">Admin</text><rect class="cell write" x="200" y="368" width="16" height="12" rx="2"/><text class="legend-text" x="224" y="380">Write</text><rect class="cell read" x="304" y="368" width="16" height="12" rx="2"/><text class="legend-text" x="328" y="380">Read</text><rect class="cell none" x="408" y="368" width="16" height="12" rx="2"/><text class="legend-text" x="432" y="380">None</text><rect class="focal" x="504" y="368" width="16" height="12" rx="2"/><text class="legend-text" x="528" y="380">partner read boundary</text>
</svg>
</div>
</main>
</body>
</html>
@@ -73,7 +73,7 @@
<!-- 1. Author — Article (1:N "writes") -->
<line x1="260" y1="240" x2="400" y2="240" stroke="#bfc0c0" stroke-width="1" />
<!-- 2. Article — ArticleTag (1:N) -->
<line x1="640" y1="320" x2="780" y2="328" stroke="#bfc0c0" stroke-width="1" />
<path d="M 640,320 H 702 Q 710,320 710,328 H 780" fill="none" stroke="#bfc0c0" stroke-width="1"/>
<!-- 3. Tag — ArticleTag (1:N, vertical) -->
<line x1="880" y1="248" x2="880" y2="280" stroke="#bfc0c0" stroke-width="1" />
@@ -54,7 +54,7 @@
<!-- 1. Author — Article (1:N "writes") -->
<line x1="260" y1="240" x2="400" y2="240" stroke="#4f5d75" stroke-width="1" />
<!-- 2. Article — ArticleTag (1:N) -->
<line x1="640" y1="320" x2="780" y2="328" stroke="#4f5d75" stroke-width="1" />
<path d="M 640,320 H 702 Q 710,320 710,328 H 780" fill="none" stroke="#4f5d75" stroke-width="1"/>
<!-- 3. Tag — ArticleTag (1:N, vertical) -->
<line x1="880" y1="248" x2="880" y2="280" stroke="#4f5d75" stroke-width="1" />
+1 -1
View File
@@ -73,7 +73,7 @@
<!-- 1. Author — Article (1:N "writes") -->
<line x1="260" y1="240" x2="400" y2="240" stroke="#4f5d75" stroke-width="1" />
<!-- 2. Article — ArticleTag (1:N) -->
<line x1="640" y1="320" x2="780" y2="328" stroke="#4f5d75" stroke-width="1" />
<path d="M 640,320 H 702 Q 710,320 710,328 H 780" fill="none" stroke="#4f5d75" stroke-width="1"/>
<!-- 3. Tag — ArticleTag (1:N, vertical) -->
<line x1="880" y1="248" x2="880" y2="280" stroke="#4f5d75" stroke-width="1" />
@@ -0,0 +1,135 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Q2 product launch · Gantt · dark</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root { --color-paper:#f5f5f5; --color-ink:#2d3142; --color-muted:#4f5d75; --color-accent:#eb6c36; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
body { font-family: var(--font-sans); background: var(--color-paper); color: var(--color-ink); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 3rem 2rem; }
.frame { max-width: 1200px; width: 100%; }
.eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.15; margin-bottom: 1.5rem; }
svg { width: 100%; min-width: 760px; display: block; }
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Gantt · Diagram Design</p>
<h1>Q2 product launch · 12-week plan</h1>
<!-- viewBox 0 0 1000 500. Timeline x=200-956 = 756px / 12 weeks = 63px per week -->
<!-- Rows: label col x=20-196, timeline x=200-956 -->
<!-- Row height=40, bar h=24, bar top pad=8 -->
<!-- Week x = 200 + i*63, i=0..12 -->
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(245,245,245,0.10)"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- Phase zones (drawn before everything) -->
<!-- Phase 1 DISCOVERY: rows y=84,124 → zone y=72, h=84 -->
<rect x="12" y="72" width="976" height="84" rx="6" fill="rgba(245,245,245,0.03)" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<!-- Phase 2 DESIGN: rows y=184,224,264 → zone y=172, h=120 -->
<rect x="12" y="172" width="976" height="120" rx="6" fill="rgba(245,245,245,0.03)" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<!-- Phase 3 LAUNCH: rows y=324,364 → zone y=312, h=88 -->
<rect x="12" y="312" width="976" height="88" rx="6" fill="rgba(245,245,245,0.03)" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<!-- Month headers at y=40-56 -->
<text x="326" y="52" fill="#f5f5f5" font-size="9" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">April</text>
<text x="578" y="52" fill="#f5f5f5" font-size="9" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">May</text>
<text x="830" y="52" fill="#f5f5f5" font-size="9" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">June</text>
<!-- Month separator lines -->
<line x1="452" y1="36" x2="452" y2="416" stroke="rgba(245,245,245,0.08)" stroke-width="0.8" stroke-dasharray="3,3"/>
<line x1="704" y1="36" x2="704" y2="416" stroke="rgba(245,245,245,0.08)" stroke-width="0.8" stroke-dasharray="3,3"/>
<!-- Week gridlines (vertical, hairline) -->
<line x1="263" y1="60" x2="263" y2="416" stroke="rgba(245,245,245,0.05)" stroke-width="0.6"/>
<line x1="326" y1="60" x2="326" y2="416" stroke="rgba(245,245,245,0.05)" stroke-width="0.6"/>
<line x1="389" y1="60" x2="389" y2="416" stroke="rgba(245,245,245,0.05)" stroke-width="0.6"/>
<line x1="515" y1="60" x2="515" y2="416" stroke="rgba(245,245,245,0.05)" stroke-width="0.6"/>
<line x1="578" y1="60" x2="578" y2="416" stroke="rgba(245,245,245,0.05)" stroke-width="0.6"/>
<line x1="641" y1="60" x2="641" y2="416" stroke="rgba(245,245,245,0.05)" stroke-width="0.6"/>
<line x1="767" y1="60" x2="767" y2="416" stroke="rgba(245,245,245,0.05)" stroke-width="0.6"/>
<line x1="830" y1="60" x2="830" y2="416" stroke="rgba(245,245,245,0.05)" stroke-width="0.6"/>
<line x1="893" y1="60" x2="893" y2="416" stroke="rgba(245,245,245,0.05)" stroke-width="0.6"/>
<!-- Timeline header line -->
<line x1="200" y1="60" x2="956" y2="60" stroke="rgba(245,245,245,0.15)" stroke-width="0.8"/>
<!-- Left divider -->
<line x1="200" y1="36" x2="200" y2="416" stroke="rgba(245,245,245,0.15)" stroke-width="0.8"/>
<!-- Phase labels (eyebrow style) -->
<text x="20" y="85" fill="rgba(245,245,245,0.35)" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em">DISCOVERY</text>
<text x="20" y="185" fill="rgba(245,245,245,0.35)" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em">DESIGN</text>
<text x="20" y="325" fill="rgba(245,245,245,0.35)" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em">LAUNCH</text>
<!-- ─── TASK BARS ─── -->
<!-- W_x = 200 + week_index * 63 -->
<!-- Row 1 y=84: Market research W1-W3 (i=0..2, x=200, dur=2*63=126) -->
<rect x="20" y="92" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="108" fill="#f5f5f5" font-size="10" font-weight="600" font-family="'Geist', sans-serif">Market research</text>
<rect x="200" y="92" width="126" height="24" rx="4" fill="#f5f5f5"/>
<rect x="200" y="92" width="126" height="24" rx="4" fill="rgba(191,192,192,0.15)" stroke="#bfc0c0" stroke-width="1"/>
<!-- Row 2 y=124: User interviews W2-W4 (i=1..3, x=263, dur=2*63=126) -->
<rect x="20" y="132" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="148" fill="#f5f5f5" font-size="10" font-weight="600" font-family="'Geist', sans-serif">User interviews</text>
<rect x="263" y="132" width="126" height="24" rx="4" fill="#f5f5f5"/>
<rect x="263" y="132" width="126" height="24" rx="4" fill="rgba(191,192,192,0.15)" stroke="#bfc0c0" stroke-width="1"/>
<!-- Row 3 y=184: Wireframes W4-W6 (i=3..5, x=389, dur=2*63=126) -->
<rect x="20" y="192" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="208" fill="#f5f5f5" font-size="10" font-weight="600" font-family="'Geist', sans-serif">Wireframes</text>
<rect x="389" y="192" width="126" height="24" rx="4" fill="#f5f5f5"/>
<rect x="389" y="192" width="126" height="24" rx="4" fill="rgba(191,192,192,0.15)" stroke="#bfc0c0" stroke-width="1"/>
<!-- Row 4 y=224: Prototype W5-W8 (i=4..7, x=452, dur=3*63=189) -->
<rect x="20" y="232" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="248" fill="#f5f5f5" font-size="10" font-weight="600" font-family="'Geist', sans-serif">Prototype</text>
<rect x="452" y="232" width="189" height="24" rx="4" fill="#f5f5f5"/>
<rect x="452" y="232" width="189" height="24" rx="4" fill="rgba(191,192,192,0.15)" stroke="#bfc0c0" stroke-width="1"/>
<!-- Row 5 y=264 FOCAL: Design review W8-W9 (i=7..8, x=641, dur=63) -->
<rect x="20" y="272" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="288" fill="#f08a59" font-size="10" font-weight="600" font-family="'Geist', sans-serif">Design review</text>
<rect x="641" y="272" width="63" height="24" rx="4" fill="#f5f5f5"/>
<rect x="641" y="272" width="63" height="24" rx="4" fill="rgba(240,138,89,0.14)" stroke="#f08a59" stroke-width="1"/>
<text x="672" y="289" fill="#f08a59" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">GATE</text>
<!-- Row 6 y=324: Development W7-W11 (i=6..10, x=578, dur=4*63=252) -->
<rect x="20" y="332" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="348" fill="#f5f5f5" font-size="10" font-weight="600" font-family="'Geist', sans-serif">Development</text>
<rect x="578" y="332" width="252" height="24" rx="4" fill="#f5f5f5"/>
<rect x="578" y="332" width="252" height="24" rx="4" fill="rgba(191,192,192,0.15)" stroke="#bfc0c0" stroke-width="1"/>
<!-- Row 7 y=364: Beta testing W10-W12 (i=9..11, x=767, dur=2*63=126) -->
<rect x="20" y="372" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="388" fill="#f5f5f5" font-size="10" font-weight="600" font-family="'Geist', sans-serif">Beta testing</text>
<rect x="767" y="372" width="126" height="24" rx="4" fill="#f5f5f5"/>
<rect x="767" y="372" width="126" height="24" rx="4" fill="rgba(191,192,192,0.15)" stroke="#bfc0c0" stroke-width="1"/>
<!-- Legend -->
<line x1="40" y1="430" x2="960" y2="430" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<text x="40" y="446" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<rect x="40" y="456" width="20" height="12" rx="3" fill="rgba(240,138,89,0.14)" stroke="#f08a59" stroke-width="1"/>
<text x="68" y="466" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Design review · critical gate</text>
<rect x="280" y="456" width="20" height="12" rx="3" fill="rgba(191,192,192,0.15)" stroke="#bfc0c0" stroke-width="1"/>
<text x="308" y="466" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Task</text>
<rect x="368" y="456" width="20" height="12" rx="3" fill="rgba(245,245,245,0.03)" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<text x="396" y="466" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Phase</text>
</svg>
</div>
</body>
</html>
@@ -0,0 +1,125 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Q2 product launch · Gantt</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root { --color-paper:#f5f5f5; --color-paper-2:#ececec; --color-ink:#2d3142; --color-muted:#4f5d75; --color-soft:#7a8399; --color-rule:rgba(45,49,66,0.12); --color-accent:#eb6c36; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
.container { max-width: 1200px; margin: 0 auto; }
.header { margin-bottom: 2.5rem; }
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 58ch; }
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
svg { width: 100%; min-width: 760px; display: block; }
.cards { display: grid; grid-template-columns: 1.1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
@media (max-width: 720px) { .cards { grid-template-columns: 1fr; } }
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(45,49,66,0.08); }
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
.card-dot.coral { background: var(--color-accent); } .card-dot.ink { background: var(--color-ink); } .card-dot.muted { background: var(--color-muted); }
.card h3 { font-size: 0.875rem; font-weight: 600; }
.card p { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; }
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(45,49,66,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<p class="header-eyebrow">Gantt · Diagram Design</p>
<h1>Q2 product launch · 12-week plan</h1>
<p class="subtitle">Three phases from research through launch. Design review in week 8 is the critical gate — development doesn't continue until sign-off.</p>
</div>
<div class="diagram-container">
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
<defs><pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse"><circle cx="1" cy="1" r="0.9" fill="rgba(45,49,66,0.10)"/></pattern></defs>
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<rect x="12" y="72" width="976" height="84" rx="6" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<rect x="12" y="172" width="976" height="120" rx="6" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<rect x="12" y="312" width="976" height="88" rx="6" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="326" y="52" fill="#2d3142" font-size="9" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">April</text>
<text x="578" y="52" fill="#2d3142" font-size="9" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">May</text>
<text x="830" y="52" fill="#2d3142" font-size="9" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">June</text>
<line x1="452" y1="36" x2="452" y2="416" stroke="rgba(45,49,66,0.08)" stroke-width="0.8" stroke-dasharray="3,3"/>
<line x1="704" y1="36" x2="704" y2="416" stroke="rgba(45,49,66,0.08)" stroke-width="0.8" stroke-dasharray="3,3"/>
<line x1="263" y1="60" x2="263" y2="416" stroke="rgba(45,49,66,0.05)" stroke-width="0.6"/>
<line x1="326" y1="60" x2="326" y2="416" stroke="rgba(45,49,66,0.05)" stroke-width="0.6"/>
<line x1="389" y1="60" x2="389" y2="416" stroke="rgba(45,49,66,0.05)" stroke-width="0.6"/>
<line x1="515" y1="60" x2="515" y2="416" stroke="rgba(45,49,66,0.05)" stroke-width="0.6"/>
<line x1="578" y1="60" x2="578" y2="416" stroke="rgba(45,49,66,0.05)" stroke-width="0.6"/>
<line x1="641" y1="60" x2="641" y2="416" stroke="rgba(45,49,66,0.05)" stroke-width="0.6"/>
<line x1="767" y1="60" x2="767" y2="416" stroke="rgba(45,49,66,0.05)" stroke-width="0.6"/>
<line x1="830" y1="60" x2="830" y2="416" stroke="rgba(45,49,66,0.05)" stroke-width="0.6"/>
<line x1="893" y1="60" x2="893" y2="416" stroke="rgba(45,49,66,0.05)" stroke-width="0.6"/>
<line x1="200" y1="60" x2="956" y2="60" stroke="rgba(45,49,66,0.20)" stroke-width="0.8"/>
<line x1="200" y1="36" x2="200" y2="416" stroke="rgba(45,49,66,0.20)" stroke-width="0.8"/>
<text x="20" y="85" fill="rgba(45,49,66,0.40)" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em">DISCOVERY</text>
<text x="20" y="185" fill="rgba(45,49,66,0.40)" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em">DESIGN</text>
<text x="20" y="325" fill="rgba(45,49,66,0.40)" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em">LAUNCH</text>
<rect x="20" y="92" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="108" fill="#2d3142" font-size="10" font-weight="600" font-family="'Geist', sans-serif">Market research</text>
<rect x="200" y="92" width="126" height="24" rx="4" fill="#f5f5f5"/>
<rect x="200" y="92" width="126" height="24" rx="4" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<rect x="20" y="132" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="148" fill="#2d3142" font-size="10" font-weight="600" font-family="'Geist', sans-serif">User interviews</text>
<rect x="263" y="132" width="126" height="24" rx="4" fill="#f5f5f5"/>
<rect x="263" y="132" width="126" height="24" rx="4" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<rect x="20" y="192" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="208" fill="#2d3142" font-size="10" font-weight="600" font-family="'Geist', sans-serif">Wireframes</text>
<rect x="389" y="192" width="126" height="24" rx="4" fill="#f5f5f5"/>
<rect x="389" y="192" width="126" height="24" rx="4" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<rect x="20" y="232" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="248" fill="#2d3142" font-size="10" font-weight="600" font-family="'Geist', sans-serif">Prototype</text>
<rect x="452" y="232" width="189" height="24" rx="4" fill="#f5f5f5"/>
<rect x="452" y="232" width="189" height="24" rx="4" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<rect x="20" y="272" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="288" fill="#eb6c36" font-size="10" font-weight="600" font-family="'Geist', sans-serif">Design review</text>
<rect x="641" y="272" width="63" height="24" rx="4" fill="#f5f5f5"/>
<rect x="641" y="272" width="63" height="24" rx="4" fill="rgba(235,108,54,0.12)" stroke="#eb6c36" stroke-width="1"/>
<text x="672" y="289" fill="#eb6c36" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">GATE</text>
<rect x="20" y="332" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="348" fill="#2d3142" font-size="10" font-weight="600" font-family="'Geist', sans-serif">Development</text>
<rect x="578" y="332" width="252" height="24" rx="4" fill="#f5f5f5"/>
<rect x="578" y="332" width="252" height="24" rx="4" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<rect x="20" y="372" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="388" fill="#2d3142" font-size="10" font-weight="600" font-family="'Geist', sans-serif">Beta testing</text>
<rect x="767" y="372" width="126" height="24" rx="4" fill="#f5f5f5"/>
<rect x="767" y="372" width="126" height="24" rx="4" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<line x1="40" y1="430" x2="960" y2="430" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="40" y="446" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<rect x="40" y="456" width="20" height="12" rx="3" fill="rgba(235,108,54,0.12)" stroke="#eb6c36" stroke-width="1"/>
<text x="68" y="466" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Design review · critical gate</text>
<rect x="280" y="456" width="20" height="12" rx="3" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<text x="308" y="466" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Task</text>
<rect x="368" y="456" width="20" height="12" rx="3" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="396" y="466" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Phase</text>
</svg>
</div>
<div class="cards">
<div class="card">
<p class="eyebrow">CRITICAL GATE</p>
<div class="card-header"><span class="card-dot coral"></span><h3>Design review — week 8</h3></div>
<p>Development begins in week 7 but the full build is contingent on design sign-off. Starting before the gate means dev can fix implementation issues early, not rebuild from scratch post-review.</p>
</div>
<div class="card">
<div class="card-header"><span class="card-dot ink"></span><h3>Discovery ↔ Design overlap</h3></div>
<p>User interviews (W2–W4) overlap with wireframes (W4–W6) by one week. This is intentional — the first round of interview findings feeds directly into iteration without a formal handoff ceremony.</p>
</div>
<div class="card">
<div class="card-header"><span class="card-dot muted"></span><h3>Float in the schedule</h3></div>
<p>Beta testing (W10–W12) has two weeks of overlap with dev end (W11). If dev slips by one week, beta absorbs it. If dev slips two weeks, the launch date moves — there is no buffer beyond that.</p>
</div>
</div>
<div class="footer">
<span>Q2 product launch · 12-week plan</span>
<span>example · diagram design</span>
</div>
</div>
</body>
</html>
@@ -0,0 +1,135 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Q2 product launch · Gantt</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root { --color-paper:#f5f5f5; --color-ink:#2d3142; --color-muted:#4f5d75; --color-accent:#eb6c36; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
body { font-family: var(--font-sans); background: var(--color-paper); color: var(--color-ink); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 3rem 2rem; }
.frame { max-width: 1200px; width: 100%; }
.eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.15; margin-bottom: 1.5rem; }
svg { width: 100%; min-width: 760px; display: block; }
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Gantt · Diagram Design</p>
<h1>Q2 product launch · 12-week plan</h1>
<!-- viewBox 0 0 1000 500. Timeline x=200-956 = 756px / 12 weeks = 63px per week -->
<!-- Rows: label col x=20-196, timeline x=200-956 -->
<!-- Row height=40, bar h=24, bar top pad=8 -->
<!-- Week x = 200 + i*63, i=0..12 -->
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(45,49,66,0.10)"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- Phase zones (drawn before everything) -->
<!-- Phase 1 DISCOVERY: rows y=84,124 → zone y=72, h=84 -->
<rect x="12" y="72" width="976" height="84" rx="6" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<!-- Phase 2 DESIGN: rows y=184,224,264 → zone y=172, h=120 -->
<rect x="12" y="172" width="976" height="120" rx="6" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<!-- Phase 3 LAUNCH: rows y=324,364 → zone y=312, h=88 -->
<rect x="12" y="312" width="976" height="88" rx="6" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<!-- Month headers at y=40-56 -->
<text x="326" y="52" fill="#2d3142" font-size="9" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">April</text>
<text x="578" y="52" fill="#2d3142" font-size="9" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">May</text>
<text x="830" y="52" fill="#2d3142" font-size="9" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">June</text>
<!-- Month separator lines -->
<line x1="452" y1="36" x2="452" y2="416" stroke="rgba(45,49,66,0.08)" stroke-width="0.8" stroke-dasharray="3,3"/>
<line x1="704" y1="36" x2="704" y2="416" stroke="rgba(45,49,66,0.08)" stroke-width="0.8" stroke-dasharray="3,3"/>
<!-- Week gridlines (vertical, hairline) -->
<line x1="263" y1="60" x2="263" y2="416" stroke="rgba(45,49,66,0.05)" stroke-width="0.6"/>
<line x1="326" y1="60" x2="326" y2="416" stroke="rgba(45,49,66,0.05)" stroke-width="0.6"/>
<line x1="389" y1="60" x2="389" y2="416" stroke="rgba(45,49,66,0.05)" stroke-width="0.6"/>
<line x1="515" y1="60" x2="515" y2="416" stroke="rgba(45,49,66,0.05)" stroke-width="0.6"/>
<line x1="578" y1="60" x2="578" y2="416" stroke="rgba(45,49,66,0.05)" stroke-width="0.6"/>
<line x1="641" y1="60" x2="641" y2="416" stroke="rgba(45,49,66,0.05)" stroke-width="0.6"/>
<line x1="767" y1="60" x2="767" y2="416" stroke="rgba(45,49,66,0.05)" stroke-width="0.6"/>
<line x1="830" y1="60" x2="830" y2="416" stroke="rgba(45,49,66,0.05)" stroke-width="0.6"/>
<line x1="893" y1="60" x2="893" y2="416" stroke="rgba(45,49,66,0.05)" stroke-width="0.6"/>
<!-- Timeline header line -->
<line x1="200" y1="60" x2="956" y2="60" stroke="rgba(45,49,66,0.20)" stroke-width="0.8"/>
<!-- Left divider -->
<line x1="200" y1="36" x2="200" y2="416" stroke="rgba(45,49,66,0.20)" stroke-width="0.8"/>
<!-- Phase labels (eyebrow style) -->
<text x="20" y="85" fill="rgba(45,49,66,0.40)" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em">DISCOVERY</text>
<text x="20" y="185" fill="rgba(45,49,66,0.40)" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em">DESIGN</text>
<text x="20" y="325" fill="rgba(45,49,66,0.40)" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em">LAUNCH</text>
<!-- ─── TASK BARS ─── -->
<!-- W_x = 200 + week_index * 63 -->
<!-- Row 1 y=84: Market research W1-W3 (i=0..2, x=200, dur=2*63=126) -->
<rect x="20" y="92" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="108" fill="#2d3142" font-size="10" font-weight="600" font-family="'Geist', sans-serif">Market research</text>
<rect x="200" y="92" width="126" height="24" rx="4" fill="#f5f5f5"/>
<rect x="200" y="92" width="126" height="24" rx="4" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<!-- Row 2 y=124: User interviews W2-W4 (i=1..3, x=263, dur=2*63=126) -->
<rect x="20" y="132" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="148" fill="#2d3142" font-size="10" font-weight="600" font-family="'Geist', sans-serif">User interviews</text>
<rect x="263" y="132" width="126" height="24" rx="4" fill="#f5f5f5"/>
<rect x="263" y="132" width="126" height="24" rx="4" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<!-- Row 3 y=184: Wireframes W4-W6 (i=3..5, x=389, dur=2*63=126) -->
<rect x="20" y="192" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="208" fill="#2d3142" font-size="10" font-weight="600" font-family="'Geist', sans-serif">Wireframes</text>
<rect x="389" y="192" width="126" height="24" rx="4" fill="#f5f5f5"/>
<rect x="389" y="192" width="126" height="24" rx="4" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<!-- Row 4 y=224: Prototype W5-W8 (i=4..7, x=452, dur=3*63=189) -->
<rect x="20" y="232" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="248" fill="#2d3142" font-size="10" font-weight="600" font-family="'Geist', sans-serif">Prototype</text>
<rect x="452" y="232" width="189" height="24" rx="4" fill="#f5f5f5"/>
<rect x="452" y="232" width="189" height="24" rx="4" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<!-- Row 5 y=264 FOCAL: Design review W8-W9 (i=7..8, x=641, dur=63) -->
<rect x="20" y="272" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="288" fill="#eb6c36" font-size="10" font-weight="600" font-family="'Geist', sans-serif">Design review</text>
<rect x="641" y="272" width="63" height="24" rx="4" fill="#f5f5f5"/>
<rect x="641" y="272" width="63" height="24" rx="4" fill="rgba(235,108,54,0.12)" stroke="#eb6c36" stroke-width="1"/>
<text x="672" y="289" fill="#eb6c36" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">GATE</text>
<!-- Row 6 y=324: Development W7-W11 (i=6..10, x=578, dur=4*63=252) -->
<rect x="20" y="332" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="348" fill="#2d3142" font-size="10" font-weight="600" font-family="'Geist', sans-serif">Development</text>
<rect x="578" y="332" width="252" height="24" rx="4" fill="#f5f5f5"/>
<rect x="578" y="332" width="252" height="24" rx="4" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<!-- Row 7 y=364: Beta testing W10-W12 (i=9..11, x=767, dur=2*63=126) -->
<rect x="20" y="372" width="176" height="24" fill="#f5f5f5"/>
<text x="20" y="388" fill="#2d3142" font-size="10" font-weight="600" font-family="'Geist', sans-serif">Beta testing</text>
<rect x="767" y="372" width="126" height="24" rx="4" fill="#f5f5f5"/>
<rect x="767" y="372" width="126" height="24" rx="4" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<!-- Legend -->
<line x1="40" y1="430" x2="960" y2="430" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="40" y="446" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<rect x="40" y="456" width="20" height="12" rx="3" fill="rgba(235,108,54,0.12)" stroke="#eb6c36" stroke-width="1"/>
<text x="68" y="466" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Design review · critical gate</text>
<rect x="280" y="456" width="20" height="12" rx="3" fill="rgba(79,93,117,0.15)" stroke="#4f5d75" stroke-width="1"/>
<text x="308" y="466" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Task</text>
<rect x="368" y="456" width="20" height="12" rx="3" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="396" y="466" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Phase</text>
</svg>
</div>
</body>
</html>
@@ -0,0 +1,249 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>High-level architecture · End-to-end stack</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--color-paper: #2d3142;
--color-ink: #f5f5f5;
--color-muted: #bfc0c0;
--color-accent: #f08a59;
--font-sans: 'Geist', system-ui, sans-serif;
--font-serif: 'Instrument Serif', serif;
--font-mono: 'Geist Mono', ui-monospace, monospace;
}
body {
font-family: var(--font-sans);
background: var(--color-paper);
color: var(--color-ink);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 3rem 2rem;
}
.frame { max-width: 1200px; width: 100%; }
.eyebrow {
font-family: var(--font-mono);
font-size: 0.66rem;
font-weight: 500;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--color-muted);
margin-bottom: 0.5rem;
}
h1 {
font-family: var(--font-serif);
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
font-weight: 400;
letter-spacing: -0.02em;
line-height: 1.15;
margin-bottom: 1.5rem;
}
svg { width: 100%; min-width: 960px; display: block; }
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Architecture · Diagram Design</p>
<h1>High-level architecture · End-to-end stack</h1>
<svg viewBox="0 0 1000 540" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(245,245,245,0.10)"/>
</pattern>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="#bfc0c0"/>
</marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="#f08a59"/>
</marker>
<marker id="arrow-sm" markerWidth="6" markerHeight="5" refX="5" refY="2.5" orient="auto">
<polygon points="0 0, 6 2.5, 0 5" fill="#bfc0c0"/>
</marker>
</defs>
<rect width="100%" height="100%" fill="#2d3142"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- Phase chevron banner -->
<polygon points="0,4 188,4 200,18 188,32 0,32" fill="#3d4460"/>
<polygon points="200,4 388,4 400,18 388,32 200,32 212,18" fill="#4a5270"/>
<polygon points="400,4 588,4 600,18 588,32 400,32 412,18" fill="#3d4460"/>
<polygon points="600,4 788,4 800,18 788,32 600,32 612,18" fill="#4a5270"/>
<polygon points="800,4 1000,4 1000,32 800,32 812,18" fill="#3d4460"/>
<text x="100" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">DATA SOURCES</text>
<text x="300" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">INGESTION</text>
<text x="500" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">STORAGE</text>
<text x="700" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.13em" text-anchor="middle">TRANSFORMATION &amp; ANALYSIS</text>
<text x="900" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">VISUALIZATION</text>
<!-- Structural zones -->
<rect x="4" y="40" width="152" height="336" rx="6" fill="rgba(245,245,245,0.03)" stroke="rgba(245,245,245,0.22)" stroke-width="0.8" stroke-dasharray="6,3"/>
<text x="80" y="52" fill="#bfc0c0" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">SOURCES</text>
<rect x="164" y="40" width="832" height="336" rx="8" fill="rgba(245,245,245,0.03)" stroke="rgba(245,245,245,0.18)" stroke-width="1.2"/>
<rect x="4" y="388" width="992" height="40" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.18)" stroke-width="0.8"/>
<!-- Arrows -->
<path d="M 144,92 H 156 Q 164,92 164,100 V 112 Q 164,120 176,120" fill="none" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 144,172 H 156 Q 164,172 164,164 V 144 Q 164,136 176,136" fill="none" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 144,252 H 156 Q 164,252 164,244 V 160 Q 164,152 176,152" fill="none" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 144,336 H 156 Q 164,336 164,328 V 176 Q 164,168 176,168" fill="none" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<line x1="452" y1="96" x2="452" y2="104" stroke="#bfc0c0" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<line x1="682" y1="96" x2="682" y2="144" stroke="#bfc0c0" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<line x1="896" y1="96" x2="896" y2="144" stroke="#bfc0c0" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<path d="M 328,152 H 344 Q 348,152 348,148 V 144 Q 348,140 352,140 H 372" fill="none" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 328,152 H 344 Q 352,152 352,160 V 240 Q 352,248 372,248" fill="none" stroke="#f08a59" stroke-width="1.2" marker-end="url(#arrow-accent)"/>
<line x1="448" y1="176" x2="448" y2="208" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<line x1="456" y1="208" x2="456" y2="176" stroke="rgba(245,245,245,0.25)" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
<path d="M 532,140 H 556 Q 564,140 564,148 V 176 Q 564,184 600,184" fill="none" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 532,248 H 556 Q 564,248 564,240 V 192 Q 564,184 600,184" fill="none" stroke="#f08a59" stroke-width="1.2" marker-end="url(#arrow-accent)"/>
<line x1="764" y1="184" x2="820" y2="184" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<!-- Nodes -->
<!-- WEB -->
<rect x="12" y="60" width="132" height="64" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="20" y="66" width="22" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.25)" stroke-width="0.8"/>
<text x="31" y="75" fill="rgba(245,245,245,0.45)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="66" y="66" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#f5f5f5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="9"/>
<path d="M3.6 9h16.8M3.6 15h16.8"/>
<path d="M11.5 3a17 17 0 0 0 0 18M12.5 3a17 17 0 0 1 0 18"/>
</svg>
<text x="78" y="102" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">WEB</text>
<text x="78" y="114" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Census · Survey · Admin</text>
<!-- FTP -->
<rect x="12" y="140" width="132" height="64" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="20" y="146" width="22" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.25)" stroke-width="0.8"/>
<text x="31" y="155" fill="rgba(245,245,245,0.45)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="66" y="146" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#f5f5f5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"/>
<path d="M7 9l5 -5l5 5"/><path d="M12 4l0 12"/>
</svg>
<text x="78" y="182" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">FTP</text>
<text x="78" y="194" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Census · Business</text>
<!-- DB Connection -->
<rect x="12" y="220" width="132" height="64" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="20" y="226" width="22" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.25)" stroke-width="0.8"/>
<text x="31" y="235" fill="rgba(245,245,245,0.45)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="66" y="226" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#f5f5f5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 6a8 3 0 1 0 16 0a8 3 0 1 0 -16 0"/>
<path d="M4 6v6a8 3 0 0 0 16 0v-6"/>
<path d="M4 12v6a8 3 0 0 0 16 0v-6"/>
</svg>
<text x="78" y="262" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">DB Connection</text>
<text x="78" y="274" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">CDC · SQL · API</text>
<!-- Legacy System -->
<rect x="12" y="304" width="132" height="64" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="20" y="310" width="22" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.25)" stroke-width="0.8"/>
<text x="31" y="319" fill="rgba(245,245,245,0.45)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="66" y="310" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#f5f5f5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 7a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3"/>
<path d="M3 15a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3l0 -2"/>
<path d="M7 8l0 .01"/><path d="M7 16l0 .01"/>
</svg>
<text x="78" y="346" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Legacy System</text>
<text x="78" y="358" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Trade data</text>
<!-- Airflow bar -->
<rect x="176" y="52" width="808" height="44" rx="4" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.18)" stroke-width="0.8"/>
<svg x="950" y="58" width="24" height="24" viewBox="0 0 24 24" fill="#bfc0c0">
<path d="M17.195 16.822l4.002-4.102C23.55 10.308 23.934 5.154 24 .43a.396.396 0 0 0-.246-.373.392.392 0 0 0-.437.09l-6.495 6.658-4.102-4.003C10.309.45 5.154.066.43 0H.423a.397.397 0 0 0-.277.683l6.658 6.494-4.003 4.103C.45 13.692.065 18.846 0 23.57a.398.398 0 0 0 .683.282l6.494-6.657 3.934 3.837.17.165c2.41 2.353 7.565 2.737 12.288 2.803h.006a.397.397 0 0 0 .277-.683l-6.657-6.495z"/>
</svg>
<text x="564" y="71" fill="#f5f5f5" font-size="10" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Orchestration / Workflow Automation</text>
<text x="564" y="84" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">(Apache Airflow)</text>
<!-- NiFi -->
<rect x="176" y="112" width="152" height="80" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="184" y="118" width="30" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.25)" stroke-width="0.8"/>
<text x="199" y="127" fill="rgba(245,245,245,0.45)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">COLL</text>
<svg x="240" y="118" width="24" height="24" viewBox="0 0 24 24" fill="#f5f5f5">
<path d="M11.648 0a.093.093 0 0 0-.084.053A30.71 30.71 0 0 1 8.592 4.73c-2.09 2.728-5.145 6.466-5.145 10.364a8.201 8.201 0 0 0 8.201 8.2v-5.003c0-.106.087-.193.194-.193h2.81v-2.813c0-.106.087-.191.194-.191h5.004c0-3.9-3.056-7.636-5.145-10.364A30.712 30.712 0 0 1 11.732.053.094.094 0 0 0 11.648 0zm-1.632 3.867c.05 0 .08.034.037.112-.11.197-.218.397-.328.593-.396.702-.819 1.389-1.23 2.08-.196-.032-.39-.06-.585-.088.495-.651 1-1.296 1.48-1.959.153-.209.302-.423.454-.634a.24.24 0 0 1 .172-.104zM7.44 7.186c.221.035.444.076.666.119-.073.129-.15.256-.223.383a29.073 29.073 0 0 0-1.625 3.261c-.874 2.123-1.383 4.444-.77 6.707a8.222 8.222 0 0 0 2.217 3.74c.083.083-.02.216-.119.155a7.568 7.568 0 0 1-.93-.686A7.674 7.674 0 0 1 4.1 16.248c-.329-2.156.387-4.246 1.418-6.115a27.44 27.44 0 0 1 1.92-2.947zm7.931 8.435a.193.193 0 0 0-.191.191V18.3h2.677V15.62zm3.299 0V18.3h1.348a7.975 7.975 0 0 0 .515-2.678zm-6.303 3.004a.193.193 0 0 0-.191.193v2.485h2.678v-2.678Zm3.295.484v2.68h2.115a.562.562 0 0 0 .399-.162v-.004a.562.562 0 0 0 .16-.397V19.11zm3.674.182v1.98a7.999 7.999 0 0 0 1.217-1.98zm-6.969 2.824a.193.193 0 0 0-.191.192v1.672a7.997 7.997 0 0 0 2.678-.516v-1.348zm3.48.668V24a8.008 8.008 0 0 0 1.98-1.217z"/>
</svg>
<text x="252" y="154" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Apache NiFi</text>
<text x="252" y="166" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Collect · route · transform</text>
<!-- Trino -->
<rect x="372" y="104" width="160" height="72" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="380" y="110" width="28" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.25)" stroke-width="0.8"/>
<text x="394" y="119" fill="rgba(245,245,245,0.45)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">VIRT</text>
<svg x="440" y="110" width="24" height="24" viewBox="0 0 24 24" fill="#f5f5f5">
<path d="M14.124 16.8529a.1615.1615 0 1 1 .1576.1614.1577.1577 0 0 1-.1576-.1614zm-5.607-.1576a.1614.1614 0 1 0 0 .3228.1614.1614 0 0 0 0-.3228zm10.1341-.6648v1.9869c-.031.5788-.524 1.0237-1.1029.9954h-.3843a5.0596 5.0596 0 0 1-1.1298 1.7178.3192.3192 0 0 0 0 .465l.2382.2191a.3036.3036 0 0 1 .0385.4304c-1.126 1.3835-2.9669 2.1521-5.0498 2.1521a6.575 6.575 0 0 1-4.8192-1.8985c-.0029-.0032-.0059-.0063-.0087-.0096a.6302.6302 0 0 1 .0548-.8896c.137-.1265.1371-.3462 0-.4727a4.944 4.944 0 0 1-1.126-1.714h-.3497c-.5797.0284-1.0737-.416-1.1068-.9954v-1.9869c.0351-.5779.5286-1.02 1.1068-.9915h.2728a5.7648 5.7648 0 0 1 2.0791-3.0936c-.4227-1.0991-1.1529-3.2551-1.226-5.0075C6.0229 4.4705 6.2189.078 7.8253.001c1.6064-.0768 1.3719 4.0275 1.0991 6.6946a32.732 32.732 0 0 0-.123 4.4503 6.994 6.994 0 0 1 2.4826-.4304 7.2414 7.2414 0 0 1 1.7371.2075c.2614-1.2682.8762-3.574 2.0292-5.1958 1.6717-2.352 3.4357-4.7808 4.6116-4.1006 1.176.6802-.3074 3.1398-1.3297 4.4272-1.0222 1.2874-2.7862 3.2089-3.3742 4.2274-.2114.3843-.4304.8032-.5956 1.1529a5.7375 5.7375 0 0 1 2.9169 3.6125h.073v-2.3058a.3075.3075 0 0 0-.1806-.2844.9148.9148 0 0 1-.5573-.8148 1.0184 1.0184 0 0 1 .9045-.9044c.5593-.0598 1.061.3452 1.1208.9044a.9187.9187 0 0 1-.5534.8148.3074.3074 0 0 0-.1691.2844v2.1522a.3113.3113 0 0 0 .1691.2805.9724.9724 0 0 1 .5648.857z"/>
</svg>
<text x="452" y="146" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Trino</text>
<text x="452" y="158" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">SQL · data virtualization</text>
<!-- MinIO (FOCAL) -->
<rect x="372" y="208" width="160" height="80" rx="8" fill="rgba(240,138,89,0.12)" stroke="#f08a59" stroke-width="1.2"/>
<rect x="380" y="214" width="36" height="12" rx="2" fill="transparent" stroke="rgba(240,138,89,0.55)" stroke-width="0.8"/>
<text x="398" y="223" fill="rgba(240,138,89,0.85)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STORE</text>
<svg x="440" y="214" width="24" height="24" viewBox="0 0 24 24" fill="#f08a59">
<path d="M13.2072.006c-.6216-.0478-1.2.1943-1.6211.582a2.15 2.15 0 0 0-.0938 3.0352l3.4082 3.5507a3.042 3.042 0 0 1-.664 4.6875l-.463.2383V7.2853a15.4198 15.4198 0 0 0-8.0174 10.4862v.0176l6.5487-3.3281v7.621L13.7794 24V13.6817l.8965-.4629a4.4432 4.4432 0 0 0 1.2207-7.0292l-3.371-3.5254a.7489.7489 0 0 1 .037-1.0547.7522.7522 0 0 1 1.0567.0371l.4668.4863-.006.0059 4.0704 4.2441a.0566.0566 0 0 0 .082 0 .06.06 0 0 0 0-.0703l-3.1406-5.1425-.1484.1425.1484-.1445C14.4945.3926 13.8287.0538 13.2072.006Zm-.9024 9.8652v2.9941l-4.1523 2.1484a13.9787 13.9787 0 0 1 2.7676-3.9277 14.1784 14.1784 0 0 1 1.3847-1.2148z"/>
</svg>
<text x="452" y="252" fill="#f08a59" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">MinIO / S3</text>
<text x="452" y="268" fill="#f08a59" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" opacity="0.75">Object store · S3-API</text>
<!-- Data Modeling -->
<rect x="600" y="144" width="164" height="80" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="608" y="150" width="32" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.25)" stroke-width="0.8"/>
<text x="624" y="159" fill="rgba(245,245,245,0.45)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ANLZ</text>
<svg x="670" y="150" width="24" height="24" viewBox="0 0 24 24" fill="#f5f5f5">
<path d="M7.157 22.201A1.784 1.799 0 0 1 5.374 24a1.784 1.799 0 0 1-1.784-1.799 1.784 1.799 0 0 1 1.784-1.799 1.784 1.799 0 0 1 1.783 1.799zM20.582 1.427a1.415 1.427 0 0 1-1.415 1.428 1.415 1.427 0 0 1-1.416-1.428A1.415 1.427 0 0 1 19.167 0a1.415 1.427 0 0 1 1.415 1.427zM4.992 3.336A1.047 1.056 0 0 1 3.946 4.39a1.047 1.056 0 0 1-1.047-1.055A1.047 1.056 0 0 1 3.946 2.28a1.047 1.056 0 0 1 1.046 1.056zm7.336 1.517c3.769 0 7.06 1.38 8.768 3.424a9.363 9.363 0 0 0-3.393-4.547 9.238 9.238 0 0 0-5.377-1.728A9.238 9.238 0 0 0 6.95 3.73a9.363 9.363 0 0 0-3.394 4.547c1.713-2.04 5.004-3.424 8.772-3.424zm.001 13.295c-3.768 0-7.06-1.381-8.768-3.425a9.363 9.363 0 0 0 3.394 4.547A9.238 9.238 0 0 0 12.33 21a9.238 9.238 0 0 0 5.377-1.729 9.363 9.363 0 0 0 3.393-4.547c-1.712 2.044-5.003 3.425-8.772 3.425z"/>
</svg>
<text x="682" y="186" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Data Modeling</text>
<text x="682" y="198" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Notebook · Python · R</text>
<!-- Dashboards -->
<rect x="820" y="144" width="152" height="80" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="828" y="150" width="28" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.25)" stroke-width="0.8"/>
<text x="842" y="159" fill="rgba(245,245,245,0.45)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">DASH</text>
<svg x="884" y="150" width="24" height="24" viewBox="0 0 24 24" fill="#f5f5f5">
<path d="M6.168 6.045C2.603 6.045 0 8.579 0 12.014c0 3.434 2.603 5.941 6.168 5.941 2.184 0 3.888-1.026 5.775-3.078 1.53 2.033 4.037 3.136 5.89 3.078 3.566 0 6.167-2.503 6.167-5.941 0-3.438-2.601-5.97-6.168-5.97-2.864 0-5.138 2.425-5.771 3.173-.76-.9-1.674-1.665-2.682-2.274-1.019-.588-2.084-.898-3.211-.898z"/>
</svg>
<text x="896" y="186" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Dashboards</text>
<text x="896" y="198" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Superset · reports</text>
<!-- Kubernetes label -->
<svg x="180" y="348" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#bfc0c0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M19.875 6.27a2.225 2.225 0 0 1 1.125 1.948v7.284c0 .809-.443 1.555-1.158 1.948l-6.75 3.98a2.5 2.5 0 0 1-2.184 0l-6.75-3.98a2.225 2.225 0 0 1-1.158-1.948v-7.284c0-.809.443-1.555 1.158-1.948l6.75-3.98a2.5 2.5 0 0 1 2.184 0z"/>
<circle cx="12" cy="12" r="3"/>
<line x1="12" y1="3" x2="12" y2="9"/><line x1="12" y1="15" x2="12" y2="21"/>
<line x1="20.2" y1="7.5" x2="14.9" y2="10.5"/><line x1="9.1" y1="13.5" x2="3.8" y2="16.5"/>
<line x1="20.2" y1="16.5" x2="14.9" y2="13.5"/><line x1="9.1" y1="10.5" x2="3.8" y2="7.5"/>
</svg>
<text x="204" y="362" fill="#bfc0c0" font-size="8" font-weight="500" font-family="'Geist', sans-serif">Kubernetes</text>
<!-- Identity Manager -->
<svg x="16" y="398" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#bfc0c0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 3a12 12 0 0 0 8.5 3a12 12 0 0 1-8.5 15a12 12 0 0 1-8.5-15a12 12 0 0 0 8.5-3"/>
<circle cx="12" cy="11" r="1" fill="#bfc0c0"/>
<line x1="12" y1="12" x2="12" y2="14.5"/>
</svg>
<text x="500" y="410" fill="#f5f5f5" font-size="10" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Identity Manager</text>
<text x="500" y="422" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">(Active Directory)</text>
<!-- Legend -->
<line x1="40" y1="440" x2="960" y2="440" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<text x="40" y="456" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<rect x="40" y="464" width="20" height="12" rx="3" fill="transparent" stroke="rgba(245,245,245,0.30)" stroke-width="1" stroke-dasharray="4,3"/>
<text x="68" y="474" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">External source · outside cluster</text>
<rect x="252" y="464" width="20" height="12" rx="3" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.25)" stroke-width="1"/>
<text x="280" y="474" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Kubernetes boundary</text>
<rect x="436" y="464" width="20" height="12" rx="3" fill="rgba(240,138,89,0.18)" stroke="#f08a59" stroke-width="1.2"/>
<text x="464" y="474" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Focal component (MinIO)</text>
<line x1="648" y1="466" x2="668" y2="466" stroke="#f08a59" stroke-width="1.2"/>
<text x="676" y="471" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Primary data path</text>
<line x1="808" y1="466" x2="828" y2="466" stroke="rgba(245,245,245,0.35)" stroke-width="1" stroke-dasharray="4,3"/>
<text x="836" y="471" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Orchestration trigger</text>
</svg>
</div>
</body>
</html>
@@ -0,0 +1,245 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>High-level architecture · End-to-end stack</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root { --color-paper:#f5f5f5; --color-paper-2:#ececec; --color-ink:#2d3142; --color-muted:#4f5d75; --color-soft:#7a8399; --color-rule:rgba(45,49,66,0.12); --color-accent:#eb6c36; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
.container { max-width: 1200px; margin: 0 auto; }
.header { margin-bottom: 2.5rem; }
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 72ch; }
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
svg { width: 100%; min-width: 960px; display: block; }
.cards { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1rem; margin-top: 1.5rem; }
@media (max-width: 900px) { .cards { grid-template-columns: 1fr; } }
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(45,49,66,0.08); }
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
.card-dot.coral { background: var(--color-accent); }
.card-dot.ink { background: var(--color-ink); }
.card-dot.muted { background: var(--color-muted); }
.card h3 { font-size: 0.875rem; font-weight: 600; }
.card ul { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; list-style: none; }
.card li { margin-bottom: 0.3rem; padding-left: 0.875rem; position: relative; }
.card li::before { content: '—'; position: absolute; left: 0; color: rgba(45,49,66,0.25); font-size: 0.75rem; }
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(45,49,66,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<p class="header-eyebrow">Architecture · Diagram Design</p>
<h1>High-level architecture · End-to-end stack</h1>
<p class="subtitle">Five functional phases from raw source ingestion to business visualization. Apache NiFi collects, MinIO stores, Trino virtualizes, notebooks model — all orchestrated by Airflow and secured by Active Directory, running on Kubernetes.</p>
</div>
<div class="diagram-container">
<svg viewBox="0 0 1000 540" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(45,49,66,0.10)"/>
</pattern>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="#4f5d75"/>
</marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="#eb6c36"/>
</marker>
<marker id="arrow-sm" markerWidth="6" markerHeight="5" refX="5" refY="2.5" orient="auto">
<polygon points="0 0, 6 2.5, 0 5" fill="#4f5d75"/>
</marker>
</defs>
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- Phase chevrons -->
<polygon points="0,4 188,4 200,18 188,32 0,32" fill="#2d3142"/>
<polygon points="200,4 388,4 400,18 388,32 200,32 212,18" fill="#3d4460"/>
<polygon points="400,4 588,4 600,18 588,32 400,32 412,18" fill="#2d3142"/>
<polygon points="600,4 788,4 800,18 788,32 600,32 612,18" fill="#3d4460"/>
<polygon points="800,4 1000,4 1000,32 800,32 812,18" fill="#2d3142"/>
<text x="100" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">DATA SOURCES</text>
<text x="300" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">INGESTION</text>
<text x="500" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">STORAGE</text>
<text x="700" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.13em" text-anchor="middle">TRANSFORMATION &amp; ANALYSIS</text>
<text x="900" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">VISUALIZATION</text>
<!-- Zones -->
<rect x="4" y="40" width="152" height="336" rx="6" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.20)" stroke-width="0.8" stroke-dasharray="6,3"/>
<text x="80" y="52" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">SOURCES</text>
<rect x="164" y="40" width="832" height="336" rx="8" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.18)" stroke-width="1.2"/>
<rect x="4" y="388" width="992" height="40" rx="6" fill="rgba(45,49,66,0.05)" stroke="rgba(45,49,66,0.20)" stroke-width="0.8"/>
<!-- Arrows -->
<path d="M 144,92 H 156 Q 164,92 164,100 V 112 Q 164,120 176,120" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 144,172 H 156 Q 164,172 164,164 V 144 Q 164,136 176,136" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 144,252 H 156 Q 164,252 164,244 V 160 Q 164,152 176,152" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 144,336 H 156 Q 164,336 164,328 V 176 Q 164,168 176,168" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<line x1="452" y1="96" x2="452" y2="104" stroke="#4f5d75" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<line x1="682" y1="96" x2="682" y2="144" stroke="#4f5d75" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<line x1="896" y1="96" x2="896" y2="144" stroke="#4f5d75" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<path d="M 328,152 H 344 Q 348,152 348,148 V 144 Q 348,140 352,140 H 372" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 328,152 H 344 Q 352,152 352,160 V 240 Q 352,248 372,248" fill="none" stroke="#eb6c36" stroke-width="1.2" marker-end="url(#arrow-accent)"/>
<line x1="448" y1="176" x2="448" y2="208" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<line x1="456" y1="208" x2="456" y2="176" stroke="rgba(45,49,66,0.30)" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
<path d="M 532,140 H 556 Q 564,140 564,148 V 176 Q 564,184 600,184" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 532,248 H 556 Q 564,248 564,240 V 192 Q 564,184 600,184" fill="none" stroke="#eb6c36" stroke-width="1.2" marker-end="url(#arrow-accent)"/>
<line x1="764" y1="184" x2="820" y2="184" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<!-- Source nodes -->
<rect x="12" y="60" width="132" height="64" rx="6" fill="#f5f5f5"/><rect x="12" y="60" width="132" height="64" rx="6" fill="white" stroke="rgba(45,49,66,0.22)" stroke-width="1"/>
<rect x="20" y="66" width="22" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="31" y="75" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="66" y="66" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#2d3142" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M3.6 9h16.8M3.6 15h16.8"/><path d="M11.5 3a17 17 0 0 0 0 18M12.5 3a17 17 0 0 1 0 18"/></svg>
<text x="78" y="102" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">WEB</text>
<text x="78" y="114" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Census · Survey · Admin</text>
<rect x="12" y="140" width="132" height="64" rx="6" fill="#f5f5f5"/><rect x="12" y="140" width="132" height="64" rx="6" fill="white" stroke="rgba(45,49,66,0.22)" stroke-width="1"/>
<rect x="20" y="146" width="22" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="31" y="155" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="66" y="146" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#2d3142" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"/><path d="M7 9l5 -5l5 5"/><path d="M12 4l0 12"/></svg>
<text x="78" y="182" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">FTP</text>
<text x="78" y="194" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Census · Business</text>
<rect x="12" y="220" width="132" height="64" rx="6" fill="#f5f5f5"/><rect x="12" y="220" width="132" height="64" rx="6" fill="white" stroke="rgba(45,49,66,0.22)" stroke-width="1"/>
<rect x="20" y="226" width="22" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="31" y="235" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="66" y="226" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#2d3142" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 6a8 3 0 1 0 16 0a8 3 0 1 0 -16 0"/><path d="M4 6v6a8 3 0 0 0 16 0v-6"/><path d="M4 12v6a8 3 0 0 0 16 0v-6"/></svg>
<text x="78" y="262" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">DB Connection</text>
<text x="78" y="274" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">CDC · SQL · API</text>
<rect x="12" y="304" width="132" height="64" rx="6" fill="#f5f5f5"/><rect x="12" y="304" width="132" height="64" rx="6" fill="white" stroke="rgba(45,49,66,0.22)" stroke-width="1"/>
<rect x="20" y="310" width="22" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="31" y="319" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="66" y="310" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#2d3142" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3"/><path d="M3 15a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3l0 -2"/><path d="M7 8l0 .01"/><path d="M7 16l0 .01"/></svg>
<text x="78" y="346" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Legacy System</text>
<text x="78" y="358" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Trade data</text>
<!-- Airflow bar -->
<rect x="176" y="52" width="808" height="44" rx="4" fill="rgba(45,49,66,0.05)" stroke="rgba(45,49,66,0.18)" stroke-width="0.8"/>
<svg x="950" y="58" width="24" height="24" viewBox="0 0 24 24" fill="#4f5d75"><path d="M17.195 16.822l4.002-4.102C23.55 10.308 23.934 5.154 24 .43a.396.396 0 0 0-.246-.373.392.392 0 0 0-.437.09l-6.495 6.658-4.102-4.003C10.309.45 5.154.066.43 0H.423a.397.397 0 0 0-.277.683l6.658 6.494-4.003 4.103C.45 13.692.065 18.846 0 23.57a.398.398 0 0 0 .683.282l6.494-6.657 3.934 3.837.17.165c2.41 2.353 7.565 2.737 12.288 2.803h.006a.397.397 0 0 0 .277-.683l-6.657-6.495z"/></svg>
<text x="564" y="71" fill="#2d3142" font-size="10" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Orchestration / Workflow Automation</text>
<text x="564" y="84" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">(Apache Airflow)</text>
<!-- NiFi -->
<rect x="176" y="112" width="152" height="80" rx="6" fill="#f5f5f5"/><rect x="176" y="112" width="152" height="80" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="184" y="118" width="30" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="199" y="127" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">COLL</text>
<svg x="240" y="118" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142"><path d="M11.648 0a.093.093 0 0 0-.084.053A30.71 30.71 0 0 1 8.592 4.73c-2.09 2.728-5.145 6.466-5.145 10.364a8.201 8.201 0 0 0 8.201 8.2v-5.003c0-.106.087-.193.194-.193h2.81v-2.813c0-.106.087-.191.194-.191h5.004c0-3.9-3.056-7.636-5.145-10.364A30.712 30.712 0 0 1 11.732.053.094.094 0 0 0 11.648 0zm-1.632 3.867c.05 0 .08.034.037.112-.11.197-.218.397-.328.593-.396.702-.819 1.389-1.23 2.08-.196-.032-.39-.06-.585-.088.495-.651 1-1.296 1.48-1.959.153-.209.302-.423.454-.634a.24.24 0 0 1 .172-.104zM7.44 7.186c.221.035.444.076.666.119-.073.129-.15.256-.223.383a29.073 29.073 0 0 0-1.625 3.261c-.874 2.123-1.383 4.444-.77 6.707a8.222 8.222 0 0 0 2.217 3.74c.083.083-.02.216-.119.155a7.568 7.568 0 0 1-.93-.686A7.674 7.674 0 0 1 4.1 16.248c-.329-2.156.387-4.246 1.418-6.115a27.44 27.44 0 0 1 1.92-2.947zm7.931 8.435a.193.193 0 0 0-.191.191V18.3h2.677V15.62zm3.299 0V18.3h1.348a7.975 7.975 0 0 0 .515-2.678zm-6.303 3.004a.193.193 0 0 0-.191.193v2.485h2.678v-2.678Zm3.295.484v2.68h2.115a.562.562 0 0 0 .399-.162v-.004a.562.562 0 0 0 .16-.397V19.11zm3.674.182v1.98a7.999 7.999 0 0 0 1.217-1.98zm-6.969 2.824a.193.193 0 0 0-.191.192v1.672a7.997 7.997 0 0 0 2.678-.516v-1.348zm3.48.668V24a8.008 8.008 0 0 0 1.98-1.217z"/></svg>
<text x="252" y="154" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Apache NiFi</text>
<text x="252" y="166" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Collect · route · transform</text>
<!-- Trino -->
<rect x="372" y="104" width="160" height="72" rx="6" fill="#f5f5f5"/><rect x="372" y="104" width="160" height="72" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="380" y="110" width="28" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="394" y="119" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">VIRT</text>
<svg x="440" y="110" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142"><path d="M14.124 16.8529a.1615.1615 0 1 1 .1576.1614.1577.1577 0 0 1-.1576-.1614zm-5.607-.1576a.1614.1614 0 1 0 0 .3228.1614.1614 0 0 0 0-.3228zm10.1341-.6648v1.9869c-.031.5788-.524 1.0237-1.1029.9954h-.3843a5.0596 5.0596 0 0 1-1.1298 1.7178.3192.3192 0 0 0 0 .465l.2382.2191a.3036.3036 0 0 1 .0385.4304c-1.126 1.3835-2.9669 2.1521-5.0498 2.1521a6.575 6.575 0 0 1-4.8192-1.8985c-.0029-.0032-.0059-.0063-.0087-.0096a.6302.6302 0 0 1 .0548-.8896c.137-.1265.1371-.3462 0-.4727a4.944 4.944 0 0 1-1.126-1.714h-.3497c-.5797.0284-1.0737-.416-1.1068-.9954v-1.9869c.0351-.5779.5286-1.02 1.1068-.9915h.2728a5.7648 5.7648 0 0 1 2.0791-3.0936c-.4227-1.0991-1.1529-3.2551-1.226-5.0075C6.0229 4.4705 6.2189.078 7.8253.001c1.6064-.0768 1.3719 4.0275 1.0991 6.6946a32.732 32.732 0 0 0-.123 4.4503 6.994 6.994 0 0 1 2.4826-.4304 7.2414 7.2414 0 0 1 1.7371.2075c.2614-1.2682.8762-3.574 2.0292-5.1958 1.6717-2.352 3.4357-4.7808 4.6116-4.1006 1.176.6802-.3074 3.1398-1.3297 4.4272-1.0222 1.2874-2.7862 3.2089-3.3742 4.2274-.2114.3843-.4304.8032-.5956 1.1529a5.7375 5.7375 0 0 1 2.9169 3.6125h.073v-2.3058a.3075.3075 0 0 0-.1806-.2844.9148.9148 0 0 1-.5573-.8148 1.0184 1.0184 0 0 1 .9045-.9044c.5593-.0598 1.061.3452 1.1208.9044a.9187.9187 0 0 1-.5534.8148.3074.3074 0 0 0-.1691.2844v2.1522a.3113.3113 0 0 0 .1691.2805.9724.9724 0 0 1 .5648.857z"/></svg>
<text x="452" y="146" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Trino</text>
<text x="452" y="158" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">SQL · data virtualization</text>
<!-- MinIO FOCAL -->
<rect x="372" y="208" width="160" height="80" rx="8" fill="#f5f5f5"/><rect x="372" y="208" width="160" height="80" rx="8" fill="rgba(235,108,54,0.08)" stroke="#eb6c36" stroke-width="1.2"/>
<rect x="380" y="214" width="36" height="12" rx="2" fill="transparent" stroke="rgba(235,108,54,0.50)" stroke-width="0.8"/>
<text x="398" y="223" fill="rgba(235,108,54,0.80)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STORE</text>
<svg x="440" y="214" width="24" height="24" viewBox="0 0 24 24" fill="#eb6c36"><path d="M13.2072.006c-.6216-.0478-1.2.1943-1.6211.582a2.15 2.15 0 0 0-.0938 3.0352l3.4082 3.5507a3.042 3.042 0 0 1-.664 4.6875l-.463.2383V7.2853a15.4198 15.4198 0 0 0-8.0174 10.4862v.0176l6.5487-3.3281v7.621L13.7794 24V13.6817l.8965-.4629a4.4432 4.4432 0 0 0 1.2207-7.0292l-3.371-3.5254a.7489.7489 0 0 1 .037-1.0547.7522.7522 0 0 1 1.0567.0371l.4668.4863-.006.0059 4.0704 4.2441a.0566.0566 0 0 0 .082 0 .06.06 0 0 0 0-.0703l-3.1406-5.1425-.1484.1425.1484-.1445C14.4945.3926 13.8287.0538 13.2072.006Zm-.9024 9.8652v2.9941l-4.1523 2.1484a13.9787 13.9787 0 0 1 2.7676-3.9277 14.1784 14.1784 0 0 1 1.3847-1.2148z"/></svg>
<text x="452" y="252" fill="#eb6c36" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">MinIO / S3</text>
<text x="452" y="268" fill="#eb6c36" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" opacity="0.75">Object store · S3-API</text>
<!-- Data Modeling -->
<rect x="600" y="144" width="164" height="80" rx="6" fill="#f5f5f5"/><rect x="600" y="144" width="164" height="80" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="608" y="150" width="32" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="624" y="159" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ANLZ</text>
<svg x="670" y="150" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142"><path d="M7.157 22.201A1.784 1.799 0 0 1 5.374 24a1.784 1.799 0 0 1-1.784-1.799 1.784 1.799 0 0 1 1.784-1.799 1.784 1.799 0 0 1 1.783 1.799zM20.582 1.427a1.415 1.427 0 0 1-1.415 1.428 1.415 1.427 0 0 1-1.416-1.428A1.415 1.427 0 0 1 19.167 0a1.415 1.427 0 0 1 1.415 1.427zM4.992 3.336A1.047 1.056 0 0 1 3.946 4.39a1.047 1.056 0 0 1-1.047-1.055A1.047 1.056 0 0 1 3.946 2.28a1.047 1.056 0 0 1 1.046 1.056zm7.336 1.517c3.769 0 7.06 1.38 8.768 3.424a9.363 9.363 0 0 0-3.393-4.547 9.238 9.238 0 0 0-5.377-1.728A9.238 9.238 0 0 0 6.95 3.73a9.363 9.363 0 0 0-3.394 4.547c1.713-2.04 5.004-3.424 8.772-3.424zm.001 13.295c-3.768 0-7.06-1.381-8.768-3.425a9.363 9.363 0 0 0 3.394 4.547A9.238 9.238 0 0 0 12.33 21a9.238 9.238 0 0 0 5.377-1.729 9.363 9.363 0 0 0 3.393-4.547c-1.712 2.044-5.003 3.425-8.772 3.425z"/></svg>
<text x="682" y="186" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Data Modeling</text>
<text x="682" y="198" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Notebook · Python · R</text>
<!-- Dashboards -->
<rect x="820" y="144" width="152" height="80" rx="6" fill="#f5f5f5"/><rect x="820" y="144" width="152" height="80" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="828" y="150" width="28" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="842" y="159" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">DASH</text>
<svg x="884" y="150" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142"><path d="M6.168 6.045C2.603 6.045 0 8.579 0 12.014c0 3.434 2.603 5.941 6.168 5.941 2.184 0 3.888-1.026 5.775-3.078 1.53 2.033 4.037 3.136 5.89 3.078 3.566 0 6.167-2.503 6.167-5.941 0-3.438-2.601-5.97-6.168-5.97-2.864 0-5.138 2.425-5.771 3.173-.76-.9-1.674-1.665-2.682-2.274-1.019-.588-2.084-.898-3.211-.898z"/></svg>
<text x="896" y="186" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Dashboards</text>
<text x="896" y="198" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Superset · reports</text>
<!-- Kubernetes label -->
<svg x="180" y="348" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#4f5d75" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19.875 6.27a2.225 2.225 0 0 1 1.125 1.948v7.284c0 .809-.443 1.555-1.158 1.948l-6.75 3.98a2.5 2.5 0 0 1-2.184 0l-6.75-3.98a2.225 2.225 0 0 1-1.158-1.948v-7.284c0-.809.443-1.555 1.158-1.948l6.75-3.98a2.5 2.5 0 0 1 2.184 0z"/><circle cx="12" cy="12" r="3"/><line x1="12" y1="3" x2="12" y2="9"/><line x1="12" y1="15" x2="12" y2="21"/><line x1="20.2" y1="7.5" x2="14.9" y2="10.5"/><line x1="9.1" y1="13.5" x2="3.8" y2="16.5"/><line x1="20.2" y1="16.5" x2="14.9" y2="13.5"/><line x1="9.1" y1="10.5" x2="3.8" y2="7.5"/></svg>
<text x="204" y="362" fill="#4f5d75" font-size="8" font-weight="500" font-family="'Geist', sans-serif">Kubernetes</text>
<!-- Identity Manager -->
<svg x="16" y="398" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#4f5d75" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3a12 12 0 0 0 8.5 3a12 12 0 0 1-8.5 15a12 12 0 0 1-8.5-15a12 12 0 0 0 8.5-3"/><circle cx="12" cy="11" r="1" fill="#4f5d75"/><line x1="12" y1="12" x2="12" y2="14.5"/></svg>
<text x="500" y="410" fill="#2d3142" font-size="10" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Identity Manager</text>
<text x="500" y="422" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">(Active Directory)</text>
<!-- Legend -->
<line x1="40" y1="440" x2="960" y2="440" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="40" y="456" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<rect x="40" y="464" width="20" height="12" rx="3" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="1" stroke-dasharray="4,3"/>
<text x="68" y="474" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">External source · outside cluster</text>
<rect x="252" y="464" width="20" height="12" rx="3" fill="rgba(45,49,66,0.04)" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<text x="280" y="474" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Kubernetes boundary</text>
<rect x="436" y="464" width="20" height="12" rx="3" fill="rgba(235,108,54,0.10)" stroke="#eb6c36" stroke-width="1.2"/>
<text x="464" y="474" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Focal component (MinIO)</text>
<line x1="648" y1="466" x2="668" y2="466" stroke="#eb6c36" stroke-width="1.2"/>
<text x="676" y="471" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Primary data path</text>
<line x1="808" y1="466" x2="828" y2="466" stroke="rgba(45,49,66,0.35)" stroke-width="1" stroke-dasharray="4,3"/>
<text x="836" y="471" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Orchestration trigger</text>
</svg>
</div>
<div class="cards">
<div class="card">
<p class="eyebrow">Ingestion · Apache NiFi</p>
<div class="card-header">
<div class="card-dot ink"></div>
<h3>Data collection</h3>
</div>
<ul>
<li>Collects from WEB (HTTP/REST), FTP, database CDC, and legacy file exports</li>
<li>No-code dataflow routing with back-pressure and guaranteed delivery</li>
<li>Lands raw data into MinIO and exposes it to Trino simultaneously</li>
<li>Orchestrated by Airflow DAGs for scheduled batch and event-driven runs</li>
</ul>
</div>
<div class="card">
<p class="eyebrow">Storage · MinIO / S3</p>
<div class="card-header">
<div class="card-dot coral"></div>
<h3>Object store (focal)</h3>
</div>
<ul>
<li>S3-compatible API — single storage surface for all tools in the stack</li>
<li>Holds raw, cleansed, and curated layers (bronze / silver / gold paths)</li>
<li>Trino virtualizes directly over MinIO buckets — no ETL copy step needed</li>
<li>Python notebooks write model outputs back as Parquet for further querying</li>
</ul>
</div>
<div class="card">
<p class="eyebrow">Query &amp; Serve · Trino + Superset</p>
<div class="card-header">
<div class="card-dot muted"></div>
<h3>Analysis &amp; visualization</h3>
</div>
<ul>
<li>Trino federates SQL queries across MinIO, databases, and APIs in one query</li>
<li>JupyterLab notebooks run Python and R for statistical modeling and ML</li>
<li>Superset dashboards connect to Trino for live, governed report delivery</li>
<li>Active Directory enforces row- and column-level access across all surfaces</li>
</ul>
</div>
</div>
<div class="footer">
<span>High-level architecture · Kubernetes-deployed open-source stack</span>
<span>Sources → NiFi → MinIO → Trino → Notebooks → Superset</span>
</div>
</div>
</body>
</html>
@@ -0,0 +1,272 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>High-level architecture · Vertical chevrons (dark)</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--color-paper: #2d3142;
--color-ink: #f5f5f5;
--color-muted: #bfc0c0;
--color-accent: #f08a59;
--font-sans: 'Geist', system-ui, sans-serif;
--font-serif: 'Instrument Serif', serif;
--font-mono: 'Geist Mono', ui-monospace, monospace;
}
body {
font-family: var(--font-sans);
background: var(--color-paper);
color: var(--color-ink);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 3rem 2rem;
}
.frame { max-width: 1200px; width: 100%; }
.eyebrow {
font-family: var(--font-mono);
font-size: 0.66rem;
font-weight: 500;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--color-muted);
margin-bottom: 0.5rem;
}
h1 {
font-family: var(--font-serif);
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
font-weight: 400;
letter-spacing: -0.02em;
line-height: 1.15;
margin-bottom: 1.5rem;
}
svg { width: 100%; min-width: 960px; display: block; }
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Architecture · Diagram Design · Parametric</p>
<h1>High-level architecture · Vertical Orchestration + Security</h1>
<svg viewBox="0 0 1000 600" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(245,245,245,0.10)"/>
</pattern>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="#bfc0c0"/>
</marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="#f08a59"/>
</marker>
<marker id="arrow-sm" markerWidth="6" markerHeight="5" refX="5" refY="2.5" orient="auto">
<polygon points="0 0, 6 2.5, 0 5" fill="#bfc0c0"/>
</marker>
<marker id="arrow-dim" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="rgba(245,245,245,0.45)"/>
</marker>
</defs>
<rect width="100%" height="100%" fill="#2d3142"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- Horizontal chevron banner (effective_w=964 with 8-px margin to right strip) -->
<polygon points="0,4 180,4 192,18 180,32 0,32" fill="#3d4460"/>
<polygon points="192,4 372,4 384,18 372,32 192,32 204,18" fill="#4a5270"/>
<polygon points="384,4 564,4 576,18 564,32 384,32 396,18" fill="#3d4460"/>
<polygon points="576,4 756,4 768,18 756,32 576,32 588,18" fill="#4a5270"/>
<polygon points="768,4 964,4 964,32 768,32 780,18" fill="#3d4460"/>
<text x="96" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">DATA SOURCES</text>
<text x="288" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">INGESTION</text>
<text x="480" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">STORAGE</text>
<text x="672" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">TRANSFORM</text>
<text x="866" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">VISUALIZATION</text>
<!-- Right strip — 3 vertical chevrons (Orchestration / Security / Observability) -->
<polygon points="972,40 1000,40 1000,172 986,184 972,172" fill="#3d4460"/>
<text x="986" y="112" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle" transform="rotate(-90 986 112)">ORCHESTRATION</text>
<polygon points="972,184 986,196 1000,184 1000,316 986,328 972,316" fill="#b85450"/>
<text x="986" y="256" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle" transform="rotate(-90 986 256)">SECURITY</text>
<polygon points="972,328 986,340 1000,328 1000,472 972,472" fill="#3d4460"/>
<text x="986" y="400" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle" transform="rotate(-90 986 400)">OBSERVABILITY</text>
<!-- Structural zones -->
<rect x="4" y="40" width="184" height="336" rx="6" fill="rgba(245,245,245,0.03)" stroke="rgba(245,245,245,0.22)" stroke-width="0.8" stroke-dasharray="6,3"/>
<text x="96" y="52" fill="#bfc0c0" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">SOURCES</text>
<rect x="196" y="40" width="768" height="336" rx="8" fill="rgba(245,245,245,0.03)" stroke="rgba(245,245,245,0.18)" stroke-width="1.2"/>
<rect x="4" y="388" width="960" height="40" rx="6" fill="rgba(217,122,120,0.12)" stroke="rgba(217,122,120,0.45)" stroke-width="0.8"/>
<!-- Arrows (drawn before nodes) -->
<path d="M 184,92 H 196 Q 200,92 200,96 V 128 Q 200,132 204,132 H 212" fill="none" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 184,172 H 196 Q 200,172 200,168 V 156 Q 200,152 204,152 H 212" fill="none" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 184,252 H 196 Q 200,252 200,248 V 176 Q 200,172 204,172 H 212" fill="none" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 184,332 H 196 Q 200,332 200,328 V 192 Q 200,188 204,188 H 212" fill="none" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<line x1="288" y1="96" x2="288" y2="120" stroke="#bfc0c0" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<line x1="480" y1="96" x2="480" y2="120" stroke="#bfc0c0" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<line x1="672" y1="96" x2="672" y2="120" stroke="#bfc0c0" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<line x1="364" y1="164" x2="404" y2="164" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 364,156 H 384 Q 388,156 388,164 V 248 Q 388,256 396,256 H 404" fill="none" stroke="#f08a59" stroke-width="1.2" marker-end="url(#arrow-accent)"/>
<line x1="480" y1="200" x2="480" y2="216" stroke="rgba(245,245,245,0.30)" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-dim)"/>
<path d="M 556,256 H 576 Q 580,256 580,248 V 168 Q 580,160 588,160 H 596" fill="none" stroke="#f08a59" stroke-width="1.2" marker-end="url(#arrow-accent)"/>
<line x1="748" y1="160" x2="790" y2="160" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<!-- Source nodes -->
<rect x="8" y="60" width="176" height="64" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="16" y="66" width="22" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.30)" stroke-width="0.8"/>
<text x="27" y="75" fill="rgba(245,245,245,0.65)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="62" y="66" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#f5f5f5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 6a8 3 0 1 0 16 0a8 3 0 1 0 -16 0"/>
<path d="M4 6v6a8 3 0 0 0 16 0v-6"/>
<path d="M4 12v6a8 3 0 0 0 16 0v-6"/>
</svg>
<text x="96" y="102" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">PostgreSQL</text>
<text x="96" y="114" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">CDC · SQL · API</text>
<rect x="8" y="140" width="176" height="64" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="16" y="146" width="22" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.30)" stroke-width="0.8"/>
<text x="27" y="155" fill="rgba(245,245,245,0.65)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="62" y="146" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#f5f5f5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"/>
<path d="M7 9l5 -5l5 5"/>
<path d="M12 4l0 12"/>
</svg>
<text x="96" y="182" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">SFTP drop</text>
<text x="96" y="194" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Batch · landing</text>
<rect x="8" y="220" width="176" height="64" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="16" y="226" width="22" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.30)" stroke-width="0.8"/>
<text x="27" y="235" fill="rgba(245,245,245,0.65)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="62" y="226" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#f5f5f5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="9"/>
<path d="M3.6 9h16.8M3.6 15h16.8"/>
<path d="M11.5 3a17 17 0 0 0 0 18M12.5 3a17 17 0 0 1 0 18"/>
</svg>
<text x="96" y="262" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Web forms</text>
<text x="96" y="274" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Survey · admin</text>
<rect x="8" y="300" width="176" height="64" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="16" y="306" width="22" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.30)" stroke-width="0.8"/>
<text x="27" y="315" fill="rgba(245,245,245,0.65)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="62" y="306" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#f5f5f5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 7a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3"/>
<path d="M3 15a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3l0 -2"/>
<path d="M7 8l0 .01"/><path d="M7 16l0 .01"/>
</svg>
<text x="96" y="342" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Legacy system</text>
<text x="96" y="354" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Trade · mainframe</text>
<!-- Airflow bar -->
<rect x="208" y="52" width="744" height="44" rx="4" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.18)" stroke-width="0.8"/>
<svg x="914" y="58" width="24" height="24" viewBox="0 0 24 24" fill="#bfc0c0">
<path d="M17.195 16.822l4.002-4.102C23.55 10.308 23.934 5.154 24 .43a.396.396 0 0 0-.246-.373.392.392 0 0 0-.437.09l-6.495 6.658-4.102-4.003C10.309.45 5.154.066.43 0H.423a.397.397 0 0 0-.277.683l6.658 6.494-4.003 4.103C.45 13.692.065 18.846 0 23.57a.398.398 0 0 0 .683.282l6.494-6.657 3.934 3.837.17.165c2.41 2.353 7.565 2.737 12.288 2.803h.006a.397.397 0 0 0 .277-.683l-6.657-6.495z"/>
</svg>
<text x="580" y="71" fill="#f5f5f5" font-size="10" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Orchestration / Workflow Automation</text>
<text x="580" y="84" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">(Apache Airflow)</text>
<!-- Cluster nodes -->
<rect x="212" y="120" width="152" height="80" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="220" y="126" width="30" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.30)" stroke-width="0.8"/>
<text x="235" y="135" fill="rgba(245,245,245,0.65)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">COLL</text>
<svg x="332" y="126" width="24" height="24" viewBox="0 0 24 24" fill="#f5f5f5">
<path d="M11.648 0a.093.093 0 0 0-.084.053A30.71 30.71 0 0 1 8.592 4.73c-2.09 2.728-5.145 6.466-5.145 10.364a8.201 8.201 0 0 0 8.201 8.2v-5.003c0-.106.087-.193.194-.193h2.81v-2.813c0-.106.087-.191.194-.191h5.004c0-3.9-3.056-7.636-5.145-10.364A30.712 30.712 0 0 1 11.732.053.094.094 0 0 0 11.648 0z"/>
</svg>
<text x="288" y="166" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Apache NiFi</text>
<text x="288" y="178" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Collect · route · transform</text>
<rect x="404" y="120" width="152" height="80" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="412" y="126" width="28" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.30)" stroke-width="0.8"/>
<text x="426" y="135" fill="rgba(245,245,245,0.65)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">VIRT</text>
<svg x="524" y="126" width="24" height="24" viewBox="0 0 24 24" fill="#f5f5f5">
<path d="M14.124 16.8529a.1615.1615 0 1 1 .1576.1614.1577.1577 0 0 1-.1576-.1614zm-5.607-.1576a.1614.1614 0 1 0 0 .3228.1614.1614 0 0 0 0-.3228zm10.1341-.6648v1.9869c-.031.5788-.524 1.0237-1.1029.9954h-.3843a5.0596 5.0596 0 0 1-1.1298 1.7178.3192.3192 0 0 0 0 .465l.2382.2191a.3036.3036 0 0 1 .0385.4304c-1.126 1.3835-2.9669 2.1521-5.0498 2.1521a6.575 6.575 0 0 1-4.8192-1.8985c-.0029-.0032-.0059-.0063-.0087-.0096a.6302.6302 0 0 1 .0548-.8896c.137-.1265.1371-.3462 0-.4727a4.944 4.944 0 0 1-1.126-1.714h-.3497c-.5797.0284-1.0737-.416-1.1068-.9954v-1.9869c.0351-.5779.5286-1.02 1.1068-.9915h.2728a5.7648 5.7648 0 0 1 2.0791-3.0936c-.4227-1.0991-1.1529-3.2551-1.226-5.0075C6.0229 4.4705 6.2189.078 7.8253.001c1.6064-.0768 1.3719 4.0275 1.0991 6.6946a32.732 32.732 0 0 0-.123 4.4503 6.994 6.994 0 0 1 2.4826-.4304 7.2414 7.2414 0 0 1 1.7371.2075z"/>
</svg>
<text x="480" y="166" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Trino</text>
<text x="480" y="178" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">SQL · data virtualization</text>
<!-- MinIO focal -->
<rect x="404" y="216" width="152" height="80" rx="8" fill="rgba(240,138,89,0.12)" stroke="#f08a59" stroke-width="1.2"/>
<rect x="412" y="222" width="36" height="12" rx="2" fill="transparent" stroke="rgba(240,138,89,0.55)" stroke-width="0.8"/>
<text x="430" y="231" fill="rgba(240,138,89,0.95)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STORE</text>
<svg x="524" y="222" width="24" height="24" viewBox="0 0 24 24" fill="#f08a59">
<path d="M13.2072.006c-.6216-.0478-1.2.1943-1.6211.582a2.15 2.15 0 0 0-.0938 3.0352l3.4082 3.5507a3.042 3.042 0 0 1-.664 4.6875l-.463.2383V7.2853a15.4198 15.4198 0 0 0-8.0174 10.4862v.0176l6.5487-3.3281v7.621L13.7794 24V13.6817l.8965-.4629a4.4432 4.4432 0 0 0 1.2207-7.0292l-3.371-3.5254a.7489.7489 0 0 1 .037-1.0547.7522.7522 0 0 1 1.0567.0371l.4668.4863-.006.0059 4.0704 4.2441a.0566.0566 0 0 0 .082 0 .06.06 0 0 0 0-.0703l-3.1406-5.1425-.1484.1425.1484-.1445C14.4945.3926 13.8287.0538 13.2072.006Z"/>
</svg>
<text x="480" y="262" fill="#f08a59" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">MinIO / S3</text>
<text x="480" y="276" fill="#f08a59" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" opacity="0.78">Object store · S3-API</text>
<rect x="596" y="120" width="152" height="80" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="604" y="126" width="32" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.30)" stroke-width="0.8"/>
<text x="620" y="135" fill="rgba(245,245,245,0.65)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ANLZ</text>
<svg x="716" y="126" width="24" height="24" viewBox="0 0 24 24" fill="#f5f5f5">
<path d="M7.157 22.201A1.784 1.799 0 0 1 5.374 24a1.784 1.799 0 0 1-1.784-1.799 1.784 1.799 0 0 1 1.784-1.799 1.784 1.799 0 0 1 1.783 1.799zM20.582 1.427a1.415 1.427 0 0 1-1.415 1.428 1.415 1.427 0 0 1-1.416-1.428A1.415 1.427 0 0 1 19.167 0a1.415 1.427 0 0 1 1.415 1.427zM4.992 3.336A1.047 1.056 0 0 1 3.946 4.39a1.047 1.056 0 0 1-1.047-1.055A1.047 1.056 0 0 1 3.946 2.28a1.047 1.056 0 0 1 1.046 1.056zm7.336 1.517c3.769 0 7.06 1.38 8.768 3.424a9.363 9.363 0 0 0-3.393-4.547 9.238 9.238 0 0 0-5.377-1.728A9.238 9.238 0 0 0 6.95 3.73a9.363 9.363 0 0 0-3.394 4.547c1.713-2.04 5.004-3.424 8.772-3.424zm.001 13.295c-3.768 0-7.06-1.381-8.768-3.425a9.363 9.363 0 0 0 3.394 4.547A9.238 9.238 0 0 0 12.33 21a9.238 9.238 0 0 0 5.377-1.729 9.363 9.363 0 0 0 3.393-4.547c-1.712 2.044-5.003 3.425-8.772 3.425z"/>
</svg>
<text x="672" y="166" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Notebooks</text>
<text x="672" y="178" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Jupyter · Python · R</text>
<rect x="790" y="120" width="152" height="80" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<rect x="798" y="126" width="28" height="12" rx="2" fill="transparent" stroke="rgba(245,245,245,0.30)" stroke-width="0.8"/>
<text x="812" y="135" fill="rgba(245,245,245,0.65)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">DASH</text>
<svg x="910" y="126" width="24" height="24" viewBox="0 0 24 24" fill="#f5f5f5">
<path d="M6.168 6.045C2.603 6.045 0 8.579 0 12.014c0 3.434 2.603 5.941 6.168 5.941 2.184 0 3.888-1.026 5.775-3.078 1.53 2.033 4.037 3.136 5.89 3.078 3.566 0 6.167-2.503 6.167-5.941 0-3.438-2.601-5.97-6.168-5.97-2.864 0-5.138 2.425-5.771 3.173-.76-.9-1.674-1.665-2.682-2.274-1.019-.588-2.084-.898-3.211-.898z"/>
</svg>
<text x="866" y="166" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Superset</text>
<text x="866" y="178" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Dashboards · reports</text>
<!-- Kubernetes label -->
<svg x="212" y="348" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#bfc0c0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M19.875 6.27a2.225 2.225 0 0 1 1.125 1.948v7.284c0 .809-.443 1.555-1.158 1.948l-6.75 3.98a2.5 2.5 0 0 1-2.184 0l-6.75-3.98a2.225 2.225 0 0 1-1.158-1.948v-7.284c0-.809.443-1.555 1.158-1.948l6.75-3.98a2.5 2.5 0 0 1 2.184 0z"/>
<circle cx="12" cy="12" r="3"/>
<line x1="12" y1="3" x2="12" y2="9"/><line x1="12" y1="15" x2="12" y2="21"/>
<line x1="20.2" y1="7.5" x2="14.9" y2="10.5"/><line x1="9.1" y1="13.5" x2="3.8" y2="16.5"/>
<line x1="20.2" y1="16.5" x2="14.9" y2="13.5"/><line x1="9.1" y1="10.5" x2="3.8" y2="7.5"/>
</svg>
<text x="236" y="362" fill="#bfc0c0" font-size="8" font-weight="500" font-family="'Geist', sans-serif">Kubernetes</text>
<!-- Cross-cutting bar #1 — Identity (Security row, y=388..428, color=#d97a78 lightened red for dark mode) -->
<svg x="16" y="398" width="20" height="20" viewBox="0 0 24 24" fill="#d97a78">
<path d="M12.0001.0036C5.376.0036 0 5.3796 0 12.0036c0 6.6276 5.376 12 12.0001 12 6.6275 0 12-5.3724 12-12 0-6.624-5.3725-12-12-12zm5.532 17.0496c-.612 0-3.972-3.144-5.532-5.046C10.452 13.9356 7.092 17.0796 6.48 17.0796c-1.788 0-2.052-3.564-2.052-5.076 0-1.512.264-5.076 2.052-5.076.612 0 3.972 3.144 5.5201 5.046 1.548-1.932 4.908-5.046 5.532-5.046 1.788 0 2.052 3.564 2.052 5.076 0 1.512-.264 5.076-2.052 5.076z"/>
</svg>
<text x="484" y="410" fill="#d97a78" font-size="10" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Identity Manager</text>
<text x="484" y="422" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">(Keycloak · LDAP · OIDC)</text>
<!-- Cross-cutting bar #2 — Monitoring (Observability row, y=432..472) -->
<rect x="4" y="432" width="960" height="40" rx="6" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.18)" stroke-width="0.8"/>
<svg x="16" y="442" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#bfc0c0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 3v18h18"/>
<path d="M7 14l3-3 2 2 5-5"/>
<path d="M14 8h3v3"/>
</svg>
<text x="484" y="454" fill="#f5f5f5" font-size="10" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Monitoring &amp; Observability</text>
<text x="484" y="466" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">(Prometheus · Grafana · Loki)</text>
<!-- Legend (pushed down past 2nd crosscut) -->
<line x1="40" y1="496" x2="952" y2="496" stroke="rgba(245,245,245,0.18)" stroke-width="0.8"/>
<text x="40" y="512" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<rect x="40" y="520" width="20" height="12" rx="3" fill="transparent" stroke="rgba(245,245,245,0.30)" stroke-width="1" stroke-dasharray="4,3"/>
<text x="68" y="530" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">External · outside cluster</text>
<rect x="232" y="520" width="20" height="12" rx="3" fill="rgba(245,245,245,0.06)" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<text x="260" y="530" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Kubernetes boundary</text>
<rect x="404" y="520" width="20" height="12" rx="3" fill="rgba(240,138,89,0.14)" stroke="#f08a59" stroke-width="1.2"/>
<text x="432" y="530" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Focal (storage hub)</text>
<line x1="552" y1="522" x2="572" y2="522" stroke="#f08a59" stroke-width="1.2" marker-end="url(#arrow-accent)"/>
<text x="580" y="527" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Primary path</text>
<line x1="680" y1="522" x2="700" y2="522" stroke="#bfc0c0" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<text x="708" y="527" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Orchestration trigger</text>
<line x1="836" y1="522" x2="856" y2="522" stroke="rgba(245,245,245,0.30)" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-dim)"/>
<text x="864" y="527" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Query read-back</text>
</svg>
</div>
</body>
</html>
@@ -0,0 +1,250 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>High-level architecture · Vertical chevrons (editorial)</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root { --color-paper:#f5f5f5; --color-paper-2:#ececec; --color-ink:#2d3142; --color-muted:#4f5d75; --color-soft:#7a8399; --color-rule:rgba(45,49,66,0.12); --color-accent:#eb6c36; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
.container { max-width: 1200px; margin: 0 auto; }
.header { margin-bottom: 2.5rem; }
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 72ch; }
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
svg { width: 100%; min-width: 960px; display: block; }
.cards { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1rem; margin-top: 1.5rem; }
@media (max-width: 900px) { .cards { grid-template-columns: 1fr; } }
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(45,49,66,0.08); }
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
.card-dot.coral { background: var(--color-accent); }
.card-dot.ink { background: var(--color-ink); }
.card-dot.muted { background: var(--color-muted); }
.card h3 { font-size: 0.875rem; font-weight: 600; }
.card ul { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; list-style: none; }
.card li { margin-bottom: 0.3rem; padding-left: 0.875rem; position: relative; }
.card li::before { content: '—'; position: absolute; left: 0; color: rgba(45,49,66,0.25); font-size: 0.75rem; }
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(45,49,66,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<p class="header-eyebrow">Architecture · Diagram Design · Parametric</p>
<h1>High-level architecture · Vertical Orchestration + Security</h1>
<p class="subtitle">Five horizontal phases (Sources → Ingestion → Storage → Transformation → Visualization) plus three cross-cutting verticals (Orchestration, Security, Observability) rendered as a reserved right strip. Two cross-cutting bars stack below the cluster — Identity and Monitoring — each paired 1:1 with a vertical chevron. The diagram is fully parametric: chevrons, sources, components, and connections drive every coordinate.</p>
</div>
<div class="diagram-container">
<svg viewBox="0 0 1000 600" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(45,49,66,0.10)"/>
</pattern>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#4f5d75"/></marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#eb6c36"/></marker>
<marker id="arrow-sm" markerWidth="6" markerHeight="5" refX="5" refY="2.5" orient="auto"><polygon points="0 0, 6 2.5, 0 5" fill="#4f5d75"/></marker>
<marker id="arrow-dim" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="rgba(45,49,66,0.45)"/></marker>
</defs>
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- Horizontal chevron banner -->
<polygon points="0,4 180,4 192,18 180,32 0,32" fill="#2d3142"/>
<polygon points="192,4 372,4 384,18 372,32 192,32 204,18" fill="#3d4460"/>
<polygon points="384,4 564,4 576,18 564,32 384,32 396,18" fill="#2d3142"/>
<polygon points="576,4 756,4 768,18 756,32 576,32 588,18" fill="#3d4460"/>
<polygon points="768,4 964,4 964,32 768,32 780,18" fill="#2d3142"/>
<text x="96" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">DATA SOURCES</text>
<text x="288" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">INGESTION</text>
<text x="480" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">STORAGE</text>
<text x="672" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">TRANSFORM</text>
<text x="866" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">VISUALIZATION</text>
<!-- Right strip — 3 vertical chevrons (Orchestration / Security / Observability) -->
<polygon points="972,40 1000,40 1000,172 986,184 972,172" fill="#2d3142"/>
<text x="986" y="112" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle" transform="rotate(-90 986 112)">ORCHESTRATION</text>
<polygon points="972,184 986,196 1000,184 1000,316 986,328 972,316" fill="#b85450"/>
<text x="986" y="256" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle" transform="rotate(-90 986 256)">SECURITY</text>
<polygon points="972,328 986,340 1000,328 1000,472 972,472" fill="#2d3142"/>
<text x="986" y="400" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle" transform="rotate(-90 986 400)">OBSERVABILITY</text>
<!-- Zones -->
<rect x="4" y="40" width="184" height="336" rx="6" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.20)" stroke-width="0.8" stroke-dasharray="6,3"/>
<text x="96" y="52" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">SOURCES</text>
<rect x="196" y="40" width="768" height="336" rx="8" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.18)" stroke-width="1.2"/>
<rect x="4" y="388" width="960" height="40" rx="6" fill="rgba(184,84,80,0.06)" stroke="rgba(184,84,80,0.35)" stroke-width="0.8"/>
<!-- Arrows -->
<path d="M 184,92 H 196 Q 200,92 200,96 V 128 Q 200,132 204,132 H 212" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 184,172 H 196 Q 200,172 200,168 V 156 Q 200,152 204,152 H 212" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 184,252 H 196 Q 200,252 200,248 V 176 Q 200,172 204,172 H 212" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 184,332 H 196 Q 200,332 200,328 V 192 Q 200,188 204,188 H 212" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<line x1="288" y1="96" x2="288" y2="120" stroke="#4f5d75" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<line x1="480" y1="96" x2="480" y2="120" stroke="#4f5d75" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<line x1="672" y1="96" x2="672" y2="120" stroke="#4f5d75" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<line x1="364" y1="164" x2="404" y2="164" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 364,156 H 384 Q 388,156 388,164 V 248 Q 388,256 396,256 H 404" fill="none" stroke="#eb6c36" stroke-width="1.2" marker-end="url(#arrow-accent)"/>
<line x1="480" y1="200" x2="480" y2="216" stroke="rgba(45,49,66,0.30)" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-dim)"/>
<path d="M 556,256 H 576 Q 580,256 580,248 V 168 Q 580,160 588,160 H 596" fill="none" stroke="#eb6c36" stroke-width="1.2" marker-end="url(#arrow-accent)"/>
<line x1="748" y1="160" x2="790" y2="160" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<!-- Source nodes -->
<rect x="8" y="60" width="176" height="64" rx="6" fill="white" stroke="rgba(45,49,66,0.22)" stroke-width="1"/>
<rect x="16" y="66" width="22" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="27" y="75" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="62" y="66" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#2d3142" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 6a8 3 0 1 0 16 0a8 3 0 1 0 -16 0"/><path d="M4 6v6a8 3 0 0 0 16 0v-6"/><path d="M4 12v6a8 3 0 0 0 16 0v-6"/></svg>
<text x="96" y="102" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">PostgreSQL</text>
<text x="96" y="114" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">CDC · SQL · API</text>
<rect x="8" y="140" width="176" height="64" rx="6" fill="white" stroke="rgba(45,49,66,0.22)" stroke-width="1"/>
<rect x="16" y="146" width="22" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="27" y="155" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="62" y="146" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#2d3142" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"/><path d="M7 9l5 -5l5 5"/><path d="M12 4l0 12"/></svg>
<text x="96" y="182" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">SFTP drop</text>
<text x="96" y="194" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Batch · landing</text>
<rect x="8" y="220" width="176" height="64" rx="6" fill="white" stroke="rgba(45,49,66,0.22)" stroke-width="1"/>
<rect x="16" y="226" width="22" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="27" y="235" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="62" y="226" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#2d3142" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M3.6 9h16.8M3.6 15h16.8"/><path d="M11.5 3a17 17 0 0 0 0 18M12.5 3a17 17 0 0 1 0 18"/></svg>
<text x="96" y="262" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Web forms</text>
<text x="96" y="274" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Survey · admin</text>
<rect x="8" y="300" width="176" height="64" rx="6" fill="white" stroke="rgba(45,49,66,0.22)" stroke-width="1"/>
<rect x="16" y="306" width="22" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="27" y="315" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="62" y="306" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#2d3142" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3"/><path d="M3 15a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3l0 -2"/><path d="M7 8l0 .01"/><path d="M7 16l0 .01"/></svg>
<text x="96" y="342" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Legacy system</text>
<text x="96" y="354" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Trade · mainframe</text>
<!-- Airflow bar -->
<rect x="208" y="52" width="744" height="44" rx="4" fill="rgba(45,49,66,0.05)" stroke="rgba(45,49,66,0.18)" stroke-width="0.8"/>
<svg x="914" y="58" width="24" height="24" viewBox="0 0 24 24" fill="#4f5d75"><path d="M17.195 16.822l4.002-4.102C23.55 10.308 23.934 5.154 24 .43a.396.396 0 0 0-.246-.373.392.392 0 0 0-.437.09l-6.495 6.658-4.102-4.003C10.309.45 5.154.066.43 0H.423a.397.397 0 0 0-.277.683l6.658 6.494-4.003 4.103C.45 13.692.065 18.846 0 23.57a.398.398 0 0 0 .683.282l6.494-6.657 3.934 3.837.17.165c2.41 2.353 7.565 2.737 12.288 2.803h.006a.397.397 0 0 0 .277-.683l-6.657-6.495z"/></svg>
<text x="580" y="71" fill="#2d3142" font-size="10" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Orchestration / Workflow Automation</text>
<text x="580" y="84" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">(Apache Airflow)</text>
<!-- Cluster nodes -->
<rect x="212" y="120" width="152" height="80" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="220" y="126" width="30" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="235" y="135" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">COLL</text>
<svg x="332" y="126" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142"><path d="M11.648 0a.093.093 0 0 0-.084.053A30.71 30.71 0 0 1 8.592 4.73c-2.09 2.728-5.145 6.466-5.145 10.364a8.201 8.201 0 0 0 8.201 8.2v-5.003c0-.106.087-.193.194-.193h2.81v-2.813c0-.106.087-.191.194-.191h5.004c0-3.9-3.056-7.636-5.145-10.364A30.712 30.712 0 0 1 11.732.053.094.094 0 0 0 11.648 0z"/></svg>
<text x="288" y="166" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Apache NiFi</text>
<text x="288" y="178" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Collect · route · transform</text>
<rect x="404" y="120" width="152" height="80" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="412" y="126" width="28" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="426" y="135" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">VIRT</text>
<svg x="524" y="126" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142"><path d="M14.124 16.8529a.1615.1615 0 1 1 .1576.1614.1577.1577 0 0 1-.1576-.1614zm-5.607-.1576a.1614.1614 0 1 0 0 .3228.1614.1614 0 0 0 0-.3228zm10.1341-.6648v1.9869c-.031.5788-.524 1.0237-1.1029.9954h-.3843a5.0596 5.0596 0 0 1-1.1298 1.7178.3192.3192 0 0 0 0 .465l.2382.2191a.3036.3036 0 0 1 .0385.4304c-1.126 1.3835-2.9669 2.1521-5.0498 2.1521a6.575 6.575 0 0 1-4.8192-1.8985c-.0029-.0032-.0059-.0063-.0087-.0096a.6302.6302 0 0 1 .0548-.8896c.137-.1265.1371-.3462 0-.4727a4.944 4.944 0 0 1-1.126-1.714h-.3497c-.5797.0284-1.0737-.416-1.1068-.9954v-1.9869c.0351-.5779.5286-1.02 1.1068-.9915h.2728a5.7648 5.7648 0 0 1 2.0791-3.0936c-.4227-1.0991-1.1529-3.2551-1.226-5.0075C6.0229 4.4705 6.2189.078 7.8253.001c1.6064-.0768 1.3719 4.0275 1.0991 6.6946a32.732 32.732 0 0 0-.123 4.4503 6.994 6.994 0 0 1 2.4826-.4304 7.2414 7.2414 0 0 1 1.7371.2075z"/></svg>
<text x="480" y="166" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Trino</text>
<text x="480" y="178" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">SQL · data virtualization</text>
<rect x="404" y="216" width="152" height="80" rx="8" fill="rgba(235,108,54,0.08)" stroke="#eb6c36" stroke-width="1.2"/>
<rect x="412" y="222" width="36" height="12" rx="2" fill="transparent" stroke="rgba(235,108,54,0.50)" stroke-width="0.8"/>
<text x="430" y="231" fill="rgba(235,108,54,0.80)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STORE</text>
<svg x="524" y="222" width="24" height="24" viewBox="0 0 24 24" fill="#eb6c36"><path d="M13.2072.006c-.6216-.0478-1.2.1943-1.6211.582a2.15 2.15 0 0 0-.0938 3.0352l3.4082 3.5507a3.042 3.042 0 0 1-.664 4.6875l-.463.2383V7.2853a15.4198 15.4198 0 0 0-8.0174 10.4862v.0176l6.5487-3.3281v7.621L13.7794 24V13.6817l.8965-.4629a4.4432 4.4432 0 0 0 1.2207-7.0292l-3.371-3.5254a.7489.7489 0 0 1 .037-1.0547.7522.7522 0 0 1 1.0567.0371l.4668.4863-.006.0059 4.0704 4.2441a.0566.0566 0 0 0 .082 0 .06.06 0 0 0 0-.0703l-3.1406-5.1425-.1484.1425.1484-.1445C14.4945.3926 13.8287.0538 13.2072.006Z"/></svg>
<text x="480" y="262" fill="#eb6c36" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">MinIO / S3</text>
<text x="480" y="276" fill="#eb6c36" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" opacity="0.75">Object store · S3-API</text>
<rect x="596" y="120" width="152" height="80" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="604" y="126" width="32" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="620" y="135" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ANLZ</text>
<svg x="716" y="126" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142"><path d="M7.157 22.201A1.784 1.799 0 0 1 5.374 24a1.784 1.799 0 0 1-1.784-1.799 1.784 1.799 0 0 1 1.784-1.799 1.784 1.799 0 0 1 1.783 1.799zM20.582 1.427a1.415 1.427 0 0 1-1.415 1.428 1.415 1.427 0 0 1-1.416-1.428A1.415 1.427 0 0 1 19.167 0a1.415 1.427 0 0 1 1.415 1.427zM4.992 3.336A1.047 1.056 0 0 1 3.946 4.39a1.047 1.056 0 0 1-1.047-1.055A1.047 1.056 0 0 1 3.946 2.28a1.047 1.056 0 0 1 1.046 1.056zm7.336 1.517c3.769 0 7.06 1.38 8.768 3.424a9.363 9.363 0 0 0-3.393-4.547 9.238 9.238 0 0 0-5.377-1.728A9.238 9.238 0 0 0 6.95 3.73a9.363 9.363 0 0 0-3.394 4.547c1.713-2.04 5.004-3.424 8.772-3.424z"/></svg>
<text x="672" y="166" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Notebooks</text>
<text x="672" y="178" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Jupyter · Python · R</text>
<rect x="790" y="120" width="152" height="80" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="798" y="126" width="28" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="812" y="135" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">DASH</text>
<svg x="910" y="126" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142"><path d="M6.168 6.045C2.603 6.045 0 8.579 0 12.014c0 3.434 2.603 5.941 6.168 5.941 2.184 0 3.888-1.026 5.775-3.078 1.53 2.033 4.037 3.136 5.89 3.078 3.566 0 6.167-2.503 6.167-5.941 0-3.438-2.601-5.97-6.168-5.97-2.864 0-5.138 2.425-5.771 3.173-.76-.9-1.674-1.665-2.682-2.274-1.019-.588-2.084-.898-3.211-.898z"/></svg>
<text x="866" y="166" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Superset</text>
<text x="866" y="178" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Dashboards · reports</text>
<!-- Kubernetes label -->
<svg x="212" y="348" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#4f5d75" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19.875 6.27a2.225 2.225 0 0 1 1.125 1.948v7.284c0 .809-.443 1.555-1.158 1.948l-6.75 3.98a2.5 2.5 0 0 1-2.184 0l-6.75-3.98a2.225 2.225 0 0 1-1.158-1.948v-7.284c0-.809.443-1.555 1.158-1.948l6.75-3.98a2.5 2.5 0 0 1 2.184 0z"/><circle cx="12" cy="12" r="3"/><line x1="12" y1="3" x2="12" y2="9"/><line x1="12" y1="15" x2="12" y2="21"/><line x1="20.2" y1="7.5" x2="14.9" y2="10.5"/><line x1="9.1" y1="13.5" x2="3.8" y2="16.5"/><line x1="20.2" y1="16.5" x2="14.9" y2="13.5"/><line x1="9.1" y1="10.5" x2="3.8" y2="7.5"/></svg>
<text x="236" y="362" fill="#4f5d75" font-size="8" font-weight="500" font-family="'Geist', sans-serif">Kubernetes</text>
<!-- Cross-cutting bar #1 — Identity (color=#b85450 rust-red) -->
<svg x="16" y="398" width="20" height="20" viewBox="0 0 24 24" fill="#b85450"><path d="M12.0001.0036C5.376.0036 0 5.3796 0 12.0036c0 6.6276 5.376 12 12.0001 12 6.6275 0 12-5.3724 12-12 0-6.624-5.3725-12-12-12zm5.532 17.0496c-.612 0-3.972-3.144-5.532-5.046C10.452 13.9356 7.092 17.0796 6.48 17.0796c-1.788 0-2.052-3.564-2.052-5.076 0-1.512.264-5.076 2.052-5.076.612 0 3.972 3.144 5.5201 5.046 1.548-1.932 4.908-5.046 5.532-5.046 1.788 0 2.052 3.564 2.052 5.076 0 1.512-.264 5.076-2.052 5.076z"/></svg>
<text x="484" y="410" fill="#b85450" font-size="10" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Identity Manager</text>
<text x="484" y="422" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">(Keycloak · LDAP · OIDC)</text>
<!-- Cross-cutting bar #2 — Monitoring -->
<rect x="4" y="432" width="960" height="40" rx="6" fill="rgba(45,49,66,0.05)" stroke="rgba(45,49,66,0.20)" stroke-width="0.8"/>
<svg x="16" y="442" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#4f5d75" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3v18h18"/><path d="M7 14l3-3 2 2 5-5"/><path d="M14 8h3v3"/></svg>
<text x="484" y="454" fill="#2d3142" font-size="10" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Monitoring &amp; Observability</text>
<text x="484" y="466" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">(Prometheus · Grafana · Loki)</text>
<!-- Legend (pushed down past 2nd crosscut) -->
<line x1="40" y1="496" x2="952" y2="496" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="40" y="512" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<rect x="40" y="520" width="20" height="12" rx="3" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="1" stroke-dasharray="4,3"/>
<text x="68" y="530" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">External · outside cluster</text>
<rect x="232" y="520" width="20" height="12" rx="3" fill="rgba(45,49,66,0.04)" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<text x="260" y="530" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Kubernetes boundary</text>
<rect x="404" y="520" width="20" height="12" rx="3" fill="rgba(235,108,54,0.10)" stroke="#eb6c36" stroke-width="1.2"/>
<text x="432" y="530" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Focal (storage hub)</text>
<line x1="552" y1="522" x2="572" y2="522" stroke="#eb6c36" stroke-width="1.2" marker-end="url(#arrow-accent)"/>
<text x="580" y="527" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Primary path</text>
<line x1="680" y1="522" x2="700" y2="522" stroke="#4f5d75" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<text x="708" y="527" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Orchestration trigger</text>
<line x1="836" y1="522" x2="856" y2="522" stroke="rgba(45,49,66,0.30)" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-dim)"/>
<text x="864" y="527" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Query read-back</text>
</svg>
</div>
<div class="cards">
<div class="card">
<p class="eyebrow">Vertical · Orchestration</p>
<div class="card-header">
<div class="card-dot ink"></div>
<h3>Workflow automation</h3>
</div>
<ul>
<li>Apache Airflow runs as a full-width <em>bar</em> at the top of the cluster — visible to every cluster node</li>
<li>Trigger drops are always dashed (style: trigger), originating from the bar's bottom edge</li>
<li>The vertical Orchestration chevron in the right strip labels the cross-cutting column</li>
<li>One bar component per vertical chevron — enforced by the pairing rule</li>
</ul>
</div>
<div class="card">
<p class="eyebrow">Storage · MinIO / S3 (focal)</p>
<div class="card-header">
<div class="card-dot coral"></div>
<h3>Object store hub</h3>
</div>
<ul>
<li>Single focal node — accent border, accent ink, accent connectors</li>
<li>Every edge touching the focal node is automatically <em>style: primary</em> (accent stroke)</li>
<li>Trino virtualizes MinIO buckets — read-back drawn as <em>style: query</em> (dashed dim)</li>
<li>Notebooks consume curated data; their output writes back as Parquet</li>
</ul>
</div>
<div class="card">
<p class="eyebrow">Verticals · Security + Observability</p>
<div class="card-header">
<div class="card-dot muted"></div>
<h3>Cross-cutting concerns</h3>
</div>
<ul>
<li>Multiple <em>cross-cutting</em> bars stack below the cluster, one row per concern (44 px each)</li>
<li>Each crosscut pairs 1:1 with a vertical chevron in the right strip (count constraint)</li>
<li>Bars emit no individual connectors — semantic is "applies to everything above"</li>
<li>Components carry an optional <code>color</code> override (Identity Manager rendered in rust-red here) — applies to border, icon, and name; connectors stay topology-driven</li>
</ul>
</div>
</div>
<div class="footer">
<span>High-level architecture · Parametric (chevrons + sources + components → SVG)</span>
<span>5 horizontal phases × 3 vertical chevrons · 2 cross-cutting bars · reserved right strip</span>
</div>
</div>
</body>
</html>
@@ -0,0 +1,323 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>High-level architecture · Vertical chevrons (Orchestration + Security)</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--color-paper: #f5f5f5;
--color-ink: #2d3142;
--color-muted: #4f5d75;
--color-accent: #eb6c36;
--font-sans: 'Geist', system-ui, sans-serif;
--font-serif: 'Instrument Serif', serif;
--font-mono: 'Geist Mono', ui-monospace, monospace;
}
body {
font-family: var(--font-sans);
background: var(--color-paper);
color: var(--color-ink);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 3rem 2rem;
}
.frame { max-width: 1200px; width: 100%; }
.eyebrow {
font-family: var(--font-mono);
font-size: 0.66rem;
font-weight: 500;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--color-muted);
margin-bottom: 0.5rem;
}
h1 {
font-family: var(--font-serif);
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
font-weight: 400;
letter-spacing: -0.02em;
line-height: 1.15;
margin-bottom: 1.5rem;
}
svg { width: 100%; min-width: 960px; display: block; }
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Architecture · Diagram Design · Parametric</p>
<h1>High-level architecture · Vertical Orchestration + Security</h1>
<svg viewBox="0 0 1000 600" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(45,49,66,0.10)"/>
</pattern>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="#4f5d75"/>
</marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="#eb6c36"/>
</marker>
<marker id="arrow-sm" markerWidth="6" markerHeight="5" refX="5" refY="2.5" orient="auto">
<polygon points="0 0, 6 2.5, 0 5" fill="#4f5d75"/>
</marker>
<marker id="arrow-dim" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="rgba(45,49,66,0.45)"/>
</marker>
</defs>
<!-- Background -->
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- ═══════════════════════════════════════════
PHASE CHEVRON BANNER (horizontal, 5 phases)
effective_w=964 (8-px margin to right strip), widths=[192,192,192,192,196], cx=[96,288,480,672,866]
═══════════════════════════════════════════ -->
<polygon points="0,4 180,4 192,18 180,32 0,32" fill="#2d3142"/>
<polygon points="192,4 372,4 384,18 372,32 192,32 204,18" fill="#3d4460"/>
<polygon points="384,4 564,4 576,18 564,32 384,32 396,18" fill="#2d3142"/>
<polygon points="576,4 756,4 768,18 756,32 576,32 588,18" fill="#3d4460"/>
<polygon points="768,4 964,4 964,32 768,32 780,18" fill="#2d3142"/>
<text x="96" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">DATA SOURCES</text>
<text x="288" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">INGESTION</text>
<text x="480" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">STORAGE</text>
<text x="672" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">TRANSFORM</text>
<text x="866" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">VISUALIZATION</text>
<!-- ═══════════════════════════════════════════
RIGHT STRIP — vertical chevrons (Orchestration / Security / Observability)
strip_x=972 w=28, n_cross=2 → strip_y_bot=472, heights=[144,144,144]
layout: [40..184, 184..328, 328..472]
═══════════════════════════════════════════ -->
<!-- Orchestration (first/topmost): flat top, point at bottom -->
<polygon points="972,40 1000,40 1000,172 986,184 972,172" fill="#2d3142"/>
<text x="986" y="112" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle" transform="rotate(-90 986 112)">ORCHESTRATION</text>
<!-- Security (middle): notch on top, point on bottom — chevron color override #b85450 to pair with Identity bar -->
<polygon points="972,184 986,196 1000,184 1000,316 986,328 972,316" fill="#b85450"/>
<text x="986" y="256" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle" transform="rotate(-90 986 256)">SECURITY</text>
<!-- Observability (last/bottommost): notch on top, flat bottom -->
<polygon points="972,328 986,340 1000,328 1000,472 972,472" fill="#2d3142"/>
<text x="986" y="400" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle" transform="rotate(-90 986 400)">OBSERVABILITY</text>
<!-- ═══════════════════════════════════════════
STRUCTURAL ZONES
═══════════════════════════════════════════ -->
<!-- Sources zone (dashed external) -->
<rect x="4" y="40" width="184" height="336" rx="6" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.20)" stroke-width="0.8" stroke-dasharray="6,3"/>
<text x="96" y="52" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">SOURCES</text>
<!-- Cluster boundary (solid) — right edge at 964 (8-px gap to strip at 972) -->
<rect x="196" y="40" width="768" height="336" rx="8" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.18)" stroke-width="1.2"/>
<!-- Identity / cross-cutting bar (color override: #b85450 rust-red) -->
<rect x="4" y="388" width="960" height="40" rx="6" fill="rgba(184,84,80,0.06)" stroke="rgba(184,84,80,0.35)" stroke-width="0.8"/>
<!-- ═══════════════════════════════════════════
ARROWS (drawn before nodes — z-order rule)
═══════════════════════════════════════════ -->
<!-- Sources → NiFi (staggered entry on NiFi left edge x=212; entry_y = 132,152,172,188) -->
<path d="M 184,92 H 196 Q 200,92 200,96 V 128 Q 200,132 204,132 H 212" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 184,172 H 196 Q 200,172 200,168 V 156 Q 200,152 204,152 H 212" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 184,252 H 196 Q 200,252 200,248 V 176 Q 200,172 204,172 H 212" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 184,332 H 196 Q 200,332 200,328 V 192 Q 200,188 204,188 H 212" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<!-- Airflow bar → NiFi, Trino, Notebooks (trigger drops at each node's cx, from bar.bottom=96 to node.top=120) -->
<line x1="288" y1="96" x2="288" y2="120" stroke="#4f5d75" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<line x1="480" y1="96" x2="480" y2="120" stroke="#4f5d75" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<line x1="672" y1="96" x2="672" y2="120" stroke="#4f5d75" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<!-- NiFi → Trino (secondary, straight at cy=164) -->
<line x1="364" y1="164" x2="404" y2="164" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<!-- NiFi → MinIO (PRIMARY accent — trunk at boundary x=388, drop to MinIO cy=256) -->
<path d="M 364,156 H 384 Q 388,156 388,164 V 248 Q 388,256 396,256 H 404" fill="none" stroke="#eb6c36" stroke-width="1.2" marker-end="url(#arrow-accent)"/>
<!-- Trino ↔ MinIO (query read-back, dashed) -->
<line x1="480" y1="200" x2="480" y2="216" stroke="rgba(45,49,66,0.30)" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-dim)"/>
<!-- MinIO → Notebooks (PRIMARY accent — trunk at boundary x=580, rise to Notebooks cy=160) -->
<path d="M 556,256 H 576 Q 580,256 580,248 V 168 Q 580,160 588,160 H 596" fill="none" stroke="#eb6c36" stroke-width="1.2" marker-end="url(#arrow-accent)"/>
<!-- Notebooks → Superset (secondary, straight at cy=160) -->
<line x1="748" y1="160" x2="790" y2="160" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<!-- ═══════════════════════════════════════════
SOURCE NODES (w=176, h=64, x=8)
═══════════════════════════════════════════ -->
<!-- PostgreSQL (db) -->
<rect x="8" y="60" width="176" height="64" rx="6" fill="white" stroke="rgba(45,49,66,0.22)" stroke-width="1"/>
<rect x="16" y="66" width="22" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="27" y="75" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="62" y="66" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#2d3142" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 6a8 3 0 1 0 16 0a8 3 0 1 0 -16 0"/>
<path d="M4 6v6a8 3 0 0 0 16 0v-6"/>
<path d="M4 12v6a8 3 0 0 0 16 0v-6"/>
</svg>
<text x="96" y="102" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">PostgreSQL</text>
<text x="96" y="114" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">CDC · SQL · API</text>
<!-- SFTP (ftp) -->
<rect x="8" y="140" width="176" height="64" rx="6" fill="white" stroke="rgba(45,49,66,0.22)" stroke-width="1"/>
<rect x="16" y="146" width="22" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="27" y="155" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="62" y="146" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#2d3142" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"/>
<path d="M7 9l5 -5l5 5"/>
<path d="M12 4l0 12"/>
</svg>
<text x="96" y="182" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">SFTP drop</text>
<text x="96" y="194" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Batch · landing</text>
<!-- Web forms (web) -->
<rect x="8" y="220" width="176" height="64" rx="6" fill="white" stroke="rgba(45,49,66,0.22)" stroke-width="1"/>
<rect x="16" y="226" width="22" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="27" y="235" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="62" y="226" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#2d3142" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="9"/>
<path d="M3.6 9h16.8M3.6 15h16.8"/>
<path d="M11.5 3a17 17 0 0 0 0 18M12.5 3a17 17 0 0 1 0 18"/>
</svg>
<text x="96" y="262" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Web forms</text>
<text x="96" y="274" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Survey · admin</text>
<!-- Legacy (legacy) -->
<rect x="8" y="300" width="176" height="64" rx="6" fill="white" stroke="rgba(45,49,66,0.22)" stroke-width="1"/>
<rect x="16" y="306" width="22" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="27" y="315" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="62" y="306" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#2d3142" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 7a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3"/>
<path d="M3 15a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3l0 -2"/>
<path d="M7 8l0 .01"/><path d="M7 16l0 .01"/>
</svg>
<text x="96" y="342" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Legacy system</text>
<text x="96" y="354" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Trade · mainframe</text>
<!-- ═══════════════════════════════════════════
AIRFLOW BAR (Orchestration, x=208 y=52 w=744 h=44 — 12-px inside cluster on both sides)
═══════════════════════════════════════════ -->
<rect x="208" y="52" width="744" height="44" rx="4" fill="rgba(45,49,66,0.05)" stroke="rgba(45,49,66,0.18)" stroke-width="0.8"/>
<svg x="914" y="58" width="24" height="24" viewBox="0 0 24 24" fill="#4f5d75">
<path d="M17.195 16.822l4.002-4.102C23.55 10.308 23.934 5.154 24 .43a.396.396 0 0 0-.246-.373.392.392 0 0 0-.437.09l-6.495 6.658-4.102-4.003C10.309.45 5.154.066.43 0H.423a.397.397 0 0 0-.277.683l6.658 6.494-4.003 4.103C.45 13.692.065 18.846 0 23.57a.398.398 0 0 0 .683.282l6.494-6.657 3.934 3.837.17.165c2.41 2.353 7.565 2.737 12.288 2.803h.006a.397.397 0 0 0 .277-.683l-6.657-6.495z"/>
</svg>
<text x="580" y="71" fill="#2d3142" font-size="10" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Orchestration / Workflow Automation</text>
<text x="580" y="84" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">(Apache Airflow)</text>
<!-- ═══════════════════════════════════════════
CLUSTER NODES (w=152, h=80; first_top_y=120)
═══════════════════════════════════════════ -->
<!-- NiFi (Ingestion, cx=288) -->
<rect x="212" y="120" width="152" height="80" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="220" y="126" width="30" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="235" y="135" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">COLL</text>
<svg x="332" y="126" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142">
<path d="M11.648 0a.093.093 0 0 0-.084.053A30.71 30.71 0 0 1 8.592 4.73c-2.09 2.728-5.145 6.466-5.145 10.364a8.201 8.201 0 0 0 8.201 8.2v-5.003c0-.106.087-.193.194-.193h2.81v-2.813c0-.106.087-.191.194-.191h5.004c0-3.9-3.056-7.636-5.145-10.364A30.712 30.712 0 0 1 11.732.053.094.094 0 0 0 11.648 0zm-1.632 3.867c.05 0 .08.034.037.112-.11.197-.218.397-.328.593-.396.702-.819 1.389-1.23 2.08-.196-.032-.39-.06-.585-.088.495-.651 1-1.296 1.48-1.959.153-.209.302-.423.454-.634a.24.24 0 0 1 .172-.104zM7.44 7.186c.221.035.444.076.666.119-.073.129-.15.256-.223.383a29.073 29.073 0 0 0-1.625 3.261c-.874 2.123-1.383 4.444-.77 6.707a8.222 8.222 0 0 0 2.217 3.74c.083.083-.02.216-.119.155a7.568 7.568 0 0 1-.93-.686A7.674 7.674 0 0 1 4.1 16.248c-.329-2.156.387-4.246 1.418-6.115a27.44 27.44 0 0 1 1.92-2.947z"/>
</svg>
<text x="288" y="166" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Apache NiFi</text>
<text x="288" y="178" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Collect · route · transform</text>
<!-- Trino (Storage col, top row, cx=480, y=120) -->
<rect x="404" y="120" width="152" height="80" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="412" y="126" width="28" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="426" y="135" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">VIRT</text>
<svg x="524" y="126" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142">
<path d="M14.124 16.8529a.1615.1615 0 1 1 .1576.1614.1577.1577 0 0 1-.1576-.1614zm-5.607-.1576a.1614.1614 0 1 0 0 .3228.1614.1614 0 0 0 0-.3228zm10.1341-.6648v1.9869c-.031.5788-.524 1.0237-1.1029.9954h-.3843a5.0596 5.0596 0 0 1-1.1298 1.7178.3192.3192 0 0 0 0 .465l.2382.2191a.3036.3036 0 0 1 .0385.4304c-1.126 1.3835-2.9669 2.1521-5.0498 2.1521a6.575 6.575 0 0 1-4.8192-1.8985c-.0029-.0032-.0059-.0063-.0087-.0096a.6302.6302 0 0 1 .0548-.8896c.137-.1265.1371-.3462 0-.4727a4.944 4.944 0 0 1-1.126-1.714h-.3497c-.5797.0284-1.0737-.416-1.1068-.9954v-1.9869c.0351-.5779.5286-1.02 1.1068-.9915h.2728a5.7648 5.7648 0 0 1 2.0791-3.0936c-.4227-1.0991-1.1529-3.2551-1.226-5.0075C6.0229 4.4705 6.2189.078 7.8253.001c1.6064-.0768 1.3719 4.0275 1.0991 6.6946a32.732 32.732 0 0 0-.123 4.4503 6.994 6.994 0 0 1 2.4826-.4304 7.2414 7.2414 0 0 1 1.7371.2075c.2614-1.2682.8762-3.574 2.0292-5.1958 1.6717-2.352 3.4357-4.7808 4.6116-4.1006 1.176.6802-.3074 3.1398-1.3297 4.4272-1.0222 1.2874-2.7862 3.2089-3.3742 4.2274-.2114.3843-.4304.8032-.5956 1.1529a5.7375 5.7375 0 0 1 2.9169 3.6125h.073v-2.3058a.3075.3075 0 0 0-.1806-.2844.9148.9148 0 0 1-.5573-.8148 1.0184 1.0184 0 0 1 .9045-.9044c.5593-.0598 1.061.3452 1.1208.9044a.9187.9187 0 0 1-.5534.8148.3074.3074 0 0 0-.1691.2844v2.1522a.3113.3113 0 0 0 .1691.2805.9724.9724 0 0 1 .5648.857z"/>
</svg>
<text x="480" y="166" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Trino</text>
<text x="480" y="178" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">SQL · data virtualization</text>
<!-- MinIO / S3 (Storage col, bottom row, FOCAL, cx=480, y=216) -->
<rect x="404" y="216" width="152" height="80" rx="8" fill="rgba(235,108,54,0.08)" stroke="#eb6c36" stroke-width="1.2"/>
<rect x="412" y="222" width="36" height="12" rx="2" fill="transparent" stroke="rgba(235,108,54,0.50)" stroke-width="0.8"/>
<text x="430" y="231" fill="rgba(235,108,54,0.80)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STORE</text>
<svg x="524" y="222" width="24" height="24" viewBox="0 0 24 24" fill="#eb6c36">
<path d="M13.2072.006c-.6216-.0478-1.2.1943-1.6211.582a2.15 2.15 0 0 0-.0938 3.0352l3.4082 3.5507a3.042 3.042 0 0 1-.664 4.6875l-.463.2383V7.2853a15.4198 15.4198 0 0 0-8.0174 10.4862v.0176l6.5487-3.3281v7.621L13.7794 24V13.6817l.8965-.4629a4.4432 4.4432 0 0 0 1.2207-7.0292l-3.371-3.5254a.7489.7489 0 0 1 .037-1.0547.7522.7522 0 0 1 1.0567.0371l.4668.4863-.006.0059 4.0704 4.2441a.0566.0566 0 0 0 .082 0 .06.06 0 0 0 0-.0703l-3.1406-5.1425-.1484.1425.1484-.1445C14.4945.3926 13.8287.0538 13.2072.006Zm-.9024 9.8652v2.9941l-4.1523 2.1484a13.9787 13.9787 0 0 1 2.7676-3.9277 14.1784 14.1784 0 0 1 1.3847-1.2148z"/>
</svg>
<text x="480" y="262" fill="#eb6c36" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">MinIO / S3</text>
<text x="480" y="276" fill="#eb6c36" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" opacity="0.75">Object store · S3-API</text>
<!-- Notebooks (Transformation, cx=672) -->
<rect x="596" y="120" width="152" height="80" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="604" y="126" width="32" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="620" y="135" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ANLZ</text>
<svg x="716" y="126" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142">
<path d="M7.157 22.201A1.784 1.799 0 0 1 5.374 24a1.784 1.799 0 0 1-1.784-1.799 1.784 1.799 0 0 1 1.784-1.799 1.784 1.799 0 0 1 1.783 1.799zM20.582 1.427a1.415 1.427 0 0 1-1.415 1.428 1.415 1.427 0 0 1-1.416-1.428A1.415 1.427 0 0 1 19.167 0a1.415 1.427 0 0 1 1.415 1.427zM4.992 3.336A1.047 1.056 0 0 1 3.946 4.39a1.047 1.056 0 0 1-1.047-1.055A1.047 1.056 0 0 1 3.946 2.28a1.047 1.056 0 0 1 1.046 1.056zm7.336 1.517c3.769 0 7.06 1.38 8.768 3.424a9.363 9.363 0 0 0-3.393-4.547 9.238 9.238 0 0 0-5.377-1.728A9.238 9.238 0 0 0 6.95 3.73a9.363 9.363 0 0 0-3.394 4.547c1.713-2.04 5.004-3.424 8.772-3.424zm.001 13.295c-3.768 0-7.06-1.381-8.768-3.425a9.363 9.363 0 0 0 3.394 4.547A9.238 9.238 0 0 0 12.33 21a9.238 9.238 0 0 0 5.377-1.729 9.363 9.363 0 0 0 3.393-4.547c-1.712 2.044-5.003 3.425-8.772 3.425z"/>
</svg>
<text x="672" y="166" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Notebooks</text>
<text x="672" y="178" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Jupyter · Python · R</text>
<!-- Superset (Visualization, cx=866) -->
<rect x="790" y="120" width="152" height="80" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="798" y="126" width="28" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="812" y="135" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">DASH</text>
<svg x="910" y="126" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142">
<path d="M6.168 6.045C2.603 6.045 0 8.579 0 12.014c0 3.434 2.603 5.941 6.168 5.941 2.184 0 3.888-1.026 5.775-3.078 1.53 2.033 4.037 3.136 5.89 3.078 3.566 0 6.167-2.503 6.167-5.941 0-3.438-2.601-5.97-6.168-5.97-2.864 0-5.138 2.425-5.771 3.173-.76-.9-1.674-1.665-2.682-2.274-1.019-.588-2.084-.898-3.211-.898z"/>
</svg>
<text x="866" y="166" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Superset</text>
<text x="866" y="178" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Dashboards · reports</text>
<!-- ── Kubernetes label (bottom-left of cluster) ── -->
<svg x="212" y="348" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#4f5d75" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M19.875 6.27a2.225 2.225 0 0 1 1.125 1.948v7.284c0 .809-.443 1.555-1.158 1.948l-6.75 3.98a2.5 2.5 0 0 1-2.184 0l-6.75-3.98a2.225 2.225 0 0 1-1.158-1.948v-7.284c0-.809.443-1.555 1.158-1.948l6.75-3.98a2.5 2.5 0 0 1 2.184 0z"/>
<circle cx="12" cy="12" r="3"/>
<line x1="12" y1="3" x2="12" y2="9"/><line x1="12" y1="15" x2="12" y2="21"/>
<line x1="20.2" y1="7.5" x2="14.9" y2="10.5"/><line x1="9.1" y1="13.5" x2="3.8" y2="16.5"/>
<line x1="20.2" y1="16.5" x2="14.9" y2="13.5"/><line x1="9.1" y1="10.5" x2="3.8" y2="7.5"/>
</svg>
<text x="236" y="362" fill="#4f5d75" font-size="8" font-weight="500" font-family="'Geist', sans-serif">Kubernetes</text>
<!-- ═══════════════════════════════════════════
CROSS-CUTTING BAR #1 — Identity (Security, y=388..428, color=#b85450)
═══════════════════════════════════════════ -->
<svg x="16" y="398" width="20" height="20" viewBox="0 0 24 24" fill="#b85450">
<path d="M12.0001.0036C5.376.0036 0 5.3796 0 12.0036c0 6.6276 5.376 12 12.0001 12 6.6275 0 12-5.3724 12-12 0-6.624-5.3725-12-12-12zm5.532 17.0496c-.612 0-3.972-3.144-5.532-5.046C10.452 13.9356 7.092 17.0796 6.48 17.0796c-1.788 0-2.052-3.564-2.052-5.076 0-1.512.264-5.076 2.052-5.076.612 0 3.972 3.144 5.5201 5.046 1.548-1.932 4.908-5.046 5.532-5.046 1.788 0 2.052 3.564 2.052 5.076 0 1.512-.264 5.076-2.052 5.076z"/>
</svg>
<text x="484" y="410" fill="#b85450" font-size="10" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Identity Manager</text>
<text x="484" y="422" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">(Keycloak · LDAP · OIDC)</text>
<!-- ═══════════════════════════════════════════
CROSS-CUTTING BAR #2 — Monitoring (Observability, y=432..472)
═══════════════════════════════════════════ -->
<rect x="4" y="432" width="960" height="40" rx="6" fill="rgba(45,49,66,0.05)" stroke="rgba(45,49,66,0.20)" stroke-width="0.8"/>
<svg x="16" y="442" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#4f5d75" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 3v18h18"/>
<path d="M7 14l3-3 2 2 5-5"/>
<path d="M14 8h3v3"/>
</svg>
<text x="484" y="454" fill="#2d3142" font-size="10" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Monitoring &amp; Observability</text>
<text x="484" y="466" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">(Prometheus · Grafana · Loki)</text>
<!-- ═══════════════════════════════════
LEGEND (pushed down to clear 2nd crosscut at y=472)
═══════════════════════════════════ -->
<line x1="40" y1="496" x2="952" y2="496" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="40" y="512" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<rect x="40" y="520" width="20" height="12" rx="3" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="1" stroke-dasharray="4,3"/>
<text x="68" y="530" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">External · outside cluster</text>
<rect x="232" y="520" width="20" height="12" rx="3" fill="rgba(45,49,66,0.04)" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<text x="260" y="530" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Kubernetes boundary</text>
<rect x="404" y="520" width="20" height="12" rx="3" fill="rgba(235,108,54,0.10)" stroke="#eb6c36" stroke-width="1.2"/>
<text x="432" y="530" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Focal (storage hub)</text>
<line x1="552" y1="522" x2="572" y2="522" stroke="#eb6c36" stroke-width="1.2" marker-end="url(#arrow-accent)"/>
<text x="580" y="527" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Primary path</text>
<line x1="680" y1="522" x2="700" y2="522" stroke="#4f5d75" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<text x="708" y="527" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Orchestration trigger</text>
<line x1="836" y1="522" x2="856" y2="522" stroke="rgba(45,49,66,0.30)" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-dim)"/>
<text x="864" y="527" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Query read-back</text>
</svg>
</div>
</body>
</html>
@@ -0,0 +1,299 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>High-level architecture · End-to-end stack</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--color-paper: #f5f5f5;
--color-ink: #2d3142;
--color-muted: #4f5d75;
--color-accent: #eb6c36;
--font-sans: 'Geist', system-ui, sans-serif;
--font-serif: 'Instrument Serif', serif;
--font-mono: 'Geist Mono', ui-monospace, monospace;
}
body {
font-family: var(--font-sans);
background: var(--color-paper);
color: var(--color-ink);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 3rem 2rem;
}
.frame { max-width: 1200px; width: 100%; }
.eyebrow {
font-family: var(--font-mono);
font-size: 0.66rem;
font-weight: 500;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--color-muted);
margin-bottom: 0.5rem;
}
h1 {
font-family: var(--font-serif);
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
font-weight: 400;
letter-spacing: -0.02em;
line-height: 1.15;
margin-bottom: 1.5rem;
}
svg { width: 100%; min-width: 960px; display: block; }
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Architecture · Diagram Design</p>
<h1>High-level architecture · End-to-end stack</h1>
<svg viewBox="0 0 1000 540" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(45,49,66,0.10)"/>
</pattern>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="#4f5d75"/>
</marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="#eb6c36"/>
</marker>
<marker id="arrow-sm" markerWidth="6" markerHeight="5" refX="5" refY="2.5" orient="auto">
<polygon points="0 0, 6 2.5, 0 5" fill="#4f5d75"/>
</marker>
</defs>
<!-- Background -->
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- ═══════════════════════════════════════════
PHASE CHEVRON BANNER
═══════════════════════════════════════════ -->
<polygon points="0,4 188,4 200,18 188,32 0,32" fill="#2d3142"/>
<polygon points="200,4 388,4 400,18 388,32 200,32 212,18" fill="#3d4460"/>
<polygon points="400,4 588,4 600,18 588,32 400,32 412,18" fill="#2d3142"/>
<polygon points="600,4 788,4 800,18 788,32 600,32 612,18" fill="#3d4460"/>
<polygon points="800,4 1000,4 1000,32 800,32 812,18" fill="#2d3142"/>
<text x="100" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">DATA SOURCES</text>
<text x="300" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">INGESTION</text>
<text x="500" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">STORAGE</text>
<text x="700" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.13em" text-anchor="middle">TRANSFORMATION &amp; ANALYSIS</text>
<text x="900" y="21" fill="#f5f5f5" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">VISUALIZATION</text>
<!-- ═══════════════════════════════════════════
STRUCTURAL ZONES
═══════════════════════════════════════════ -->
<!-- Data sources zone (external, dashed border) -->
<rect x="4" y="40" width="152" height="336" rx="6" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.20)" stroke-width="0.8" stroke-dasharray="6,3"/>
<text x="80" y="52" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">SOURCES</text>
<!-- Kubernetes boundary (solid) -->
<rect x="164" y="40" width="832" height="336" rx="8" fill="rgba(45,49,66,0.02)" stroke="rgba(45,49,66,0.18)" stroke-width="1.2"/>
<!-- Identity Manager bar -->
<rect x="4" y="388" width="992" height="40" rx="6" fill="rgba(45,49,66,0.05)" stroke="rgba(45,49,66,0.20)" stroke-width="0.8"/>
<!-- ═══════════════════════════════════════════
ARROWS — drawn before boxes (z-order rule)
═══════════════════════════════════════════ -->
<!-- Data sources → NiFi (staggered entry points) -->
<path d="M 144,92 H 156 Q 164,92 164,100 V 112 Q 164,120 176,120" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 144,172 H 156 Q 164,172 164,164 V 144 Q 164,136 176,136" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 144,252 H 156 Q 164,252 164,244 V 160 Q 164,152 176,152" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 144,336 H 156 Q 164,336 164,328 V 176 Q 164,168 176,168" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<!-- Airflow → tools (dashed orchestration drops) -->
<line x1="452" y1="96" x2="452" y2="104" stroke="#4f5d75" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<line x1="682" y1="96" x2="682" y2="144" stroke="#4f5d75" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<line x1="896" y1="96" x2="896" y2="144" stroke="#4f5d75" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow-sm)"/>
<!-- NiFi → Trino (bend up) -->
<path d="M 328,152 H 344 Q 348,152 348,148 V 144 Q 348,140 352,140 H 372" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<!-- NiFi → MinIO (bend down — ACCENT primary path) -->
<path d="M 328,152 H 344 Q 352,152 352,160 V 240 Q 352,248 372,248" fill="none" stroke="#eb6c36" stroke-width="1.2" marker-end="url(#arrow-accent)"/>
<!-- Trino → MinIO (vertical, Trino queries storage) -->
<line x1="448" y1="176" x2="448" y2="208" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<!-- MinIO → Trino (write-back, dashed) -->
<line x1="456" y1="208" x2="456" y2="176" stroke="rgba(45,49,66,0.30)" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
<!-- Trino → Data Modeling (bend down) -->
<path d="M 532,140 H 556 Q 564,140 564,148 V 176 Q 564,184 600,184" fill="none" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<!-- MinIO → Data Modeling (bend up — ACCENT primary path) -->
<path d="M 532,248 H 556 Q 564,248 564,240 V 192 Q 564,184 600,184" fill="none" stroke="#eb6c36" stroke-width="1.2" marker-end="url(#arrow-accent)"/>
<!-- Data Modeling → Dashboards (straight) -->
<line x1="764" y1="184" x2="820" y2="184" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<!-- ═══════════════════════════════════════════
NODES
═══════════════════════════════════════════ -->
<!-- ── WEB ── -->
<rect x="12" y="60" width="132" height="64" rx="6" fill="#f5f5f5"/>
<rect x="12" y="60" width="132" height="64" rx="6" fill="white" stroke="rgba(45,49,66,0.22)" stroke-width="1"/>
<rect x="20" y="66" width="22" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="31" y="75" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="66" y="66" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#2d3142" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="9"/>
<path d="M3.6 9h16.8M3.6 15h16.8"/>
<path d="M11.5 3a17 17 0 0 0 0 18M12.5 3a17 17 0 0 1 0 18"/>
</svg>
<text x="78" y="102" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">WEB</text>
<text x="78" y="114" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Census · Survey · Admin</text>
<!-- ── FTP ── -->
<rect x="12" y="140" width="132" height="64" rx="6" fill="#f5f5f5"/>
<rect x="12" y="140" width="132" height="64" rx="6" fill="white" stroke="rgba(45,49,66,0.22)" stroke-width="1"/>
<rect x="20" y="146" width="22" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="31" y="155" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="66" y="146" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#2d3142" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2"/>
<path d="M7 9l5 -5l5 5"/>
<path d="M12 4l0 12"/>
</svg>
<text x="78" y="182" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">FTP</text>
<text x="78" y="194" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Census · Business</text>
<!-- ── DB Connection ── -->
<rect x="12" y="220" width="132" height="64" rx="6" fill="#f5f5f5"/>
<rect x="12" y="220" width="132" height="64" rx="6" fill="white" stroke="rgba(45,49,66,0.22)" stroke-width="1"/>
<rect x="20" y="226" width="22" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="31" y="235" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="66" y="226" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#2d3142" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 6a8 3 0 1 0 16 0a8 3 0 1 0 -16 0"/>
<path d="M4 6v6a8 3 0 0 0 16 0v-6"/>
<path d="M4 12v6a8 3 0 0 0 16 0v-6"/>
</svg>
<text x="78" y="262" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">DB Connection</text>
<text x="78" y="274" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">CDC · SQL · API</text>
<!-- ── Legacy System ── -->
<rect x="12" y="304" width="132" height="64" rx="6" fill="#f5f5f5"/>
<rect x="12" y="304" width="132" height="64" rx="6" fill="white" stroke="rgba(45,49,66,0.22)" stroke-width="1"/>
<rect x="20" y="310" width="22" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="31" y="319" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">EXT</text>
<svg x="66" y="310" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#2d3142" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 7a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3"/>
<path d="M3 15a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3l0 -2"/>
<path d="M7 8l0 .01"/><path d="M7 16l0 .01"/>
</svg>
<text x="78" y="346" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Legacy System</text>
<text x="78" y="358" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Trade data</text>
<!-- ── Airflow orchestration bar ── -->
<rect x="176" y="52" width="808" height="44" rx="4" fill="rgba(45,49,66,0.05)" stroke="rgba(45,49,66,0.18)" stroke-width="0.8"/>
<svg x="950" y="58" width="24" height="24" viewBox="0 0 24 24" fill="#4f5d75">
<path d="M17.195 16.822l4.002-4.102C23.55 10.308 23.934 5.154 24 .43a.396.396 0 0 0-.246-.373.392.392 0 0 0-.437.09l-6.495 6.658-4.102-4.003C10.309.45 5.154.066.43 0H.423a.397.397 0 0 0-.277.683l6.658 6.494-4.003 4.103C.45 13.692.065 18.846 0 23.57a.398.398 0 0 0 .683.282l6.494-6.657 3.934 3.837.17.165c2.41 2.353 7.565 2.737 12.288 2.803h.006a.397.397 0 0 0 .277-.683l-6.657-6.495z"/>
</svg>
<text x="564" y="71" fill="#2d3142" font-size="10" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Orchestration / Workflow Automation</text>
<text x="564" y="84" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">(Apache Airflow)</text>
<!-- ── Apache NiFi ── -->
<rect x="176" y="112" width="152" height="80" rx="6" fill="#f5f5f5"/>
<rect x="176" y="112" width="152" height="80" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="184" y="118" width="30" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="199" y="127" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">COLL</text>
<svg x="240" y="118" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142">
<path d="M11.648 0a.093.093 0 0 0-.084.053A30.71 30.71 0 0 1 8.592 4.73c-2.09 2.728-5.145 6.466-5.145 10.364a8.201 8.201 0 0 0 8.201 8.2v-5.003c0-.106.087-.193.194-.193h2.81v-2.813c0-.106.087-.191.194-.191h5.004c0-3.9-3.056-7.636-5.145-10.364A30.712 30.712 0 0 1 11.732.053.094.094 0 0 0 11.648 0zm-1.632 3.867c.05 0 .08.034.037.112-.11.197-.218.397-.328.593-.396.702-.819 1.389-1.23 2.08-.196-.032-.39-.06-.585-.088.495-.651 1-1.296 1.48-1.959.153-.209.302-.423.454-.634a.24.24 0 0 1 .172-.104zM7.44 7.186c.221.035.444.076.666.119-.073.129-.15.256-.223.383a29.073 29.073 0 0 0-1.625 3.261c-.874 2.123-1.383 4.444-.77 6.707a8.222 8.222 0 0 0 2.217 3.74c.083.083-.02.216-.119.155a7.568 7.568 0 0 1-.93-.686A7.674 7.674 0 0 1 4.1 16.248c-.329-2.156.387-4.246 1.418-6.115a27.44 27.44 0 0 1 1.92-2.947zm7.931 8.435a.193.193 0 0 0-.191.191V18.3h2.677V15.62zm3.299 0V18.3h1.348a7.975 7.975 0 0 0 .515-2.678zm-6.303 3.004a.193.193 0 0 0-.191.193v2.485h2.678v-2.678Zm3.295.484v2.68h2.115a.562.562 0 0 0 .399-.162v-.004a.562.562 0 0 0 .16-.397V19.11zm3.674.182v1.98a7.999 7.999 0 0 0 1.217-1.98zm-6.969 2.824a.193.193 0 0 0-.191.192v1.672a7.997 7.997 0 0 0 2.678-.516v-1.348zm3.48.668V24a8.008 8.008 0 0 0 1.98-1.217z"/>
</svg>
<text x="252" y="154" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Apache NiFi</text>
<text x="252" y="166" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Collect · route · transform</text>
<!-- ── Trino ── -->
<rect x="372" y="104" width="160" height="72" rx="6" fill="#f5f5f5"/>
<rect x="372" y="104" width="160" height="72" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="380" y="110" width="28" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="394" y="119" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">VIRT</text>
<svg x="440" y="110" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142">
<path d="M14.124 16.8529a.1615.1615 0 1 1 .1576.1614.1577.1577 0 0 1-.1576-.1614zm-5.607-.1576a.1614.1614 0 1 0 0 .3228.1614.1614 0 0 0 0-.3228zm10.1341-.6648v1.9869c-.031.5788-.524 1.0237-1.1029.9954h-.3843a5.0596 5.0596 0 0 1-1.1298 1.7178.3192.3192 0 0 0 0 .465l.2382.2191a.3036.3036 0 0 1 .0385.4304c-1.126 1.3835-2.9669 2.1521-5.0498 2.1521a6.575 6.575 0 0 1-4.8192-1.8985c-.0029-.0032-.0059-.0063-.0087-.0096a.6302.6302 0 0 1 .0548-.8896c.137-.1265.1371-.3462 0-.4727a4.944 4.944 0 0 1-1.126-1.714h-.3497c-.5797.0284-1.0737-.416-1.1068-.9954v-1.9869c.0351-.5779.5286-1.02 1.1068-.9915h.2728a5.7648 5.7648 0 0 1 2.0791-3.0936c-.4227-1.0991-1.1529-3.2551-1.226-5.0075C6.0229 4.4705 6.2189.078 7.8253.001c1.6064-.0768 1.3719 4.0275 1.0991 6.6946a32.732 32.732 0 0 0-.123 4.4503 6.994 6.994 0 0 1 2.4826-.4304 7.2414 7.2414 0 0 1 1.7371.2075c.2614-1.2682.8762-3.574 2.0292-5.1958 1.6717-2.352 3.4357-4.7808 4.6116-4.1006 1.176.6802-.3074 3.1398-1.3297 4.4272-1.0222 1.2874-2.7862 3.2089-3.3742 4.2274-.2114.3843-.4304.8032-.5956 1.1529a5.7375 5.7375 0 0 1 2.9169 3.6125h.073v-2.3058a.3075.3075 0 0 0-.1806-.2844.9148.9148 0 0 1-.5573-.8148 1.0184 1.0184 0 0 1 .9045-.9044c.5593-.0598 1.061.3452 1.1208.9044a.9187.9187 0 0 1-.5534.8148.3074.3074 0 0 0-.1691.2844v2.1522a.3113.3113 0 0 0 .1691.2805.9724.9724 0 0 1 .5648.857z"/>
</svg>
<text x="452" y="146" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Trino</text>
<text x="452" y="158" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">SQL · data virtualization</text>
<!-- ── MinIO / S3 (FOCAL) ── -->
<rect x="372" y="208" width="160" height="80" rx="8" fill="#f5f5f5"/>
<rect x="372" y="208" width="160" height="80" rx="8" fill="rgba(235,108,54,0.08)" stroke="#eb6c36" stroke-width="1.2"/>
<rect x="380" y="214" width="36" height="12" rx="2" fill="transparent" stroke="rgba(235,108,54,0.50)" stroke-width="0.8"/>
<text x="398" y="223" fill="rgba(235,108,54,0.80)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">STORE</text>
<svg x="440" y="214" width="24" height="24" viewBox="0 0 24 24" fill="#eb6c36">
<path d="M13.2072.006c-.6216-.0478-1.2.1943-1.6211.582a2.15 2.15 0 0 0-.0938 3.0352l3.4082 3.5507a3.042 3.042 0 0 1-.664 4.6875l-.463.2383V7.2853a15.4198 15.4198 0 0 0-8.0174 10.4862v.0176l6.5487-3.3281v7.621L13.7794 24V13.6817l.8965-.4629a4.4432 4.4432 0 0 0 1.2207-7.0292l-3.371-3.5254a.7489.7489 0 0 1 .037-1.0547.7522.7522 0 0 1 1.0567.0371l.4668.4863-.006.0059 4.0704 4.2441a.0566.0566 0 0 0 .082 0 .06.06 0 0 0 0-.0703l-3.1406-5.1425-.1484.1425.1484-.1445C14.4945.3926 13.8287.0538 13.2072.006Zm-.9024 9.8652v2.9941l-4.1523 2.1484a13.9787 13.9787 0 0 1 2.7676-3.9277 14.1784 14.1784 0 0 1 1.3847-1.2148z"/>
</svg>
<text x="452" y="252" fill="#eb6c36" font-size="12" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">MinIO / S3</text>
<text x="452" y="268" fill="#eb6c36" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" opacity="0.75">Object store · S3-API</text>
<!-- ── Data Modeling & Analysis ── -->
<rect x="600" y="144" width="164" height="80" rx="6" fill="#f5f5f5"/>
<rect x="600" y="144" width="164" height="80" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="608" y="150" width="32" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="624" y="159" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">ANLZ</text>
<svg x="670" y="150" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142">
<path d="M7.157 22.201A1.784 1.799 0 0 1 5.374 24a1.784 1.799 0 0 1-1.784-1.799 1.784 1.799 0 0 1 1.784-1.799 1.784 1.799 0 0 1 1.783 1.799zM20.582 1.427a1.415 1.427 0 0 1-1.415 1.428 1.415 1.427 0 0 1-1.416-1.428A1.415 1.427 0 0 1 19.167 0a1.415 1.427 0 0 1 1.415 1.427zM4.992 3.336A1.047 1.056 0 0 1 3.946 4.39a1.047 1.056 0 0 1-1.047-1.055A1.047 1.056 0 0 1 3.946 2.28a1.047 1.056 0 0 1 1.046 1.056zm7.336 1.517c3.769 0 7.06 1.38 8.768 3.424a9.363 9.363 0 0 0-3.393-4.547 9.238 9.238 0 0 0-5.377-1.728A9.238 9.238 0 0 0 6.95 3.73a9.363 9.363 0 0 0-3.394 4.547c1.713-2.04 5.004-3.424 8.772-3.424zm.001 13.295c-3.768 0-7.06-1.381-8.768-3.425a9.363 9.363 0 0 0 3.394 4.547A9.238 9.238 0 0 0 12.33 21a9.238 9.238 0 0 0 5.377-1.729 9.363 9.363 0 0 0 3.393-4.547c-1.712 2.044-5.003 3.425-8.772 3.425z"/>
</svg>
<text x="682" y="186" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Data Modeling</text>
<text x="682" y="198" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Notebook · Python · R</text>
<!-- ── Dashboards & Report ── -->
<rect x="820" y="144" width="152" height="80" rx="6" fill="#f5f5f5"/>
<rect x="820" y="144" width="152" height="80" rx="6" fill="white" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<rect x="828" y="150" width="28" height="12" rx="2" fill="transparent" stroke="rgba(45,49,66,0.28)" stroke-width="0.8"/>
<text x="842" y="159" fill="rgba(45,49,66,0.55)" font-size="7" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.08em">DASH</text>
<svg x="884" y="150" width="24" height="24" viewBox="0 0 24 24" fill="#2d3142">
<path d="M6.168 6.045C2.603 6.045 0 8.579 0 12.014c0 3.434 2.603 5.941 6.168 5.941 2.184 0 3.888-1.026 5.775-3.078 1.53 2.033 4.037 3.136 5.89 3.078 3.566 0 6.167-2.503 6.167-5.941 0-3.438-2.601-5.97-6.168-5.97-2.864 0-5.138 2.425-5.771 3.173-.76-.9-1.674-1.665-2.682-2.274-1.019-.588-2.084-.898-3.211-.898z"/>
</svg>
<text x="896" y="186" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Dashboards</text>
<text x="896" y="198" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">Superset · reports</text>
<!-- ── Kubernetes label (bottom-left of boundary) ── -->
<svg x="180" y="348" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#4f5d75" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M19.875 6.27a2.225 2.225 0 0 1 1.125 1.948v7.284c0 .809-.443 1.555-1.158 1.948l-6.75 3.98a2.5 2.5 0 0 1-2.184 0l-6.75-3.98a2.225 2.225 0 0 1-1.158-1.948v-7.284c0-.809.443-1.555 1.158-1.948l6.75-3.98a2.5 2.5 0 0 1 2.184 0z"/>
<circle cx="12" cy="12" r="3"/>
<line x1="12" y1="3" x2="12" y2="9"/><line x1="12" y1="15" x2="12" y2="21"/>
<line x1="20.2" y1="7.5" x2="14.9" y2="10.5"/><line x1="9.1" y1="13.5" x2="3.8" y2="16.5"/>
<line x1="20.2" y1="16.5" x2="14.9" y2="13.5"/><line x1="9.1" y1="10.5" x2="3.8" y2="7.5"/>
</svg>
<text x="204" y="362" fill="#4f5d75" font-size="8" font-weight="500" font-family="'Geist', sans-serif">Kubernetes</text>
<!-- ── Identity Manager bar ── -->
<svg x="16" y="398" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#4f5d75" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 3a12 12 0 0 0 8.5 3a12 12 0 0 1-8.5 15a12 12 0 0 1-8.5-15a12 12 0 0 0 8.5-3"/>
<circle cx="12" cy="11" r="1" fill="#4f5d75"/>
<line x1="12" y1="12" x2="12" y2="14.5"/>
</svg>
<text x="500" y="410" fill="#2d3142" font-size="10" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Identity Manager</text>
<text x="500" y="422" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">(Active Directory)</text>
<!-- ═══════════════════════════════════
LEGEND
═══════════════════════════════════ -->
<line x1="40" y1="440" x2="960" y2="440" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="40" y="456" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<rect x="40" y="464" width="20" height="12" rx="3" fill="transparent" stroke="rgba(45,49,66,0.30)" stroke-width="1" stroke-dasharray="4,3"/>
<text x="68" y="474" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">External source · outside cluster</text>
<rect x="252" y="464" width="20" height="12" rx="3" fill="rgba(45,49,66,0.04)" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<text x="280" y="474" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Kubernetes boundary</text>
<rect x="436" y="464" width="20" height="12" rx="3" fill="rgba(235,108,54,0.10)" stroke="#eb6c36" stroke-width="1.2"/>
<text x="464" y="474" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Focal component (MinIO)</text>
<line x1="648" y1="466" x2="668" y2="466" stroke="#eb6c36" stroke-width="1.2"/>
<text x="676" y="471" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Primary data path</text>
<line x1="808" y1="466" x2="828" y2="466" stroke="rgba(45,49,66,0.35)" stroke-width="1" stroke-dasharray="4,3"/>
<text x="836" y="471" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Orchestration trigger</text>
</svg>
</div>
</body>
</html>
@@ -0,0 +1,90 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Northwind Retail · Current IT Landscape · Dark</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&amp;family=Geist:wght@400;500;600&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root { --paper:#2d3142; --paper-2:#393e53; --ink:#f5f5f5; --muted:#bfc0c0; --soft:#8e98ac; --rule:rgba(245,245,245,0.12); --accent:#f08a59; --link:#6a95d8; --sans:'Geist',system-ui,sans-serif; --serif:'Instrument Serif',serif; --mono:'Geist Mono',ui-monospace,monospace; }
body { min-height:100vh; display:flex; align-items:center; justify-content:center; padding:3rem 2rem; background:var(--paper); color:var(--ink); font-family:var(--sans); }
.frame { width:100%; max-width:1200px; }
.eyebrow { margin-bottom:.5rem; color:var(--muted); font:500 .66rem/1 var(--mono); letter-spacing:.18em; text-transform:uppercase; }
h1 { margin-bottom:.35rem; color:var(--ink); font:400 clamp(1.5rem,2.4vw + .75rem,2rem)/1.15 var(--serif); letter-spacing:-.02em; }
.subtitle { margin-bottom:1.5rem; color:var(--muted); font-size:.9rem; line-height:1.5; }
.diagram { overflow-x:auto; } svg { display:block; width:100%; min-width:920px; }
svg .zone { fill:rgba(245,245,245,.04); stroke:rgba(245,245,245,.14); stroke-width:.8; }
svg .zone-mask, svg .node-mask, svg .label-mask { fill:#2d3142; }
svg .zone-label, svg .arrow-label, svg .legend-label { font-family:'Geist Mono',monospace; }
svg .zone-label { fill:rgba(245,245,245,.40); font-size:8px; letter-spacing:.14em; }
svg .node { fill:rgba(245,245,245,.04); stroke:rgba(245,245,245,.32); stroke-width:1; }
svg .node.external { stroke:#bfc0c0; stroke-dasharray:4 3; }
svg .node.focal { fill:rgba(240,138,89,.12); stroke:#f08a59; stroke-width:1.4; }
svg .node.survivor { fill:rgba(154,172,103,.10); stroke:rgba(154,172,103,.55); }
svg .node-name { fill:#f5f5f5; font:600 12px 'Geist',sans-serif; }
svg .node-name.survivor { fill:#9aac67; }
svg .sub { fill:#bfc0c0; font:400 10px 'Geist Mono',monospace; } svg .sub.focal { fill:#f08a59; }
svg .arrow-label { font-size:8px; font-weight:600; letter-spacing:.08em; } svg .legend-label { fill:#bfc0c0; font-size:8px; }
</style>
</head>
<body>
<main class="frame">
<p class="eyebrow">IT current-state · Northwind Retail</p>
<h1>Legacy retail reporting landscape</h1>
<p class="subtitle">File exports, desktop spreadsheets, and manual publication before a shared data platform.</p>
<div class="diagram">
<svg viewBox="0 0 960 500" role="img" aria-labelledby="it-dark-title it-dark-desc" xmlns="http://www.w3.org/2000/svg">
<title id="it-dark-title">Northwind Retail current IT landscape</title><desc id="it-dark-desc">Three zones show collection, processing, and dissemination through manual file handoffs.</desc>
<defs>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#bfc0c0"/></marker>
<marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#6a95d8"/></marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#f08a59"/></marker>
<symbol id="icon-file" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14 3v4a1 1 0 0 0 1 1h4"/><path d="M17 21H7a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7l5 5v11a2 2 0 0 1-2 2"/></symbol>
<symbol id="icon-screen" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="12" rx="1"/><path d="M7 20h10M9 16v4M15 16v4"/></symbol>
<symbol id="icon-db" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><ellipse cx="12" cy="6" rx="8" ry="3"/><path d="M4 6v6c0 1.7 3.6 3 8 3s8-1.3 8-3V6M4 12v6c0 1.7 3.6 3 8 3s8-1.3 8-3v-6"/></symbol>
<symbol id="icon-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="9" cy="8" r="4"/><path d="M3 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2M16 4.5a4 4 0 0 1 0 7M18 15a4 4 0 0 1 3 4v2"/></symbol>
</defs>
<rect width="960" height="500" fill="#2d3142"/>
<rect class="zone" x="16" y="52" width="256" height="360" rx="8"/><rect class="zone-mask" x="36" y="44" width="92" height="16"/><text class="zone-label" x="40" y="56">COLLECTION</text>
<rect class="zone" x="292" y="52" width="360" height="360" rx="8"/><rect class="zone-mask" x="312" y="44" width="92" height="16"/><text class="zone-label" x="316" y="56">PROCESSING</text>
<rect class="zone" x="672" y="52" width="272" height="360" rx="8"/><rect class="zone-mask" x="692" y="44" width="116" height="16"/><text class="zone-label" x="696" y="56">DISSEMINATION</text>
<path d="M 252,108 H 340" fill="none" stroke="#6a95d8" stroke-width="1.2" marker-end="url(#arrow-link)"/>
<path d="M 252,196 H 308 Q 316,196 316,188 V 132 Q 316,124 324,124 H 340" fill="none" stroke="#6a95d8" stroke-width="1.2" marker-end="url(#arrow-link)"/>
<path d="M 252,284 H 324 Q 332,284 332,276 V 148 Q 332,140 340,140" fill="none" stroke="#6a95d8" stroke-width="1.2" stroke-dasharray="4 3" marker-end="url(#arrow-link)"/>
<line x1="472" y1="148" x2="472" y2="232" stroke="#f08a59" stroke-width="1.4" stroke-dasharray="4 3" marker-end="url(#arrow-accent)"/>
<line x1="472" y1="304" x2="472" y2="344" stroke="#bfc0c0" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 604,268 H 654 Q 662,268 662,260 V 72 Q 662,64 670,64 H 800 Q 808,64 808,72 V 80" fill="none" stroke="#f08a59" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
<line x1="808" y1="140" x2="808" y2="216" stroke="#6a95d8" stroke-width="1.2" marker-end="url(#arrow-link)"/>
<line x1="808" y1="272" x2="808" y2="344" stroke="#6a95d8" stroke-width="1.2" stroke-dasharray="4 3" marker-end="url(#arrow-link)"/>
<rect class="node-mask" x="36" y="80" width="216" height="56" rx="6"/><rect class="node" x="36" y="80" width="216" height="56" rx="6"/><use href="#icon-file" x="48" y="96" width="24" height="24" color="#f5f5f5"/><text class="node-name" x="80" y="106">Point-of-Sale Exports</text><text class="sub" x="80" y="122">nightly · CSV</text>
<rect class="node-mask" x="36" y="168" width="216" height="56" rx="6"/><rect class="node" x="36" y="168" width="216" height="56" rx="6"/><use href="#icon-file" x="48" y="184" width="24" height="24" color="#f5f5f5"/><text class="node-name" x="80" y="194">Online Store Export</text><text class="sub" x="80" y="210">orders · flat file</text>
<rect class="node-mask" x="36" y="256" width="216" height="56" rx="6"/><rect class="node external" x="36" y="256" width="216" height="56" rx="6"/><use href="#icon-file" x="48" y="272" width="24" height="24" color="#bfc0c0"/><text class="node-name" x="80" y="282">Supplier Price Lists</text><text class="sub" x="80" y="298">external · XLSX</text>
<rect class="node-mask" x="340" y="80" width="264" height="68" rx="6"/><rect class="node focal" x="340" y="80" width="264" height="68" rx="6"/><use href="#icon-file" x="352" y="100" width="24" height="24" color="#f08a59"/><text class="node-name" x="384" y="108">Shared Drive</text><text class="sub focal" x="384" y="124">no version control</text><text class="sub" x="384" y="140">department folders</text>
<rect class="node-mask" x="340" y="232" width="264" height="72" rx="6"/><rect class="node" x="340" y="232" width="264" height="72" rx="6"/><use href="#icon-screen" x="352" y="256" width="24" height="24" color="#f5f5f5"/><text class="node-name" x="384" y="262">Spreadsheet Handoffs</text><text class="sub" x="384" y="278">local workbooks · macros</text><text class="sub" x="384" y="292">manual reconciliation</text>
<rect class="node-mask" x="340" y="344" width="264" height="56" rx="6"/><rect class="node survivor" x="340" y="344" width="264" height="56" rx="6"/><use href="#icon-db" x="352" y="360" width="24" height="24" color="#9aac67"/><text class="node-name survivor" x="384" y="370">On-prem RDBMS</text><text class="sub" x="384" y="386">inventory · finance</text>
<rect class="node-mask" x="704" y="80" width="208" height="60" rx="6"/><rect class="node focal" x="704" y="80" width="208" height="60" rx="6"/><use href="#icon-screen" x="716" y="100" width="24" height="24" color="#f08a59"/><text class="node-name" x="748" y="108">Reporting Portal</text><text class="sub focal" x="748" y="124">manual refresh</text>
<rect class="node-mask" x="704" y="216" width="208" height="56" rx="6"/><rect class="node" x="704" y="216" width="208" height="56" rx="6"/><use href="#icon-file" x="716" y="232" width="24" height="24" color="#f5f5f5"/><text class="node-name" x="748" y="242">Email Report Packs</text><text class="sub" x="748" y="258">weekly · PDF</text>
<rect class="node-mask" x="704" y="344" width="208" height="56" rx="6"/><rect class="node external" x="704" y="344" width="208" height="56" rx="6"/><use href="#icon-users" x="716" y="360" width="24" height="24" color="#bfc0c0"/><text class="node-name" x="748" y="370">Regional Managers</text><text class="sub" x="748" y="386">12 store regions</text>
<rect class="label-mask" x="260" y="86" width="40" height="16" rx="2"/><text class="arrow-label" x="280" y="98" text-anchor="middle" fill="#6a95d8">CSV</text>
<rect class="label-mask" x="260" y="174" width="52" height="16" rx="2"/><text class="arrow-label" x="286" y="186" text-anchor="middle" fill="#6a95d8">EXPORT</text>
<rect class="label-mask" x="260" y="262" width="44" height="16" rx="2"/><text class="arrow-label" x="282" y="274" text-anchor="middle" fill="#6a95d8">XLSX</text>
<rect class="label-mask" x="478" y="156" width="40" height="16" rx="2"/><text class="arrow-label" x="498" y="168" text-anchor="middle" fill="#f08a59">COPY</text>
<rect class="label-mask" x="478" y="312" width="40" height="16" rx="2"/><text class="arrow-label" x="498" y="324" text-anchor="middle" fill="#bfc0c0">LOAD</text>
<rect class="label-mask" x="612" y="246" width="44" height="16" rx="2"/><text class="arrow-label" x="634" y="258" text-anchor="middle" fill="#f08a59">XLSX</text>
<rect class="label-mask" x="814" y="148" width="36" height="16" rx="2"/><text class="arrow-label" x="832" y="160" text-anchor="middle" fill="#6a95d8">PDF</text>
<rect class="label-mask" x="814" y="280" width="48" height="16" rx="2"/><text class="arrow-label" x="838" y="292" text-anchor="middle" fill="#6a95d8">EMAIL</text>
<line x1="16" y1="440" x2="944" y2="440" stroke="rgba(245,245,245,.12)" stroke-width="0.8"/><text class="legend-label" x="16" y="460" letter-spacing=".14em">LEGEND</text>
<line x1="80" y1="456" x2="104" y2="456" stroke="#6a95d8" stroke-width="1.2"/><text class="legend-label" x="112" y="460">data flow</text>
<line x1="208" y1="456" x2="232" y2="456" stroke="#f08a59" stroke-width="1.4"/><text class="legend-label" x="240" y="460">pain-point</text>
<rect x="372" y="450" width="16" height="12" rx="2" fill="rgba(245,245,245,.04)" stroke="#bfc0c0" stroke-width=".8" stroke-dasharray="4 3"/><text class="legend-label" x="396" y="460">external</text>
<rect x="492" y="450" width="16" height="12" rx="2" fill="rgba(240,138,89,.12)" stroke="#f08a59"/><text class="legend-label" x="516" y="460">bottleneck</text>
</svg>
</div>
</main>
</body>
</html>
@@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Northwind Retail · Current IT Landscape · Editorial</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&amp;family=Geist:wght@400;500;600&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing:border-box; margin:0; padding:0; }
:root { --paper:#f5f5f5; --paper-2:#ececec; --ink:#2d3142; --muted:#4f5d75; --soft:#7a8399; --rule:rgba(45,49,66,.12); --accent:#eb6c36; --link:#2e5aa8; --sans:'Geist',system-ui,sans-serif; --serif:'Instrument Serif',serif; --mono:'Geist Mono',ui-monospace,monospace; }
body { min-height:100vh; padding:3rem 2rem; background:var(--paper); color:var(--ink); font-family:var(--sans); }
.container { max-width:1200px; margin:0 auto; } .header { margin-bottom:2.5rem; }
.header-eyebrow,.card .eyebrow { color:var(--muted); font-family:var(--mono); font-weight:500; letter-spacing:.18em; text-transform:uppercase; }
.header-eyebrow { margin-bottom:.75rem; font-size:.66rem; }
h1 { margin-bottom:.5rem; font:400 clamp(1.75rem,3vw + 1rem,2.5rem)/1.1 var(--serif); letter-spacing:-.02em; }
.subtitle { max-width:68ch; color:var(--muted); font-size:1rem; line-height:1.55; }
.diagram-container { overflow-x:auto; padding:1.5rem; background:var(--paper-2); border:1px solid var(--rule); border-radius:8px; }
svg { display:block; width:100%; min-width:920px; }
svg .zone { fill:rgba(45,49,66,.02); stroke:rgba(45,49,66,.10); stroke-width:.8; } svg .zone-mask,svg .node-mask,svg .label-mask { fill:#f5f5f5; }
svg .zone-label,svg .arrow-label,svg .legend-label { font-family:'Geist Mono',monospace; } svg .zone-label { fill:rgba(45,49,66,.40); font-size:8px; letter-spacing:.14em; }
svg .node { fill:#fff; stroke:#2d3142; stroke-width:1; } svg .node.external { stroke:#4f5d75; stroke-dasharray:4 3; } svg .node.focal { fill:rgba(235,108,54,.07); stroke:#eb6c36; stroke-width:1.4; } svg .node.survivor { fill:rgba(122,140,71,.06); stroke:rgba(122,140,71,.45); }
svg .node-name { fill:#2d3142; font:600 12px 'Geist',sans-serif; } svg .node-name.survivor { fill:#7a8c47; } svg .sub { fill:#4f5d75; font:400 10px 'Geist Mono',monospace; } svg .sub.focal { fill:#eb6c36; }
svg .arrow-label { font-size:8px; font-weight:600; letter-spacing:.08em; } svg .legend-label { fill:#4f5d75; font-size:8px; }
.cards { display:grid; grid-template-columns:1.1fr 1fr .9fr; gap:1rem; margin-top:1.5rem; } @media(max-width:820px){.cards{grid-template-columns:1fr;}}
.card { padding:1.25rem; background:#fff; border:1px solid var(--rule); border-radius:6px; } .card .eyebrow { margin-bottom:.5rem; font-size:.5rem; }
.card-header { display:flex; align-items:center; gap:.6rem; margin-bottom:.875rem; padding-bottom:.875rem; border-bottom:1px solid rgba(45,49,66,.08); }
.card-dot { width:7px; height:7px; flex:0 0 auto; border-radius:50%; } .card-dot.coral{background:var(--accent)} .card-dot.link{background:var(--link)} .card-dot.muted{background:var(--muted)}
.card h2 { font-size:.875rem; font-weight:600; } .card p,.card ul { color:var(--muted); font-size:.8125rem; line-height:1.55; list-style:none; }
.card li { position:relative; margin-bottom:.3rem; padding-left:.875rem; } .card li::before { content:'—'; position:absolute; left:0; color:rgba(45,49,66,.25); }
.footer { display:flex; justify-content:space-between; flex-wrap:wrap; gap:.5rem; margin-top:2rem; padding-top:1.5rem; color:var(--soft); border-top:1px solid rgba(45,49,66,.10); font:400 .72rem/1.4 var(--mono); letter-spacing:.06em; }
</style>
</head>
<body>
<main class="container">
<header class="header"><p class="header-eyebrow">IT current-state · Northwind Retail</p><h1>Legacy retail reporting landscape</h1><p class="subtitle">Sales and supplier files converge on a shared drive, move through desktop workbooks, and reach regional teams through a manually refreshed portal and emailed report packs.</p></header>
<div class="diagram-container">
<svg viewBox="0 0 960 500" role="img" aria-labelledby="it-full-title it-full-desc" xmlns="http://www.w3.org/2000/svg">
<title id="it-full-title">Northwind Retail current IT landscape</title><desc id="it-full-desc">Three zones show collection, processing, and dissemination through manual file handoffs.</desc>
<defs>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#4f5d75"/></marker><marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#2e5aa8"/></marker><marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#eb6c36"/></marker>
<symbol id="icon-file" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14 3v4a1 1 0 0 0 1 1h4"/><path d="M17 21H7a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7l5 5v11a2 2 0 0 1-2 2"/></symbol><symbol id="icon-screen" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="12" rx="1"/><path d="M7 20h10M9 16v4M15 16v4"/></symbol><symbol id="icon-db" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><ellipse cx="12" cy="6" rx="8" ry="3"/><path d="M4 6v6c0 1.7 3.6 3 8 3s8-1.3 8-3V6M4 12v6c0 1.7 3.6 3 8 3s8-1.3 8-3v-6"/></symbol><symbol id="icon-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="9" cy="8" r="4"/><path d="M3 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2M16 4.5a4 4 0 0 1 0 7M18 15a4 4 0 0 1 3 4v2"/></symbol>
</defs>
<rect width="960" height="500" fill="#f5f5f5"/>
<rect class="zone" x="16" y="52" width="256" height="360" rx="8"/><rect class="zone-mask" x="36" y="44" width="92" height="16"/><text class="zone-label" x="40" y="56">COLLECTION</text><rect class="zone" x="292" y="52" width="360" height="360" rx="8"/><rect class="zone-mask" x="312" y="44" width="92" height="16"/><text class="zone-label" x="316" y="56">PROCESSING</text><rect class="zone" x="672" y="52" width="272" height="360" rx="8"/><rect class="zone-mask" x="692" y="44" width="116" height="16"/><text class="zone-label" x="696" y="56">DISSEMINATION</text>
<path d="M 252,108 H 340" fill="none" stroke="#2e5aa8" stroke-width="1.2" marker-end="url(#arrow-link)"/><path d="M 252,196 H 308 Q 316,196 316,188 V 132 Q 316,124 324,124 H 340" fill="none" stroke="#2e5aa8" stroke-width="1.2" marker-end="url(#arrow-link)"/><path d="M 252,284 H 324 Q 332,284 332,276 V 148 Q 332,140 340,140" fill="none" stroke="#2e5aa8" stroke-width="1.2" stroke-dasharray="4 3" marker-end="url(#arrow-link)"/>
<line x1="472" y1="148" x2="472" y2="232" stroke="#eb6c36" stroke-width="1.4" stroke-dasharray="4 3" marker-end="url(#arrow-accent)"/><line x1="472" y1="304" x2="472" y2="344" stroke="#4f5d75" marker-end="url(#arrow)"/><path d="M 604,268 H 654 Q 662,268 662,260 V 72 Q 662,64 670,64 H 800 Q 808,64 808,72 V 80" fill="none" stroke="#eb6c36" stroke-width="1.4" marker-end="url(#arrow-accent)"/><line x1="808" y1="140" x2="808" y2="216" stroke="#2e5aa8" stroke-width="1.2" marker-end="url(#arrow-link)"/><line x1="808" y1="272" x2="808" y2="344" stroke="#2e5aa8" stroke-width="1.2" stroke-dasharray="4 3" marker-end="url(#arrow-link)"/>
<rect class="node-mask" x="36" y="80" width="216" height="56" rx="6"/><rect class="node" x="36" y="80" width="216" height="56" rx="6"/><use href="#icon-file" x="48" y="96" width="24" height="24" color="#2d3142"/><text class="node-name" x="80" y="106">Point-of-Sale Exports</text><text class="sub" x="80" y="122">nightly · CSV</text>
<rect class="node-mask" x="36" y="168" width="216" height="56" rx="6"/><rect class="node" x="36" y="168" width="216" height="56" rx="6"/><use href="#icon-file" x="48" y="184" width="24" height="24" color="#2d3142"/><text class="node-name" x="80" y="194">Online Store Export</text><text class="sub" x="80" y="210">orders · flat file</text>
<rect class="node-mask" x="36" y="256" width="216" height="56" rx="6"/><rect class="node external" x="36" y="256" width="216" height="56" rx="6"/><use href="#icon-file" x="48" y="272" width="24" height="24" color="#4f5d75"/><text class="node-name" x="80" y="282">Supplier Price Lists</text><text class="sub" x="80" y="298">external · XLSX</text>
<rect class="node-mask" x="340" y="80" width="264" height="68" rx="6"/><rect class="node focal" x="340" y="80" width="264" height="68" rx="6"/><use href="#icon-file" x="352" y="100" width="24" height="24" color="#eb6c36"/><text class="node-name" x="384" y="108">Shared Drive</text><text class="sub focal" x="384" y="124">no version control</text><text class="sub" x="384" y="140">department folders</text>
<rect class="node-mask" x="340" y="232" width="264" height="72" rx="6"/><rect class="node" x="340" y="232" width="264" height="72" rx="6"/><use href="#icon-screen" x="352" y="256" width="24" height="24" color="#2d3142"/><text class="node-name" x="384" y="262">Spreadsheet Handoffs</text><text class="sub" x="384" y="278">local workbooks · macros</text><text class="sub" x="384" y="292">manual reconciliation</text>
<rect class="node-mask" x="340" y="344" width="264" height="56" rx="6"/><rect class="node survivor" x="340" y="344" width="264" height="56" rx="6"/><use href="#icon-db" x="352" y="360" width="24" height="24" color="#7a8c47"/><text class="node-name survivor" x="384" y="370">On-prem RDBMS</text><text class="sub" x="384" y="386">inventory · finance</text>
<rect class="node-mask" x="704" y="80" width="208" height="60" rx="6"/><rect class="node focal" x="704" y="80" width="208" height="60" rx="6"/><use href="#icon-screen" x="716" y="100" width="24" height="24" color="#eb6c36"/><text class="node-name" x="748" y="108">Reporting Portal</text><text class="sub focal" x="748" y="124">manual refresh</text>
<rect class="node-mask" x="704" y="216" width="208" height="56" rx="6"/><rect class="node" x="704" y="216" width="208" height="56" rx="6"/><use href="#icon-file" x="716" y="232" width="24" height="24" color="#2d3142"/><text class="node-name" x="748" y="242">Email Report Packs</text><text class="sub" x="748" y="258">weekly · PDF</text>
<rect class="node-mask" x="704" y="344" width="208" height="56" rx="6"/><rect class="node external" x="704" y="344" width="208" height="56" rx="6"/><use href="#icon-users" x="716" y="360" width="24" height="24" color="#4f5d75"/><text class="node-name" x="748" y="370">Regional Managers</text><text class="sub" x="748" y="386">12 store regions</text>
<rect class="label-mask" x="260" y="86" width="40" height="16" rx="2"/><text class="arrow-label" x="280" y="98" text-anchor="middle" fill="#2e5aa8">CSV</text><rect class="label-mask" x="260" y="174" width="52" height="16" rx="2"/><text class="arrow-label" x="286" y="186" text-anchor="middle" fill="#2e5aa8">EXPORT</text><rect class="label-mask" x="260" y="262" width="44" height="16" rx="2"/><text class="arrow-label" x="282" y="274" text-anchor="middle" fill="#2e5aa8">XLSX</text>
<rect class="label-mask" x="478" y="156" width="40" height="16" rx="2"/><text class="arrow-label" x="498" y="168" text-anchor="middle" fill="#eb6c36">COPY</text><rect class="label-mask" x="478" y="312" width="40" height="16" rx="2"/><text class="arrow-label" x="498" y="324" text-anchor="middle" fill="#4f5d75">LOAD</text><rect class="label-mask" x="612" y="246" width="44" height="16" rx="2"/><text class="arrow-label" x="634" y="258" text-anchor="middle" fill="#eb6c36">XLSX</text><rect class="label-mask" x="814" y="148" width="36" height="16" rx="2"/><text class="arrow-label" x="832" y="160" text-anchor="middle" fill="#2e5aa8">PDF</text><rect class="label-mask" x="814" y="280" width="48" height="16" rx="2"/><text class="arrow-label" x="838" y="292" text-anchor="middle" fill="#2e5aa8">EMAIL</text>
<line x1="16" y1="440" x2="944" y2="440" stroke="rgba(45,49,66,.12)" stroke-width=".8"/><text class="legend-label" x="16" y="460" letter-spacing=".14em">LEGEND</text><line x1="80" y1="456" x2="104" y2="456" stroke="#2e5aa8" stroke-width="1.2"/><text class="legend-label" x="112" y="460">data flow</text><line x1="208" y1="456" x2="232" y2="456" stroke="#eb6c36" stroke-width="1.4"/><text class="legend-label" x="240" y="460">pain-point</text><rect x="372" y="450" width="16" height="12" rx="2" fill="#fff" stroke="#4f5d75" stroke-width=".8" stroke-dasharray="4 3"/><text class="legend-label" x="396" y="460">external</text><rect x="492" y="450" width="16" height="12" rx="2" fill="rgba(235,108,54,.07)" stroke="#eb6c36"/><text class="legend-label" x="516" y="460">bottleneck</text>
</svg>
</div>
<section class="cards" aria-label="Current-state summary">
<article class="card"><p class="eyebrow">THE HEADLINE</p><div class="card-header"><span class="card-dot coral"></span><h2>Two manual bottlenecks</h2></div><ul><li>The shared drive has no version history.</li><li>The portal depends on a person-led refresh.</li><li>Spreadsheet copies break traceability.</li></ul></article>
<article class="card"><div class="card-header"><span class="card-dot link"></span><h2>File-based exchanges</h2></div><ul><li>Stores export nightly CSV files.</li><li>Suppliers email changing workbooks.</li><li>Regional packs leave as PDF attachments.</li></ul></article>
<article class="card"><div class="card-header"><span class="card-dot muted"></span><h2>System that remains</h2></div><p>The on-premises relational database remains the inventory and finance source of record while the surrounding handoffs are modernized.</p></article>
</section>
<footer class="footer"><span>Northwind Retail · IT current-state</span><span>example · diagram design</span></footer>
</main>
</body>
</html>
@@ -0,0 +1,128 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Northwind Retail · Current IT Landscape</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&amp;family=Geist:wght@400;500;600&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--paper: #f5f5f5; --paper-2: #ececec; --ink: #2d3142;
--muted: #4f5d75; --soft: #7a8399; --rule: rgba(45,49,66,0.12);
--accent: #eb6c36; --link: #2e5aa8;
--sans: 'Geist', system-ui, sans-serif;
--serif: 'Instrument Serif', serif;
--mono: 'Geist Mono', ui-monospace, monospace;
}
body { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 3rem 2rem; background: var(--paper); color: var(--ink); font-family: var(--sans); }
.frame { width: 100%; max-width: 1200px; }
.eyebrow { margin-bottom: 0.5rem; color: var(--muted); font: 500 0.66rem/1 var(--mono); letter-spacing: 0.18em; text-transform: uppercase; }
h1 { margin-bottom: 0.35rem; color: var(--ink); font: 400 clamp(1.5rem, 2.4vw + 0.75rem, 2rem)/1.15 var(--serif); letter-spacing: -0.02em; }
.subtitle { margin-bottom: 1.5rem; color: var(--muted); font-size: 0.9rem; line-height: 1.5; }
.diagram { overflow-x: auto; }
svg { display: block; width: 100%; min-width: 920px; }
svg .zone { fill: rgba(45,49,66,0.02); stroke: rgba(45,49,66,0.10); stroke-width: 0.8; }
svg .zone-mask, svg .node-mask, svg .label-mask { fill: #f5f5f5; }
svg .zone-label, svg .arrow-label, svg .legend-label { font-family: 'Geist Mono', monospace; }
svg .zone-label { fill: rgba(45,49,66,0.40); font-size: 8px; letter-spacing: 0.14em; }
svg .node { fill: #fff; stroke: #2d3142; stroke-width: 1; }
svg .node.external { stroke: #4f5d75; stroke-dasharray: 4 3; }
svg .node.focal { fill: rgba(235,108,54,0.07); stroke: #eb6c36; stroke-width: 1.4; }
svg .node.survivor { fill: rgba(122,140,71,0.06); stroke: rgba(122,140,71,0.45); }
svg .node-name { fill: #2d3142; font: 600 12px 'Geist', sans-serif; }
svg .node-name.survivor { fill: #7a8c47; }
svg .sub { fill: #4f5d75; font: 400 10px 'Geist Mono', monospace; }
svg .sub.focal { fill: #eb6c36; }
svg .arrow-label { font-size: 8px; font-weight: 600; letter-spacing: 0.08em; }
svg .legend-label { fill: #4f5d75; font-size: 8px; }
</style>
</head>
<body>
<main class="frame">
<p class="eyebrow">IT current-state · Northwind Retail</p>
<h1>Legacy retail reporting landscape</h1>
<p class="subtitle">File exports, desktop spreadsheets, and manual publication before a shared data platform.</p>
<div class="diagram">
<svg viewBox="0 0 960 500" role="img" aria-labelledby="it-title it-desc" xmlns="http://www.w3.org/2000/svg">
<title id="it-title">Northwind Retail current IT landscape</title>
<desc id="it-desc">Three zones show collection, processing, and dissemination through manual file handoffs.</desc>
<defs>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#4f5d75"/></marker>
<marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#2e5aa8"/></marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#eb6c36"/></marker>
<symbol id="icon-file" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14 3v4a1 1 0 0 0 1 1h4"/><path d="M17 21H7a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h7l5 5v11a2 2 0 0 1-2 2"/></symbol>
<symbol id="icon-screen" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="12" rx="1"/><path d="M7 20h10M9 16v4M15 16v4"/></symbol>
<symbol id="icon-db" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><ellipse cx="12" cy="6" rx="8" ry="3"/><path d="M4 6v6c0 1.7 3.6 3 8 3s8-1.3 8-3V6M4 12v6c0 1.7 3.6 3 8 3s8-1.3 8-3v-6"/></symbol>
<symbol id="icon-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="9" cy="8" r="4"/><path d="M3 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2M16 4.5a4 4 0 0 1 0 7M18 15a4 4 0 0 1 3 4v2"/></symbol>
</defs>
<rect width="960" height="500" fill="#f5f5f5"/>
<!-- Phase zones -->
<rect class="zone" x="16" y="52" width="256" height="360" rx="8"/>
<rect class="zone-mask" x="36" y="44" width="92" height="16"/>
<text class="zone-label" x="40" y="56">COLLECTION</text>
<rect class="zone" x="292" y="52" width="360" height="360" rx="8"/>
<rect class="zone-mask" x="312" y="44" width="92" height="16"/>
<text class="zone-label" x="316" y="56">PROCESSING</text>
<rect class="zone" x="672" y="52" width="272" height="360" rx="8"/>
<rect class="zone-mask" x="692" y="44" width="116" height="16"/>
<text class="zone-label" x="696" y="56">DISSEMINATION</text>
<!-- Connectors first; Shared Drive entries fan to y=108,124,140 -->
<path d="M 252,108 H 340" fill="none" stroke="#2e5aa8" stroke-width="1.2" marker-end="url(#arrow-link)"/>
<path d="M 252,196 H 308 Q 316,196 316,188 V 132 Q 316,124 324,124 H 340" fill="none" stroke="#2e5aa8" stroke-width="1.2" marker-end="url(#arrow-link)"/>
<path d="M 252,284 H 324 Q 332,284 332,276 V 148 Q 332,140 340,140" fill="none" stroke="#2e5aa8" stroke-width="1.2" stroke-dasharray="4 3" marker-end="url(#arrow-link)"/>
<line x1="472" y1="148" x2="472" y2="232" stroke="#eb6c36" stroke-width="1.4" stroke-dasharray="4 3" marker-end="url(#arrow-accent)"/>
<line x1="472" y1="304" x2="472" y2="344" stroke="#4f5d75" stroke-width="1" marker-end="url(#arrow)"/>
<path d="M 604,268 H 654 Q 662,268 662,260 V 72 Q 662,64 670,64 H 800 Q 808,64 808,72 V 80" fill="none" stroke="#eb6c36" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
<line x1="808" y1="140" x2="808" y2="216" stroke="#2e5aa8" stroke-width="1.2" marker-end="url(#arrow-link)"/>
<line x1="808" y1="272" x2="808" y2="344" stroke="#2e5aa8" stroke-width="1.2" stroke-dasharray="4 3" marker-end="url(#arrow-link)"/>
<!-- Collection -->
<rect class="node-mask" x="36" y="80" width="216" height="56" rx="6"/><rect class="node" x="36" y="80" width="216" height="56" rx="6"/>
<use href="#icon-file" x="48" y="96" width="24" height="24" color="#2d3142"/><text class="node-name" x="80" y="106">Point-of-Sale Exports</text><text class="sub" x="80" y="122">nightly · CSV</text>
<rect class="node-mask" x="36" y="168" width="216" height="56" rx="6"/><rect class="node" x="36" y="168" width="216" height="56" rx="6"/>
<use href="#icon-file" x="48" y="184" width="24" height="24" color="#2d3142"/><text class="node-name" x="80" y="194">Online Store Export</text><text class="sub" x="80" y="210">orders · flat file</text>
<rect class="node-mask" x="36" y="256" width="216" height="56" rx="6"/><rect class="node external" x="36" y="256" width="216" height="56" rx="6"/>
<use href="#icon-file" x="48" y="272" width="24" height="24" color="#4f5d75"/><text class="node-name" x="80" y="282">Supplier Price Lists</text><text class="sub" x="80" y="298">external · XLSX</text>
<!-- Processing -->
<rect class="node-mask" x="340" y="80" width="264" height="68" rx="6"/><rect class="node focal" x="340" y="80" width="264" height="68" rx="6"/>
<use href="#icon-file" x="352" y="100" width="24" height="24" color="#eb6c36"/><text class="node-name" x="384" y="108">Shared Drive</text><text class="sub focal" x="384" y="124">no version control</text><text class="sub" x="384" y="140">department folders</text>
<rect class="node-mask" x="340" y="232" width="264" height="72" rx="6"/><rect class="node" x="340" y="232" width="264" height="72" rx="6"/>
<use href="#icon-screen" x="352" y="256" width="24" height="24" color="#2d3142"/><text class="node-name" x="384" y="262">Spreadsheet Handoffs</text><text class="sub" x="384" y="278">local workbooks · macros</text><text class="sub" x="384" y="292">manual reconciliation</text>
<rect class="node-mask" x="340" y="344" width="264" height="56" rx="6"/><rect class="node survivor" x="340" y="344" width="264" height="56" rx="6"/>
<use href="#icon-db" x="352" y="360" width="24" height="24" color="#7a8c47"/><text class="node-name survivor" x="384" y="370">On-prem RDBMS</text><text class="sub" x="384" y="386">inventory · finance</text>
<!-- Dissemination -->
<rect class="node-mask" x="704" y="80" width="208" height="60" rx="6"/><rect class="node focal" x="704" y="80" width="208" height="60" rx="6"/>
<use href="#icon-screen" x="716" y="100" width="24" height="24" color="#eb6c36"/><text class="node-name" x="748" y="108">Reporting Portal</text><text class="sub focal" x="748" y="124">manual refresh</text>
<rect class="node-mask" x="704" y="216" width="208" height="56" rx="6"/><rect class="node" x="704" y="216" width="208" height="56" rx="6"/>
<use href="#icon-file" x="716" y="232" width="24" height="24" color="#2d3142"/><text class="node-name" x="748" y="242">Email Report Packs</text><text class="sub" x="748" y="258">weekly · PDF</text>
<rect class="node-mask" x="704" y="344" width="208" height="56" rx="6"/><rect class="node external" x="704" y="344" width="208" height="56" rx="6"/>
<use href="#icon-users" x="716" y="360" width="24" height="24" color="#4f5d75"/><text class="node-name" x="748" y="370">Regional Managers</text><text class="sub" x="748" y="386">12 store regions</text>
<!-- Labels are source-bound with a 6px perpendicular gap -->
<rect class="label-mask" x="260" y="86" width="40" height="16" rx="2"/><text class="arrow-label" x="280" y="98" text-anchor="middle" fill="#2e5aa8">CSV</text>
<rect class="label-mask" x="260" y="174" width="52" height="16" rx="2"/><text class="arrow-label" x="286" y="186" text-anchor="middle" fill="#2e5aa8">EXPORT</text>
<rect class="label-mask" x="260" y="262" width="44" height="16" rx="2"/><text class="arrow-label" x="282" y="274" text-anchor="middle" fill="#2e5aa8">XLSX</text>
<rect class="label-mask" x="478" y="156" width="40" height="16" rx="2"/><text class="arrow-label" x="498" y="168" text-anchor="middle" fill="#eb6c36">COPY</text>
<rect class="label-mask" x="478" y="312" width="40" height="16" rx="2"/><text class="arrow-label" x="498" y="324" text-anchor="middle" fill="#4f5d75">LOAD</text>
<rect class="label-mask" x="612" y="246" width="44" height="16" rx="2"/><text class="arrow-label" x="634" y="258" text-anchor="middle" fill="#eb6c36">XLSX</text>
<rect class="label-mask" x="814" y="148" width="36" height="16" rx="2"/><text class="arrow-label" x="832" y="160" text-anchor="middle" fill="#2e5aa8">PDF</text>
<rect class="label-mask" x="814" y="280" width="48" height="16" rx="2"/><text class="arrow-label" x="838" y="292" text-anchor="middle" fill="#2e5aa8">EMAIL</text>
<!-- Legend strip -->
<line x1="16" y1="440" x2="944" y2="440" stroke="rgba(45,49,66,0.12)" stroke-width="0.8"/>
<text class="legend-label" x="16" y="460" letter-spacing="0.14em">LEGEND</text>
<line x1="80" y1="456" x2="104" y2="456" stroke="#2e5aa8" stroke-width="1.2"/><text class="legend-label" x="112" y="460">data flow</text>
<line x1="208" y1="456" x2="232" y2="456" stroke="#eb6c36" stroke-width="1.4"/><text class="legend-label" x="240" y="460">pain-point</text>
<rect x="372" y="450" width="16" height="12" rx="2" fill="#fff" stroke="#4f5d75" stroke-width="0.8" stroke-dasharray="4 3"/><text class="legend-label" x="396" y="460">external</text>
<rect x="492" y="450" width="16" height="12" rx="2" fill="rgba(235,108,54,0.07)" stroke="#eb6c36"/><text class="legend-label" x="516" y="460">bottleneck</text>
</svg>
</div>
</main>
</body>
</html>
@@ -0,0 +1,109 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Weekly signups · Line chart · dark</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root { --color-paper:#f5f5f5; --color-ink:#2d3142; --color-muted:#4f5d75; --color-accent:#eb6c36; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
body { font-family: var(--font-sans); background: var(--color-paper); color: var(--color-ink); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 3rem 2rem; }
.frame { max-width: 1200px; width: 100%; }
.eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.15; margin-bottom: 1.5rem; }
svg { width: 100%; min-width: 760px; display: block; }
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Line · Diagram Design</p>
<h1>Weekly signups · Organic leads the growth</h1>
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(245,245,245,0.10)"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- Y-axis rotated label -->
<text transform="rotate(-90 24 230)" x="24" y="230" fill="#bfc0c0" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">SIGNUPS / WEEK</text>
<!-- Gridlines -->
<line x1="80" y1="357" x2="960" y2="357" stroke="rgba(245,245,245,0.08)" stroke-width="0.8"/>
<line x1="80" y1="293" x2="960" y2="293" stroke="rgba(245,245,245,0.08)" stroke-width="0.8"/>
<line x1="80" y1="230" x2="960" y2="230" stroke="rgba(245,245,245,0.08)" stroke-width="0.8"/>
<line x1="80" y1="167" x2="960" y2="167" stroke="rgba(245,245,245,0.08)" stroke-width="0.8"/>
<line x1="80" y1="103" x2="960" y2="103" stroke="rgba(245,245,245,0.08)" stroke-width="0.8"/>
<line x1="80" y1="40" x2="960" y2="40" stroke="rgba(245,245,245,0.06)" stroke-width="0.8"/>
<!-- Axes -->
<line x1="80" y1="40" x2="80" y2="420" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<line x1="80" y1="420" x2="960" y2="420" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<!-- Y-axis labels -->
<text x="72" y="361" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">40</text>
<text x="72" y="297" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">80</text>
<text x="72" y="234" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">120</text>
<text x="72" y="171" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">160</text>
<text x="72" y="107" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">200</text>
<text x="72" y="44" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">240</text>
<!-- ─── SERIES (non-focal drawn first) ─── -->
<!-- Referral: [45,52,48,60,55,68,62,75] series-2 dusty-blue -->
<polyline points="80,349 200,338 320,344 440,325 560,333 680,312 800,322 920,301"
fill="none" stroke="#82a0c0" stroke-width="1.2" stroke-linejoin="round"/>
<!-- Direct: [80,82,88,90,86,92,95,98] series-1 sage -->
<polyline points="80,293 200,290 320,281 440,278 560,284 680,274 800,270 920,265"
fill="none" stroke="#9caf8f" stroke-width="1.2" stroke-linejoin="round"/>
<!-- Organic FOCAL area fill (subtle) -->
<polygon points="80,230 200,206 320,186 440,164 560,175 680,138 800,111 920,88 920,420 80,420"
fill="rgba(240,138,89,0.08)"/>
<!-- Organic FOCAL line: [120,135,148,162,155,178,195,210] accent -->
<polyline points="80,230 200,206 320,186 440,164 560,175 680,138 800,111 920,88"
fill="none" stroke="#f08a59" stroke-width="1.8" stroke-linejoin="round"/>
<!-- Focal vertex dots -->
<circle cx="80" cy="230" r="4" fill="#f08a59"/>
<circle cx="200" cy="206" r="4" fill="#f08a59"/>
<circle cx="320" cy="186" r="4" fill="#f08a59"/>
<circle cx="440" cy="164" r="4" fill="#f08a59"/>
<circle cx="560" cy="175" r="4" fill="#f08a59"/>
<circle cx="680" cy="138" r="4" fill="#f08a59"/>
<circle cx="800" cy="111" r="4" fill="#f08a59"/>
<circle cx="920" cy="88" r="4" fill="#f08a59"/>
<!-- X-axis labels -->
<text x="80" y="440" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W1</text>
<text x="200" y="440" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W2</text>
<text x="320" y="440" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W3</text>
<text x="440" y="440" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W4</text>
<text x="560" y="440" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W5</text>
<text x="680" y="440" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W6</text>
<text x="800" y="440" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W7</text>
<text x="920" y="440" fill="#bfc0c0" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W8</text>
<!-- Legend -->
<line x1="40" y1="462" x2="960" y2="462" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<text x="40" y="478" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<line x1="40" y1="492" x2="64" y2="492" stroke="#f08a59" stroke-width="1.8"/>
<circle cx="52" cy="492" r="4" fill="#f08a59"/>
<text x="72" y="496" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Organic · focal</text>
<line x1="200" y1="492" x2="224" y2="492" stroke="#9caf8f" stroke-width="1.2"/>
<text x="232" y="496" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Direct</text>
<line x1="316" y1="492" x2="340" y2="492" stroke="#82a0c0" stroke-width="1.2"/>
<text x="348" y="496" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Referral</text>
</svg>
</div>
</body>
</html>
@@ -0,0 +1,103 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Weekly signups · Line chart</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root { --color-paper:#f5f5f5; --color-paper-2:#ececec; --color-ink:#2d3142; --color-muted:#4f5d75; --color-soft:#7a8399; --color-rule:rgba(45,49,66,0.12); --color-accent:#eb6c36; --series-1:#7c8f6f; --series-2:#5e7a9b; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
.container { max-width: 1200px; margin: 0 auto; }
.header { margin-bottom: 2.5rem; }
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 58ch; }
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
svg { width: 100%; min-width: 760px; display: block; }
.cards { display: grid; grid-template-columns: 1.1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
@media (max-width: 720px) { .cards { grid-template-columns: 1fr; } }
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(45,49,66,0.08); }
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
.card-dot.coral { background: var(--color-accent); } .card-dot.sage { background: var(--series-1); } .card-dot.dusty { background: var(--series-2); }
.card h3 { font-size: 0.875rem; font-weight: 600; }
.card p { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; }
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(45,49,66,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<p class="header-eyebrow">Line · Diagram Design</p>
<h1>Weekly signups · Organic leads the growth</h1>
<p class="subtitle">Signup volume by acquisition channel over eight weeks. Organic search is both the largest and fastest-growing channel; Direct is the stable baseline.</p>
</div>
<div class="diagram-container">
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
<defs><pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse"><circle cx="1" cy="1" r="0.9" fill="rgba(45,49,66,0.10)"/></pattern></defs>
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<text transform="rotate(-90 24 230)" x="24" y="230" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">SIGNUPS / WEEK</text>
<line x1="80" y1="357" x2="960" y2="357" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="293" x2="960" y2="293" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="230" x2="960" y2="230" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="167" x2="960" y2="167" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="103" x2="960" y2="103" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="40" x2="960" y2="40" stroke="rgba(45,49,66,0.06)" stroke-width="0.8"/>
<line x1="80" y1="40" x2="80" y2="420" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<line x1="80" y1="420" x2="960" y2="420" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<text x="72" y="361" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">40</text>
<text x="72" y="297" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">80</text>
<text x="72" y="234" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">120</text>
<text x="72" y="171" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">160</text>
<text x="72" y="107" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">200</text>
<text x="72" y="44" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">240</text>
<polyline points="80,349 200,338 320,344 440,325 560,333 680,312 800,322 920,301" fill="none" stroke="#5e7a9b" stroke-width="1.2" stroke-linejoin="round"/>
<polyline points="80,293 200,290 320,281 440,278 560,284 680,274 800,270 920,265" fill="none" stroke="#7c8f6f" stroke-width="1.2" stroke-linejoin="round"/>
<polygon points="80,230 200,206 320,186 440,164 560,175 680,138 800,111 920,88 920,420 80,420" fill="rgba(235,108,54,0.06)"/>
<polyline points="80,230 200,206 320,186 440,164 560,175 680,138 800,111 920,88" fill="none" stroke="#eb6c36" stroke-width="1.8" stroke-linejoin="round"/>
<circle cx="80" cy="230" r="4" fill="#eb6c36"/><circle cx="200" cy="206" r="4" fill="#eb6c36"/><circle cx="320" cy="186" r="4" fill="#eb6c36"/><circle cx="440" cy="164" r="4" fill="#eb6c36"/>
<circle cx="560" cy="175" r="4" fill="#eb6c36"/><circle cx="680" cy="138" r="4" fill="#eb6c36"/><circle cx="800" cy="111" r="4" fill="#eb6c36"/><circle cx="920" cy="88" r="4" fill="#eb6c36"/>
<text x="80" y="440" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W1</text>
<text x="200" y="440" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W2</text>
<text x="320" y="440" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W3</text>
<text x="440" y="440" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W4</text>
<text x="560" y="440" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W5</text>
<text x="680" y="440" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W6</text>
<text x="800" y="440" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W7</text>
<text x="920" y="440" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W8</text>
<line x1="40" y1="462" x2="960" y2="462" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="40" y="478" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<line x1="40" y1="492" x2="64" y2="492" stroke="#eb6c36" stroke-width="1.8"/><circle cx="52" cy="492" r="4" fill="#eb6c36"/>
<text x="72" y="496" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Organic · focal</text>
<line x1="200" y1="492" x2="224" y2="492" stroke="#7c8f6f" stroke-width="1.2"/>
<text x="232" y="496" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Direct</text>
<line x1="316" y1="492" x2="340" y2="492" stroke="#5e7a9b" stroke-width="1.2"/>
<text x="348" y="496" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Referral</text>
</svg>
</div>
<div class="cards">
<div class="card">
<p class="eyebrow">FOCAL · ORGANIC</p>
<div class="card-header"><span class="card-dot coral"></span><h3>Organic search driving 55% of new signups</h3></div>
<p>75% week-over-week growth over 8 weeks — from 120 to 210 signups. Two long-form guides published in W4 account for the step change. The W5 dip is a weekday holiday effect.</p>
</div>
<div class="card">
<div class="card-header"><span class="card-dot sage"></span><h3>Direct: stable core</h3></div>
<p>22% growth (80→98) — this is mostly returning users and brand-aware discovery. The near-flat slope is a healthy baseline, not stagnation. It means the product retains its direct audience while organic expands.</p>
</div>
<div class="card">
<div class="card-header"><span class="card-dot dusty"></span><h3>Referral: early signal</h3></div>
<p>67% growth (45→75), driven by one integration partner. Volatile week-to-week but directionally positive. Worth a dedicated channel review before investing further in partner programmes.</p>
</div>
</div>
<div class="footer">
<span>weekly signups · acquisition channels</span>
<span>example · diagram design</span>
</div>
</div>
</body>
</html>
@@ -0,0 +1,109 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Weekly signups · Line chart</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root { --color-paper:#f5f5f5; --color-ink:#2d3142; --color-muted:#4f5d75; --color-accent:#eb6c36; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
body { font-family: var(--font-sans); background: var(--color-paper); color: var(--color-ink); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 3rem 2rem; }
.frame { max-width: 1200px; width: 100%; }
.eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.15; margin-bottom: 1.5rem; }
svg { width: 100%; min-width: 760px; display: block; }
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Line · Diagram Design</p>
<h1>Weekly signups · Organic leads the growth</h1>
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(45,49,66,0.10)"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- Y-axis rotated label -->
<text transform="rotate(-90 24 230)" x="24" y="230" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">SIGNUPS / WEEK</text>
<!-- Gridlines -->
<line x1="80" y1="357" x2="960" y2="357" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="293" x2="960" y2="293" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="230" x2="960" y2="230" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="167" x2="960" y2="167" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="103" x2="960" y2="103" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="40" x2="960" y2="40" stroke="rgba(45,49,66,0.06)" stroke-width="0.8"/>
<!-- Axes -->
<line x1="80" y1="40" x2="80" y2="420" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<line x1="80" y1="420" x2="960" y2="420" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<!-- Y-axis labels -->
<text x="72" y="361" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">40</text>
<text x="72" y="297" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">80</text>
<text x="72" y="234" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">120</text>
<text x="72" y="171" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">160</text>
<text x="72" y="107" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">200</text>
<text x="72" y="44" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">240</text>
<!-- ─── SERIES (non-focal drawn first) ─── -->
<!-- Referral: [45,52,48,60,55,68,62,75] series-2 dusty-blue -->
<polyline points="80,349 200,338 320,344 440,325 560,333 680,312 800,322 920,301"
fill="none" stroke="#5e7a9b" stroke-width="1.2" stroke-linejoin="round"/>
<!-- Direct: [80,82,88,90,86,92,95,98] series-1 sage -->
<polyline points="80,293 200,290 320,281 440,278 560,284 680,274 800,270 920,265"
fill="none" stroke="#7c8f6f" stroke-width="1.2" stroke-linejoin="round"/>
<!-- Organic FOCAL area fill (subtle) -->
<polygon points="80,230 200,206 320,186 440,164 560,175 680,138 800,111 920,88 920,420 80,420"
fill="rgba(235,108,54,0.06)"/>
<!-- Organic FOCAL line: [120,135,148,162,155,178,195,210] accent -->
<polyline points="80,230 200,206 320,186 440,164 560,175 680,138 800,111 920,88"
fill="none" stroke="#eb6c36" stroke-width="1.8" stroke-linejoin="round"/>
<!-- Focal vertex dots -->
<circle cx="80" cy="230" r="4" fill="#eb6c36"/>
<circle cx="200" cy="206" r="4" fill="#eb6c36"/>
<circle cx="320" cy="186" r="4" fill="#eb6c36"/>
<circle cx="440" cy="164" r="4" fill="#eb6c36"/>
<circle cx="560" cy="175" r="4" fill="#eb6c36"/>
<circle cx="680" cy="138" r="4" fill="#eb6c36"/>
<circle cx="800" cy="111" r="4" fill="#eb6c36"/>
<circle cx="920" cy="88" r="4" fill="#eb6c36"/>
<!-- X-axis labels -->
<text x="80" y="440" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W1</text>
<text x="200" y="440" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W2</text>
<text x="320" y="440" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W3</text>
<text x="440" y="440" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W4</text>
<text x="560" y="440" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W5</text>
<text x="680" y="440" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W6</text>
<text x="800" y="440" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W7</text>
<text x="920" y="440" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" text-anchor="middle">W8</text>
<!-- Legend -->
<line x1="40" y1="462" x2="960" y2="462" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="40" y="478" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<line x1="40" y1="492" x2="64" y2="492" stroke="#eb6c36" stroke-width="1.8"/>
<circle cx="52" cy="492" r="4" fill="#eb6c36"/>
<text x="72" y="496" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Organic · focal</text>
<line x1="200" y1="492" x2="224" y2="492" stroke="#7c8f6f" stroke-width="1.2"/>
<text x="232" y="496" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Direct</text>
<line x1="316" y1="492" x2="340" y2="492" stroke="#5e7a9b" stroke-width="1.2"/>
<text x="348" y="496" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Referral</text>
</svg>
</div>
</body>
</html>
@@ -0,0 +1,159 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>E-commerce analytics · Medallion · Dark</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&amp;family=Geist:wght@400;500;600&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--paper: #2d3142; --paper-2: #393e53; --ink: #f5f5f5; --muted: #bfc0c0;
--soft: #8e98ac; --rule: rgba(245,245,245,0.12); --accent: #f08a59;
--accent-tint: rgba(240,138,89,0.10); --link: #6a95d8;
--sans: 'Geist', system-ui, sans-serif; --serif: 'Instrument Serif', serif;
--mono: 'Geist Mono', ui-monospace, monospace;
}
body { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 3rem 2rem; background: var(--paper); color: var(--ink); font-family: var(--sans); }
.frame { width: 100%; max-width: 1200px; }
.eyebrow { margin-bottom: 0.5rem; color: var(--muted); font: 500 0.66rem var(--mono); letter-spacing: 0.18em; text-transform: uppercase; }
h1 { margin-bottom: 1.5rem; color: var(--ink); font: 400 clamp(1.5rem, 2.4vw + 0.75rem, 2rem)/1.15 var(--serif); letter-spacing: -0.02em; }
svg { display: block; width: 100%; min-width: 1040px; }
.tier-title { fill: var(--ink); font: 600 13px var(--sans); text-anchor: middle; }
.bucket, .field-value, .example-value { fill: var(--muted); font: 400 9px var(--mono); }
.bucket { text-anchor: middle; }
.field-value { color: var(--muted); }
.focal-text { fill: var(--accent); }
.field-label { fill: var(--ink); font: 600 11px var(--sans); }
.example-label, .arc-label, .path-tag { font-family: var(--mono); font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; }
.example-label { fill: var(--soft); font-size: 8px; }
.arc-label { font-size: 10px; text-anchor: middle; }
.path-title { fill: var(--ink); font: 600 11px var(--sans); }
.path-sub { fill: var(--muted); font: 400 9px var(--mono); }
</style>
</head>
<body>
<main class="frame">
<p class="eyebrow">Medallion · Diagram Design</p>
<h1>Five-tier e-commerce analytics storage</h1>
<svg viewBox="0 0 1040 548" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="medallion-title medallion-desc">
<title id="medallion-title">Five-tier e-commerce analytics medallion</title>
<desc id="medallion-desc">Clickstream events and order records move from raw storage through anonymized, staging, and aggregated tiers before lifecycle archiving.</desc>
<defs>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#bfc0c0"/></marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#f08a59"/></marker>
<marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#6a95d8"/></marker>
<clipPath id="clip-raw"><rect x="16" y="80" width="172" height="380" rx="6"/></clipPath>
<clipPath id="clip-anon"><rect x="204" y="80" width="172" height="380" rx="6"/></clipPath>
<clipPath id="clip-stage"><rect x="392" y="80" width="172" height="380" rx="6"/></clipPath>
<clipPath id="clip-aggregate"><rect x="580" y="80" width="172" height="380" rx="6"/></clipPath>
<clipPath id="clip-archive"><rect x="768" y="80" width="172" height="380" rx="6"/></clipPath>
</defs>
<rect width="1040" height="548" fill="#2d3142"/>
<!-- Promotion arcs are the medallion type's required connector grammar. -->
<path d="M102 80 C102 0 290 0 290 80" fill="none" stroke="#bfc0c0" stroke-width="1.4" marker-end="url(#arrow)"/>
<path d="M290 80 C290 0 478 0 478 80" fill="none" stroke="#bfc0c0" stroke-width="1.4" marker-end="url(#arrow)"/>
<path d="M478 80 C478 0 666 0 666 80" fill="none" stroke="#f08a59" stroke-width="1.6" marker-end="url(#arrow-accent)"/>
<path d="M666 80 C666 0 854 0 854 80" fill="none" stroke="#bfc0c0" stroke-width="1.4" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
<text x="196" y="50" class="arc-label" fill="#bfc0c0">MASK IDS</text>
<text x="384" y="50" class="arc-label" fill="#bfc0c0">CLEAN + JOIN</text>
<text x="572" y="50" class="arc-label" fill="#f08a59">AGGREGATE</text>
<text x="760" y="50" class="arc-label" fill="#bfc0c0">LIFECYCLE</text>
<!-- Raw -->
<rect x="16" y="80" width="172" height="380" rx="6" fill="rgba(245,245,245,0.04)"/>
<rect x="16" y="80" width="172" height="50" fill="rgba(142,152,172,0.16)" clip-path="url(#clip-raw)"/>
<rect x="16" y="80" width="172" height="380" rx="6" fill="none" stroke="#bfc0c0"/>
<text x="102" y="106" class="tier-title">Raw</text>
<text x="102" y="144" class="bucket">raw-commerce</text>
<text x="32" y="180" class="field-label">Tool</text>
<foreignObject x="32" y="186" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">NiFi ingest</div></foreignObject>
<text x="32" y="220" class="field-label">Format</text>
<foreignObject x="32" y="226" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">JSON · Parquet</div></foreignObject>
<text x="32" y="260" class="field-label">Writer</text>
<foreignObject x="32" y="266" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Data Engineer</div></foreignObject>
<text x="32" y="360" class="example-label">E-COMMERCE EXAMPLE</text>
<text x="32" y="374" class="example-value">clickstream events</text>
<text x="32" y="388" class="example-value">order records</text>
<!-- Anonymized -->
<rect x="204" y="80" width="172" height="380" rx="6" fill="rgba(245,245,245,0.04)"/>
<rect x="204" y="80" width="172" height="50" fill="rgba(245,245,245,0.08)" clip-path="url(#clip-anon)"/>
<rect x="204" y="80" width="172" height="380" rx="6" fill="none" stroke="rgba(245,245,245,0.30)"/>
<text x="290" y="106" class="tier-title">Anonymized</text>
<text x="290" y="144" class="bucket">anon-commerce</text>
<text x="220" y="180" class="field-label">Tool</text>
<foreignObject x="220" y="186" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Trino SQL</div></foreignObject>
<text x="220" y="220" class="field-label">Format</text>
<foreignObject x="220" y="226" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Iceberg · partitioned</div></foreignObject>
<text x="220" y="260" class="field-label">Writer</text>
<foreignObject x="220" y="266" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Data Engineer</div></foreignObject>
<text x="220" y="360" class="example-label">E-COMMERCE EXAMPLE</text>
<text x="220" y="374" class="example-value">session pseudonyms</text>
<text x="220" y="388" class="example-value">masked customer IDs</text>
<!-- Staging -->
<rect x="392" y="80" width="172" height="380" rx="6" fill="rgba(245,245,245,0.04)"/>
<rect x="392" y="80" width="172" height="50" fill="rgba(245,245,245,0.08)" clip-path="url(#clip-stage)"/>
<rect x="392" y="80" width="172" height="380" rx="6" fill="none" stroke="rgba(245,245,245,0.30)"/>
<text x="478" y="106" class="tier-title">Staging</text>
<text x="478" y="144" class="bucket">staging-commerce</text>
<text x="408" y="180" class="field-label">Tool</text>
<foreignObject x="408" y="186" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Trino · notebooks</div></foreignObject>
<text x="408" y="220" class="field-label">Format</text>
<foreignObject x="408" y="226" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Iceberg · cleaned</div></foreignObject>
<text x="408" y="260" class="field-label">Writer</text>
<foreignObject x="408" y="266" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Data Scientist</div></foreignObject>
<text x="408" y="360" class="example-label">E-COMMERCE EXAMPLE</text>
<text x="408" y="374" class="example-value">joined order facts</text>
<text x="408" y="388" class="example-value">validated event rows</text>
<!-- Aggregated focal tier -->
<rect x="580" y="80" width="172" height="380" rx="6" fill="rgba(240,138,89,0.12)"/>
<rect x="580" y="80" width="172" height="50" fill="rgba(240,138,89,0.20)" clip-path="url(#clip-aggregate)"/>
<rect x="580" y="80" width="172" height="380" rx="6" fill="none" stroke="#f08a59" stroke-width="1.6"/>
<text x="666" y="106" class="tier-title">Aggregated</text>
<text x="666" y="144" class="bucket focal-text">analytics-marts</text>
<text x="596" y="180" class="field-label">Tool</text>
<foreignObject x="596" y="186" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Trino INSERT</div></foreignObject>
<text x="596" y="220" class="field-label">Format</text>
<foreignObject x="596" y="226" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Iceberg · metrics</div></foreignObject>
<text x="596" y="260" class="field-label">Writer</text>
<foreignObject x="596" y="266" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Data Scientist</div></foreignObject>
<text x="596" y="360" class="example-label">E-COMMERCE EXAMPLE</text>
<text x="596" y="374" class="example-value focal-text">daily conversion rate</text>
<text x="596" y="388" class="example-value focal-text">revenue by channel</text>
<!-- Archive -->
<rect x="768" y="80" width="172" height="380" rx="6" fill="rgba(245,245,245,0.06)"/>
<rect x="768" y="80" width="172" height="50" fill="rgba(142,152,172,0.24)" clip-path="url(#clip-archive)"/>
<rect x="768" y="80" width="172" height="380" rx="6" fill="none" stroke="#bfc0c0" stroke-dasharray="5,3"/>
<text x="854" y="106" class="tier-title">Archive</text>
<text x="854" y="144" class="bucket">archive-commerce</text>
<text x="784" y="180" class="field-label">Tool</text>
<foreignObject x="784" y="186" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Object storage lifecycle</div></foreignObject>
<text x="784" y="220" class="field-label">Format</text>
<foreignObject x="784" y="226" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Cold tier · immutable</div></foreignObject>
<text x="784" y="260" class="field-label">Writer</text>
<foreignObject x="784" y="266" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Data Engineer</div></foreignObject>
<text x="784" y="360" class="example-label">E-COMMERCE EXAMPLE</text>
<text x="784" y="374" class="example-value">retained clickstream</text>
<text x="784" y="388" class="example-value">closed order snapshots</text>
<!-- Write-method paths -->
<rect x="16" y="476" width="460" height="56" rx="6" fill="rgba(245,245,245,0.04)" stroke="rgba(245,245,245,0.20)"/>
<rect x="24" y="482" width="64" height="12" rx="2" fill="none" stroke="rgba(245,245,245,0.30)" stroke-width="0.8"/>
<text x="56" y="491" class="path-tag" fill="#f5f5f5" font-size="7" text-anchor="middle">SQL PATH</text>
<text x="96" y="506" class="path-title">Trino INSERT INTO … SELECT</text>
<text x="96" y="522" class="path-sub">filter · join · aggregate — repeatable transforms</text>
<rect x="492" y="476" width="460" height="56" rx="6" fill="rgba(245,245,245,0.04)" stroke="rgba(245,245,245,0.20)"/>
<rect x="500" y="482" width="96" height="12" rx="2" fill="none" stroke="rgba(245,245,245,0.30)" stroke-width="0.8"/>
<text x="548" y="491" class="path-tag" fill="#f5f5f5" font-size="7" text-anchor="middle">NOTEBOOK PATH</text>
<text x="604" y="506" class="path-title">Python notebooks on object storage</text>
<text x="604" y="522" class="path-sub">explore · validate · model — interactive analysis</text>
</svg>
</main>
</body>
</html>
@@ -0,0 +1,201 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>E-commerce analytics · Medallion · Full</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&amp;family=Geist:wght@400;500;600&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--paper: #f5f5f5; --paper-2: #ececec; --ink: #2d3142; --muted: #4f5d75;
--soft: #7a8399; --rule: rgba(45,49,66,0.12); --accent: #eb6c36;
--accent-tint: rgba(235,108,54,0.08); --link: #2e5aa8;
--sans: 'Geist', system-ui, sans-serif; --serif: 'Instrument Serif', serif;
--mono: 'Geist Mono', ui-monospace, monospace;
}
body { min-height: 100vh; padding: 3rem 2rem; background: var(--paper); color: var(--ink); font-family: var(--sans); }
.container { width: 100%; max-width: 1200px; margin: 0 auto; }
.eyebrow { margin-bottom: 0.5rem; color: var(--muted); font: 500 0.66rem var(--mono); letter-spacing: 0.18em; text-transform: uppercase; }
h1 { margin-bottom: 1.5rem; color: var(--ink); font: 400 clamp(1.5rem, 2.4vw + 0.75rem, 2rem)/1.15 var(--serif); letter-spacing: -0.02em; }
svg { display: block; width: 100%; min-width: 1040px; }
.tier-title { fill: var(--ink); font: 600 13px var(--sans); text-anchor: middle; }
.bucket, .field-value, .example-value { fill: var(--muted); font: 400 9px var(--mono); }
.bucket { text-anchor: middle; }
.field-value { color: var(--muted); }
.focal-text { fill: var(--accent); }
.field-label { fill: var(--ink); font: 600 11px var(--sans); }
.example-label, .arc-label, .path-tag { font-family: var(--mono); font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; }
.example-label { fill: var(--soft); font-size: 8px; }
.arc-label { font-size: 10px; text-anchor: middle; }
.path-title { fill: var(--ink); font: 600 11px var(--sans); }
.path-sub { fill: var(--muted); font: 400 9px var(--mono); }
.header { margin-bottom: 2.5rem; }
.header h1 { margin-bottom: 0.5rem; }
.subtitle { max-width: 62ch; color: var(--muted); font-size: 1rem; line-height: 1.55; }
.diagram-container { overflow-x: auto; padding: 1.5rem; background: var(--paper-2); border: 1px solid var(--rule); border-radius: 8px; }
.cards { display: grid; grid-template-columns: 1.1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
.card { padding: 1.25rem; background: #fff; border: 1px solid var(--rule); border-radius: 6px; }
.card .card-eyebrow { margin-bottom: 0.5rem; color: var(--muted); font: 500 0.5rem var(--mono); letter-spacing: 0.18em; text-transform: uppercase; }
.card-header { display: flex; gap: 0.6rem; align-items: center; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(45,49,66,0.08); }
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
.card-dot.coral { background: var(--accent); } .card-dot.ink { background: var(--ink); } .card-dot.muted { background: var(--muted); }
.card h2 { font-size: 0.875rem; font-weight: 600; }
.card p, .card ul { color: var(--muted); font-size: 0.8125rem; line-height: 1.55; list-style: none; }
.card li { position: relative; margin-bottom: 0.3rem; padding-left: 0.875rem; }
.card li::before { position: absolute; left: 0; color: rgba(45,49,66,0.25); content: '—'; }
.footer { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.5rem; margin-top: 2rem; padding-top: 1.5rem; color: var(--soft); border-top: 1px solid rgba(45,49,66,0.10); font: 400 0.72rem var(--mono); letter-spacing: 0.06em; }
@media (max-width: 820px) { .cards { grid-template-columns: 1fr; } }
</style>
</head>
<body>
<main class="container">
<header class="header">
<p class="eyebrow">Medallion · Diagram Design</p>
<h1>Five-tier e-commerce analytics storage</h1>
<p class="subtitle">A quality ladder for clickstream events and order records, from immutable landing data to dashboard-ready metrics and cold retention.</p>
</header>
<div class="diagram-container">
<svg viewBox="0 0 1040 548" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="medallion-title medallion-desc">
<title id="medallion-title">Five-tier e-commerce analytics medallion</title>
<desc id="medallion-desc">Clickstream events and order records move from raw storage through anonymized, staging, and aggregated tiers before lifecycle archiving.</desc>
<defs>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#4f5d75"/></marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#eb6c36"/></marker>
<marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#2e5aa8"/></marker>
<clipPath id="clip-raw"><rect x="16" y="80" width="172" height="380" rx="6"/></clipPath>
<clipPath id="clip-anon"><rect x="204" y="80" width="172" height="380" rx="6"/></clipPath>
<clipPath id="clip-stage"><rect x="392" y="80" width="172" height="380" rx="6"/></clipPath>
<clipPath id="clip-aggregate"><rect x="580" y="80" width="172" height="380" rx="6"/></clipPath>
<clipPath id="clip-archive"><rect x="768" y="80" width="172" height="380" rx="6"/></clipPath>
</defs>
<rect width="1040" height="548" fill="#f5f5f5"/>
<!-- Promotion arcs are the medallion type's required connector grammar. -->
<path d="M102 80 C102 0 290 0 290 80" fill="none" stroke="#4f5d75" stroke-width="1.4" marker-end="url(#arrow)"/>
<path d="M290 80 C290 0 478 0 478 80" fill="none" stroke="#4f5d75" stroke-width="1.4" marker-end="url(#arrow)"/>
<path d="M478 80 C478 0 666 0 666 80" fill="none" stroke="#eb6c36" stroke-width="1.6" marker-end="url(#arrow-accent)"/>
<path d="M666 80 C666 0 854 0 854 80" fill="none" stroke="#4f5d75" stroke-width="1.4" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
<text x="196" y="50" class="arc-label" fill="#4f5d75">MASK IDS</text>
<text x="384" y="50" class="arc-label" fill="#4f5d75">CLEAN + JOIN</text>
<text x="572" y="50" class="arc-label" fill="#eb6c36">AGGREGATE</text>
<text x="760" y="50" class="arc-label" fill="#4f5d75">LIFECYCLE</text>
<!-- Raw -->
<rect x="16" y="80" width="172" height="380" rx="6" fill="#fff"/>
<rect x="16" y="80" width="172" height="50" fill="rgba(79,93,117,0.10)" clip-path="url(#clip-raw)"/>
<rect x="16" y="80" width="172" height="380" rx="6" fill="none" stroke="#4f5d75"/>
<text x="102" y="106" class="tier-title">Raw</text>
<text x="102" y="144" class="bucket">raw-commerce</text>
<text x="32" y="180" class="field-label">Tool</text>
<foreignObject x="32" y="186" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">NiFi ingest</div></foreignObject>
<text x="32" y="220" class="field-label">Format</text>
<foreignObject x="32" y="226" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">JSON · Parquet</div></foreignObject>
<text x="32" y="260" class="field-label">Writer</text>
<foreignObject x="32" y="266" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Data Engineer</div></foreignObject>
<text x="32" y="360" class="example-label">E-COMMERCE EXAMPLE</text>
<text x="32" y="374" class="example-value">clickstream events</text>
<text x="32" y="388" class="example-value">order records</text>
<!-- Anonymized -->
<rect x="204" y="80" width="172" height="380" rx="6" fill="#fff"/>
<rect x="204" y="80" width="172" height="50" fill="rgba(45,49,66,0.06)" clip-path="url(#clip-anon)"/>
<rect x="204" y="80" width="172" height="380" rx="6" fill="none" stroke="#2d3142"/>
<text x="290" y="106" class="tier-title">Anonymized</text>
<text x="290" y="144" class="bucket">anon-commerce</text>
<text x="220" y="180" class="field-label">Tool</text>
<foreignObject x="220" y="186" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Trino SQL</div></foreignObject>
<text x="220" y="220" class="field-label">Format</text>
<foreignObject x="220" y="226" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Iceberg · partitioned</div></foreignObject>
<text x="220" y="260" class="field-label">Writer</text>
<foreignObject x="220" y="266" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Data Engineer</div></foreignObject>
<text x="220" y="360" class="example-label">E-COMMERCE EXAMPLE</text>
<text x="220" y="374" class="example-value">session pseudonyms</text>
<text x="220" y="388" class="example-value">masked customer IDs</text>
<!-- Staging -->
<rect x="392" y="80" width="172" height="380" rx="6" fill="#fff"/>
<rect x="392" y="80" width="172" height="50" fill="rgba(45,49,66,0.06)" clip-path="url(#clip-stage)"/>
<rect x="392" y="80" width="172" height="380" rx="6" fill="none" stroke="#2d3142"/>
<text x="478" y="106" class="tier-title">Staging</text>
<text x="478" y="144" class="bucket">staging-commerce</text>
<text x="408" y="180" class="field-label">Tool</text>
<foreignObject x="408" y="186" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Trino · notebooks</div></foreignObject>
<text x="408" y="220" class="field-label">Format</text>
<foreignObject x="408" y="226" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Iceberg · cleaned</div></foreignObject>
<text x="408" y="260" class="field-label">Writer</text>
<foreignObject x="408" y="266" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Data Scientist</div></foreignObject>
<text x="408" y="360" class="example-label">E-COMMERCE EXAMPLE</text>
<text x="408" y="374" class="example-value">joined order facts</text>
<text x="408" y="388" class="example-value">validated event rows</text>
<!-- Aggregated focal tier -->
<rect x="580" y="80" width="172" height="380" rx="6" fill="rgba(235,108,54,0.07)"/>
<rect x="580" y="80" width="172" height="50" fill="rgba(235,108,54,0.14)" clip-path="url(#clip-aggregate)"/>
<rect x="580" y="80" width="172" height="380" rx="6" fill="none" stroke="#eb6c36" stroke-width="1.6"/>
<text x="666" y="106" class="tier-title">Aggregated</text>
<text x="666" y="144" class="bucket focal-text">analytics-marts</text>
<text x="596" y="180" class="field-label">Tool</text>
<foreignObject x="596" y="186" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Trino INSERT</div></foreignObject>
<text x="596" y="220" class="field-label">Format</text>
<foreignObject x="596" y="226" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Iceberg · metrics</div></foreignObject>
<text x="596" y="260" class="field-label">Writer</text>
<foreignObject x="596" y="266" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Data Scientist</div></foreignObject>
<text x="596" y="360" class="example-label">E-COMMERCE EXAMPLE</text>
<text x="596" y="374" class="example-value focal-text">daily conversion rate</text>
<text x="596" y="388" class="example-value focal-text">revenue by channel</text>
<!-- Archive -->
<rect x="768" y="80" width="172" height="380" rx="6" fill="#ececec"/>
<rect x="768" y="80" width="172" height="50" fill="rgba(79,93,117,0.18)" clip-path="url(#clip-archive)"/>
<rect x="768" y="80" width="172" height="380" rx="6" fill="none" stroke="#4f5d75" stroke-dasharray="5,3"/>
<text x="854" y="106" class="tier-title">Archive</text>
<text x="854" y="144" class="bucket">archive-commerce</text>
<text x="784" y="180" class="field-label">Tool</text>
<foreignObject x="784" y="186" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Object storage lifecycle</div></foreignObject>
<text x="784" y="220" class="field-label">Format</text>
<foreignObject x="784" y="226" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Cold tier · immutable</div></foreignObject>
<text x="784" y="260" class="field-label">Writer</text>
<foreignObject x="784" y="266" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Data Engineer</div></foreignObject>
<text x="784" y="360" class="example-label">E-COMMERCE EXAMPLE</text>
<text x="784" y="374" class="example-value">retained clickstream</text>
<text x="784" y="388" class="example-value">closed order snapshots</text>
<!-- Write-method paths -->
<rect x="16" y="476" width="460" height="56" rx="6" fill="#fff" stroke="rgba(45,49,66,0.20)"/>
<rect x="24" y="482" width="64" height="12" rx="2" fill="none" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="56" y="491" class="path-tag" fill="#2d3142" font-size="7" text-anchor="middle">SQL PATH</text>
<text x="96" y="506" class="path-title">Trino INSERT INTO … SELECT</text>
<text x="96" y="522" class="path-sub">filter · join · aggregate — repeatable transforms</text>
<rect x="492" y="476" width="460" height="56" rx="6" fill="#fff" stroke="rgba(45,49,66,0.20)"/>
<rect x="500" y="482" width="96" height="12" rx="2" fill="none" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="548" y="491" class="path-tag" fill="#2d3142" font-size="7" text-anchor="middle">NOTEBOOK PATH</text>
<text x="604" y="506" class="path-title">Python notebooks on object storage</text>
<text x="604" y="522" class="path-sub">explore · validate · model — interactive analysis</text>
</svg>
</div>
<section class="cards" aria-label="Diagram summary">
<article class="card">
<p class="card-eyebrow">THE HEADLINE</p>
<div class="card-header"><span class="card-dot coral"></span><h2>Aggregated is the query surface</h2></div>
<p>Dashboards read stable metrics instead of recomputing joins across raw commerce records.</p>
</article>
<article class="card">
<div class="card-header"><span class="card-dot ink"></span><h2>Quality rises by tier</h2></div>
<ul><li>Identifiers are masked first</li><li>Events and orders are validated</li><li>Metrics are versioned for reuse</li></ul>
</article>
<article class="card">
<div class="card-header"><span class="card-dot muted"></span><h2>Two write modes</h2></div>
<p>Trino handles repeatable promotion jobs; notebooks support exploration before logic is operationalized.</p>
</article>
</section>
<footer class="footer">
<span>e-commerce analytics · medallion</span>
<span>example · schematic skill</span>
</footer>
</main>
</body>
</html>
@@ -0,0 +1,159 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>E-commerce analytics · Medallion</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&amp;family=Geist:wght@400;500;600&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--paper: #f5f5f5; --paper-2: #ececec; --ink: #2d3142; --muted: #4f5d75;
--soft: #7a8399; --rule: rgba(45,49,66,0.12); --accent: #eb6c36;
--accent-tint: rgba(235,108,54,0.08); --link: #2e5aa8;
--sans: 'Geist', system-ui, sans-serif; --serif: 'Instrument Serif', serif;
--mono: 'Geist Mono', ui-monospace, monospace;
}
body { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 3rem 2rem; background: var(--paper); color: var(--ink); font-family: var(--sans); }
.frame { width: 100%; max-width: 1200px; }
.eyebrow { margin-bottom: 0.5rem; color: var(--muted); font: 500 0.66rem var(--mono); letter-spacing: 0.18em; text-transform: uppercase; }
h1 { margin-bottom: 1.5rem; color: var(--ink); font: 400 clamp(1.5rem, 2.4vw + 0.75rem, 2rem)/1.15 var(--serif); letter-spacing: -0.02em; }
svg { display: block; width: 100%; min-width: 1040px; }
.tier-title { fill: var(--ink); font: 600 13px var(--sans); text-anchor: middle; }
.bucket, .field-value, .example-value { fill: var(--muted); font: 400 9px var(--mono); }
.bucket { text-anchor: middle; }
.field-value { color: var(--muted); }
.focal-text { fill: var(--accent); }
.field-label { fill: var(--ink); font: 600 11px var(--sans); }
.example-label, .arc-label, .path-tag { font-family: var(--mono); font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; }
.example-label { fill: var(--soft); font-size: 8px; }
.arc-label { font-size: 10px; text-anchor: middle; }
.path-title { fill: var(--ink); font: 600 11px var(--sans); }
.path-sub { fill: var(--muted); font: 400 9px var(--mono); }
</style>
</head>
<body>
<main class="frame">
<p class="eyebrow">Medallion · Diagram Design</p>
<h1>Five-tier e-commerce analytics storage</h1>
<svg viewBox="0 0 1040 548" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="medallion-title medallion-desc">
<title id="medallion-title">Five-tier e-commerce analytics medallion</title>
<desc id="medallion-desc">Clickstream events and order records move from raw storage through anonymized, staging, and aggregated tiers before lifecycle archiving.</desc>
<defs>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#4f5d75"/></marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#eb6c36"/></marker>
<marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#2e5aa8"/></marker>
<clipPath id="clip-raw"><rect x="16" y="80" width="172" height="380" rx="6"/></clipPath>
<clipPath id="clip-anon"><rect x="204" y="80" width="172" height="380" rx="6"/></clipPath>
<clipPath id="clip-stage"><rect x="392" y="80" width="172" height="380" rx="6"/></clipPath>
<clipPath id="clip-aggregate"><rect x="580" y="80" width="172" height="380" rx="6"/></clipPath>
<clipPath id="clip-archive"><rect x="768" y="80" width="172" height="380" rx="6"/></clipPath>
</defs>
<rect width="1040" height="548" fill="#f5f5f5"/>
<!-- Promotion arcs are the medallion type's required connector grammar. -->
<path d="M102 80 C102 0 290 0 290 80" fill="none" stroke="#4f5d75" stroke-width="1.4" marker-end="url(#arrow)"/>
<path d="M290 80 C290 0 478 0 478 80" fill="none" stroke="#4f5d75" stroke-width="1.4" marker-end="url(#arrow)"/>
<path d="M478 80 C478 0 666 0 666 80" fill="none" stroke="#eb6c36" stroke-width="1.6" marker-end="url(#arrow-accent)"/>
<path d="M666 80 C666 0 854 0 854 80" fill="none" stroke="#4f5d75" stroke-width="1.4" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
<text x="196" y="50" class="arc-label" fill="#4f5d75">MASK IDS</text>
<text x="384" y="50" class="arc-label" fill="#4f5d75">CLEAN + JOIN</text>
<text x="572" y="50" class="arc-label" fill="#eb6c36">AGGREGATE</text>
<text x="760" y="50" class="arc-label" fill="#4f5d75">LIFECYCLE</text>
<!-- Raw -->
<rect x="16" y="80" width="172" height="380" rx="6" fill="#fff"/>
<rect x="16" y="80" width="172" height="50" fill="rgba(79,93,117,0.10)" clip-path="url(#clip-raw)"/>
<rect x="16" y="80" width="172" height="380" rx="6" fill="none" stroke="#4f5d75"/>
<text x="102" y="106" class="tier-title">Raw</text>
<text x="102" y="144" class="bucket">raw-commerce</text>
<text x="32" y="180" class="field-label">Tool</text>
<foreignObject x="32" y="186" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">NiFi ingest</div></foreignObject>
<text x="32" y="220" class="field-label">Format</text>
<foreignObject x="32" y="226" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">JSON · Parquet</div></foreignObject>
<text x="32" y="260" class="field-label">Writer</text>
<foreignObject x="32" y="266" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Data Engineer</div></foreignObject>
<text x="32" y="360" class="example-label">E-COMMERCE EXAMPLE</text>
<text x="32" y="374" class="example-value">clickstream events</text>
<text x="32" y="388" class="example-value">order records</text>
<!-- Anonymized -->
<rect x="204" y="80" width="172" height="380" rx="6" fill="#fff"/>
<rect x="204" y="80" width="172" height="50" fill="rgba(45,49,66,0.06)" clip-path="url(#clip-anon)"/>
<rect x="204" y="80" width="172" height="380" rx="6" fill="none" stroke="#2d3142"/>
<text x="290" y="106" class="tier-title">Anonymized</text>
<text x="290" y="144" class="bucket">anon-commerce</text>
<text x="220" y="180" class="field-label">Tool</text>
<foreignObject x="220" y="186" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Trino SQL</div></foreignObject>
<text x="220" y="220" class="field-label">Format</text>
<foreignObject x="220" y="226" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Iceberg · partitioned</div></foreignObject>
<text x="220" y="260" class="field-label">Writer</text>
<foreignObject x="220" y="266" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Data Engineer</div></foreignObject>
<text x="220" y="360" class="example-label">E-COMMERCE EXAMPLE</text>
<text x="220" y="374" class="example-value">session pseudonyms</text>
<text x="220" y="388" class="example-value">masked customer IDs</text>
<!-- Staging -->
<rect x="392" y="80" width="172" height="380" rx="6" fill="#fff"/>
<rect x="392" y="80" width="172" height="50" fill="rgba(45,49,66,0.06)" clip-path="url(#clip-stage)"/>
<rect x="392" y="80" width="172" height="380" rx="6" fill="none" stroke="#2d3142"/>
<text x="478" y="106" class="tier-title">Staging</text>
<text x="478" y="144" class="bucket">staging-commerce</text>
<text x="408" y="180" class="field-label">Tool</text>
<foreignObject x="408" y="186" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Trino · notebooks</div></foreignObject>
<text x="408" y="220" class="field-label">Format</text>
<foreignObject x="408" y="226" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Iceberg · cleaned</div></foreignObject>
<text x="408" y="260" class="field-label">Writer</text>
<foreignObject x="408" y="266" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Data Scientist</div></foreignObject>
<text x="408" y="360" class="example-label">E-COMMERCE EXAMPLE</text>
<text x="408" y="374" class="example-value">joined order facts</text>
<text x="408" y="388" class="example-value">validated event rows</text>
<!-- Aggregated focal tier -->
<rect x="580" y="80" width="172" height="380" rx="6" fill="rgba(235,108,54,0.07)"/>
<rect x="580" y="80" width="172" height="50" fill="rgba(235,108,54,0.14)" clip-path="url(#clip-aggregate)"/>
<rect x="580" y="80" width="172" height="380" rx="6" fill="none" stroke="#eb6c36" stroke-width="1.6"/>
<text x="666" y="106" class="tier-title">Aggregated</text>
<text x="666" y="144" class="bucket focal-text">analytics-marts</text>
<text x="596" y="180" class="field-label">Tool</text>
<foreignObject x="596" y="186" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Trino INSERT</div></foreignObject>
<text x="596" y="220" class="field-label">Format</text>
<foreignObject x="596" y="226" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Iceberg · metrics</div></foreignObject>
<text x="596" y="260" class="field-label">Writer</text>
<foreignObject x="596" y="266" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Data Scientist</div></foreignObject>
<text x="596" y="360" class="example-label">E-COMMERCE EXAMPLE</text>
<text x="596" y="374" class="example-value focal-text">daily conversion rate</text>
<text x="596" y="388" class="example-value focal-text">revenue by channel</text>
<!-- Archive -->
<rect x="768" y="80" width="172" height="380" rx="6" fill="#ececec"/>
<rect x="768" y="80" width="172" height="50" fill="rgba(79,93,117,0.18)" clip-path="url(#clip-archive)"/>
<rect x="768" y="80" width="172" height="380" rx="6" fill="none" stroke="#4f5d75" stroke-dasharray="5,3"/>
<text x="854" y="106" class="tier-title">Archive</text>
<text x="854" y="144" class="bucket">archive-commerce</text>
<text x="784" y="180" class="field-label">Tool</text>
<foreignObject x="784" y="186" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Object storage lifecycle</div></foreignObject>
<text x="784" y="220" class="field-label">Format</text>
<foreignObject x="784" y="226" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Cold tier · immutable</div></foreignObject>
<text x="784" y="260" class="field-label">Writer</text>
<foreignObject x="784" y="266" width="140" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="field-value" style="line-height:1.25">Data Engineer</div></foreignObject>
<text x="784" y="360" class="example-label">E-COMMERCE EXAMPLE</text>
<text x="784" y="374" class="example-value">retained clickstream</text>
<text x="784" y="388" class="example-value">closed order snapshots</text>
<!-- Write-method paths -->
<rect x="16" y="476" width="460" height="56" rx="6" fill="#fff" stroke="rgba(45,49,66,0.20)"/>
<rect x="24" y="482" width="64" height="12" rx="2" fill="none" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="56" y="491" class="path-tag" fill="#2d3142" font-size="7" text-anchor="middle">SQL PATH</text>
<text x="96" y="506" class="path-title">Trino INSERT INTO … SELECT</text>
<text x="96" y="522" class="path-sub">filter · join · aggregate — repeatable transforms</text>
<rect x="492" y="476" width="460" height="56" rx="6" fill="#fff" stroke="rgba(45,49,66,0.20)"/>
<rect x="500" y="482" width="96" height="12" rx="2" fill="none" stroke="rgba(45,49,66,0.30)" stroke-width="0.8"/>
<text x="548" y="491" class="path-tag" fill="#2d3142" font-size="7" text-anchor="middle">NOTEBOOK PATH</text>
<text x="604" y="506" class="path-title">Python notebooks on object storage</text>
<text x="604" y="522" class="path-sub">explore · validate · model — interactive analysis</text>
</svg>
</main>
</body>
</html>
@@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Order fulfillment · Process (dark)</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&amp;family=Geist:wght@400;500;600&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet">
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{--paper:#f5f5f5;--ink:#2d3142;--muted:#4f5d75;--soft:#7a8399;--accent:#eb6c36;--accent-tint:rgba(235,108,54,.08);--rule:rgba(45,49,66,.12);--label-rule:rgba(45,49,66,.20);--lane:rgba(45,49,66,.018);--node:#fff;--node-stroke:rgba(45,49,66,.25);--chip:rgba(45,49,66,.12);--dot:rgba(245,245,245,.10);--db:#5e7a9b;--ls:#7c8f6f;--fl:#9c6b50;--tb:#b8915a;--wb:#6e6479;--sans:'Geist',system-ui,sans-serif;--serif:'Instrument Serif',serif;--mono:'Geist Mono',ui-monospace,monospace}
body{font-family:var(--sans);background:var(--paper);color:var(--ink);min-height:100vh;display:flex;align-items:center;justify-content:center;padding:3rem 2rem}
.frame{max-width:1120px;width:100%}.eyebrow{font:500 .66rem var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--muted);margin-bottom:.5rem}h1{font:400 clamp(1.5rem,2.4vw + .75rem,2rem)/1.15 var(--serif);letter-spacing:-.02em;margin-bottom:1.5rem}svg{width:100%;min-width:1000px;display:block}
svg text{font-family:var(--sans)}.mono{font-family:var(--mono)}.lane{fill:var(--lane)}.rule{stroke:var(--rule)}.label-rule{stroke:var(--label-rule)}.actor{fill:var(--muted);font:500 8px var(--mono);letter-spacing:.08em}.step-label{fill:var(--muted);font:500 6px var(--mono);letter-spacing:.12em}.step-chip{fill:var(--chip)}.step-num{fill:var(--ink);font:600 7px var(--mono)}.connector{fill:none;stroke:var(--muted);stroke-width:1}.connector.focal{stroke:var(--accent);stroke-width:1.2}.node-mask{fill:var(--paper)}.node{fill:var(--node);stroke:var(--node-stroke)}.node.focal{fill:var(--accent-tint);stroke:var(--accent);stroke-width:1.2}.role{fill:rgba(245,245,245,.10)}.role.focal{fill:rgba(240,138,89,.26)}.role-text{fill:var(--ink);font:600 6px var(--mono)}.role-text.focal{fill:var(--accent)}.node-title{fill:var(--ink);font:600 9px var(--sans)}.node-sub{fill:var(--muted);font:6.5px var(--mono)}.node-tool{fill:var(--soft);font:6.5px var(--mono)}.data-text{fill:white;font:700 5px var(--mono)}.legend-key{fill:var(--muted);font:500 7px var(--mono);letter-spacing:.12em}.legend-text{fill:var(--muted);font:8px var(--sans)}
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Process · Diagram Design</p>
<h1>Order fulfillment across four teams</h1>
<svg viewBox="0 0 1064 436" xmlns="http://www.w3.org/2000/svg" aria-label="Order fulfillment process from customer order to support closure">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse"><circle cx="11" cy="11" r="0.8" fill="var(--dot)"/></pattern>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="var(--muted)"/></marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="var(--accent)"/></marker>
<marker id="arrow-sm" markerWidth="6" markerHeight="5" refX="5" refY="2.5" orient="auto"><polygon points="0 0,6 2.5,0 5" fill="var(--muted)"/></marker>
</defs>
<rect width="1064" height="436" fill="var(--paper)"/><rect width="1064" height="436" fill="url(#dots)" opacity=".55"/>
<rect class="lane" x="140" y="36" width="924" height="80"/><rect class="lane" x="140" y="196" width="924" height="80"/>
<g class="rule" stroke-width=".8"><line x1="0" y1="36" x2="1064" y2="36"/><line x1="0" y1="116" x2="1064" y2="116"/><line x1="0" y1="196" x2="1064" y2="196"/><line x1="0" y1="276" x2="1064" y2="276"/><line x1="0" y1="356" x2="1064" y2="356"/></g>
<line class="label-rule" x1="140" y1="36" x2="140" y2="356"/>
<g class="actor" text-anchor="middle"><text x="70" y="80">CUSTOMER</text><text x="70" y="160">SUPPORT</text><text x="70" y="240">WAREHOUSE</text><text x="70" y="320">FINANCE</text></g>
<g text-anchor="middle">
<rect class="step-chip" x="190" y="8" width="16" height="16" rx="8"/><text class="step-num" x="198" y="19">1</text><text class="step-label" x="198" y="32">ORDER</text>
<rect class="step-chip" x="302" y="8" width="16" height="16" rx="8"/><text class="step-num" x="310" y="19">2</text><text class="step-label" x="310" y="32">VERIFY</text>
<rect x="414" y="8" width="16" height="16" rx="8" fill="rgba(240,138,89,.22)"/><text x="422" y="19" fill="var(--accent)" font-size="7" font-weight="600" class="mono">3</text><text x="422" y="32" fill="var(--accent)" font-size="6" font-weight="500" letter-spacing=".12em" class="mono">ALLOCATE</text>
<rect class="step-chip" x="526" y="8" width="16" height="16" rx="8"/><text class="step-num" x="534" y="19">4</text><text class="step-label" x="534" y="32">PICK</text>
<rect class="step-chip" x="638" y="8" width="16" height="16" rx="8"/><text class="step-num" x="646" y="19">5</text><text class="step-label" x="646" y="32">PACK</text>
<rect class="step-chip" x="750" y="8" width="16" height="16" rx="8"/><text class="step-num" x="758" y="19">6</text><text class="step-label" x="758" y="32">PAY</text>
<rect class="step-chip" x="862" y="8" width="16" height="16" rx="8"/><text class="step-num" x="870" y="19">7</text><text class="step-label" x="870" y="32">RECEIVE</text>
<rect class="step-chip" x="974" y="8" width="16" height="16" rx="8"/><text class="step-num" x="982" y="19">8</text><text class="step-label" x="982" y="32">CLOSE</text>
</g>
<!-- Connectors: right exit, r=8 orthogonal bend, top/bottom entry. -->
<path class="connector" d="M248 76 H302 Q310 76 310 84 V124" marker-end="url(#arrow)"/><path class="connector focal" d="M360 156 H414 Q422 156 422 164 V204" marker-end="url(#arrow-accent)"/><line class="connector focal" x1="472" y1="236" x2="484" y2="236" marker-end="url(#arrow-accent)"/><line class="connector" x1="584" y1="236" x2="596" y2="236" marker-end="url(#arrow)"/><path class="connector" d="M696 236 H750 Q758 236 758 244 V284" marker-end="url(#arrow)"/><path class="connector" d="M808 316 H862 Q870 316 870 308 V108" marker-end="url(#arrow)"/><path class="connector" d="M920 76 H974 Q982 76 982 84 V124" marker-end="url(#arrow)"/>
<!-- Nodes -->
<g text-anchor="middle">
<g><rect class="node-mask" x="148" y="44" width="100" height="64" rx="6"/><rect class="node" x="148" y="44" width="100" height="64" rx="6"/><rect class="role" x="152" y="48" width="20" height="10" rx="2"/><text class="role-text" x="162" y="56">CUS</text><text class="node-title" x="198" y="70">Place order</text><text class="node-sub" x="198" y="84">cart → order</text><text class="node-tool" x="198" y="96">storefront</text><rect x="228" y="98" width="16" height="8" rx="2" fill="var(--db)"/><text class="data-text" x="236" y="104">DB</text></g>
<g><rect class="node-mask" x="260" y="124" width="100" height="64" rx="6"/><rect class="node" x="260" y="124" width="100" height="64" rx="6"/><rect class="role" x="264" y="128" width="20" height="10" rx="2"/><text class="role-text" x="274" y="136">SUP</text><text class="node-title" x="310" y="150">Verify order</text><text class="node-sub" x="310" y="164">order → cleared</text><text class="node-tool" x="310" y="176">service desk</text><rect x="264" y="178" width="16" height="8" rx="2" fill="var(--db)"/><text class="data-text" x="272" y="184">DB</text><rect x="340" y="178" width="16" height="8" rx="2" fill="var(--db)"/><text class="data-text" x="348" y="184">DB</text></g>
<g><rect class="node-mask" x="372" y="204" width="100" height="64" rx="6"/><rect class="node focal" x="372" y="204" width="100" height="64" rx="6"/><rect class="role focal" x="376" y="208" width="20" height="10" rx="2"/><text class="role-text focal" x="386" y="216">WHS</text><text class="node-title" x="422" y="230">Allocate stock</text><text class="node-sub" x="422" y="244">order → pick list</text><text class="node-tool" x="422" y="256">inventory system</text><rect x="376" y="258" width="16" height="8" rx="2" fill="var(--db)"/><text class="data-text" x="384" y="264">DB</text><rect x="452" y="258" width="16" height="8" rx="2" fill="var(--ls)"/><text class="data-text" x="460" y="264">LS</text></g>
<g><rect class="node-mask" x="484" y="204" width="100" height="64" rx="6"/><rect class="node" x="484" y="204" width="100" height="64" rx="6"/><rect class="role" x="488" y="208" width="20" height="10" rx="2"/><text class="role-text" x="498" y="216">WHS</text><text class="node-title" x="534" y="230">Pick items</text><text class="node-sub" x="534" y="244">list → picked</text><text class="node-tool" x="534" y="256">handheld scanner</text><rect x="488" y="258" width="16" height="8" rx="2" fill="var(--ls)"/><text class="data-text" x="496" y="264">LS</text><rect x="564" y="258" width="16" height="8" rx="2" fill="var(--ls)"/><text class="data-text" x="572" y="264">LS</text></g>
<g><rect class="node-mask" x="596" y="204" width="100" height="64" rx="6"/><rect class="node" x="596" y="204" width="100" height="64" rx="6"/><rect class="role" x="600" y="208" width="20" height="10" rx="2"/><text class="role-text" x="610" y="216">WHS</text><text class="node-title" x="646" y="230">Pack order</text><text class="node-sub" x="646" y="244">picked → shipment</text><text class="node-tool" x="646" y="256">packing station</text><rect x="600" y="258" width="16" height="8" rx="2" fill="var(--ls)"/><text class="data-text" x="608" y="264">LS</text><rect x="676" y="258" width="16" height="8" rx="2" fill="var(--fl)"/><text class="data-text" x="684" y="264">FL</text></g>
<g><rect class="node-mask" x="708" y="284" width="100" height="64" rx="6"/><rect class="node" x="708" y="284" width="100" height="64" rx="6"/><rect class="role" x="712" y="288" width="20" height="10" rx="2"/><text class="role-text" x="722" y="296">FIN</text><text class="node-title" x="758" y="310">Capture payment</text><text class="node-sub" x="758" y="324">shipment → receipt</text><text class="node-tool" x="758" y="336">payment gateway</text><rect x="712" y="338" width="16" height="8" rx="2" fill="var(--fl)"/><text class="data-text" x="720" y="344">FL</text><rect x="788" y="338" width="16" height="8" rx="2" fill="var(--tb)"/><text class="data-text" x="796" y="344">TB</text></g>
<g><rect class="node-mask" x="820" y="44" width="100" height="64" rx="6"/><rect class="node" x="820" y="44" width="100" height="64" rx="6"/><rect class="role" x="824" y="48" width="20" height="10" rx="2"/><text class="role-text" x="834" y="56">CUS</text><text class="node-title" x="870" y="70">Receive shipment</text><text class="node-sub" x="870" y="84">receipt → confirmed</text><text class="node-tool" x="870" y="96">delivery portal</text><rect x="824" y="98" width="16" height="8" rx="2" fill="var(--tb)"/><text class="data-text" x="832" y="104">TB</text><rect x="900" y="98" width="16" height="8" rx="2" fill="var(--wb)"/><text class="data-text" x="908" y="104">WB</text></g>
<g><rect class="node-mask" x="932" y="124" width="100" height="64" rx="6"/><rect class="node" x="932" y="124" width="100" height="64" rx="6"/><rect class="role" x="936" y="128" width="20" height="10" rx="2"/><text class="role-text" x="946" y="136">SUP</text><text class="node-title" x="982" y="150">Close order</text><text class="node-sub" x="982" y="164">confirmed → closed</text><text class="node-tool" x="982" y="176">service desk</text><rect x="936" y="178" width="16" height="8" rx="2" fill="var(--wb)"/><text class="data-text" x="944" y="184">WB</text></g>
</g>
<!-- Three-row type key -->
<g><text class="legend-key" x="144" y="372">STEPS</text><g text-anchor="middle"><rect class="step-chip" x="208" y="364" width="16" height="16" rx="8"/><text class="step-num" x="216" y="375">1</text><text class="legend-text" x="248" y="375">ORDER</text><rect class="step-chip" x="300" y="364" width="16" height="16" rx="8"/><text class="step-num" x="308" y="375">2</text><text class="legend-text" x="340" y="375">VERIFY</text><rect x="392" y="364" width="16" height="16" rx="8" fill="rgba(240,138,89,.22)"/><text x="400" y="375" fill="var(--accent)" font-size="7" class="mono">3</text><text x="432" y="375" fill="var(--accent)" font-size="8">ALLOCATE</text><rect class="step-chip" x="496" y="364" width="16" height="16" rx="8"/><text class="step-num" x="504" y="375">4</text><text class="legend-text" x="532" y="375">PICK</text><rect class="step-chip" x="576" y="364" width="16" height="16" rx="8"/><text class="step-num" x="584" y="375">5</text><text class="legend-text" x="612" y="375">PACK</text><rect class="step-chip" x="656" y="364" width="16" height="16" rx="8"/><text class="step-num" x="664" y="375">6</text><text class="legend-text" x="692" y="375">PAY</text><rect class="step-chip" x="736" y="364" width="16" height="16" rx="8"/><text class="step-num" x="744" y="375">7</text><text class="legend-text" x="780" y="375">RECEIVE</text><rect class="step-chip" x="832" y="364" width="16" height="16" rx="8"/><text class="step-num" x="840" y="375">8</text><text class="legend-text" x="872" y="375">CLOSE</text></g>
<text class="legend-key" x="144" y="393">DATA TYPE</text><rect x="220" y="386" width="16" height="8" rx="2" fill="var(--db)"/><text class="legend-text" x="244" y="393">DB · records</text><rect x="328" y="386" width="16" height="8" rx="2" fill="var(--ls)"/><text class="legend-text" x="352" y="393">LS · task</text><rect x="428" y="386" width="16" height="8" rx="2" fill="var(--fl)"/><text class="legend-text" x="452" y="393">FL · document</text><rect x="552" y="386" width="16" height="8" rx="2" fill="var(--tb)"/><text class="legend-text" x="576" y="393">TB · transaction</text><rect x="688" y="386" width="16" height="8" rx="2" fill="var(--wb)"/><text class="legend-text" x="712" y="393">WB · web status</text><text class="legend-key" x="856" y="393">LEFT IN · RIGHT OUT</text>
<text class="legend-key" x="144" y="414">FLOW</text><line x1="200" y1="410" x2="228" y2="410" stroke="var(--accent)" stroke-width="1.2" marker-end="url(#arrow-accent)"/><text class="legend-text" x="240" y="414">Critical handoff</text><line x1="360" y1="410" x2="388" y2="410" stroke="var(--muted)" marker-end="url(#arrow)"/><text class="legend-text" x="400" y="414">Sequential handoff</text></g>
</svg>
</div>
</body>
</html>
@@ -0,0 +1,72 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Order fulfillment · Process overview</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&amp;family=Geist:wght@400;500;600&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet">
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{--paper:#f5f5f5;--ink:#2d3142;--muted:#4f5d75;--soft:#7a8399;--accent:#eb6c36;--accent-tint:rgba(235,108,54,.08);--rule:rgba(45,49,66,.12);--label-rule:rgba(45,49,66,.20);--lane:rgba(45,49,66,.018);--node:#fff;--node-stroke:rgba(45,49,66,.25);--chip:rgba(45,49,66,.12);--dot:rgba(45,49,66,.10);--db:#5e7a9b;--ls:#7c8f6f;--fl:#9c6b50;--tb:#b8915a;--wb:#6e6479;--sans:'Geist',system-ui,sans-serif;--serif:'Instrument Serif',serif;--mono:'Geist Mono',ui-monospace,monospace}
body{font-family:var(--sans);background:var(--paper);color:var(--ink);min-height:100vh;padding:3rem 2rem}
.frame{max-width:1120px;width:100%;margin:0 auto}.eyebrow{font:500 .66rem var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--muted);margin-bottom:.5rem}h1{font:400 clamp(1.5rem,2.4vw + .75rem,2rem)/1.15 var(--serif);letter-spacing:-.02em;margin-bottom:1.5rem}svg{width:100%;min-width:1000px;display:block}
svg text{font-family:var(--sans)}.mono{font-family:var(--mono)}.lane{fill:var(--lane)}.rule{stroke:var(--rule)}.label-rule{stroke:var(--label-rule)}.actor{fill:var(--muted);font:500 8px var(--mono);letter-spacing:.08em}.step-label{fill:var(--muted);font:500 6px var(--mono);letter-spacing:.12em}.step-chip{fill:var(--chip)}.step-num{fill:var(--ink);font:600 7px var(--mono)}.connector{fill:none;stroke:var(--muted);stroke-width:1}.connector.focal{stroke:var(--accent);stroke-width:1.2}.node-mask{fill:var(--paper)}.node{fill:var(--node);stroke:var(--node-stroke)}.node.focal{fill:var(--accent-tint);stroke:var(--accent);stroke-width:1.2}.role{fill:rgba(45,49,66,.10)}.role.focal{fill:rgba(235,108,54,.25)}.role-text{fill:var(--ink);font:600 6px var(--mono)}.role-text.focal{fill:var(--accent)}.node-title{fill:var(--ink);font:600 9px var(--sans)}.node-sub{fill:var(--muted);font:6.5px var(--mono)}.node-tool{fill:var(--soft);font:6.5px var(--mono)}.data-text{fill:white;font:700 5px var(--mono)}.legend-key{fill:var(--muted);font:500 7px var(--mono);letter-spacing:.12em}.legend-text{fill:var(--muted);font:8px var(--sans)}
.subtitle{max-width:62ch;color:var(--muted);font-size:1rem;line-height:1.55;margin:-.75rem 0 2rem}.diagram-container{background:#ececec;border:1px solid var(--rule);border-radius:8px;padding:1.5rem;overflow-x:auto}.cards{display:grid;grid-template-columns:1.1fr 1fr .9fr;gap:1rem;margin-top:1.5rem}.card{background:#fff;border:1px solid var(--rule);border-radius:6px;padding:1.25rem}.card .card-label{font:500 .5rem var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--muted);margin-bottom:.5rem}.card-header{display:flex;align-items:center;gap:.6rem;margin-bottom:.875rem;padding-bottom:.875rem;border-bottom:1px solid rgba(45,49,66,.08)}.card-dot{width:7px;height:7px;border-radius:50%;background:var(--muted)}.card-dot.coral{background:var(--accent)}.card-dot.ink{background:var(--ink)}.card h3{font-size:.875rem;font-weight:600}.card p,.card li{color:var(--muted);font-size:.8125rem;line-height:1.55}.card ul{list-style:none}.card li{padding-left:.875rem;position:relative}.card li::before{content:'—';position:absolute;left:0;color:rgba(45,49,66,.25)}.footer{display:flex;justify-content:space-between;gap:.5rem;flex-wrap:wrap;margin-top:2rem;padding-top:1.5rem;border-top:1px solid var(--rule);color:var(--soft);font:.72rem var(--mono);letter-spacing:.06em}@media(max-width:820px){.cards{grid-template-columns:1fr}}
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Process · Diagram Design</p>
<h1>Order fulfillment across four teams</h1>
<p class="subtitle">One order moves through customer, support, warehouse, and finance ownership, while the payload chips show what each handoff carries.</p>
<div class="diagram-container">
<svg viewBox="0 0 1064 436" xmlns="http://www.w3.org/2000/svg" aria-label="Order fulfillment process from customer order to support closure">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse"><circle cx="11" cy="11" r="0.8" fill="var(--dot)"/></pattern>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="var(--muted)"/></marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="var(--accent)"/></marker>
<marker id="arrow-sm" markerWidth="6" markerHeight="5" refX="5" refY="2.5" orient="auto"><polygon points="0 0,6 2.5,0 5" fill="var(--muted)"/></marker>
</defs>
<rect width="1064" height="436" fill="var(--paper)"/><rect width="1064" height="436" fill="url(#dots)" opacity=".55"/>
<rect class="lane" x="140" y="36" width="924" height="80"/><rect class="lane" x="140" y="196" width="924" height="80"/>
<g class="rule" stroke-width=".8"><line x1="0" y1="36" x2="1064" y2="36"/><line x1="0" y1="116" x2="1064" y2="116"/><line x1="0" y1="196" x2="1064" y2="196"/><line x1="0" y1="276" x2="1064" y2="276"/><line x1="0" y1="356" x2="1064" y2="356"/></g>
<line class="label-rule" x1="140" y1="36" x2="140" y2="356"/>
<g class="actor" text-anchor="middle"><text x="70" y="80">CUSTOMER</text><text x="70" y="160">SUPPORT</text><text x="70" y="240">WAREHOUSE</text><text x="70" y="320">FINANCE</text></g>
<g text-anchor="middle">
<rect class="step-chip" x="190" y="8" width="16" height="16" rx="8"/><text class="step-num" x="198" y="19">1</text><text class="step-label" x="198" y="32">ORDER</text>
<rect class="step-chip" x="302" y="8" width="16" height="16" rx="8"/><text class="step-num" x="310" y="19">2</text><text class="step-label" x="310" y="32">VERIFY</text>
<rect x="414" y="8" width="16" height="16" rx="8" fill="rgba(235,108,54,.20)"/><text x="422" y="19" fill="var(--accent)" font-size="7" font-weight="600" class="mono">3</text><text x="422" y="32" fill="var(--accent)" font-size="6" font-weight="500" letter-spacing=".12em" class="mono">ALLOCATE</text>
<rect class="step-chip" x="526" y="8" width="16" height="16" rx="8"/><text class="step-num" x="534" y="19">4</text><text class="step-label" x="534" y="32">PICK</text>
<rect class="step-chip" x="638" y="8" width="16" height="16" rx="8"/><text class="step-num" x="646" y="19">5</text><text class="step-label" x="646" y="32">PACK</text>
<rect class="step-chip" x="750" y="8" width="16" height="16" rx="8"/><text class="step-num" x="758" y="19">6</text><text class="step-label" x="758" y="32">PAY</text>
<rect class="step-chip" x="862" y="8" width="16" height="16" rx="8"/><text class="step-num" x="870" y="19">7</text><text class="step-label" x="870" y="32">RECEIVE</text>
<rect class="step-chip" x="974" y="8" width="16" height="16" rx="8"/><text class="step-num" x="982" y="19">8</text><text class="step-label" x="982" y="32">CLOSE</text>
</g>
<!-- Connectors: right exit, r=8 orthogonal bend, top/bottom entry. -->
<path class="connector" d="M248 76 H302 Q310 76 310 84 V124" marker-end="url(#arrow)"/><path class="connector focal" d="M360 156 H414 Q422 156 422 164 V204" marker-end="url(#arrow-accent)"/><line class="connector focal" x1="472" y1="236" x2="484" y2="236" marker-end="url(#arrow-accent)"/><line class="connector" x1="584" y1="236" x2="596" y2="236" marker-end="url(#arrow)"/><path class="connector" d="M696 236 H750 Q758 236 758 244 V284" marker-end="url(#arrow)"/><path class="connector" d="M808 316 H862 Q870 316 870 308 V108" marker-end="url(#arrow)"/><path class="connector" d="M920 76 H974 Q982 76 982 84 V124" marker-end="url(#arrow)"/>
<!-- Nodes -->
<g text-anchor="middle">
<g><rect class="node-mask" x="148" y="44" width="100" height="64" rx="6"/><rect class="node" x="148" y="44" width="100" height="64" rx="6"/><rect class="role" x="152" y="48" width="20" height="10" rx="2"/><text class="role-text" x="162" y="56">CUS</text><text class="node-title" x="198" y="70">Place order</text><text class="node-sub" x="198" y="84">cart → order</text><text class="node-tool" x="198" y="96">storefront</text><rect x="228" y="98" width="16" height="8" rx="2" fill="var(--db)"/><text class="data-text" x="236" y="104">DB</text></g>
<g><rect class="node-mask" x="260" y="124" width="100" height="64" rx="6"/><rect class="node" x="260" y="124" width="100" height="64" rx="6"/><rect class="role" x="264" y="128" width="20" height="10" rx="2"/><text class="role-text" x="274" y="136">SUP</text><text class="node-title" x="310" y="150">Verify order</text><text class="node-sub" x="310" y="164">order → cleared</text><text class="node-tool" x="310" y="176">service desk</text><rect x="264" y="178" width="16" height="8" rx="2" fill="var(--db)"/><text class="data-text" x="272" y="184">DB</text><rect x="340" y="178" width="16" height="8" rx="2" fill="var(--db)"/><text class="data-text" x="348" y="184">DB</text></g>
<g><rect class="node-mask" x="372" y="204" width="100" height="64" rx="6"/><rect class="node focal" x="372" y="204" width="100" height="64" rx="6"/><rect class="role focal" x="376" y="208" width="20" height="10" rx="2"/><text class="role-text focal" x="386" y="216">WHS</text><text class="node-title" x="422" y="230">Allocate stock</text><text class="node-sub" x="422" y="244">order → pick list</text><text class="node-tool" x="422" y="256">inventory system</text><rect x="376" y="258" width="16" height="8" rx="2" fill="var(--db)"/><text class="data-text" x="384" y="264">DB</text><rect x="452" y="258" width="16" height="8" rx="2" fill="var(--ls)"/><text class="data-text" x="460" y="264">LS</text></g>
<g><rect class="node-mask" x="484" y="204" width="100" height="64" rx="6"/><rect class="node" x="484" y="204" width="100" height="64" rx="6"/><rect class="role" x="488" y="208" width="20" height="10" rx="2"/><text class="role-text" x="498" y="216">WHS</text><text class="node-title" x="534" y="230">Pick items</text><text class="node-sub" x="534" y="244">list → picked</text><text class="node-tool" x="534" y="256">handheld scanner</text><rect x="488" y="258" width="16" height="8" rx="2" fill="var(--ls)"/><text class="data-text" x="496" y="264">LS</text><rect x="564" y="258" width="16" height="8" rx="2" fill="var(--ls)"/><text class="data-text" x="572" y="264">LS</text></g>
<g><rect class="node-mask" x="596" y="204" width="100" height="64" rx="6"/><rect class="node" x="596" y="204" width="100" height="64" rx="6"/><rect class="role" x="600" y="208" width="20" height="10" rx="2"/><text class="role-text" x="610" y="216">WHS</text><text class="node-title" x="646" y="230">Pack order</text><text class="node-sub" x="646" y="244">picked → shipment</text><text class="node-tool" x="646" y="256">packing station</text><rect x="600" y="258" width="16" height="8" rx="2" fill="var(--ls)"/><text class="data-text" x="608" y="264">LS</text><rect x="676" y="258" width="16" height="8" rx="2" fill="var(--fl)"/><text class="data-text" x="684" y="264">FL</text></g>
<g><rect class="node-mask" x="708" y="284" width="100" height="64" rx="6"/><rect class="node" x="708" y="284" width="100" height="64" rx="6"/><rect class="role" x="712" y="288" width="20" height="10" rx="2"/><text class="role-text" x="722" y="296">FIN</text><text class="node-title" x="758" y="310">Capture payment</text><text class="node-sub" x="758" y="324">shipment → receipt</text><text class="node-tool" x="758" y="336">payment gateway</text><rect x="712" y="338" width="16" height="8" rx="2" fill="var(--fl)"/><text class="data-text" x="720" y="344">FL</text><rect x="788" y="338" width="16" height="8" rx="2" fill="var(--tb)"/><text class="data-text" x="796" y="344">TB</text></g>
<g><rect class="node-mask" x="820" y="44" width="100" height="64" rx="6"/><rect class="node" x="820" y="44" width="100" height="64" rx="6"/><rect class="role" x="824" y="48" width="20" height="10" rx="2"/><text class="role-text" x="834" y="56">CUS</text><text class="node-title" x="870" y="70">Receive shipment</text><text class="node-sub" x="870" y="84">receipt → confirmed</text><text class="node-tool" x="870" y="96">delivery portal</text><rect x="824" y="98" width="16" height="8" rx="2" fill="var(--tb)"/><text class="data-text" x="832" y="104">TB</text><rect x="900" y="98" width="16" height="8" rx="2" fill="var(--wb)"/><text class="data-text" x="908" y="104">WB</text></g>
<g><rect class="node-mask" x="932" y="124" width="100" height="64" rx="6"/><rect class="node" x="932" y="124" width="100" height="64" rx="6"/><rect class="role" x="936" y="128" width="20" height="10" rx="2"/><text class="role-text" x="946" y="136">SUP</text><text class="node-title" x="982" y="150">Close order</text><text class="node-sub" x="982" y="164">confirmed → closed</text><text class="node-tool" x="982" y="176">service desk</text><rect x="936" y="178" width="16" height="8" rx="2" fill="var(--wb)"/><text class="data-text" x="944" y="184">WB</text></g>
</g>
<!-- Three-row type key -->
<g><text class="legend-key" x="144" y="372">STEPS</text><g text-anchor="middle"><rect class="step-chip" x="208" y="364" width="16" height="16" rx="8"/><text class="step-num" x="216" y="375">1</text><text class="legend-text" x="248" y="375">ORDER</text><rect class="step-chip" x="300" y="364" width="16" height="16" rx="8"/><text class="step-num" x="308" y="375">2</text><text class="legend-text" x="340" y="375">VERIFY</text><rect x="392" y="364" width="16" height="16" rx="8" fill="rgba(235,108,54,.20)"/><text x="400" y="375" fill="var(--accent)" font-size="7" class="mono">3</text><text x="432" y="375" fill="var(--accent)" font-size="8">ALLOCATE</text><rect class="step-chip" x="496" y="364" width="16" height="16" rx="8"/><text class="step-num" x="504" y="375">4</text><text class="legend-text" x="532" y="375">PICK</text><rect class="step-chip" x="576" y="364" width="16" height="16" rx="8"/><text class="step-num" x="584" y="375">5</text><text class="legend-text" x="612" y="375">PACK</text><rect class="step-chip" x="656" y="364" width="16" height="16" rx="8"/><text class="step-num" x="664" y="375">6</text><text class="legend-text" x="692" y="375">PAY</text><rect class="step-chip" x="736" y="364" width="16" height="16" rx="8"/><text class="step-num" x="744" y="375">7</text><text class="legend-text" x="780" y="375">RECEIVE</text><rect class="step-chip" x="832" y="364" width="16" height="16" rx="8"/><text class="step-num" x="840" y="375">8</text><text class="legend-text" x="872" y="375">CLOSE</text></g>
<text class="legend-key" x="144" y="393">DATA TYPE</text><rect x="220" y="386" width="16" height="8" rx="2" fill="var(--db)"/><text class="legend-text" x="244" y="393">DB · records</text><rect x="328" y="386" width="16" height="8" rx="2" fill="var(--ls)"/><text class="legend-text" x="352" y="393">LS · task</text><rect x="428" y="386" width="16" height="8" rx="2" fill="var(--fl)"/><text class="legend-text" x="452" y="393">FL · document</text><rect x="552" y="386" width="16" height="8" rx="2" fill="var(--tb)"/><text class="legend-text" x="576" y="393">TB · transaction</text><rect x="688" y="386" width="16" height="8" rx="2" fill="var(--wb)"/><text class="legend-text" x="712" y="393">WB · web status</text><text class="legend-key" x="856" y="393">LEFT IN · RIGHT OUT</text>
<text class="legend-key" x="144" y="414">FLOW</text><line x1="200" y1="410" x2="228" y2="410" stroke="var(--accent)" stroke-width="1.2" marker-end="url(#arrow-accent)"/><text class="legend-text" x="240" y="414">Critical handoff</text><line x1="360" y1="410" x2="388" y2="410" stroke="var(--muted)" marker-end="url(#arrow)"/><text class="legend-text" x="400" y="414">Sequential handoff</text></g>
</svg>
</div>
<div class="cards">
<article class="card"><p class="card-label">THE PIVOT</p><div class="card-header"><span class="card-dot coral"></span><h3>Stock allocation commits the order</h3></div><p>The focal handoff turns a verified order record into an actionable warehouse pick list.</p></article>
<article class="card"><div class="card-header"><span class="card-dot ink"></span><h3>Ownership stays explicit</h3></div><ul><li>Support verifies and closes</li><li>Warehouse allocates, picks, and packs</li><li>Finance captures the final payment</li></ul></article>
<article class="card"><div class="card-header"><span class="card-dot"></span><h3>Payloads change with the work</h3></div><p>Records become tasks, then documents, transactions, and a customer-visible status.</p></article>
</div>
<footer class="footer"><span>order fulfillment · process</span><span>example · diagram design</span></footer>
</div>
</body>
</html>
@@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Order fulfillment · Process</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&amp;family=Geist:wght@400;500;600&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet">
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{--paper:#f5f5f5;--ink:#2d3142;--muted:#4f5d75;--soft:#7a8399;--accent:#eb6c36;--accent-tint:rgba(235,108,54,.08);--rule:rgba(45,49,66,.12);--label-rule:rgba(45,49,66,.20);--lane:rgba(45,49,66,.018);--node:#fff;--node-stroke:rgba(45,49,66,.25);--chip:rgba(45,49,66,.12);--dot:rgba(45,49,66,.10);--db:#5e7a9b;--ls:#7c8f6f;--fl:#9c6b50;--tb:#b8915a;--wb:#6e6479;--sans:'Geist',system-ui,sans-serif;--serif:'Instrument Serif',serif;--mono:'Geist Mono',ui-monospace,monospace}
body{font-family:var(--sans);background:var(--paper);color:var(--ink);min-height:100vh;display:flex;align-items:center;justify-content:center;padding:3rem 2rem}
.frame{max-width:1120px;width:100%}.eyebrow{font:500 .66rem var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--muted);margin-bottom:.5rem}h1{font:400 clamp(1.5rem,2.4vw + .75rem,2rem)/1.15 var(--serif);letter-spacing:-.02em;margin-bottom:1.5rem}svg{width:100%;min-width:1000px;display:block}
svg text{font-family:var(--sans)}.mono{font-family:var(--mono)}.lane{fill:var(--lane)}.rule{stroke:var(--rule)}.label-rule{stroke:var(--label-rule)}.actor{fill:var(--muted);font:500 8px var(--mono);letter-spacing:.08em}.step-label{fill:var(--muted);font:500 6px var(--mono);letter-spacing:.12em}.step-chip{fill:var(--chip)}.step-num{fill:var(--ink);font:600 7px var(--mono)}.connector{fill:none;stroke:var(--muted);stroke-width:1}.connector.focal{stroke:var(--accent);stroke-width:1.2}.node-mask{fill:var(--paper)}.node{fill:var(--node);stroke:var(--node-stroke)}.node.focal{fill:var(--accent-tint);stroke:var(--accent);stroke-width:1.2}.role{fill:rgba(45,49,66,.10)}.role.focal{fill:rgba(235,108,54,.25)}.role-text{fill:var(--ink);font:600 6px var(--mono)}.role-text.focal{fill:var(--accent)}.node-title{fill:var(--ink);font:600 9px var(--sans)}.node-sub{fill:var(--muted);font:6.5px var(--mono)}.node-tool{fill:var(--soft);font:6.5px var(--mono)}.data-text{fill:white;font:700 5px var(--mono)}.legend-key{fill:var(--muted);font:500 7px var(--mono);letter-spacing:.12em}.legend-text{fill:var(--muted);font:8px var(--sans)}
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Process · Diagram Design</p>
<h1>Order fulfillment across four teams</h1>
<svg viewBox="0 0 1064 436" xmlns="http://www.w3.org/2000/svg" aria-label="Order fulfillment process from customer order to support closure">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse"><circle cx="11" cy="11" r="0.8" fill="var(--dot)"/></pattern>
<marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="var(--muted)"/></marker>
<marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="var(--accent)"/></marker>
<marker id="arrow-sm" markerWidth="6" markerHeight="5" refX="5" refY="2.5" orient="auto"><polygon points="0 0,6 2.5,0 5" fill="var(--muted)"/></marker>
</defs>
<rect width="1064" height="436" fill="var(--paper)"/><rect width="1064" height="436" fill="url(#dots)" opacity=".55"/>
<rect class="lane" x="140" y="36" width="924" height="80"/><rect class="lane" x="140" y="196" width="924" height="80"/>
<g class="rule" stroke-width=".8"><line x1="0" y1="36" x2="1064" y2="36"/><line x1="0" y1="116" x2="1064" y2="116"/><line x1="0" y1="196" x2="1064" y2="196"/><line x1="0" y1="276" x2="1064" y2="276"/><line x1="0" y1="356" x2="1064" y2="356"/></g>
<line class="label-rule" x1="140" y1="36" x2="140" y2="356"/>
<g class="actor" text-anchor="middle"><text x="70" y="80">CUSTOMER</text><text x="70" y="160">SUPPORT</text><text x="70" y="240">WAREHOUSE</text><text x="70" y="320">FINANCE</text></g>
<g text-anchor="middle">
<rect class="step-chip" x="190" y="8" width="16" height="16" rx="8"/><text class="step-num" x="198" y="19">1</text><text class="step-label" x="198" y="32">ORDER</text>
<rect class="step-chip" x="302" y="8" width="16" height="16" rx="8"/><text class="step-num" x="310" y="19">2</text><text class="step-label" x="310" y="32">VERIFY</text>
<rect x="414" y="8" width="16" height="16" rx="8" fill="rgba(235,108,54,.20)"/><text x="422" y="19" fill="var(--accent)" font-size="7" font-weight="600" class="mono">3</text><text x="422" y="32" fill="var(--accent)" font-size="6" font-weight="500" letter-spacing=".12em" class="mono">ALLOCATE</text>
<rect class="step-chip" x="526" y="8" width="16" height="16" rx="8"/><text class="step-num" x="534" y="19">4</text><text class="step-label" x="534" y="32">PICK</text>
<rect class="step-chip" x="638" y="8" width="16" height="16" rx="8"/><text class="step-num" x="646" y="19">5</text><text class="step-label" x="646" y="32">PACK</text>
<rect class="step-chip" x="750" y="8" width="16" height="16" rx="8"/><text class="step-num" x="758" y="19">6</text><text class="step-label" x="758" y="32">PAY</text>
<rect class="step-chip" x="862" y="8" width="16" height="16" rx="8"/><text class="step-num" x="870" y="19">7</text><text class="step-label" x="870" y="32">RECEIVE</text>
<rect class="step-chip" x="974" y="8" width="16" height="16" rx="8"/><text class="step-num" x="982" y="19">8</text><text class="step-label" x="982" y="32">CLOSE</text>
</g>
<!-- Connectors: right exit, r=8 orthogonal bend, top/bottom entry. -->
<path class="connector" d="M248 76 H302 Q310 76 310 84 V124" marker-end="url(#arrow)"/><path class="connector focal" d="M360 156 H414 Q422 156 422 164 V204" marker-end="url(#arrow-accent)"/><line class="connector focal" x1="472" y1="236" x2="484" y2="236" marker-end="url(#arrow-accent)"/><line class="connector" x1="584" y1="236" x2="596" y2="236" marker-end="url(#arrow)"/><path class="connector" d="M696 236 H750 Q758 236 758 244 V284" marker-end="url(#arrow)"/><path class="connector" d="M808 316 H862 Q870 316 870 308 V108" marker-end="url(#arrow)"/><path class="connector" d="M920 76 H974 Q982 76 982 84 V124" marker-end="url(#arrow)"/>
<!-- Nodes -->
<g text-anchor="middle">
<g><rect class="node-mask" x="148" y="44" width="100" height="64" rx="6"/><rect class="node" x="148" y="44" width="100" height="64" rx="6"/><rect class="role" x="152" y="48" width="20" height="10" rx="2"/><text class="role-text" x="162" y="56">CUS</text><text class="node-title" x="198" y="70">Place order</text><text class="node-sub" x="198" y="84">cart → order</text><text class="node-tool" x="198" y="96">storefront</text><rect x="228" y="98" width="16" height="8" rx="2" fill="var(--db)"/><text class="data-text" x="236" y="104">DB</text></g>
<g><rect class="node-mask" x="260" y="124" width="100" height="64" rx="6"/><rect class="node" x="260" y="124" width="100" height="64" rx="6"/><rect class="role" x="264" y="128" width="20" height="10" rx="2"/><text class="role-text" x="274" y="136">SUP</text><text class="node-title" x="310" y="150">Verify order</text><text class="node-sub" x="310" y="164">order → cleared</text><text class="node-tool" x="310" y="176">service desk</text><rect x="264" y="178" width="16" height="8" rx="2" fill="var(--db)"/><text class="data-text" x="272" y="184">DB</text><rect x="340" y="178" width="16" height="8" rx="2" fill="var(--db)"/><text class="data-text" x="348" y="184">DB</text></g>
<g><rect class="node-mask" x="372" y="204" width="100" height="64" rx="6"/><rect class="node focal" x="372" y="204" width="100" height="64" rx="6"/><rect class="role focal" x="376" y="208" width="20" height="10" rx="2"/><text class="role-text focal" x="386" y="216">WHS</text><text class="node-title" x="422" y="230">Allocate stock</text><text class="node-sub" x="422" y="244">order → pick list</text><text class="node-tool" x="422" y="256">inventory system</text><rect x="376" y="258" width="16" height="8" rx="2" fill="var(--db)"/><text class="data-text" x="384" y="264">DB</text><rect x="452" y="258" width="16" height="8" rx="2" fill="var(--ls)"/><text class="data-text" x="460" y="264">LS</text></g>
<g><rect class="node-mask" x="484" y="204" width="100" height="64" rx="6"/><rect class="node" x="484" y="204" width="100" height="64" rx="6"/><rect class="role" x="488" y="208" width="20" height="10" rx="2"/><text class="role-text" x="498" y="216">WHS</text><text class="node-title" x="534" y="230">Pick items</text><text class="node-sub" x="534" y="244">list → picked</text><text class="node-tool" x="534" y="256">handheld scanner</text><rect x="488" y="258" width="16" height="8" rx="2" fill="var(--ls)"/><text class="data-text" x="496" y="264">LS</text><rect x="564" y="258" width="16" height="8" rx="2" fill="var(--ls)"/><text class="data-text" x="572" y="264">LS</text></g>
<g><rect class="node-mask" x="596" y="204" width="100" height="64" rx="6"/><rect class="node" x="596" y="204" width="100" height="64" rx="6"/><rect class="role" x="600" y="208" width="20" height="10" rx="2"/><text class="role-text" x="610" y="216">WHS</text><text class="node-title" x="646" y="230">Pack order</text><text class="node-sub" x="646" y="244">picked → shipment</text><text class="node-tool" x="646" y="256">packing station</text><rect x="600" y="258" width="16" height="8" rx="2" fill="var(--ls)"/><text class="data-text" x="608" y="264">LS</text><rect x="676" y="258" width="16" height="8" rx="2" fill="var(--fl)"/><text class="data-text" x="684" y="264">FL</text></g>
<g><rect class="node-mask" x="708" y="284" width="100" height="64" rx="6"/><rect class="node" x="708" y="284" width="100" height="64" rx="6"/><rect class="role" x="712" y="288" width="20" height="10" rx="2"/><text class="role-text" x="722" y="296">FIN</text><text class="node-title" x="758" y="310">Capture payment</text><text class="node-sub" x="758" y="324">shipment → receipt</text><text class="node-tool" x="758" y="336">payment gateway</text><rect x="712" y="338" width="16" height="8" rx="2" fill="var(--fl)"/><text class="data-text" x="720" y="344">FL</text><rect x="788" y="338" width="16" height="8" rx="2" fill="var(--tb)"/><text class="data-text" x="796" y="344">TB</text></g>
<g><rect class="node-mask" x="820" y="44" width="100" height="64" rx="6"/><rect class="node" x="820" y="44" width="100" height="64" rx="6"/><rect class="role" x="824" y="48" width="20" height="10" rx="2"/><text class="role-text" x="834" y="56">CUS</text><text class="node-title" x="870" y="70">Receive shipment</text><text class="node-sub" x="870" y="84">receipt → confirmed</text><text class="node-tool" x="870" y="96">delivery portal</text><rect x="824" y="98" width="16" height="8" rx="2" fill="var(--tb)"/><text class="data-text" x="832" y="104">TB</text><rect x="900" y="98" width="16" height="8" rx="2" fill="var(--wb)"/><text class="data-text" x="908" y="104">WB</text></g>
<g><rect class="node-mask" x="932" y="124" width="100" height="64" rx="6"/><rect class="node" x="932" y="124" width="100" height="64" rx="6"/><rect class="role" x="936" y="128" width="20" height="10" rx="2"/><text class="role-text" x="946" y="136">SUP</text><text class="node-title" x="982" y="150">Close order</text><text class="node-sub" x="982" y="164">confirmed → closed</text><text class="node-tool" x="982" y="176">service desk</text><rect x="936" y="178" width="16" height="8" rx="2" fill="var(--wb)"/><text class="data-text" x="944" y="184">WB</text></g>
</g>
<!-- Three-row type key -->
<g><text class="legend-key" x="144" y="372">STEPS</text><g text-anchor="middle"><rect class="step-chip" x="208" y="364" width="16" height="16" rx="8"/><text class="step-num" x="216" y="375">1</text><text class="legend-text" x="248" y="375">ORDER</text><rect class="step-chip" x="300" y="364" width="16" height="16" rx="8"/><text class="step-num" x="308" y="375">2</text><text class="legend-text" x="340" y="375">VERIFY</text><rect x="392" y="364" width="16" height="16" rx="8" fill="rgba(235,108,54,.20)"/><text x="400" y="375" fill="var(--accent)" font-size="7" class="mono">3</text><text x="432" y="375" fill="var(--accent)" font-size="8">ALLOCATE</text><rect class="step-chip" x="496" y="364" width="16" height="16" rx="8"/><text class="step-num" x="504" y="375">4</text><text class="legend-text" x="532" y="375">PICK</text><rect class="step-chip" x="576" y="364" width="16" height="16" rx="8"/><text class="step-num" x="584" y="375">5</text><text class="legend-text" x="612" y="375">PACK</text><rect class="step-chip" x="656" y="364" width="16" height="16" rx="8"/><text class="step-num" x="664" y="375">6</text><text class="legend-text" x="692" y="375">PAY</text><rect class="step-chip" x="736" y="364" width="16" height="16" rx="8"/><text class="step-num" x="744" y="375">7</text><text class="legend-text" x="780" y="375">RECEIVE</text><rect class="step-chip" x="832" y="364" width="16" height="16" rx="8"/><text class="step-num" x="840" y="375">8</text><text class="legend-text" x="872" y="375">CLOSE</text></g>
<text class="legend-key" x="144" y="393">DATA TYPE</text><rect x="220" y="386" width="16" height="8" rx="2" fill="var(--db)"/><text class="legend-text" x="244" y="393">DB · records</text><rect x="328" y="386" width="16" height="8" rx="2" fill="var(--ls)"/><text class="legend-text" x="352" y="393">LS · task</text><rect x="428" y="386" width="16" height="8" rx="2" fill="var(--fl)"/><text class="legend-text" x="452" y="393">FL · document</text><rect x="552" y="386" width="16" height="8" rx="2" fill="var(--tb)"/><text class="legend-text" x="576" y="393">TB · transaction</text><rect x="688" y="386" width="16" height="8" rx="2" fill="var(--wb)"/><text class="legend-text" x="712" y="393">WB · web status</text><text class="legend-key" x="856" y="393">LEFT IN · RIGHT OUT</text>
<text class="legend-key" x="144" y="414">FLOW</text><line x1="200" y1="410" x2="228" y2="410" stroke="var(--accent)" stroke-width="1.2" marker-end="url(#arrow-accent)"/><text class="legend-text" x="240" y="414">Critical handoff</text><line x1="360" y1="410" x2="388" y2="410" stroke="var(--muted)" marker-end="url(#arrow)"/><text class="legend-text" x="400" y="414">Sequential handoff</text></g>
</svg>
</div>
</body>
</html>
@@ -0,0 +1,137 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Storage backends · Capability radar</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--color-paper: #2d3142;
--color-ink: #f5f5f5;
--color-muted: #bfc0c0;
--color-accent: #f08a59;
--font-sans: 'Geist', system-ui, sans-serif;
--font-serif: 'Instrument Serif', serif;
--font-mono: 'Geist Mono', ui-monospace, monospace;
}
body {
font-family: var(--font-sans);
background: var(--color-paper);
color: var(--color-ink);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 3rem 2rem;
}
.frame { max-width: 1200px; width: 100%; }
.eyebrow {
font-family: var(--font-mono);
font-size: 0.66rem;
font-weight: 500;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--color-muted);
margin-bottom: 0.5rem;
}
h1 {
font-family: var(--font-serif);
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
font-weight: 400;
letter-spacing: -0.02em;
line-height: 1.15;
color: var(--color-ink);
margin-bottom: 1.5rem;
}
svg { width: 100%; min-width: 900px; display: block; }
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Radar · Diagram Design</p>
<h1>Storage backends · Capability radar</h1>
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(245,245,245,0.10)"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="#2d3142"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- Concentric grid rings -->
<polygon points="500,208 530,230 519,266 481,266 470,230" fill="none" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<polygon points="500,176 561,220 538,292 462,292 439,220" fill="none" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<polygon points="500,144 591,211 556,317 444,317 409,211" fill="none" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<polygon points="500,112 622,201 575,343 425,343 378,201" fill="none" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<polygon points="500,80 652,191 594,369 406,369 348,191" fill="none" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<!-- Axis spokes -->
<line x1="500" y1="240" x2="500" y2="80" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<line x1="500" y1="240" x2="652" y2="191" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<line x1="500" y1="240" x2="594" y2="369" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<line x1="500" y1="240" x2="406" y2="369" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<line x1="500" y1="240" x2="348" y2="191" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<!-- Axis labels -->
<text x="500" y="62" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Small-file handling</text>
<text x="669" y="189" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="start">Large-object reads</text>
<text x="605" y="400" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="start">Write throughput</text>
<text x="395" y="400" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="end">Operational simplicity</text>
<text x="331" y="189" fill="#f5f5f5" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="end">Iceberg integration</text>
<!-- Scale ticks -->
<text x="494" y="82" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">10</text>
<text x="494" y="114" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">8</text>
<text x="494" y="146" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">6</text>
<text x="494" y="178" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">4</text>
<text x="494" y="210" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">2</text>
<!-- Non-focal series -->
<!-- Ceph · series-2 dusty-blue dark -->
<polygon points="500,128 606,206 566,330 462,292 409,211" fill="rgba(130,160,192,0.22)" stroke="#82a0c0" stroke-width="1.5"/>
<!-- GCS · series-3 mustard dark -->
<polygon points="500,144 637,196 575,343 444,317 394,206" fill="rgba(211,173,122,0.22)" stroke="#d3ad7a" stroke-width="1.5"/>
<!-- Amazon S3 · series-1 sage dark -->
<polygon points="500,144 652,191 585,356 453,305 378,201" fill="rgba(156,175,143,0.22)" stroke="#9caf8f" stroke-width="1.5"/>
<!-- Focal series · MinIO -->
<polygon points="500,96 622,201 585,356 415,356 363,196" fill="rgba(240,138,89,0.22)" stroke="#f08a59" stroke-width="1.8"/>
<!-- Focal vertex dots -->
<circle cx="500" cy="96" r="4" fill="#f08a59"/>
<circle cx="622" cy="201" r="4" fill="#f08a59"/>
<circle cx="585" cy="356" r="4" fill="#f08a59"/>
<circle cx="415" cy="356" r="4" fill="#f08a59"/>
<circle cx="363" cy="196" r="4" fill="#f08a59"/>
<!-- Legend -->
<line x1="40" y1="456" x2="960" y2="456" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<text x="40" y="472" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<rect x="40" y="484" width="16" height="8" fill="rgba(240,138,89,0.22)" stroke="#f08a59" stroke-width="1.5"/>
<text x="64" y="492" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">MinIO · recommended</text>
<rect x="220" y="484" width="16" height="8" fill="rgba(156,175,143,0.22)" stroke="#9caf8f" stroke-width="1.5"/>
<text x="244" y="492" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Amazon S3</text>
<rect x="344" y="484" width="16" height="8" fill="rgba(130,160,192,0.22)" stroke="#82a0c0" stroke-width="1.5"/>
<text x="368" y="492" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Ceph</text>
<rect x="424" y="484" width="16" height="8" fill="rgba(211,173,122,0.22)" stroke="#d3ad7a" stroke-width="1.5"/>
<text x="448" y="492" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Google Cloud Storage</text>
<text x="608" y="492" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif" font-style="italic">One coral. Position is the signal — color reserved for the recommended option.</text>
</svg>
</div>
</body>
</html>
@@ -0,0 +1,133 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Storage backends · Capability radar</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root { --color-paper:#f5f5f5; --color-paper-2:#ececec; --color-ink:#2d3142; --color-muted:#4f5d75; --color-soft:#7a8399; --color-rule:rgba(45,49,66,0.12); --color-accent:#eb6c36; --color-link:#2e5aa8; --series-1:#7c8f6f; --series-2:#5e7a9b; --series-3:#b8915a; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
.container { max-width: 1200px; margin: 0 auto; }
.header { margin-bottom: 2.5rem; }
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 58ch; }
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
svg { width: 100%; min-width: 900px; display: block; }
.cards { display: grid; grid-template-columns: 1.1fr 1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
@media (max-width: 980px) { .cards { grid-template-columns: 1fr 1fr; } }
@media (max-width: 600px) { .cards { grid-template-columns: 1fr; } }
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(45,49,66,0.08); }
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
.card-dot.coral { background: var(--color-accent); }
.card-dot.sage { background: var(--series-1); }
.card-dot.dusty { background: var(--series-2); }
.card-dot.mustard { background: var(--series-3); }
.card h3 { font-size: 0.875rem; font-weight: 600; }
.card p { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; }
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(45,49,66,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<p class="header-eyebrow">Radar · Diagram Design</p>
<h1>Storage backends · Capability radar</h1>
<p class="subtitle">Four object-storage candidates scored 0–10 across five workload dimensions. The chart is a decision tool — the recommended option carries the only colored fill; everything else sits in the muted comparison palette.</p>
</div>
<div class="diagram-container">
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(45,49,66,0.10)"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<polygon points="500,208 530,230 519,266 481,266 470,230" fill="none" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<polygon points="500,176 561,220 538,292 462,292 439,220" fill="none" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<polygon points="500,144 591,211 556,317 444,317 409,211" fill="none" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<polygon points="500,112 622,201 575,343 425,343 378,201" fill="none" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<polygon points="500,80 652,191 594,369 406,369 348,191" fill="none" stroke="rgba(45,49,66,0.20)" stroke-width="1"/>
<line x1="500" y1="240" x2="500" y2="80" stroke="rgba(45,49,66,0.20)" stroke-width="1"/>
<line x1="500" y1="240" x2="652" y2="191" stroke="rgba(45,49,66,0.20)" stroke-width="1"/>
<line x1="500" y1="240" x2="594" y2="369" stroke="rgba(45,49,66,0.20)" stroke-width="1"/>
<line x1="500" y1="240" x2="406" y2="369" stroke="rgba(45,49,66,0.20)" stroke-width="1"/>
<line x1="500" y1="240" x2="348" y2="191" stroke="rgba(45,49,66,0.20)" stroke-width="1"/>
<text x="500" y="62" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Small-file handling</text>
<text x="669" y="189" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="start">Large-object reads</text>
<text x="605" y="400" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="start">Write throughput</text>
<text x="395" y="400" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="end">Operational simplicity</text>
<text x="331" y="189" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="end">Iceberg integration</text>
<text x="494" y="82" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">10</text>
<text x="494" y="114" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">8</text>
<text x="494" y="146" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">6</text>
<text x="494" y="178" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">4</text>
<text x="494" y="210" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">2</text>
<polygon points="500,128 606,206 566,330 462,292 409,211" fill="rgba(94,122,155,0.18)" stroke="#5e7a9b" stroke-width="1.5"/>
<polygon points="500,144 637,196 575,343 444,317 394,206" fill="rgba(184,145,90,0.18)" stroke="#b8915a" stroke-width="1.5"/>
<polygon points="500,144 652,191 585,356 453,305 378,201" fill="rgba(124,143,111,0.18)" stroke="#7c8f6f" stroke-width="1.5"/>
<polygon points="500,96 622,201 585,356 415,356 363,196" fill="rgba(235,108,54,0.18)" stroke="#eb6c36" stroke-width="1.8"/>
<circle cx="500" cy="96" r="4" fill="#eb6c36"/>
<circle cx="622" cy="201" r="4" fill="#eb6c36"/>
<circle cx="585" cy="356" r="4" fill="#eb6c36"/>
<circle cx="415" cy="356" r="4" fill="#eb6c36"/>
<circle cx="363" cy="196" r="4" fill="#eb6c36"/>
<line x1="40" y1="456" x2="960" y2="456" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="40" y="472" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<rect x="40" y="484" width="16" height="8" fill="rgba(235,108,54,0.18)" stroke="#eb6c36" stroke-width="1.5"/>
<text x="64" y="492" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">MinIO · recommended</text>
<rect x="220" y="484" width="16" height="8" fill="rgba(124,143,111,0.18)" stroke="#7c8f6f" stroke-width="1.5"/>
<text x="244" y="492" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Amazon S3</text>
<rect x="344" y="484" width="16" height="8" fill="rgba(94,122,155,0.18)" stroke="#5e7a9b" stroke-width="1.5"/>
<text x="368" y="492" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Ceph</text>
<rect x="424" y="484" width="16" height="8" fill="rgba(184,145,90,0.18)" stroke="#b8915a" stroke-width="1.5"/>
<text x="448" y="492" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Google Cloud Storage</text>
</svg>
</div>
<div class="cards">
<div class="card">
<p class="eyebrow">RECOMMENDED</p>
<div class="card-header"><span class="card-dot coral"></span><h3>MinIO</h3></div>
<p>9 across the board, weakest only on large-object reads. The clear pick when small-file performance and Iceberg integration matter and you can run the operator yourself.</p>
</div>
<div class="card">
<div class="card-header"><span class="card-dot sage"></span><h3>Amazon S3</h3></div>
<p>The reference implementation: top-class on large-object reads and write throughput, but a hard step down on operational simplicity once you factor in IAM, lifecycle, and bucket policy sprawl.</p>
</div>
<div class="card">
<div class="card-header"><span class="card-dot dusty"></span><h3>Ceph</h3></div>
<p>Capable across the board, but the lowest score on operational simplicity. Pick it when you already run Ceph or need block + object + file from one cluster — not for a fresh greenfield.</p>
</div>
<div class="card">
<div class="card-header"><span class="card-dot mustard"></span><h3>Google Cloud Storage</h3></div>
<p>Solid middle path. No standout dimension — which is itself an answer when the team optimises for "boring, managed, fine."</p>
</div>
</div>
<div class="footer">
<span>storage backends · capability radar</span>
<span>example · diagram design</span>
</div>
</div>
</body>
</html>
@@ -0,0 +1,137 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Storage backends · Capability radar</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--color-paper: #f5f5f5;
--color-ink: #2d3142;
--color-muted: #4f5d75;
--color-accent: #eb6c36;
--font-sans: 'Geist', system-ui, sans-serif;
--font-serif: 'Instrument Serif', serif;
--font-mono: 'Geist Mono', ui-monospace, monospace;
}
body {
font-family: var(--font-sans);
background: var(--color-paper);
color: var(--color-ink);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 3rem 2rem;
}
.frame { max-width: 1200px; width: 100%; }
.eyebrow {
font-family: var(--font-mono);
font-size: 0.66rem;
font-weight: 500;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--color-muted);
margin-bottom: 0.5rem;
}
h1 {
font-family: var(--font-serif);
font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
font-weight: 400;
letter-spacing: -0.02em;
line-height: 1.15;
color: var(--color-ink);
margin-bottom: 1.5rem;
}
svg { width: 100%; min-width: 900px; display: block; }
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Radar · Diagram Design</p>
<h1>Storage backends · Capability radar</h1>
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(45,49,66,0.10)"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- Concentric grid rings (f = 0.2, 0.4, 0.6, 0.8, 1.0) -->
<polygon points="500,208 530,230 519,266 481,266 470,230" fill="none" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<polygon points="500,176 561,220 538,292 462,292 439,220" fill="none" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<polygon points="500,144 591,211 556,317 444,317 409,211" fill="none" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<polygon points="500,112 622,201 575,343 425,343 378,201" fill="none" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<polygon points="500,80 652,191 594,369 406,369 348,191" fill="none" stroke="rgba(45,49,66,0.20)" stroke-width="1"/>
<!-- Axis spokes (center to outer vertex) -->
<line x1="500" y1="240" x2="500" y2="80" stroke="rgba(45,49,66,0.20)" stroke-width="1"/>
<line x1="500" y1="240" x2="652" y2="191" stroke="rgba(45,49,66,0.20)" stroke-width="1"/>
<line x1="500" y1="240" x2="594" y2="369" stroke="rgba(45,49,66,0.20)" stroke-width="1"/>
<line x1="500" y1="240" x2="406" y2="369" stroke="rgba(45,49,66,0.20)" stroke-width="1"/>
<line x1="500" y1="240" x2="348" y2="191" stroke="rgba(45,49,66,0.20)" stroke-width="1"/>
<!-- Axis labels (one word per spoke, Geist sans 11px 600) -->
<text x="500" y="62" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="middle">Small-file handling</text>
<text x="669" y="189" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="start">Large-object reads</text>
<text x="605" y="400" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="start">Write throughput</text>
<text x="395" y="400" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="end">Operational simplicity</text>
<text x="331" y="189" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif" text-anchor="end">Iceberg integration</text>
<!-- Scale ticks on first (top) axis only -->
<text x="494" y="82" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">10</text>
<text x="494" y="114" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">8</text>
<text x="494" y="146" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">6</text>
<text x="494" y="178" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">4</text>
<text x="494" y="210" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">2</text>
<!-- Non-focal series (smallest to largest, back to front) -->
<!-- Ceph · series-2 dusty-blue · [7,7,7,4,6] -->
<polygon points="500,128 606,206 566,330 462,292 409,211" fill="rgba(94,122,155,0.18)" stroke="#5e7a9b" stroke-width="1.5"/>
<!-- GCS · series-3 mustard · [6,9,8,6,7] -->
<polygon points="500,144 637,196 575,343 444,317 394,206" fill="rgba(184,145,90,0.18)" stroke="#b8915a" stroke-width="1.5"/>
<!-- Amazon S3 · series-1 sage · [6,10,9,5,8] -->
<polygon points="500,144 652,191 585,356 453,305 378,201" fill="rgba(124,143,111,0.18)" stroke="#7c8f6f" stroke-width="1.5"/>
<!-- Focal series · MinIO · accent · [9,8,9,9,9] -->
<polygon points="500,96 622,201 585,356 415,356 363,196" fill="rgba(235,108,54,0.18)" stroke="#eb6c36" stroke-width="1.8"/>
<!-- Focal vertex dots (only on focal series) -->
<circle cx="500" cy="96" r="4" fill="#eb6c36"/>
<circle cx="622" cy="201" r="4" fill="#eb6c36"/>
<circle cx="585" cy="356" r="4" fill="#eb6c36"/>
<circle cx="415" cy="356" r="4" fill="#eb6c36"/>
<circle cx="363" cy="196" r="4" fill="#eb6c36"/>
<!-- Legend -->
<line x1="40" y1="456" x2="960" y2="456" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="40" y="472" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<rect x="40" y="484" width="16" height="8" fill="rgba(235,108,54,0.18)" stroke="#eb6c36" stroke-width="1.5"/>
<text x="64" y="492" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">MinIO · recommended</text>
<rect x="220" y="484" width="16" height="8" fill="rgba(124,143,111,0.18)" stroke="#7c8f6f" stroke-width="1.5"/>
<text x="244" y="492" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Amazon S3</text>
<rect x="344" y="484" width="16" height="8" fill="rgba(94,122,155,0.18)" stroke="#5e7a9b" stroke-width="1.5"/>
<text x="368" y="492" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Ceph</text>
<rect x="424" y="484" width="16" height="8" fill="rgba(184,145,90,0.18)" stroke="#b8915a" stroke-width="1.5"/>
<text x="448" y="492" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Google Cloud Storage</text>
<text x="608" y="492" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif" font-style="italic">One coral. Position is the signal — color reserved for the recommended option.</text>
</svg>
</div>
</body>
</html>
@@ -0,0 +1,139 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Deploy frequency vs lead time · Scatter · dark</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root { --color-paper:#f5f5f5; --color-ink:#2d3142; --color-muted:#4f5d75; --color-accent:#eb6c36; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
body { font-family: var(--font-sans); background: var(--color-paper); color: var(--color-ink); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 3rem 2rem; }
.frame { max-width: 1200px; width: 100%; }
.eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.15; margin-bottom: 1.5rem; }
svg { width: 100%; min-width: 760px; display: block; }
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Scatter · Diagram Design</p>
<h1>Deploy frequency vs. lead time · 12 teams</h1>
<!-- X: 0-20 deploys/week → 880px. x = 80 + f/20*880 = 80+44*f -->
<!-- Y: 0-24 days lead time → 380px. y = 420 - lt/24*380. High lt = high y (worse) -->
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(245,245,245,0.10)"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- Axis labels -->
<text transform="rotate(-90 24 230)" x="24" y="230" fill="#bfc0c0" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">LEAD TIME (DAYS)</text>
<text x="520" y="492" fill="#bfc0c0" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">DEPLOYS PER WEEK</text>
<!-- Gridlines -->
<!-- Horizontal (constant lead time) -->
<line x1="80" y1="325" x2="960" y2="325" stroke="rgba(245,245,245,0.08)" stroke-width="0.8"/>
<line x1="80" y1="230" x2="960" y2="230" stroke="rgba(245,245,245,0.08)" stroke-width="0.8"/>
<line x1="80" y1="135" x2="960" y2="135" stroke="rgba(245,245,245,0.08)" stroke-width="0.8"/>
<line x1="80" y1="40" x2="960" y2="40" stroke="rgba(245,245,245,0.06)" stroke-width="0.8"/>
<!-- Vertical (constant deploy freq) -->
<line x1="256" y1="40" x2="256" y2="420" stroke="rgba(245,245,245,0.08)" stroke-width="0.8"/>
<line x1="432" y1="40" x2="432" y2="420" stroke="rgba(245,245,245,0.08)" stroke-width="0.8"/>
<line x1="608" y1="40" x2="608" y2="420" stroke="rgba(245,245,245,0.08)" stroke-width="0.8"/>
<line x1="784" y1="40" x2="784" y2="420" stroke="rgba(245,245,245,0.08)" stroke-width="0.8"/>
<!-- Axes -->
<line x1="80" y1="40" x2="80" y2="420" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<line x1="80" y1="420" x2="960" y2="420" stroke="rgba(245,245,245,0.20)" stroke-width="1"/>
<!-- Y-axis labels (lead time: 0 at bottom, 24 at top) -->
<text x="72" y="324" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">6</text>
<text x="72" y="234" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">12</text>
<text x="72" y="139" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">18</text>
<text x="72" y="44" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">24</text>
<text x="72" y="424" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">0</text>
<!-- X-axis labels (deploy frequency) -->
<text x="80" y="440" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">0</text>
<text x="256" y="440" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">4</text>
<text x="432" y="440" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">8</text>
<text x="608" y="440" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">12</text>
<text x="784" y="440" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">16</text>
<text x="960" y="440" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">20</text>
<!-- Trend line (dashed, approximate) -->
<line x1="168" y1="103" x2="900" y2="388" stroke="rgba(245,245,245,0.20)" stroke-width="1" stroke-dasharray="4,3"/>
<!-- ─── DATA POINTS (non-focal first) ─── -->
<!-- (freq, lt) → (x=80+44*f, y=420-380*lt/24) -->
<!-- (2,20)→(168,103), (4,18)→(256,135), (4,16)→(256,167), (6,14)→(344,198) -->
<!-- (8,12)→(432,230), (8,10)→(432,262), (10,8)→(520,293), (12,6)→(608,325) -->
<!-- (12,10)→(608,262), (16,4)→(784,357), (18,3)→(872,372) FOCAL, (20,2)→(960,388) -->
<circle cx="168" cy="103" r="5" fill="#f5f5f5"/>
<circle cx="168" cy="103" r="5" fill="rgba(191,192,192,0.20)" stroke="#bfc0c0" stroke-width="1"/>
<circle cx="256" cy="135" r="5" fill="#f5f5f5"/>
<circle cx="256" cy="135" r="5" fill="rgba(191,192,192,0.20)" stroke="#bfc0c0" stroke-width="1"/>
<circle cx="256" cy="167" r="5" fill="#f5f5f5"/>
<circle cx="256" cy="167" r="5" fill="rgba(191,192,192,0.20)" stroke="#bfc0c0" stroke-width="1"/>
<circle cx="344" cy="198" r="5" fill="#f5f5f5"/>
<circle cx="344" cy="198" r="5" fill="rgba(191,192,192,0.20)" stroke="#bfc0c0" stroke-width="1"/>
<circle cx="432" cy="230" r="5" fill="#f5f5f5"/>
<circle cx="432" cy="230" r="5" fill="rgba(191,192,192,0.20)" stroke="#bfc0c0" stroke-width="1"/>
<circle cx="432" cy="262" r="5" fill="#f5f5f5"/>
<circle cx="432" cy="262" r="5" fill="rgba(191,192,192,0.20)" stroke="#bfc0c0" stroke-width="1"/>
<circle cx="520" cy="293" r="5" fill="#f5f5f5"/>
<circle cx="520" cy="293" r="5" fill="rgba(191,192,192,0.20)" stroke="#bfc0c0" stroke-width="1"/>
<circle cx="608" cy="325" r="5" fill="#f5f5f5"/>
<circle cx="608" cy="325" r="5" fill="rgba(191,192,192,0.20)" stroke="#bfc0c0" stroke-width="1"/>
<circle cx="608" cy="262" r="5" fill="#f5f5f5"/>
<circle cx="608" cy="262" r="5" fill="rgba(191,192,192,0.20)" stroke="#bfc0c0" stroke-width="1"/>
<circle cx="784" cy="357" r="5" fill="#f5f5f5"/>
<circle cx="784" cy="357" r="5" fill="rgba(191,192,192,0.20)" stroke="#bfc0c0" stroke-width="1"/>
<circle cx="960" cy="388" r="5" fill="#f5f5f5"/>
<circle cx="960" cy="388" r="5" fill="rgba(191,192,192,0.20)" stroke="#bfc0c0" stroke-width="1"/>
<!-- FOCAL: Platform team (18,3) → (872,372) -->
<circle cx="872" cy="372" r="6" fill="#f5f5f5"/>
<circle cx="872" cy="372" r="6" fill="rgba(240,138,89,0.18)" stroke="#f08a59" stroke-width="1.2"/>
<!-- Focal label -->
<rect x="798" y="348" width="64" height="12" rx="2" fill="#f5f5f5"/>
<text x="830" y="357" fill="#f08a59" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.06em">PLATFORM</text>
<!-- Quadrant guides (optional) -->
<text x="88" y="56" fill="rgba(245,245,245,0.22)" font-size="8" font-family="'Geist Mono', monospace">HIGH LEAD TIME</text>
<text x="88" y="412" fill="rgba(245,245,245,0.22)" font-size="8" font-family="'Geist Mono', monospace">LOW LEAD TIME</text>
<!-- Legend -->
<line x1="40" y1="462" x2="960" y2="462" stroke="rgba(245,245,245,0.10)" stroke-width="0.8"/>
<text x="40" y="478" fill="#bfc0c0" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<circle cx="52" cy="490" r="6" fill="rgba(240,138,89,0.18)" stroke="#f08a59" stroke-width="1.2"/>
<text x="68" y="494" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Platform team · best performer</text>
<circle cx="256" cy="490" r="5" fill="rgba(191,192,192,0.20)" stroke="#bfc0c0" stroke-width="1"/>
<text x="272" y="494" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Engineering team</text>
<line x1="416" y1="490" x2="440" y2="490" stroke="rgba(245,245,245,0.20)" stroke-width="1" stroke-dasharray="4,3"/>
<text x="448" y="494" fill="#bfc0c0" font-size="8.5" font-family="'Geist', sans-serif">Trend</text>
</svg>
</div>
</body>
</html>
@@ -0,0 +1,114 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Deploy frequency vs lead time · Scatter</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root { --color-paper:#f5f5f5; --color-paper-2:#ececec; --color-ink:#2d3142; --color-muted:#4f5d75; --color-soft:#7a8399; --color-rule:rgba(45,49,66,0.12); --color-accent:#eb6c36; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
.container { max-width: 1200px; margin: 0 auto; }
.header { margin-bottom: 2.5rem; }
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 58ch; }
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
svg { width: 100%; min-width: 760px; display: block; }
.cards { display: grid; grid-template-columns: 1.1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
@media (max-width: 720px) { .cards { grid-template-columns: 1fr; } }
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(45,49,66,0.08); }
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
.card-dot.coral { background: var(--color-accent); } .card-dot.ink { background: var(--color-ink); } .card-dot.muted { background: var(--color-muted); }
.card h3 { font-size: 0.875rem; font-weight: 600; }
.card p { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; }
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(45,49,66,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<p class="header-eyebrow">Scatter · Diagram Design</p>
<h1>Deploy frequency vs. lead time · 12 teams</h1>
<p class="subtitle">Each point is an engineering team. Moving right means deploying more often; moving down means shipping faster. The Platform team occupies the lower-right — the ideal quadrant.</p>
</div>
<div class="diagram-container">
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
<defs><pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse"><circle cx="1" cy="1" r="0.9" fill="rgba(45,49,66,0.10)"/></pattern></defs>
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<text transform="rotate(-90 24 230)" x="24" y="230" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">LEAD TIME (DAYS)</text>
<text x="520" y="492" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">DEPLOYS PER WEEK</text>
<line x1="80" y1="325" x2="960" y2="325" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="230" x2="960" y2="230" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="135" x2="960" y2="135" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="40" x2="960" y2="40" stroke="rgba(45,49,66,0.06)" stroke-width="0.8"/>
<line x1="256" y1="40" x2="256" y2="420" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="432" y1="40" x2="432" y2="420" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="608" y1="40" x2="608" y2="420" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="784" y1="40" x2="784" y2="420" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="40" x2="80" y2="420" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<line x1="80" y1="420" x2="960" y2="420" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<text x="72" y="324" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">6</text>
<text x="72" y="234" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">12</text>
<text x="72" y="139" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">18</text>
<text x="72" y="44" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">24</text>
<text x="72" y="424" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">0</text>
<text x="80" y="440" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">0</text>
<text x="256" y="440" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">4</text>
<text x="432" y="440" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">8</text>
<text x="608" y="440" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">12</text>
<text x="784" y="440" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">16</text>
<text x="960" y="440" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">20</text>
<line x1="168" y1="103" x2="900" y2="388" stroke="rgba(45,49,66,0.18)" stroke-width="1" stroke-dasharray="4,3"/>
<circle cx="168" cy="103" r="5" fill="#f5f5f5"/><circle cx="168" cy="103" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="256" cy="135" r="5" fill="#f5f5f5"/><circle cx="256" cy="135" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="256" cy="167" r="5" fill="#f5f5f5"/><circle cx="256" cy="167" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="344" cy="198" r="5" fill="#f5f5f5"/><circle cx="344" cy="198" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="432" cy="230" r="5" fill="#f5f5f5"/><circle cx="432" cy="230" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="432" cy="262" r="5" fill="#f5f5f5"/><circle cx="432" cy="262" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="520" cy="293" r="5" fill="#f5f5f5"/><circle cx="520" cy="293" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="608" cy="325" r="5" fill="#f5f5f5"/><circle cx="608" cy="325" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="608" cy="262" r="5" fill="#f5f5f5"/><circle cx="608" cy="262" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="784" cy="357" r="5" fill="#f5f5f5"/><circle cx="784" cy="357" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="960" cy="388" r="5" fill="#f5f5f5"/><circle cx="960" cy="388" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="872" cy="372" r="6" fill="#f5f5f5"/><circle cx="872" cy="372" r="6" fill="rgba(235,108,54,0.15)" stroke="#eb6c36" stroke-width="1.2"/>
<rect x="798" y="348" width="64" height="12" rx="2" fill="#f5f5f5"/>
<text x="830" y="357" fill="#eb6c36" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.06em">PLATFORM</text>
<text x="88" y="56" fill="rgba(45,49,66,0.25)" font-size="8" font-family="'Geist Mono', monospace">HIGH LEAD TIME</text>
<text x="88" y="412" fill="rgba(45,49,66,0.25)" font-size="8" font-family="'Geist Mono', monospace">LOW LEAD TIME</text>
<line x1="40" y1="462" x2="960" y2="462" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="40" y="478" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<circle cx="52" cy="490" r="6" fill="rgba(235,108,54,0.15)" stroke="#eb6c36" stroke-width="1.2"/>
<text x="68" y="494" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Platform team · best performer</text>
<circle cx="256" cy="490" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<text x="272" y="494" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Engineering team</text>
<line x1="416" y1="490" x2="440" y2="490" stroke="rgba(45,49,66,0.18)" stroke-width="1" stroke-dasharray="4,3"/>
<text x="448" y="494" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Trend</text>
</svg>
</div>
<div class="cards">
<div class="card">
<p class="eyebrow">BEST PERFORMER</p>
<div class="card-header"><span class="card-dot coral"></span><h3>Platform team · (18, 3)</h3></div>
<p>18 deploys/week with a 3-day lead time. This combination is achievable because they own the full stack — no cross-team approval gates, and their CI pipeline runs under 4 minutes.</p>
</div>
<div class="card">
<div class="card-header"><span class="card-dot ink"></span><h3>Correlation is clear</h3></div>
<p>Every team to the right of the median deploy frequency (8/week) is also below the median lead time (12 days). The relationship isn't causal — both are symptoms of the same underlying practice: small, frequent, reversible changes.</p>
</div>
<div class="card">
<div class="card-header"><span class="card-dot muted"></span><h3>Two outliers worth examining</h3></div>
<p>The team at (2,20) ships rarely and slowly — high coupling, long approval chains. The team at (12,10) deploys frequently but with higher-than-expected lead time — likely a large monorepo with slow integration tests.</p>
</div>
</div>
<div class="footer">
<span>deploy frequency vs. lead time · 12 teams</span>
<span>example · diagram design</span>
</div>
</div>
</body>
</html>
@@ -0,0 +1,139 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Deploy frequency vs lead time · Scatter</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root { --color-paper:#f5f5f5; --color-ink:#2d3142; --color-muted:#4f5d75; --color-accent:#eb6c36; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
body { font-family: var(--font-sans); background: var(--color-paper); color: var(--color-ink); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 3rem 2rem; }
.frame { max-width: 1200px; width: 100%; }
.eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.15; margin-bottom: 1.5rem; }
svg { width: 100%; min-width: 760px; display: block; }
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">Scatter · Diagram Design</p>
<h1>Deploy frequency vs. lead time · 12 teams</h1>
<!-- X: 0-20 deploys/week → 880px. x = 80 + f/20*880 = 80+44*f -->
<!-- Y: 0-24 days lead time → 380px. y = 420 - lt/24*380. High lt = high y (worse) -->
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(45,49,66,0.10)"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- Axis labels -->
<text transform="rotate(-90 24 230)" x="24" y="230" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">LEAD TIME (DAYS)</text>
<text x="520" y="492" fill="#4f5d75" font-size="7" font-family="'Geist Mono', monospace" letter-spacing="0.14em" text-anchor="middle">DEPLOYS PER WEEK</text>
<!-- Gridlines -->
<!-- Horizontal (constant lead time) -->
<line x1="80" y1="325" x2="960" y2="325" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="230" x2="960" y2="230" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="135" x2="960" y2="135" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="80" y1="40" x2="960" y2="40" stroke="rgba(45,49,66,0.06)" stroke-width="0.8"/>
<!-- Vertical (constant deploy freq) -->
<line x1="256" y1="40" x2="256" y2="420" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="432" y1="40" x2="432" y2="420" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="608" y1="40" x2="608" y2="420" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<line x1="784" y1="40" x2="784" y2="420" stroke="rgba(45,49,66,0.08)" stroke-width="0.8"/>
<!-- Axes -->
<line x1="80" y1="40" x2="80" y2="420" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<line x1="80" y1="420" x2="960" y2="420" stroke="rgba(45,49,66,0.25)" stroke-width="1"/>
<!-- Y-axis labels (lead time: 0 at bottom, 24 at top) -->
<text x="72" y="324" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">6</text>
<text x="72" y="234" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">12</text>
<text x="72" y="139" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">18</text>
<text x="72" y="44" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">24</text>
<text x="72" y="424" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="end">0</text>
<!-- X-axis labels (deploy frequency) -->
<text x="80" y="440" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">0</text>
<text x="256" y="440" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">4</text>
<text x="432" y="440" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">8</text>
<text x="608" y="440" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">12</text>
<text x="784" y="440" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">16</text>
<text x="960" y="440" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle">20</text>
<!-- Trend line (dashed, approximate) -->
<line x1="168" y1="103" x2="900" y2="388" stroke="rgba(45,49,66,0.18)" stroke-width="1" stroke-dasharray="4,3"/>
<!-- ─── DATA POINTS (non-focal first) ─── -->
<!-- (freq, lt) → (x=80+44*f, y=420-380*lt/24) -->
<!-- (2,20)→(168,103), (4,18)→(256,135), (4,16)→(256,167), (6,14)→(344,198) -->
<!-- (8,12)→(432,230), (8,10)→(432,262), (10,8)→(520,293), (12,6)→(608,325) -->
<!-- (12,10)→(608,262), (16,4)→(784,357), (18,3)→(872,372) FOCAL, (20,2)→(960,388) -->
<circle cx="168" cy="103" r="5" fill="#f5f5f5"/>
<circle cx="168" cy="103" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="256" cy="135" r="5" fill="#f5f5f5"/>
<circle cx="256" cy="135" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="256" cy="167" r="5" fill="#f5f5f5"/>
<circle cx="256" cy="167" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="344" cy="198" r="5" fill="#f5f5f5"/>
<circle cx="344" cy="198" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="432" cy="230" r="5" fill="#f5f5f5"/>
<circle cx="432" cy="230" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="432" cy="262" r="5" fill="#f5f5f5"/>
<circle cx="432" cy="262" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="520" cy="293" r="5" fill="#f5f5f5"/>
<circle cx="520" cy="293" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="608" cy="325" r="5" fill="#f5f5f5"/>
<circle cx="608" cy="325" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="608" cy="262" r="5" fill="#f5f5f5"/>
<circle cx="608" cy="262" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="784" cy="357" r="5" fill="#f5f5f5"/>
<circle cx="784" cy="357" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<circle cx="960" cy="388" r="5" fill="#f5f5f5"/>
<circle cx="960" cy="388" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<!-- FOCAL: Platform team (18,3) → (872,372) -->
<circle cx="872" cy="372" r="6" fill="#f5f5f5"/>
<circle cx="872" cy="372" r="6" fill="rgba(235,108,54,0.15)" stroke="#eb6c36" stroke-width="1.2"/>
<!-- Focal label -->
<rect x="798" y="348" width="64" height="12" rx="2" fill="#f5f5f5"/>
<text x="830" y="357" fill="#eb6c36" font-size="8" font-family="'Geist Mono', monospace" text-anchor="middle" letter-spacing="0.06em">PLATFORM</text>
<!-- Quadrant guides (optional) -->
<text x="88" y="56" fill="rgba(45,49,66,0.25)" font-size="8" font-family="'Geist Mono', monospace">HIGH LEAD TIME</text>
<text x="88" y="412" fill="rgba(45,49,66,0.25)" font-size="8" font-family="'Geist Mono', monospace">LOW LEAD TIME</text>
<!-- Legend -->
<line x1="40" y1="462" x2="960" y2="462" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="40" y="478" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<circle cx="52" cy="490" r="6" fill="rgba(235,108,54,0.15)" stroke="#eb6c36" stroke-width="1.2"/>
<text x="68" y="494" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Platform team · best performer</text>
<circle cx="256" cy="490" r="5" fill="rgba(79,93,117,0.20)" stroke="#4f5d75" stroke-width="1"/>
<text x="272" y="494" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Engineering team</text>
<line x1="416" y1="490" x2="440" y2="490" stroke="rgba(45,49,66,0.18)" stroke-width="1" stroke-dasharray="4,3"/>
<text x="448" y="494" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Trend</text>
</svg>
</div>
</body>
</html>
File diff suppressed because one or more lines are too long
+318 -195
View File
@@ -1,207 +1,330 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Diagram Design · Gallery</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--paper: #f5f4ed;
--paper-2: #efeee5;
--ink: #0b0d0b;
--muted: #52534e;
--soft: #65655c;
--rule: rgba(11,13,11,0.12);
--rule-strong: rgba(11,13,11,0.25);
--accent: #f7591f;
--sans: 'Geist', system-ui, sans-serif;
--serif: 'Instrument Serif', serif;
--mono: 'Geist Mono', ui-monospace, monospace;
}
html, body { height: 100%; }
body {
font-family: var(--sans);
background: var(--paper);
color: var(--ink);
display: flex;
flex-direction: column;
overflow: hidden;
}
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Diagram Design · Gallery</title>
<link
href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap"
rel="stylesheet"
/>
<style>
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--paper: #f5f4ed;
--paper-2: #efeee5;
--ink: #0b0d0b;
--muted: #52534e;
--soft: #65655c;
--rule: rgba(11, 13, 11, 0.12);
--rule-strong: rgba(11, 13, 11, 0.25);
--accent: #f7591f;
--sans: "Geist", system-ui, sans-serif;
--serif: "Instrument Serif", serif;
--mono: "Geist Mono", ui-monospace, monospace;
}
html,
body {
height: 100%;
}
body {
font-family: var(--sans);
background: var(--paper);
color: var(--ink);
display: flex;
flex-direction: column;
overflow: hidden;
}
.topbar {
padding: 1rem 1.25rem 0.625rem;
border-bottom: 1px solid var(--rule);
flex: 0 0 auto;
}
.title-row {
display: flex;
align-items: baseline;
justify-content: space-between;
margin-bottom: 0.75rem;
gap: 1rem;
}
.title-row h1 {
font-family: var(--serif);
font-size: 1.5rem;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1;
}
.title-row .meta {
font-family: var(--mono);
font-size: 0.66rem;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--muted);
}
.title-row .open-raw {
font-family: var(--mono);
font-size: 0.66rem;
letter-spacing: 0.06em;
color: var(--muted);
text-decoration: none;
padding: 0.3rem 0.625rem;
border: 1px solid var(--rule);
border-radius: 4px;
transition: color 0.15s, border-color 0.15s;
}
.open-raw:hover { color: var(--accent); border-color: var(--accent); }
.topbar {
padding: 1rem 1.25rem 0.625rem;
border-bottom: 1px solid var(--rule);
flex: 0 0 auto;
}
.title-row {
display: flex;
align-items: baseline;
justify-content: space-between;
margin-bottom: 0.75rem;
gap: 1rem;
}
.title-row h1 {
font-family: var(--serif);
font-size: 1.5rem;
font-weight: 400;
letter-spacing: -0.01em;
line-height: 1;
}
.title-row .meta {
font-family: var(--mono);
font-size: 0.66rem;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--muted);
}
.title-row .open-raw {
font-family: var(--mono);
font-size: 0.66rem;
letter-spacing: 0.06em;
color: var(--muted);
text-decoration: none;
padding: 0.3rem 0.625rem;
border: 1px solid var(--rule);
border-radius: 4px;
transition:
color 0.15s,
border-color 0.15s;
}
.open-raw:hover {
color: var(--accent);
border-color: var(--accent);
}
.tabs {
display: flex;
flex-wrap: wrap;
gap: 0.375rem;
margin-bottom: 0.5rem;
}
.tabs.variants {
margin-bottom: 0;
padding-top: 0.5rem;
border-top: 1px solid var(--rule);
}
.tab {
font-family: var(--sans);
font-size: 0.8125rem;
font-weight: 500;
padding: 0.375rem 0.75rem;
background: transparent;
color: var(--muted);
border: 1px solid var(--rule);
border-radius: 4px;
cursor: pointer;
transition: all 0.12s;
letter-spacing: -0.005em;
}
.tab:hover {
color: var(--ink);
border-color: var(--rule-strong);
}
.tab.active {
background: var(--ink);
color: var(--paper);
border-color: var(--ink);
}
.tab .eyebrow {
display: inline-block;
margin-right: 0.25rem;
font-family: var(--mono);
font-size: 0.56rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: inherit;
opacity: 0.5;
}
.tab.new::after {
content: 'NEW';
display: inline-block;
margin-left: 0.35rem;
font-family: var(--mono);
font-size: 0.54rem;
letter-spacing: 0.12em;
color: var(--accent);
vertical-align: 1px;
}
.tab.active.new::after { color: var(--paper); }
.tabs {
display: flex;
flex-wrap: wrap;
gap: 0.375rem;
margin-bottom: 0.5rem;
}
.tabs.variants {
margin-bottom: 0;
padding-top: 0.5rem;
border-top: 1px solid var(--rule);
}
.tab {
font-family: var(--sans);
font-size: 0.8125rem;
font-weight: 500;
padding: 0.375rem 0.75rem;
background: transparent;
color: var(--muted);
border: 1px solid var(--rule);
border-radius: 4px;
cursor: pointer;
transition: all 0.12s;
letter-spacing: -0.005em;
}
.tab:hover {
color: var(--ink);
border-color: var(--rule-strong);
}
.tab.active {
background: var(--ink);
color: var(--paper);
border-color: var(--ink);
}
.tab .eyebrow {
display: inline-block;
margin-right: 0.25rem;
font-family: var(--mono);
font-size: 0.56rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: inherit;
opacity: 0.5;
}
.tab.new::after {
content: "NEW";
display: inline-block;
margin-left: 0.35rem;
font-family: var(--mono);
font-size: 0.54rem;
letter-spacing: 0.12em;
color: var(--accent);
vertical-align: 1px;
}
.tab.active.new::after {
color: var(--paper);
}
main {
flex: 1 1 auto;
position: relative;
background: var(--paper-2);
overflow: hidden;
}
iframe {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
border: 0;
background: var(--paper);
display: block;
}
</style>
</head>
<body>
<header class="topbar">
<div class="title-row">
<h1>Diagram Design <span class="meta" style="font-family: var(--mono); font-size: 0.66rem; margin-left: 0.5rem;">· gallery</span></h1>
<a class="open-raw" id="open-raw" href="#" target="_blank" rel="noopener">open in new tab ↗</a>
</div>
main {
flex: 1 1 auto;
position: relative;
background: var(--paper-2);
overflow: hidden;
}
iframe {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
border: 0;
background: var(--paper);
display: block;
}
</style>
</head>
<body>
<header class="topbar">
<div class="title-row">
<h1>
Diagram Design
<span
class="meta"
style="
font-family: var(--mono);
font-size: 0.66rem;
margin-left: 0.5rem;
"
>· gallery</span
>
</h1>
<div style="display: flex; gap: 0.5rem">
<a class="open-raw" href="icons.html" target="_blank" rel="noopener"
>icon library ↗</a
>
<a
class="open-raw"
id="open-raw"
href="#"
target="_blank"
rel="noopener"
>open in new tab ↗</a
>
</div>
</div>
<div class="tabs" id="type-tabs" role="tablist" aria-label="Diagram type">
<button class="tab active" data-type="architecture"><span class="eyebrow">01</span>Architecture</button>
<button class="tab" data-type="flowchart"><span class="eyebrow">02</span>Flowchart</button>
<button class="tab" data-type="sequence"><span class="eyebrow">03</span>Sequence</button>
<button class="tab" data-type="state"><span class="eyebrow">04</span>State</button>
<button class="tab" data-type="er"><span class="eyebrow">05</span>ER</button>
<button class="tab" data-type="timeline"><span class="eyebrow">06</span>Timeline</button>
<button class="tab" data-type="swimlane"><span class="eyebrow">07</span>Swimlane</button>
<button class="tab" data-type="quadrant"><span class="eyebrow">08</span>Quadrant</button>
<button class="tab new" data-type="nested"><span class="eyebrow">09</span>Nested</button>
<button class="tab new" data-type="tree"><span class="eyebrow">10</span>Tree</button>
<button class="tab new" data-type="org-chart"><span class="eyebrow">11</span>Org chart</button>
<button class="tab new" data-type="layers"><span class="eyebrow">12</span>Layers</button>
<button class="tab new" data-type="venn"><span class="eyebrow">13</span>Venn</button>
<button class="tab new" data-type="pyramid"><span class="eyebrow">14</span>Pyramid</button>
</div>
<div class="tabs" id="type-tabs" role="tablist" aria-label="Diagram type">
<button class="tab active" data-type="architecture">
<span class="eyebrow">01</span>Architecture
</button>
<button class="tab" data-type="flowchart">
<span class="eyebrow">02</span>Flowchart
</button>
<button class="tab" data-type="sequence">
<span class="eyebrow">03</span>Sequence
</button>
<button class="tab" data-type="state">
<span class="eyebrow">04</span>State
</button>
<button class="tab" data-type="er">
<span class="eyebrow">05</span>ER
</button>
<button class="tab" data-type="timeline">
<span class="eyebrow">06</span>Timeline
</button>
<button class="tab" data-type="swimlane">
<span class="eyebrow">07</span>Swimlane
</button>
<button class="tab" data-type="quadrant">
<span class="eyebrow">08</span>Quadrant
</button>
<button class="tab new" data-type="nested">
<span class="eyebrow">09</span>Nested
</button>
<button class="tab new" data-type="tree">
<span class="eyebrow">10</span>Tree
</button>
<button class="tab new" data-type="org-chart">
<span class="eyebrow">11</span>Org chart
</button>
<button class="tab new" data-type="layers">
<span class="eyebrow">12</span>Layers
</button>
<button class="tab new" data-type="venn">
<span class="eyebrow">13</span>Venn
</button>
<button class="tab new" data-type="pyramid">
<span class="eyebrow">14</span>Pyramid
</button>
<button class="tab new" data-type="radar">
<span class="eyebrow">15</span>Radar
</button>
<button class="tab new" data-type="datalake">
<span class="eyebrow">16</span>Data lake
</button>
<button class="tab new" data-type="bar">
<span class="eyebrow">17</span>Bar
</button>
<button class="tab new" data-type="line">
<span class="eyebrow">18</span>Line
</button>
<button class="tab new" data-type="gantt">
<span class="eyebrow">19</span>Gantt
</button>
<button class="tab new" data-type="scatter">
<span class="eyebrow">20</span>Scatter
</button>
<button class="tab new" data-type="medallion">
<span class="eyebrow">21</span>Medallion
</button>
<button class="tab new" data-type="high-level">
<span class="eyebrow">22</span>High-Level
</button>
<button class="tab new" data-type="process">
<span class="eyebrow">23</span>Process
</button>
<button class="tab new" data-type="data-flow">
<span class="eyebrow">25</span>Data flow
</button>
<button class="tab new" data-type="dp-integration">
<span class="eyebrow">26</span>DP integration
</button>
<button class="tab new" data-type="high-level-vertical">
<span class="eyebrow">27</span>High-Level / Parametric
</button>
<button class="tab new" data-type="dp-security-matrix">
<span class="eyebrow">31</span>DP security matrix
</button>
<button class="tab new" data-type="it-state">
<span class="eyebrow">32</span>IT current-state
</button>
</div>
<div class="tabs variants" id="variant-tabs" role="tablist" aria-label="Variant">
<button class="tab active" data-variant="">Minimal light</button>
<button class="tab" data-variant="-dark">Minimal dark</button>
<button class="tab" data-variant="-full">Full editorial</button>
</div>
</header>
<div
class="tabs variants"
id="variant-tabs"
role="tablist"
aria-label="Variant"
>
<button class="tab active" data-variant="">Minimal light</button>
<button class="tab" data-variant="-dark">Minimal dark</button>
<button class="tab" data-variant="-full">Full editorial</button>
</div>
</header>
<main>
<iframe id="preview" src="example-architecture.html" title="Preview"></iframe>
</main>
<main>
<iframe
id="preview"
src="example-architecture.html"
title="Preview"
></iframe>
</main>
<script>
const state = { type: 'architecture', variant: '' };
const iframe = document.getElementById('preview');
const openRaw = document.getElementById('open-raw');
<script>
const state = { type: "architecture", variant: "" };
const iframe = document.getElementById("preview");
const openRaw = document.getElementById("open-raw");
function update() {
const src = `example-${state.type}${state.variant}.html`;
iframe.src = src;
openRaw.href = src;
}
function update() {
const src = `example-${state.type}${state.variant}.html`;
iframe.src = src;
openRaw.href = src;
}
function bindTabs(containerId, key) {
const container = document.getElementById(containerId);
container.addEventListener('click', (e) => {
const btn = e.target.closest('.tab');
if (!btn) return;
container.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
btn.classList.add('active');
state[key] = btn.dataset[key];
update();
});
}
function bindTabs(containerId, key) {
const container = document.getElementById(containerId);
container.addEventListener("click", (e) => {
const btn = e.target.closest(".tab");
if (!btn) return;
container
.querySelectorAll(".tab")
.forEach((t) => t.classList.remove("active"));
btn.classList.add("active");
state[key] = btn.dataset[key];
update();
});
}
bindTabs('type-tabs', 'type');
bindTabs('variant-tabs', 'variant');
update();
</script>
</body>
bindTabs("type-tabs", "type");
bindTabs("variant-tabs", "variant");
update();
</script>
</body>
</html>