Under the Hood

The working system, exposed

Most portfolio sites are decorated. This one is constructed. The whole site runs on a single 40-pixel module: every column, gap and block of type is a whole number of those units.

The idea is borrowed from Müller-Brockmann and Gerstner, who worked out that a grid isn’t a limit on design but the thing that makes it repeatable. What’s different here is that the grid is machine-readable — so an AI agent can build inside it without me redrawing every screen.

I didn’t want gloss. Rendered graphics and stock imagery signal effort without carrying information — decoration standing in for thinking. What I wanted was a whiteboard crossed with an architect’s working drawing: Swiss structure underneath, the marks of someone solving a problem on top.

That’s why emphasis is a highlighter stroke rather than bold type, and why a link draws a scribbled overline on hover. Working gestures, not finished ones. Twenty years of watching software get built, and now built with AI, has only sharpened the point: structure, principles and context matter more than ever.

01

Where the marks come from

Before this was a system it was a room. Post-its climbing a wall, marker on a whiteboard nobody was allowed to erase, journey maps taped up by whoever got in first. Twenty years of product work starts like that on every project — loud, physical, unfinished on purpose. The site keeps those marks: the volt highlighter behind a phrase, the red scribble a link draws on hover. Working gestures, not finished ones.

The other half is the opposite instinct. An architect’s drawing is precise because the building has to stand: every line lands on the grid, nothing strays. That’s the 40px module — the drawing underneath the mess. The cells that appear at random in the background aren’t the system breaking; they’re the system showing itself at unplanned moments. Still a grid. Still structure. Mess can turn up anywhere, as long as it lands on the lines.

That’s the argument of the whole site, and of design in the AI era. Speed makes mess, and mess is fine — productive, even — when the foundation under it doesn’t move. Structure first. Then push.

02

The grid

One 40px module runs the whole site. Every column, gap, rule and block of type is a whole number of it — which is what makes the layout repeatable by someone who has never seen it before, human or not.

1 COLUMN · 33 MODULES
ONE MODULE = 40 →
2 COLUMNS · 15 + 15
3 COLUMNS · 9 + 9 + 9
4 COLUMNS · 6 × 4
6 COLUMNS · 3 × 6
↑ GUTTER 120 = 3 MODULES
MODULE
40px. Every width, height and offset is a whole number of modules. Nothing lands between lines.
CONTAINER
.main-container — max-width 1480px, padding 0 60px, left-anchored. Drops to 40px padding at 991 and 20px at 479. At a 1440 viewport that leaves 1320px of content: 33 modules.
COLUMN CANON
1, 2, 3, 4 or 6. No other division exists here. Five columns would give 208px and seven 125.7px — neither is a whole module. A layout that seems to want five wants six with one left open.
GUTTER
40px, 80px or 120px — all whole modules. Which one is not taste. For N equal columns in a run of R modules with a gutter of g modules, the column is (R + g) / N − g, so N has to divide R + g exactly.
At the current container that arithmetic has a tidy answer: R = 33, g = 3 gives 36, and 36 divides by 1, 2, 3, 4 and 6 — the column canon, exactly. A 120px gutter serves every legal column count. This is also why an 80px gutter cannot hold three columns in a 27-module run, which is a real bug this site shipped.
FIELDS
Bleed off one edge and terminate on a column line. Symmetric insets read as a boxed panel, which is the habit this system rejects.
RULES
Horizontal rules land on document-absolute multiples of 40 — not merely 40 apart from each other. Alignment is measured against the page, not the parent.
03

Colour

Four working tones and two accents. Nothing is a one-off — every hairline and rule is the ink at reduced opacity.

Ink
#10151F
All type and rules. A blue-black, not pure black.
Paper
#F7F7F5
Page background. Warm, so it reads as paper rather than screen.
Mist
#E8EAEE
Cards and panels that sit back from the page.
Volt
#BFFF00
The highlighter. Used sparingly, or it stops meaning anything.
Signal Red
#E62D2D
One job only: the scribbled overline drawn under a link on hover.
04

Type

Three families, each with one job. Weight carries hierarchy before size does — a 500 at 15px outranks a 400 at 16px and costs less vertical space.

DISPLAY
MANROPE 700 / 800
Manrope
Headlines and card headings only. Never body copy — its tight tracking is drawn for large sizes and closes up badly under 20px.
BODY
INTER 300 – 700
Inter
Everything you actually read. Loaded at 300, 400, 500, 600 and 700; 400 and 500 do almost all the work.
LABEL
IBM PLEX MONO 400 / 500
IBM Plex Mono
Kickers, tokens, captions, dimension labels. Always tracked at 0.14em when it is a label. Its fallback is monospace — never sans-serif.

The scale

Eight roles. If a new piece of text does not fit one of them, the answer is almost always that it is one of them.

PAGE TITLE
.big-text
MANROPE 700 · 64 / 80
AI raises the ceiling.
CARD HEADING
.rl-ai-head
MANROPE 700 · 22 / 28
Built into your team, not around it.
SECTION HEADING
.ds-h2
INTER 500 · 24 / 32
Where the marks come from
LEDE
.ds-lede
INTER 400 · 18 / 28
Most portfolio sites are decorated. This one is constructed.
BODY
.measure-body
INTER 400 · 16 / 26
The baseline has moved — everyone ships fast now. What separates the work is knowing which problems are worth solving, and having the craft to make the answer feel obvious.
SUPPORTING
.ds-sub
INTER 400 · 16 / 24
Four working tones and two accents. Nothing is a one-off.
KICKER / LABEL
.ds-kick
MONO 500 · 12 / 24 · .14em
05   SPACING AND RHYTHM
CAPTION / TOKEN
.ds-mo-token
MONO 400 · 14 / 24
--dur-panel: 400ms
BASELINE
24px. It does not divide the 40px module — it re-synchronises every 120px, which is three modules and five lines. That mismatch is deliberate and is logged as a trade-off below.
MEASURE
45 to 75 characters, 66 the target. .measure-body caps at 640px. Running text on this site currently sits near the top of that range.
EMPHASIS
Weight, not size, and never italic for emphasis. One highlighter stroke per heading, on the payoff word.
05

Spacing and rhythm

Four values for small spacing and nothing else. Anything smaller than 8 is optical correction, not spacing — and should be justified as such.

8
20
40
80
120
OPTICAL
IN A ROW
ONE MODULE
BLOCK GAP
SECTION GAP
THE 24PX BASELINE AGAINST THE 40PX MODULE
SOLID 24 · DASHED 40
BOTH GRIDS AGREE EVERY 120 ↑
SUB-MODULE SCALE
8 / 20 / 40 / 80. Nothing between them. If a gap wants 12 or 16, it wants 8 or 20 and the surrounding block is the thing that is wrong.
SECTION PADDING
120px top and bottom on .section. Modifiers: .filled (Ink ground, Paper type), .no-top-padding, .no-padding.
BETWEEN SECTIONS
120px inside one container, carried by .ds-kick. Drops to 80px on small screens.
HEADING TO BODY
16px. Between body blocks, 28px. Both are optical, both are fixed.
BASELINE SYNC
Type sits on a 24px baseline and the layout on a 40px module. They agree every 120px — three modules, five lines. Every section gap is a multiple of that number for exactly this reason.
MOTION DISTANCE
Movement travels in modules too — 8, 20, 40, 80. A transform that moves 13px belongs to no grid and reads as drift.
06

Components

Live, not pictured — every specimen below is the real class doing its real job. Reuse these. Do not create a new class for something already here.

HIGHLIGHTER
.text-span.underline-highlight-2
People still set the bar.
A textured marker stroke, not a flat fill. This is the correct way to emphasise a word in a heading — one per heading, on the payoff. It is the site's signature and the thing most often got wrong. .hd-mark is the heading-tuned variant — a tighter band, sat lower on the baseline.
FLAT VOLT
.rl-claim-volt
Choosing what to build didn't.
Exists, but the highlighter is preferred. Use flat Volt only where the marker texture cannot read — small sizes, or over a busy ground.
MUTED CLAUSE
.rl-claim-muted
Building got cheap. Choosing what to build didn't.
Only ever a substantial clause. Greying single words — an "I", an "and" — reads as a mistake rather than as hierarchy.
INLINE LINK
.rl-inline-link
Read the context file before making a change.
Inherits its colour, 1px underline at Ink 35%, 3px offset, darkening to 90% on hover. Navigation links get the Signal Red scribble instead — drawn by script, never a border.
BUTTONS
.rl-btn · --primary / --secondary
Both exactly 40px tall — one module — with 20px horizontal padding. Primary is Ink with Paper type; secondary is Mist with Ink. Never more than one primary in a view.
SERVICE CHIP
.marquee-inner-box
B2B SaaS Product DesignAI Product DesignDesign Systems at Scale
Mist ground, 40px tall, 0 20px padding, Volt on hover. Same metrics as a button so a row of chips and a row of buttons share a baseline.
NUMBER CHIP
.ds-num
01Every width, height and offset is a whole number of 40px modules.
The only place a Volt fill sits behind small type. Volt is rationed: if more than roughly one element in six on a screen is Volt, it has stopped being emphasis and become pattern.
CALLOUT
.ds-md + .ds-mdmark
A 24px Volt square, a hairline above and below, and the point. Used once or twice a page at most.
LABEL ROW
.ds-trrow + .ds-trk + .ds-trv
A 280px label column
and a value beside it. This row, the trade-off rows and the specimen rows on this page are all the same shape — one pattern, reused, rather than three that nearly match.
07

Imagery

There is no photography on this site and that is a decision, not an omission. Rendered graphics and stock imagery signal effort without carrying information — decoration standing in for thinking. Everything here is either built on the grid or is a real artefact of real work.

Blog thumbnail built from flat 80px squares with one row in Volt
Article graphic
Flat squares on an 80px grid. One shape in Volt marks the article's argument.
Blog thumbnail of horizontal bars with one bar in Volt
Same system, different argument
Composition changes; unit size, palette and accent logic never do.
Client wordmark on a Mist ground
Open Graph card
1200 × 630. The one place the mark sits on a Volt ground.
ARTICLE GRAPHICS
CANVAS
Main image 2400 × 1280. Thumbnail 800 × 800. Grid pitch is 80px on both — do not scale the unit down to fit more detail into the thumbnail; it breaks the shared visual scale with every other post.
ARTICLE GRAPHICS
CONSTRUCTION
Flat solid rectangles only. No gradients, no shadows, no text, no icons. Every shape unit is exactly one 80px cell, flush inside the cell rather than centred on a grid line. Leave a 1px inset so a hairline of grid shows between adjacent same-colour squares — 6px reads as shrunken, 0px reads as one undifferentiated blob.
Position with integer column arithmetic (left_col = centre_col − i), never centre_x − width/2. Continuous maths silently offsets odd-width rows by half a cell — invisible by eye, obvious the moment you check that every edge lands on a multiple of 80.
ARTICLE GRAPHICS
COLOUR
Ground #F7F7F5, grid line #E0E1E0, shapes Ink. Exactly one shape or row in Volt per graphic — the accent marks the key concept. Mist only if a composition genuinely needs a third tone.
CASE STUDY IMAGES
Real product screens, never mockups on a floating device. 7:5, Mist placeholder ground, a 1px Ink 8% hairline instead of a shadow, and a zoom cursor. They sit at 55% opacity until hovered, so the page reads as type first.
CLIENT MARKS
WebP, supplied artwork, on Mist with a 40px inset. Never recoloured, never cropped to a circle, never given equal optical weight to the work itself.
TEXTURES
Two, and only two: the Volt highlighter stroke and the Signal Red scribble. Both are scans of real marker on paper, which is the entire point — they are the one place the system lets a human hand show.
FORMAT
WebP for photographic and raster artwork, SVG for marks and diagrams, PNG only where an article graphic needs exact flat colour. The Open Graph image is 1200 × 630 and is the one place the logo appears on a Volt ground.
NEVER
Stock photography. Gradients. Drop shadows. 3D renders. Device frames. Abstract AI-generated imagery. Faces that are not mine. Any image whose job is to fill space rather than carry information.
08

The rules

Five. Short enough to hold in your head, which is the only kind that get followed.

01
Every width, height and offset is a whole number of 40px modules. Nothing lands between lines.
02
Columns come from 1, 2, 3, 4 or 6. No other division exists here.
03
Fields bleed off one edge and stop on a column line. Never boxed in on all four sides.
04
Running text stays between 45 and 75 characters a line, with 66 as the target.
05
Type sits on a 24px baseline, re-syncing with the module every 120px.

The system is a file, not a PDF

All of the above lives in one file — the tokens with their variable IDs, the module, the column canon, the measure, and a log of the places where this site currently breaks its own rules. It's the context an AI agent is handed before it touches anything here, so constraints arrive as constraints rather than preferences. That's why the tenth screen matches the first. The drift log is the honest half: a system that only records its intentions is marketing.

Read the file →
09

Motion

One curve, three durations, one rule.

--ease: cubic-bezier(.22, 1, .36, 1)
One curve everywhere. Things settle; they don’t stop.
--dur-micro: 240ms
Hover, focus, chip states.
--dur-panel: 400ms
Drawers, reveals, anything that opens.
--dur-page: 520ms
Page-level moves.

The rule: motion moves in modules. Nothing travels a distance that isn’t made of 40px — 8, 20, 40, 80 — which is what makes movement feel like it belongs to this grid rather than easing applied on top of one. Where the site still moves in an older dialect, that’s drift, and it’s logged.

10

Responsive

Six breakpoints, but only three do real work. Every grid needs an explicit collapse — a grid with no breakpoint variant will hold its desktop column count on a phone, which has bitten this site before.

≥ 1600
Container caps at 1480px. Content locks at 1360px — 34 modules — and stops growing. Everything above this width is margin.
1440
The design width. 1320px of content, 33 modules, 60px padding. This is the width every figure on this page is drawn at, and the only one where the whole canon divides cleanly.
1280 – 1439
Fluid. Content is whatever the viewport leaves, so it is a whole number of modules at some widths and not at others. Left edges stay on the grid because the grid is anchored to the content edge; right edges drift. Accepted.
≤ 991
Container padding drops to 40px. Multi-column grids collapse — two-ups to one, three-ups to two or one. The mobile drawer takes over from the nav bar.
≤ 767
Three-ups that survived 991 collapse here. The background margin rule and the random grid pockets switch off — at this width they are noise rather than structure.
≤ 479
Container padding drops to 20px. Display type steps 64 → 40 → 32. Nothing else changes: the module does not shrink, the page just holds fewer of them.
THE EXCEPTION
Mobile drawer rows are 64px, not 40 or 80. A touch target beats the module here, and it is the only place on the site where that is true.
11

Voice

The writing is part of the system. Same instinct as the grid: no adjective does work a fact could do.

PERSON
First person, present tense. "I work the whole arc", not "Reloop delivers end-to-end solutions". There is one person here and the writing should not pretend otherwise.
CLAIMS
Specific and falsifiable. "$6.99M ARR and 815 new doctor users" beats "drove significant growth" — and can be checked, which is the point.
HEADLINE PATTERN
Building got cheap.
Choosing what to build didn't.
A flat statement, then the turn. The highlighter lands on the turn, never on the setup.
PUNCTUATION
Em-dashes for asides. Sentences may be short. Fragments are allowed. Semicolons almost never — they slow a line down without earning it.
NEVER
guide · leader · transitioning · passionate about · leverage · solutions
Removed deliberately from positioning copy. They describe a posture rather than a capability, and a reader who has seen twenty portfolios has seen all six on every one of them.
AI, WHEN WRITING ABOUT IT
Never "working with AI" as a credential — it reads as vibe-coding. The claim is about judgement and delivery inside an existing team, not about tool access.
12

Known trade-offs

A system that only documents its intentions is marketing. These are the places where something else won, and why.

Readability beat arithmetic
The 24px baseline doesn't divide the 40px module. A 20px baseline would, but at 17px body copy the lines collide. The cost is a three-module sync instead of one.
The measure runs long
Body text sits near 75 characters — the top of the range rather than the 66 optimum. Narrowing the column would fix it and shorten the page.
Cards tighten before the grid breaks
In three columns, card text drops to about 34 characters on narrower screens — under the 45 minimum. The grid should fall to two columns sooner.
The mobile drawer keeps its own rhythm
Nav and footer were aligned to the module in August 2026 — 40px link boxes, gaps and paddings on the 8/20/40/80 scale, footer sections on whole modules. The mobile drawer’s 64px rows are the one deliberate hold-out: a touch-target choice, off-module and logged.
Tell me what you’re building. I’ll tell you if I can help.
Email me →