assets/example-state-lifecycle.html
A supporting file of the diagram-design skill, shown as source. It is not opened as a web page on this site.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Newsletter publication lifecycle · State machine</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%; overflow-x:auto; }
.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; }
svg text { font-family:'Geist',sans-serif; }
</style>
</head>
<body>
<div class="frame">
<p class="eyebrow">State · Lifecycle phase map</p>
<h1>Newsletter publication lifecycle</h1>
<svg viewBox="0 0 1000 520" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="state-lifecycle-title state-lifecycle-desc">
<title id="state-lifecycle-title">Newsletter publication lifecycle</title>
<desc id="state-lifecycle-desc">A lifecycle phase map with Draft, Editing, Scheduled, Sending, and Sent on the primary rail; Approval and Delivery Retry in a recovery band; and Cancelled and Failed terminal outcomes.</desc>
<defs>
<pattern id="lifecycle-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="lifecycle-arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="#4f5d75"/></marker>
<marker id="lifecycle-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(#lifecycle-dots)" opacity="0.55"/>
<rect x="24" y="44" width="952" height="120" rx="8" fill="rgba(79,93,117,0.035)" stroke="rgba(45,49,66,0.12)"/>
<rect x="24" y="400" width="952" height="104" rx="8" fill="rgba(45,49,66,0.035)" stroke="rgba(45,49,66,0.12)"/>
<text x="40" y="64" fill="#4f5d75" font-size="8" font-family="'Geist Mono',monospace" letter-spacing="0.14em">WAIT / RECOVERY</text>
<text x="40" y="208" fill="#4f5d75" font-size="8" font-family="'Geist Mono',monospace" letter-spacing="0.14em">PRIMARY PHASES</text>
<text x="40" y="420" fill="#4f5d75" font-size="8" font-family="'Geist Mono',monospace" letter-spacing="0.14em">TERMINAL OUTCOMES</text>
<!-- Ten labeled transitions, drawn before states. -->
<line x1="40" y1="256" x2="64" y2="256" stroke="#4f5d75" stroke-width="1.2" marker-end="url(#lifecycle-arrow)"/>
<line x1="214" y1="256" x2="244" y2="256" stroke="#4f5d75" stroke-width="1.2" marker-end="url(#lifecycle-arrow)"/>
<path d="M319 220 V176 H369 V140" fill="none" stroke="#4f5d75" stroke-width="1.2" marker-end="url(#lifecycle-arrow)"/>
<path d="M449 140 V176 H499 V220" fill="none" stroke="#4f5d75" stroke-width="1.2" marker-end="url(#lifecycle-arrow)"/>
<line x1="574" y1="256" x2="604" y2="256" stroke="#4f5d75" stroke-width="1.2" marker-end="url(#lifecycle-arrow)"/>
<line x1="754" y1="256" x2="800" y2="256" stroke="#eb6c36" stroke-width="1.4" marker-end="url(#lifecycle-arrow-accent)"/>
<path d="M649 220 V176 H639 V140" fill="none" stroke="#4f5d75" stroke-width="1.2" stroke-dasharray="5,4" marker-end="url(#lifecycle-arrow)"/>
<path d="M719 140 V188 H709 V220" fill="none" stroke="#4f5d75" stroke-width="1.2" stroke-dasharray="5,4" marker-end="url(#lifecycle-arrow)"/>
<path d="M499 292 V416" fill="none" stroke="#4f5d75" stroke-width="1.2" marker-end="url(#lifecycle-arrow)"/>
<path d="M604 104 H584 V380 H749 V416" fill="none" stroke="#4f5d75" stroke-width="1.2" marker-end="url(#lifecycle-arrow)"/>
<!-- Opaque masks keep every transition label clear of its stroke. -->
<g font-family="'Geist Mono',monospace" font-size="7" text-anchor="middle" letter-spacing="0.08em" fill="#4f5d75">
<rect x="27" y="238" width="32" height="12" rx="2" fill="#f5f5f5"/><text x="43" y="247">NEW</text>
<rect x="215" y="238" width="28" height="12" rx="2" fill="#f5f5f5"/><text x="229" y="247">WRITE</text>
<rect x="326" y="158" width="48" height="12" rx="2" fill="#f5f5f5"/><text x="350" y="167">SUBMIT</text>
<rect x="448" y="158" width="56" height="12" rx="2" fill="#f5f5f5"/><text x="476" y="167">APPROVE</text>
<rect x="575" y="238" width="28" height="12" rx="2" fill="#f5f5f5"/><text x="589" y="247">START</text>
<rect x="760" y="238" width="34" height="12" rx="2" fill="#f5f5f5"/><text x="777" y="247" fill="#eb6c36">DELIVER</text>
<rect x="620" y="158" width="46" height="12" rx="2" fill="#f5f5f5"/><text x="643" y="167">ERROR</text>
<rect x="700" y="180" width="42" height="12" rx="2" fill="#f5f5f5"/><text x="721" y="189">RETRY</text>
<rect x="474" y="350" width="52" height="12" rx="2" fill="#f5f5f5"/><text x="500" y="359">CANCEL</text>
<rect x="588" y="362" width="66" height="12" rx="2" fill="#f5f5f5"/><text x="621" y="371">EXHAUSTED</text>
</g>
<circle cx="34" cy="256" r="6" fill="#2d3142"/>
<!-- Primary rail: five phases. -->
<g font-family="'Geist',sans-serif" text-anchor="middle">
<g><rect x="64" y="220" width="150" height="72" rx="8" fill="#fff" stroke="#2d3142"/><text x="139" y="251" fill="#2d3142" font-size="12" font-weight="600">Draft</text><text x="139" y="270" fill="#4f5d75" font-size="8" font-family="'Geist Mono',monospace">content exists</text></g>
<g><rect x="244" y="220" width="150" height="72" rx="8" fill="#fff" stroke="#2d3142"/><text x="319" y="251" fill="#2d3142" font-size="12" font-weight="600">Editing</text><text x="319" y="270" fill="#4f5d75" font-size="8" font-family="'Geist Mono',monospace">owner revises</text></g>
<g><rect x="424" y="220" width="150" height="72" rx="8" fill="#fff" stroke="#2d3142"/><text x="499" y="251" fill="#2d3142" font-size="12" font-weight="600">Scheduled</text><text x="499" y="270" fill="#4f5d75" font-size="8" font-family="'Geist Mono',monospace">send time set</text></g>
<g><rect x="604" y="220" width="150" height="72" rx="8" fill="#fff" stroke="#2d3142"/><text x="679" y="251" fill="#2d3142" font-size="12" font-weight="600">Sending</text><text x="679" y="270" fill="#4f5d75" font-size="8" font-family="'Geist Mono',monospace">provider active</text></g>
<g><rect x="800" y="220" width="150" height="72" rx="8" fill="rgba(235,108,54,0.08)" stroke="#eb6c36"/><circle cx="824" cy="244" r="7" fill="none" stroke="#eb6c36"/><circle cx="824" cy="244" r="4" fill="#eb6c36"/><text x="875" y="251" fill="#2d3142" font-size="12" font-weight="600">Sent</text><text x="875" y="270" fill="#4f5d75" font-size="8" font-family="'Geist Mono',monospace">delivery complete</text></g>
</g>
<!-- Supporting states. -->
<g text-anchor="middle">
<g><rect x="334" y="68" width="150" height="72" rx="8" fill="#f5f5f5" stroke="#4f5d75" stroke-dasharray="5,4"/><rect x="344" y="78" width="34" height="12" rx="2" fill="none" stroke="rgba(79,93,117,0.45)"/><text x="361" y="87" fill="#4f5d75" font-size="7" font-family="'Geist Mono',monospace">WAIT</text><text x="409" y="111" fill="#2d3142" font-size="12" font-weight="600" font-family="'Geist',sans-serif">Approval</text><text x="409" y="126" fill="#4f5d75" font-size="8" font-family="'Geist Mono',monospace">editor decision</text></g>
<g><rect x="604" y="68" width="150" height="72" rx="8" fill="#f5f5f5" stroke="#4f5d75" stroke-dasharray="5,4"/><rect x="614" y="78" width="52" height="12" rx="2" fill="none" stroke="rgba(79,93,117,0.45)"/><text x="640" y="87" fill="#4f5d75" font-size="7" font-family="'Geist Mono',monospace">RECOVERY</text><text x="679" y="111" fill="#2d3142" font-size="12" font-weight="600" font-family="'Geist',sans-serif">Delivery Retry</text><text x="679" y="126" fill="#4f5d75" font-size="8" font-family="'Geist Mono',monospace">attempt 1 of 3</text></g>
</g>
<!-- Terminal outcomes use text, shape, and dash differences. -->
<g text-anchor="middle">
<g><rect x="424" y="416" width="150" height="72" rx="8" fill="rgba(45,49,66,0.04)" stroke="#4f5d75"/><rect x="434" y="426" width="54" height="12" rx="2" fill="none" stroke="rgba(79,93,117,0.45)"/><text x="461" y="435" fill="#4f5d75" font-size="7" font-family="'Geist Mono',monospace">TERMINAL</text><text x="499" y="459" fill="#2d3142" font-size="12" font-weight="600" font-family="'Geist',sans-serif">Cancelled</text><text x="499" y="474" fill="#4f5d75" font-size="8" font-family="'Geist Mono',monospace">stopped by owner</text></g>
<g><rect x="674" y="416" width="150" height="72" rx="8" fill="rgba(45,49,66,0.04)" stroke="#4f5d75" stroke-width="1.5" stroke-dasharray="3,3"/><rect x="684" y="426" width="54" height="12" rx="2" fill="none" stroke="rgba(79,93,117,0.45)"/><text x="711" y="435" fill="#4f5d75" font-size="7" font-family="'Geist Mono',monospace">TERMINAL</text><text x="749" y="459" fill="#2d3142" font-size="12" font-weight="600" font-family="'Geist',sans-serif">Failed</text><text x="749" y="474" fill="#4f5d75" font-size="8" font-family="'Geist Mono',monospace">retries exhausted</text></g>
</g>
</svg>
</div>
</body>
</html>