Design languageLayout and shape

Design language Foundations

Layout and shape

One column, a few section rhythms and a hairline where other systems reach for a box. This page sets out the container, spacing, rules, tones, splits and shapes the website uses. Every value is read from the file that defines it.

One container.

#

Every page sits in one column, the wrap utility of the site stylesheet. It centres the content, stops growing at 1200px and declares a 28px gutter on each side, so the widest line of content is 1144px. There is no second, narrower container and no column grid inside it.

The wrap utility at a 1440px and a 390px window, drawn to the same scale from the values in the site stylesheet.
Content width by window width
WindowwrapContent
320px320px264px
390px390px334px
900px900px844px
1280px1200px1144px
1440px1200px1144px

Content width is the wrap width less two gutters. Below 1200px the wrap fills the window; above it, the side margins grow and the column stays where it is.

Keeping the gutter

wrap is a Tailwind utility, so it sits in the utilities layer, and a component's own styles win over it. An element that carries wrap therefore sets only its block padding, with padding-block. A padding shorthand would also set the inline padding and remove the gutter.

No element in use sets the shorthand, so every wrap keeps its gutter at every window width.

Reading width

Narrower measures are set inside components, mostly in ch. Research articles and legal documents use one column of at most 68ch, centred in the container. From 1180px upwards a 220px contents rail joins it on the right, 56px away.

Full-bleed scenes

Three pinned scenes leave the container on purpose: the home hero and the two scroll scenes, results on the home page and decision on the Zero Door page. The hero's stage fills the window while its text stays in wrap. In the other two the stage runs to the right edge of the window, and the text column sets its own left padding, max(28px, calc((100vw - 1200px) / 2 + 28px)). Up to 1200px that is the gutter; on wider windows it is the side margin of wrap plus the gutter. So at every window width the text starts on the same column edge as the rest of the page.

Under the sticky header

The header stays at the top while the page scrolls: a 66px row over a 1px hair line. Every element that can take keyboard focus keeps 84px of scroll margin above it, and the Section component keeps 80px for links that jump to it, so neither lands under the header.

  • The wrap utility is at most 1200px wide and declares a 28px gutter on each side.

    WhyOne column edge lines up the header, every section and the footer. The test reads the utility from the site stylesheet and fails if a value changes or a media query there alters it.

    TestEnforced by a test on every change.

  • An element that carries wrap sets its block padding only, with padding-block.

    WhyA component's own padding wins over the utilities layer, so a padding shorthand removes the gutter. The test finds every style that sets inline padding on an element with wrap and fails on any that is not in its list of known exceptions. That list is empty today.

    TestEnforced by a test on every change.

  • Keyboard focus is not hidden under the sticky header.

    WhyWCAG 2.2 success criterion 2.4.11. Every focusable element keeps 84px of scroll margin, more than the header is tall. The tests tab forwards and backwards through /, /zero-door and /tr/contact, in the desktop window and again in a desktop window narrowed to 390px, and fail if a focused element is covered completely.

    TestEnforced by a test on every change.

  • There is one container. Narrower measures are set inside components, mostly in ch.

    WhyA second container would add a second column edge. A measure in ch follows the type size, so a line stays readable at any width.

    ReviewKept by review, not by a test.

Sections set the rhythm.

#

The website has no spacing scale. Vertical space is set by the components a page is built from, and each one carries its own padding. An inner-page section has 72px above and below at every window width.

Four bands stacked as an inner page stacks them, at real size: PageHero, a Section on paper, a Section on paper-2 and the closing CtaBand2. The brackets show each padding, read from the component.
Vertical padding of page-level bands
ComponentTop and bottom900px and below
Section72px / 72pxsame
CtaBand272px / 72pxsame
CtaBand72px / 72pxsame
PageHero72px / 40pxsame
TrustHero64px / 56px40px / 32px
ResearchHero64px / 56px40px / 32px
ServicesHero64px / 56px40px / 32px
DoorHero64px / 56px40px / 32px
CompanyHero64px / 56px40px / 32px
Principles96px / 96pxsame
Footer64px / 32pxsame
Article72px / 32pxsame
LegalDoc72px / 32pxsame

Every value in this table is a multiple of 8: 32px, 40px, 56px, 64px, 72px and 96px. The build and a unit test check this, so a change to any band padding is reviewed with this page.

Rows inside a section

Alternating feature rows are spaced by their own padding and a hair line between them: 48px above and below on the Zero Door page and 56px on the Services page, tightening to 36px and 40px at 900px and below.

Inside components

Within a component, spacing is written as literal pixel values. Of the 810 non-zero values written for padding, margin and gap in the production components, 772 are even, 471 are multiples of 4 and 268 are multiples of 8. The most used are 10px, 12px, 14px, 28px and 8px. They do not sit on a 4 or 8 pixel grid, and this page does not present one as if they did. The Zero Door interface does have a spacing scale; it is shown at the end of this page.

  • Build inner pages from Section and close them with CtaBand2.

    WhyBoth carry the same 72px padding, a hair line on top and an 80px scroll margin, so every inner page keeps the same rhythm and its anchors land in the same place.

    ReviewKept by review, not by a test.

  • Keep page-level padding on multiples of 8.

    WhyEvery band in the table above follows it. It keeps the large rhythm regular while components stay free to use finer steps. The test reads each band from its component.

    TestEnforced by a test on every change.

Hairlines carry the structure.

#

Where other systems put each item in a box, the website draws lines. A structured set opens with a 1px rule in ink, its items are divided by hair lines of the same width, and numbered items carry their index in Geist Mono. On this page a hairline is any 1px line, and a hair line is one drawn in --color-hair.

  1. A 1px rule in ink opens the set, once, at the top. It marks where a group begins.

  2. Hair lines of the same width divide the items. They are lighter than the text they separate.

  3. The index is set in Geist Mono at 11px in accent-deep, padded to two digits.

Ruled columns, built with the markup and styles of the footprint list on the Architecture page. At 900px and below the columns stack and the side lines become top lines.

Ruled rows

Ink rule
Opens a structured set: ruled columns, ruled rows, numbered lists and glossary terms.
Hair line
Divides items, opens every full-width band and closes every hero.
Hair-2 line
Draws the border of a control, such as the motion pause button, and of cards inside illustrations.
Accent stroke
Marks the current page in the header and a quotation in an article, 2px wide.

Hair list

  • A hair line on top marks a lighter list.
  • Each item closes with a hair line of its own.
  • There is no index and no ink, so the list stays quiet.
Above, ruled rows: an ink rule on top and a hair line under each row, as in the definition lists on the Architecture and Pricing pages. Below, a lighter list that opens with a hair line instead, as on the Pricing and Solution pages.

In use: which structure holds which content

Ruled columns
Three parallel points of equal weight, read side by side.
Ruled rows
A term and its definition, or a label and its value.
Hair list
Short unnumbered items that support the text around them.

Lines in use

Ink rule
border-top: 1px solid var(--color-ink)
Hair line
border-top: 1px solid var(--color-hair)
Hair-2 line
border: 1px solid var(--color-hair-2)
Accent stroke
border-left: 2px solid var(--color-accent)
DoOpen the set with one ink rule and divide the items with hair lines.
Do notDo not repeat the ink rule between items or close the set with it.
  • A 1px ink line that structures content is only ever the top border of a set.

    WhyThe ink rule says where a group starts. Between items it would turn a list into a ledger and lose that meaning. The test reads every component style on the site and on this page and fails on an ink border on any other side, except on the ink button (a control), the frame of an open rule on the home page (a box) and the Do not example above.

    TestEnforced by a test on every change.

  • Every full-width band opens with a hair line, and every hero closes with one.

    WhyA single line and a change of tone mark the change of band, so no band needs a box or a shadow.

    ReviewKept by review, not by a test.

Two papers and a tint.

#

Sections sit on paper or on paper-2, and a page usually alternates between the two so that each section reads as its own band. As a band background, the accent tint is kept for one place: the band that closes a page with a call to action.

The Architecture page in order, read from its source: 7 sections alternate between paper and paper-2 and the page closes on accent-tint.
  • --color-paper#fdfaf6The page itself and the default Section.
  • --color-paper-2#f6f3eeSection with tone paper-2, the footer and illustration frames.
  • --color-accent-tint#d9f5f3The background of closing call-to-action bands only.
  • --color-ink#0f1c26Dark bands and panels: the proof strip on the Zero Door page, code and file panels.
Text on each tone, contrast computed from the tokens
Text colourpaperpaper-2accent-tint
ink16.61:1 AAA15.62:1 AAA15.07:1 AAA
ink-muted6.85:1 AA6.44:1 AA6.21:1 AA
ink-subtle4.89:1 AA4.60:1 AA4.44:1 fail
accent-deep6.65:1 AA6.26:1 AA6.04:1 AA

Where each tone is set

paper
body { background: var(--color-paper) }
paper-2
.sec.paper-2 { background: var(--color-paper-2) }
accent-tint
.cta { background: var(--color-accent-tint) }
accent-tint
.band { background: var(--color-accent-tint) }
ink
.proof { background: var(--color-ink) }

ink-subtle on accent-tint stays below 4.5:1. The closing bands therefore set their text in ink, ink-muted and accent-deep.

Alternation is a habit.

In use, the Architecture, Pricing and Solution pages alternate strictly, while the Company, Trust centre and Zero Door pages run two sections of the same tone in a row. Nothing in the code enforces alternation, so this page does not call it a rule.

  • Use accent-tint as a band background only for the closing call to action.

    WhyThe accent means verified signal. As a band it appears once on a page, where the page asks the reader to act. The test fails if any other band sets accent-tint as its background; tags and rows inside illustrations may still use it.

    TestEnforced by a test on every change.

Uneven splits, no column grid.

#

The website has no column grid. Text-and-visual splits are written in each component as uneven fr ratios, so one side, usually the text, is visibly narrower than the other. Most of the work is done by four ratios.

Split heroes5fr 6frgap 56px

text5fr494.5pxvisual6fr593.5px

Trust centre, Research and Company heroes; Careers

Feature rows5fr 7frgap 56px

text5fr453.3pxvisual7fr634.7px

Feature rows on Zero Door and Services

Flipped rows7fr 5frgap 56px

visual7fr634.7pxtext5fr453.3px

Every second feature row

Definition rowsminmax(0, 4fr) minmax(0, 8fr)gap 40px

term4fr368pxdefinition8fr736px

Definition lists on Architecture and Pricing

Each bar is a real CSS grid with the ratio and column gap from its source file. The figures are the widths each column gets in the full 1144px column.
Two-column ratios in use
RatioColumn gapWidths at 1144px
5fr 6fr56px494.5px / 593.5px
5fr 7fr56px453.3px / 634.7px
7fr 5fr56px634.7px / 453.3px
4fr 8fr40px368px / 736px

Text first on narrow screens

At 900px and below every split becomes one column, usually with a 28px gap. A flipped row moves its text to the second column on wide screens with order: 2 and resets it to 0 at the breakpoint, so on a phone the text always comes before the visual.

The split heroes and feature rows keep bare fr tracks and set min-width: 0 on their visual column, so a wide illustration cannot widen its track. A new split can write each track as minmax(0, 5fr), as the definition rows do, and needs neither.

Equal columns and card grids

Sets of three use three equal tracks; on the Architecture page they are written repeat(3, minmax(0, 1fr)), so they may shrink. A few pairs, such as the paired cards on the Architecture page, use two equal tracks. Most card collections size themselves instead: an auto-fit track with a minimum width, so the number of columns follows the space available.

  • Write a text-and-visual split as an uneven fr ratio inside the container.

    WhyThere is no grid to snap to. The ratio is chosen per layout, and the uneven split keeps the text at a readable measure beside a larger visual. Give the visual column min-width: 0, or write the tracks with minmax(0, …), so long content cannot widen it.

    ReviewKept by review, not by a test.

  • On narrow screens the text comes before the visual.

    WhyOn narrow screens the visual order returns to source order, so reading and focus order follow what the reader sees (WCAG 1.3.2 and 2.4.3). On wide screens only the column position changes, not the meaning.

    ReviewKept by review, not by a test.

One main breakpoint.

#

The website is designed desktop first. Components that change their layout mostly do it with max-width queries, and of the 46 production component and style files that have a width query, 40 use the same one: 900px. A few components add a breakpoint of their own. There is no breakpoint token; each value is written in the component that uses it.

  • max-width: 1199.98px

    The four rules on the home page fold into an accordion; on wider windows they stand side by side as frames that widen to open.

  • min-width: 1180px

    Articles and legal documents gain a contents rail beside the text.

  • max-width: 1100px

    Glossary letters stop hanging in the margin.

  • max-width: 1000px

    The four integration columns on Architecture become two.

  • max-width: 900pxmain

    The header folds into a menu, splits and ruled columns become one column, heroes tighten.

    40 of 46 files

  • max-width: 860px

    Glossary rows stack.

  • max-width: 760px

    The architecture flow diagram scrolls inside its own frame.

  • max-width: 699.98px

    The rules accordion keeps one column, and each description runs under its glyph; wider windows get two columns.

  • max-width: 620px

    The integration columns become one.

Where each width query applies, between a 320px and a 1440px window. Each row is checked against the files named under it when the site is built. The count covers the components the production site renders; it leaves out this documentation section and the hero variants the home page does not use.

What changes at 900px

  • The header link row and its right-hand cluster hide, and a menu button opens a panel with the same links.
  • Two-column splits become one column, text first.
  • Ruled columns stack, and their side lines become top lines.
  • Split heroes tighten from 64px / 56px to 40px / 32px above and below.
  • The footer grid folds into two columns.
  • Design for the desktop first and switch at max-width: 900px.

    WhyOne shared breakpoint means the header, the splits and the ruled sets change together, not one by one as the window narrows. The test fails if 900px stops being the query most files use.

    TestEnforced by a test on every change.

  • Write a breakpoint in the component that needs it, as a literal.

    WhyThere is no breakpoint token to reach for, so the value lives with the layout it changes and can be read in one place.

    ReviewKept by review, not by a test.

No page scrolls sideways at 320px.

#

Every page has to reflow at 320 CSS pixels, the width WCAG 2.2 uses for success criterion 1.4.10, without the page scrolling sideways. A test loads every built page at that width and fails if the document is wider than the window.

How layouts stay inside 320px

minmax(0, 1fr) tracks
Stops a long word, hash or path from widening its column past the window.
min-width: 0 on visual columns
Stops an illustration from forcing its grid cell wider than the track.
Focusable scroll boxes
Keeps wide tables, code and diagrams from pushing the page sideways. They scroll inside their own box, and the box takes keyboard focus so it can be scrolled without a mouse.
overflow-wrap on prose and code
Lets text without a break opportunity wrap instead of running out of its column.
auto-fit tracks with a minimum width
Lets card grids drop from three columns to one on a phone.

Every table on this page sits in such a box. At 320px it scrolls on its own while the page stays still.

DoLet the track shrink below its content: minmax(0, 1fr), or min-width: 0 on the item. The hash then wraps inside the column.
Do notDo not let a bare 1fr track hold a long word. Its minimum is the longest word, so it pushes past the column, and at 320px the page would scroll sideways.
  • No page scrolls sideways at 320 CSS px.

    WhyWCAG 2.2 success criterion 1.4.10, Reflow. The test walks every page in the build, so a new page is covered the day it is added.

    TestEnforced by a test on every change.

Two radii and square rules.

#

Shape is restrained. There are two radius tokens: 6px for controls and overlay tags, 8px for cards and illustration frames. Ruled structures, section bands and the frames of the rules on the home page keep square corners: they are lines, not boxes.

  • Button--radius-sm6px
  • Ghost button--radius-sm6px
  • Overlay tag--radius-sm6px
  • Card--radius-md8px
  • Illustration frame--radius-md8px
  • Pause buttonliteral50 percent
  • Mono tagliteral4px
  • Search chipliteral999px
  • Ruled setnone0
Each shape drawn with the radius read from its source: the real button classes from the site stylesheet, and stand-ins for an overlay tag, a card, an illustration frame, the common literal shapes and a ruled set.

Radius in use

--radius-sm6px
Buttons, the skip link, form fields, the menu button and its links, overlay tags and stamps; also article code blocks and images, the legal banner and some finding cards
--radius-md8px
Cards, illustration frames, the menu panel and ink code panels
circle50 percent · 22 uses
The motion pause button, step discs, map nodes
small tag4px · 10 uses
Small mono tags, badges, severity labels and pips
3px4 uses
Not one of the common shapes
2px2 uses
Not one of the common shapes
12px2 uses
Not one of the common shapes
pill999px · 2 uses
Search chips and the event pill in the Zero Door illustrations
8px1 use
Not one of the common shapes
10px1 use
Not one of the common shapes
calc(8 * var(--u))1 use
Not one of the common shapes
square0
Ruled sets, section bands, the frames of the home page rules

Outside the two tokens, radii are written as literals. Circles and 4px are the common ones, and 999px rounds two pill shapes. The rows above are read from every production component, so a new literal value appears here and fails a unit test until it is reviewed. The rest appear in only a few places: 3px, 2px, 12px, 8px, 10px and calc(8 * var(--u)).

  • The two radius tokens are 6px and 8px.

    WhyThere is no larger token. The test reads the token file and fails if a value changes or a third radius token appears.

    TestEnforced by a test on every change.

  • Use radius-sm for controls and overlay tags, radius-md for cards and frames.

    WhyIn use, controls and floating tags mostly take radius-sm and cards and frames radius-md, so a corner often tells a control from a container. The split is not strict: article code blocks, the legal banner and some finding cards take radius-sm.

    ReviewKept by review, not by a test.

  • Keep ruled structures square.

    WhyA ruled set is made of lines. Rounding its corners would turn it back into a box.

    ReviewKept by review, not by a test.

One shadow, for things that float.

#

There is one shadow token and no elevation scale. The shadow is used only on objects that float over a page or an illustration: the menu panel, overlay tags and stamps, chips and floating cards inside visuals. Cards in the flow of the page have a border and no shadow.

--shadow-floatThe one shadow token, as the token file declares it
--shadow-float: 0 12px 30px -18px rgb(15 28 38 / 0.45);

The shadow is cast in the ink colour, --color-ink #0f1c26, with an alpha of 0.45, not in black.

Both radius tokens and the shadow live in the site's token file, which can be downloaded whole as tokens.css.

An illustration frame with an ink stamp and a floating card above it, both with the shadow token, next to a card in the flow of the page with none.

Every use of the shadow in production components

The menu panel on narrow screens
.menu .panel
Overlay tags on illustrations
.tag
Evidence stamp
.stamp
Floating cards inside illustrations
.card
Search bar inside an illustration
.bar
Chips inside scroll scenes and illustrations
.chip, .chip.bare
  • Only floating objects cast --shadow-float.

    WhyA shadow says that something sits above the page. In-flow cards are separated by a hair border and by tone instead. The test lists every use of the token in production components and fails on a new one, so each new use is checked against this rule.

    TestEnforced by a test on every change.

Spacing and shape in Zero Door

#

The Zero Door interface speaks the calm register of the same language, and it does what the website does not: it defines a spacing scale. The token values below come from the product token file as of 2026-08-23.

The product values in this section come from a snapshot of the product's tokens and components, published as product-tokens.json.

Spacing scale

The scale has 12 steps from 2px to 64px. Each bar is drawn at the real width of its step. In the code the steps feed one variable, the row padding shown under Density; components space themselves with Tailwind utility classes that hold the same values.

  • --space-12px
  • --space-24px
  • --space-36px
  • --space-48px
  • --space-512px
  • --space-616px
  • --space-720px
  • --space-824px
  • --space-932px
  • --space-1040px
  • --space-1148px
  • --space-1264px

Shell

The frame around every screen is written in its components, as Tailwind classes or a component default, not as tokens. The values below are read from those components as of 2026-10-09. The content gutter is 24px, against 28px on the website.

The Zero Door shell

Content gutter24px
Page headers, pagination and the outer cells of tables align to it.In codepx-6
Top bar56px
Height of the bar along the top, which holds the search button for the command palette and the user menu.In codeh-14
Sidebar240px
Open width of the navigation sidebar.In codew-60
Rail56px
The sidebar collapsed to a rail of icons.In codew-14
Side panel480px
Default width of the side panel, capped at the window width.In codewidth = 480
Command palette576px
Maximum width of the command palette.In codemax-w-xl

Radius

The product has three radius tokens: 4px for inputs, badges, tags, tooltips and small buttons; 8px for medium and large buttons, menus and empty states; 12px for the largest surfaces, such as the command palette.

  • --radius-sm4px
  • --radius-md8px
  • --radius-lg12px

Shadow

The product sets two shadow tokens per theme. The flat one marks a selected item, such as the active row in the sidebar; the raised one sits under overlays: dialogs, side panels, drawers, menus, tooltips and the command palette. Both tokens are cast in black; the website casts its single shadow in ink.

Light theme

--shadow-flat0 1px 2px rgb(0 0 0 / 0.04)
--shadow-raised0 8px 24px rgb(0 0 0 / 0.08)

Dark theme

--shadow-flat0 1px 2px rgb(0 0 0 / 0.20)
--shadow-raised0 8px 24px rgb(0 0 0 / 0.40)

Density

The product offers two density modes. Compact changes one thing in the code: the vertical padding of table rows drops from 12px to 6px. Comfortable is the default, and a control in the user menu switches between them.

Comfortable --space-5

  • Row 1padding 12px
  • Row 2padding 12px
  • Row 3padding 12px

Compact --space-3

  • Row 1padding 6px
  • Row 2padding 6px
  • Row 3padding 6px

Shared with the website: radius-md is 8px in both. Different: the small radius is 4px in the product and 6px on the website, and the product adds 12px for its largest surfaces.