Guide — how this site was built
Teaching artifact Read time ~9 minA sales page drawn as an engineering document — and every line of it is a line of code.
This page documents how AI Operations Launch™ was designed and engineered, generously enough that another designer-developer could reproduce every technique. No build tools, no frameworks, no images — one HTML file per route, one stylesheet, one script, five small font files. Everything you see is drawn by code.
Concept & creative direction
The blueprint Palette · type · layoutWhy a blueprint?
AI Operations Launch™ sells a 30-day installation — voice, WhatsApp and web chat wired into one operations system. The category it lives in is loud: gradients, glassmorphism, galaxy purple, promises. The creative decision was to do the opposite of magic: treat the offer like an engineering project and present it as a technical drawing set. Sheets, figure numbers, revision blocks, dimension ticks. If the pitch is “we install a system, measure it, and hand you the keys,” the page itself should feel like the document you'd sign off.
Every section is literally framed as a numbered sheet (Sheet 01 / 08) with a title strip, a figure reference, and a scale note. The footer is a revision block. The form is Form F-01. The metaphor is carried all the way down or it isn't carried at all.
Palette
Paper #FAFAF7, ink #0D0D0C, signal orange #FF4D00, blueprint blue #1B4DFF, silver grid #E4E4DE. Light, technical, warm — deliberately against the dark-mode AI-site convention. Blue is reserved for thin lines and annotations (drawing pen no. 2); orange is the signal colour: packets, live markers, the CTA. Neither is ever used as a slab of background except the one orange button.
Type
Space Grotesk carries display and body; IBM Plex Mono carries every label, number and annotation — sheet titles, tags, day numbers, table headers, axis ticks. The rule that makes the drawing language read instantly: if it's data, it's mono; if it's an argument, it's grotesk. Captions follow drawing convention: FIG 02 — ORCHESTRATION DIAGRAM, letter-spaced, uppercase, small.
repeating-linear-gradient layers — a 96px major grid in silver over a 12px minor grid in a fainter tone. Sheets float on it at 55% paper opacity so the grid ghosts through. The crosshair registration marks on sheet corners are ::before/::after pseudo-elements. Zero images, zero SVG backgrounds.
Toolchain — who and what built this
Bill of tools Zero dependenciesThis site is part of a 26-site collection with a shared thesis: AI doesn't replace taste — it amplifies the designer who has it. The direction, brief, critique and final judgment are Hannah Kwakye's; the engineering hands are Fable 5 (Anthropic's Claude), working as a designer-engineer inside the constraints below. Every decision on this page survived three critique passes before shipping.
The constraint set
- Static, hand-authored HTML/CSS/JS. No framework, no build step, no bundler. What ships is what was written.
- Zero external requests. No CDNs, no analytics, no font services at runtime. The page is self-contained and works from a folder.
- Zero libraries. The diagram engine, scrubber and reveal system are ~340 lines of vanilla JavaScript.
IntersectionObserver+requestAnimationFramecover everything a scroll library would. - Every visual code-drawn. No stock photos, no AI-generated rasters, no downloaded images anywhere in the collection — see the asset pipeline sheet.
Self-hosted fonts, plus a trick worth stealing
Space Grotesk (variable, 300–700) and IBM Plex Mono (400 + 500) are subsetted latin woff2 files, self-hosted, preloaded, font-display: swap. But latin subsets are missing the glyphs an engineering drawing leans on: →, ↗, ≤, ✓. Left alone, those characters silently fall back to a system font — a one-pixel-wrong detail that reads as sloppiness. The fix costs ~3 KB: two symbol-sliver fonts containing only those six glyphs, scoped with unicode-range so the browser only ever uses them for those code points.
/* symbol sliver — only the glyphs the latin subset is missing, scoped to their range */ @font-face { font-family: 'IBM Plex Mono'; src: url('plexmono-sym.woff2') format('woff2'); font-weight: 400 500; unicode-range: U+2190, U+2192, U+2197, U+2264-2265, U+2713; }
content-visibility: auto so first paint doesn't lay out the whole sheet set.
The signature — packets on a living schematic
Fig G-02 How it worksThe orchestration diagram on the main page assembles itself, then runs live: small orange packets travel the drawn wires, routed correctly for whichever scenario tab is active. Here is the entire technique, in four moves — reproducible in an afternoon.
Move 1 — edges that draw themselves
Every wire is an SVG <path> plotted by hand on the viewBox grid, using only horizontal and vertical runs (schematics don't do diagonals). On load, each path's real length is measured with getTotalLength() and written into a custom property; CSS then does the classic dash trick — stroke-dasharray: var(--len); stroke-dashoffset: var(--len) — and an IntersectionObserver flips a class that transitions the offset to zero. The edge appears to be drawn with a pen.
Move 2 — packets as advected points
A packet is a <circle> whose position is sampled from a path. A route is just an ordered list of path ids. Each frame, distance travelled is advanced by speed × Δt (so speed is framerate-independent), the current segment is found, and getPointAtLength() converts that scalar distance into x,y. When the distance overruns a segment, the remainder carries into the next — packets never stutter at joints.
Move 3 — passing through the modules
The illusion that packets enter a node and emerge on the other side costs nothing: SVG paints in document order, and the node boxes are drawn after the packet layer. Invisible “transit paths” run beneath each box, so the packet keeps moving — occluded — and reappears at the exit pin at exactly the right moment. A segment id prefixed ! is traversed in reverse; scenario S2 uses this to send a follow-up packet back out to WhatsApp.
Move 4 — re-routing live
The three scenario tabs each own a routing table: which segments a packet chains through, which standing lines dim, which modules run hot (orange stroke). Switching tabs clears in-flight packets, swaps the table, and the spawner simply reads the new one. The animation pauses whenever the diagram scrolls off-screen or the tab is hidden, and under prefers-reduced-motion packets never spawn at all — the diagram renders complete, with numbered step markers keyed to the written scenario below it.
// the advection loop, unabridged p.d += speed * dt / 1000; while (p.i < p.segs.length && p.d > p.segs[p.i].len) { p.d -= p.segs[p.i].len; // carry remainder p.i++; // next wire } const s = p.segs[p.i]; const pt = s.el.getPointAtLength( s.rev ? s.len - p.d : p.d ); p.c.setAttribute('cx', pt.x); p.c.setAttribute('cy', pt.y); // a scenario is only a routing table s3: { routes: [['e-web-q', 'tC', 'e-q-staff', 't-staff', 'e-staff-crm', 't-crm-top', 'e-crm-summary', 't-sum', 'e-summary-owner']], nodes: ['webchat', 'qualify', 'staff', 'crm', ...] }
role="img" description), keeps the packet math trivial (getPointAtLength is free geometry), scales crisply at any zoom, and lets CSS own every visual state — hot nodes and dimmed wires are just classes. A canvas would buy nothing here but pixels.
The 30-day scrubber
D01–D30 Scroll-linkedThe programme is thirty days, so the page is thirty days: a fixed rail on the right edge (a slim strip under the header on smaller screens) maps total scroll progress onto D01–D30, with week ticks at D01, D08, D15, D22 and D30. Scrolling the page is scrubbing the timeline — by the time you reach the engagement form you've arrived at D30, which is exactly the argument the offer wants to make.
Inside Sheet 04 a second, sticky ruler runs the same mapping against that section alone, so the four week chapters scrub D01→D30 while you read them. Both are one cheap computation in a requestAnimationFrame-throttled scroll handler — no scroll library, no layout thrash; the marker moves with transform only.
// scroll → day, one line of arithmetic const p = scrollY / (scrollHeight - innerHeight); const day = 1 + Math.round(p * 29); // weeks are lookup, not math W1 MAP 01–07 · W2 BUILD 08–14 W3 CONNECT 15–21 · W4 MEASURE 22–30
cubic-bezier(0.1, 0, 0, 1)), steps() timing on state flips, and reveals that translate a strict 16px. Nothing bounces. Under reduced motion, transitions collapse to 1ms and every reveal is pre-completed.
Asset pipeline — everything is drawn in code
Constraint → thesis 0 imagesThe collection this site belongs to was built in an environment with no stock libraries and no image generation — so “no photography” was first a constraint. It became the thesis: a designer who can draw with code doesn't need the stock library. On this site that means:
- The schematics (hero one-liner, orchestration, decision tree) are hand-plotted inline SVG — coordinates chosen on paper first, on a consistent module grid, orthogonal runs only.
- The UI mocks (daily summary, CRM sync) are HTML tables and lists styled to spec — which is why their type is perfectly crisp and their “data” is honest placeholder narrative, labelled as a spec render.
- The grid, sheet frames, registration marks, dimension ticks are CSS gradients and pseudo-elements. The dimension tick under each statistic —
|—— label ——|— is three tiny linear-gradients on a::before/::afterpair. - The favicon is an inline SVG data-URI of the brand mark: a module square with crosshair pins and an orange core.
Nothing on any route of this site makes an external request, and no raster image exists in the repository.
Iteration log & deploy
Passes 1–3 Rev A → C-
Pass 1 — design critique
- Full-page screenshots at 390 / 834 / 1440px exposed that scenario steps stacked — all three lists rendered at once because a display:grid rule silently beat the hidden attribute. Fixed with an explicit [hidden] rule and re-shot.
- Zoomed crops caught ≤, →, ↗ and ✓ falling back to a system font: the latin subsets don't carry them. Solved with 3 KB unicode-range symbol slivers rather than rewording the annotations.
- The hero's “≤ 60 SEC” callout sat too close to a vertical wire at drawing scale; the annotation block was re-seated clear of the edge run.
-
Pass 2 — elevation
- Deepened the scenario tabs from a visual toggle into full re-routing: standing lines dim, active modules run hot in signal orange, numbered step markers swap with the routing table, and S2 gained the reversed follow-up packet travelling back out to WhatsApp.
- Gave the assembly moment more ceremony: nodes snap in on a steps() curve after their wires finish drawing, and annotations fade in last — survey lines before buildings.
- Added the sticky in-section day ruler to Sheet 04 so the 30-day claim is scrubbed twice: once by the page, once by the programme itself.
-
Pass 3 — ship quality
- Zero console errors across all three routes and widths; every anchor, footer link and cross-link resolves. One real bug caught here:
content-visibilitysections materialising mid-jump shifted long anchor scrolls, so a settle routine now re-aligns the target (and yields to any user input). - Reduced-motion verified by emulation: diagrams render complete with numbered flow, packets never spawn, the scrubbers still read correctly as position indicators.
- Keyboard walk: skip link, roving-tabindex scenario tabs with arrow keys, focus-visible outlines on paper, form fully operable; SVG diagrams carry full prose descriptions.
- Proofread every sheet against the copy rule: outcomes and engineering targets only — industry figures appear once, labelled as illustrative ranges, never as promises.
- Zero console errors across all three routes and widths; every anchor, footer link and cross-link resolves. One real bug caught here:
Deploy pipeline
The site deploys to Netlify from a monorepo of 26 sites — each site is a folder with its own netlify.toml publishing . (no build command, because there is no build). Immutable cache headers on /assets/*, conservative security headers on everything. The dossier form is Netlify Forms: a plain HTML <form data-netlify="true"> with a honeypot field, redirecting back to /?success=1#engage, where twelve lines of JavaScript swap the fieldset for the confirmation state.
What to steal
- The
unicode-rangesymbol-sliver pattern — fixes subset fallback everywhere, costs kilobytes. - Routing tables +
getPointAtLength— any “system diagram” can run live for ~100 lines. - Transit paths under opaque nodes — free through-the-box packet illusion.
- Sheet framing as a copy discipline: numbering sections forces you to know what each one is for.