Two self-hosted typefaces set the text of this site: New Black for language and Geist Mono for what machines write. Sizes live in the components that use them, so this page copies each value from its source file and a test keeps the copy true.
New Black sets every word written for people: headlines, body text, buttons and navigation. Geist Mono has two jobs: it sets what a machine writes or reads back, such as hashes, paths, dates, versions and code, and it sets the small labels and notes that organise a page. No other typeface is downloaded.
Aa
Language
New Black
Headlines, body text, buttons and navigation.
Ç ç Ğ ğ İ ı Ö ö Ş ş Ü ü 0123456789
Weights
400, 500, 600, 700
Glyphs
311 in each weight
Files
10956 to 11048 bytes per weight, WOFF2
Figures
Proportional. At weight 400, on a 1000-unit em, a 1 is 291 units wide and a 0 is 640.
Fixed. On a 1000-unit em, every digit and letter is 600 units wide.
Licence
SIL Open Font License 1.1, from Basement.studio and Vercel.
In the file
Copyright 2024 The Geist Project Authors (https://github.com/vercel/geist-font.git)
How they load.
01
From this site, through Astro
Both families are files in this repository, registered with the font API of Astro through its local provider. Nothing is fetched from a font service, and the content security policy accepts fonts from this site only.
Font registration, as written
const nb = (w) => ({ weight: w, style: 'normal', src: [`./src/assets/fonts/NewBlack-${w}.woff2`] });
const gm = (w) => ({ weight: w, style: 'normal', src: [`./src/assets/fonts/GeistMono-${w}.woff2`] });
fonts: [
{
provider: fontProviders.local(),
name: 'New Black',
cssVariable: '--font-newblack',
options: { variants: [nb(400), nb(500), nb(600), nb(700)] },
},
{
provider: fontProviders.local(),
name: 'Geist Mono',
cssVariable: '--font-geistmono',
// Varsayilan yedek sans-serif: uretilen degisken Arial ve sans-serif
// ile bitiyor, tokenin ui-monospace / monospace yedekleri hic
// erisilmiyordu. Dosya gelmezse metin yine esit genislikli dizilir.
fallbacks: ['ui-monospace', 'monospace'],
// optional: ilk boyamaya yetismeyen dosya sonradan yerine konmaz, bu
// yuzden Geist Mono duzeni hic kaydiramaz. Olcuye gore uretilen yedek
// yuz Courier New'u local() ile ister; macOS ve Windows'ta var, Linux
// ve Android'de yok. Orada ilk ekrani genel monospace yuz dizer, swap
// ile Geist Mono gelince ana sayfa girisi kayardi: CI'daki mobil
// Lighthouse denetimi bu kaymayi 0.23 olctu. Iki agirlik da head'de
// onden yuklenir; dosya normalde ilk boyamaya yetisir.
display: 'optional',
options: { variants: [gm(400), gm(500)] },
},
],
02
One variable per family
Astro exposes each family as a CSS variable and the design tokens wrap it, so components read the token, never the family name. Each variable Astro writes already ends in fallback faces sized to match and a generic family: sans-serif for New Black and, as its registration asks, ui-monospace and monospace for Geist Mono. If Geist Mono is not ready when the page is first drawn, or fails to load, machine text is still set in a face of fixed width. The names the tokens list after the variable are never reached.
New Black 400 and 700 are preloaded: they set the body and the headline of the first screen. The 500 and 600 weights are on that screen too, in links and buttons, but holding them back by 1500 milliseconds changed layout shift by only 0.002, so they load when first used. Geist Mono 400 and 500 are preloaded too, so they are normally ready when the page is first drawn; the next step says why that matters.
For every weight of both families Astro generates a fallback face from a local system font, scaled to the metrics of the real face, so text appears at once. New Black's @font-face rules declare font-display: swap: the real file replaces the fallback when it arrives, and the measurement in the previous step shows how little that moves. The fallback of Geist Mono asks for Courier New, which Linux and Android do not have. There the generic monospace face set the first screen, and with font-display: swap the arrival of Geist Mono caused a layout shift of 0.23 in the mobile Lighthouse audit of the home page. The rules of Geist Mono therefore declare font-display: optional: a file that is not ready when the page is first drawn is never swapped in, and that page keeps the fallback.
New Black 400 and 700 also exist as TTF files. They are read only while the site is built, to draw the share image of each page, and browsers never download them. The share cards are covered on the Logo and name page.
Fonts come from this site and nowhere else.
WhyApart from the bot check on the contact form, the site loads nothing from other hosts, and a font served from elsewhere would add a request to every page. The test reads every built page and stylesheet: none links a font host, no @font-face points elsewhere and every content security policy accepts fonts from this site only.
TestEnforced by a test on every change.
Every shipped font file is a real WOFF2 of a sensible size.
WhyA truncated file, or one that was never cut down, fails quietly in the browser. The test checks the signature and the size of all 6 files.
TestEnforced by a test on every change.
Only New Black 400 and 700 and Geist Mono 400 and 500 are preloaded.
WhyThe New Black files cover the body and the headline. Its other weights swap in behind fallbacks sized to match, so preloading them would cost an early request and save almost no movement. Geist Mono declares font-display: optional, so a late file is not used at all on that page; preloading both of its weights makes them ready in time on a normal connection.
TestEnforced by a test on every change.
Components name the token, never the family.
WhyComponent styles read the two font tokens, so changing a face is one edit. The family names appear only where the fonts are registered and in the code that draws the share images. The test reads every font-family declaration, and every font shorthand, in the style blocks of the site.
New Black ships in weights 400, 500, 600 and 700 and Geist Mono in 400 and 500. Each weight has a job on the site. The lines below are sentences from this site, set in the weight the site uses for them.
700New Black
From zero trust to one verdict.
Headlines: page and section titles and large figures.
600New Black
Every signal, one verdict
Subheads, card titles, terms in definition lists and buttons.
500New Black
Read the research
Text links, among them the proof link under each rule on the home page.
400New Black
Zero Door weighs every binary, library and package on your servers against the original, and backs each verdict with evidence your auditor can check offline.
Body text, leads and form fields.
500Geist Mono
Product · Solutions · Research · Services · Company · Trust · Legal
Column headings in the footer and group headings in the site map.
400Geist Mono
No third-party trackers on this site.
Labels, dates, document details, code and notes.
Each family is shown at one size so its weights compare; the scale below gives the sizes in use.
Font files as shipped
Font files as shipped
Family
Weight
Bytes
Preloaded
New Black
400
11016
Yes
New Black
500
10984
No
New Black
600
10956
No
New Black
700
11048
Yes
Geist Mono
400
14224
Yes
Geist Mono
500
14408
Yes
Emphasis
Neither family ships an italic. The site does not style strong text in long-form copy: the browser sets it one step bolder, which New Black draws at 700. Some imported research articles still contain italic, and with no italic file the browser slants New Black itself. That slant is accepted for now; no rule asks for it or against it.
Type sizes are not tokens. Each component declares its own size, line height and tracking in its scoped style block. These are the main roles in use, copied from those blocks; a unit test fails the moment a value here stops matching its file. Smaller labels, from badges to the text inside diagrams, sit below this scale and are not listed.
Home headline
Size
clamp(40px, 5.6vw, 74px)
Line height
1
Tracking
-0.035em
Weight
700
Measure
12ch
Wrap
balance
From zero trust to one verdict.
Page headline
Size
clamp(36px, 4.6vw, 60px)
Line height
1.02
Tracking
-0.03em
Weight
700
Measure
16ch
Wrap
balance
Where it sits, what it sends, who it talks to.
Article headline
Size
clamp(32px, 4.2vw, 52px)
Line height
1.05
Tracking
-0.03em
Weight
700
Measure
26ch
Wrap
balance
Attacking kiosk devices: red team research
Statement headline
Size
clamp(30px, 3.6vw, 46px)
Line height
1.04
Tracking
-0.03em
Weight
700
Wrap
balance
Prove it on your own servers.
Section headline
Size
clamp(28px, 3.2vw, 40px)
Line height
1.05
Tracking
-0.03em
Weight
700
Measure
22ch
Wrap
balance
One agent out, nothing in.
Feature title
Size
clamp(24px, 2.4vw, 32px)
Line height
1.08
Tracking
-0.025em
Weight
700
Wrap
balance
The first question: is it the original?
Card title
Size
20px
Line height
1.15
Tracking
-0.02em
Weight
600
Every signal, one verdict
Page lead
Size
18px
Line height
1.55
Tracking
0, inherited
Weight
400, inherited
Measure
52ch
The questions an architect asks before a pilot, answered on one page: the shape of a deployment, the data that leaves a host, the systems it plugs into, and the things it deliberately does not do.
Section lead
Size
17px
Line height
1.55
Tracking
0, inherited
Weight
400, inherited
Measure
56ch
The point of the product is evidence about files, not the files. What the agent sends is bounded and listed here.
Long-form text
Size
16.5px
Line height
1.6
Tracking
0, inherited
Weight
400, inherited
Measure
68ch
The first scan weighs every binary, library and package already on the host against the original. A baseline records what is there; it does not bless it.
Body
Size
16px
Line height
1.5
Tracking
0, inherited
Weight
400, inherited
Package updates, log rotation, temp files, a real implant: they all look like change.
Card text
Size
15.5px
Line height
1.6
Tracking
0, inherited
Weight
400, inherited
A small Rust agent runs as a service on each host.
Button
Size
14px
Line height
1.5, inherited
Tracking
0, inherited
Weight
600
Discover Zero Door
Text link
Size
14px
Line height
1.5, inherited
Tracking
0, inherited
Weight
500
Read the research
Small text
Size
13.5px
Line height
1.5
Tracking
0, inherited
Weight
400, inherited
One Windows servicing run touches thousands of files.
Each sample is text the site sets in that role, styled with the declarations of its source rule at the width of your window. Fluid sizes show the size they take right now; values a rule does not declare are marked as inherited.
Fluid headlines
Headlines grow with the window through clamp(): a smallest size, a share of the viewport width and a largest size. The table gives the window width at which each one starts to grow and the width at which it stops.
On phones the home hero sets smaller sizes of its own. Where max-width: 600px matches, its headline is 36px, below the floor of its clamp(), and its lead 16px, so that the text fits the pinned first screen; where max-height: 700px matches as well, they are 32px and 15px.
Fluid sizes, computed from the declarations
Fluid sizes, computed from the declarations
Role
Declaration
Grows from
Stops at
Home headline
clamp(40px, 5.6vw, 74px)
715 px
1322 px
Page headline
clamp(36px, 4.6vw, 60px)
783 px
1305 px
Article headline
clamp(32px, 4.2vw, 52px)
762 px
1239 px
Statement headline
clamp(30px, 3.6vw, 46px)
834 px
1278 px
Section headline
clamp(28px, 3.2vw, 40px)
875 px
1250 px
Feature title
clamp(24px, 2.4vw, 32px)
1000 px
1334 px
Size follows the outline.
Each page has one h1, at the top of its hero or layout. Sections open with an h2 and divide with h3. Visual size steps down with the level, and no level is skipped to get a smaller look.
Every value in this scale matches its source file.
WhyThere is no type token to point at, so the scale points at the components. If a component changes, the test fails until this page changes with it.
TestEnforced by a test on every change.
Heading levels never skip inside the main content.
WhyA skipped level breaks the outline that screen reader users move through. The build fails on any page where, say, an h2 is followed by an h4.
TestEnforced by a test on every change.
Headlines that make a statement are sentences, in sentence case.
WhySuch a headline states something and ends with a full stop. Research articles and legal texts keep their own titles; item titles and labels carry no full stop.
New Black is set tight, and tighter as it grows. Geist Mono is never tightened; when it is set in capitals it is opened up. On web pages, capitals and open tracking belong to Geist Mono alone.
New Black: closed as it grows
Nothing is trusted for having been there yesterday.
-0.035em74px at its largestHome headline
Nothing is trusted for having been there yesterday.
-0.03em60px at its largestPage headline
Nothing is trusted for having been there yesterday.
-0.025em32px at its largestFeature title
Nothing is trusted for having been there yesterday.
-0.02em20pxCard title
Nothing is trusted for having been there yesterday.
016pxBody
Geist Mono: opened in capitals
Design language
0.12em11pxEyebrow
On this page
0.1em11pxContents title
Red team
0.08em12pxArticle category
PCI DSS · ISO 27001 · NIST 800-53
0.06em11pxTrust strip
Each sample takes the size, weight and tracking of its source rule; headlines take the size they have at the width of your window. Tracking is set in em, so it scales with the size.
Design language · FoundationsTypography
DoSet eyebrows with the kick utility: Geist Mono, 11px, capitals, tracked 0.12em.
Design language · FoundationsTypography
Do notDo not set New Black in capitals with open tracking. That treatment belongs to Geist Mono.
In the scale, tracking tightens as size grows.
WhyLarge letters at their default spacing look loose, so the larger the size, the more spacing is taken back. The test reads every New Black role above and fails if a larger one is tracked looser than a smaller one.
TestEnforced by a test on every change.
On web pages, capitals and open tracking appear only in Geist Mono.
WhyLabels in capitals need open spacing to stay legible at 11px, and labels are set in Geist Mono. New Black stays in sentence case, at zero or negative tracking. The share images drawn at build time are the exception: they load only New Black, and their kicker is set in it, in capitals, tracked 0.08em.
TestEnforced by a test on every change.
Capitals come from CSS, not from the text.
WhyDictionaries hold labels in sentence case and text-transform raises them, so the browser applies the casing rules of the declared language.
Geist Mono marks text that a machine wrote or will read back: a hash, a path, a date, a version, a line of code. Every character in it is the same width, so values line up in columns and a long string reads as one unit. It also sets the small labels and notes that organise a page.
This sentence is prose, so it is set in New Black. Its fingerprint, the address it is published at and the moment its text last changed are machine text.
The fingerprint is the SHA-256 of the sentence, computed when this page was built. The time comes from git: the last commit that changed the text of this page.
Where Geist Mono is used
Where Geist Mono is used
Kind
Example
In use on
Eyebrows and section labels
Design language
Page heads and most section heads
Dates and document details
Published 04/03/2026
The research list, articles and legal documents
Hashes and digests
5b30d3c28c8ddc6f…
Zero Door evidence diagrams and the integrity line on the trust page
Paths and code
/.well-known/security.txt
Research articles and product diagrams
Compliance framework names
ISO 27001
The trust strip on the home page and the trust page
Step numbers
01
Feature rows, the company story and the solution pages
Form notes, pricing notes and the bottom row of the footer
Figures
New Black draws proportional figures: each digit is as wide as its shape needs, so digits in a column do not line up. Geist Mono gives every digit the same width. Where values must line up column by column, as in the number columns of the tables on this page and in the legal index, the site sets them in Geist Mono.
New Black
0123456789
3,411
1
13
99.5
48
Geist Mono
0123456789
3,411
1
13
99.5
48
Each box is as wide as the digit inside it at weight 400, read from the font files. Below, figures from the Zero Door and trust pages, aligned to the right in each face.
Inline machine strings may break anywhere; code blocks scroll.
WhyA hash or a path has no spaces to wrap at, so inline code in long-form text sets overflow-wrap: anywhere. A code block keeps its lines whole and scrolls inside its own box.
TestEnforced by a test on every change.
Mono carries values and labels, not emphasis.
WhyGeist Mono sets machine text and the small labels and notes that organise a page. A sentence meant to carry weight is set in New Black; setting it in mono to look technical adds noise without adding a fact.
Line length is set in ch, the width of the digit zero in the current font. Headlines are held short so they break into even lines; long-form text stops at 68ch.
Measures in use
Measures in use
Element
Measure
At its largest, about
Home headline
12ch
572 px
Page headline
16ch
618 px
Section headline
22ch
567 px
Article headline
26ch
871 px
Legal document headline
34ch
1007 px
Feature paragraph
44ch
451 px
Home lead
44ch
507 px
Page lead
52ch
599 px
Section lead
56ch
609 px
Long-form text
68ch
718 px
In New Black the zero is wide: 0.64 em at weight 400 and 0.644 em at 700, against 0.6 em in Geist Mono and 0.546 em for an average lowercase letter. A New Black ch is therefore wider than a typical letter: a 68ch column of 16.5px text is about 718 pixels wide and holds about 80 average lowercase letters.
Balanced headlines
Headline rules set text-wrap: balance, so the browser evens out their lines instead of leaving a single word on the last one. Reading text keeps normal wrapping: balancing is meant for a few lines, not a paragraph.
Four rules, each with its proof.
DoHeadlines balance their lines: text-wrap: balance.
Four rules, each with its proof.
Do notDo not let a headline wrap greedily and end on a single word.
Long-form text stops at 68ch.
WhyOn long lines the eye loses the start of the next one. A New Black ch is wider than its letters, so a 68ch line of 16.5px text holds about 80 average lowercase letters. Research articles and legal documents share the same column.
TestEnforced by a test on every change.
Headlines balance their lines; card titles are the one exception.
WhyAn even shape reads as one statement; a lone last word reads as an accident. The test lists every headline role in the scale and fails if one neither balances nor is named as an exception.
TestEnforced by a test on every change.
When text must fit an exact box, size the box in pixels.
WhyA New Black ch is a wide zero. A description box on the home page, once sized in ch, ran past the frame it sat in; it is now sized in pixels.
The site is written by hand in English and Turkish, at the same addresses under /tr. Type has to hold in both: every page declares its language, and the English phrases the Turkish home page keeps are marked as English, so that the browser applies the right casing and a screen reader the right voice.
EN
From zero trust to one verdict.
TR
Sıfır güvenden tek karara.
The home headline in both languages, from the site dictionaries, each marked with its language.
Zero Trust, One Security
DoOn a Turkish page, mark English with lang="en". Its capitals then follow English rules.
Zero Trust, One Security
Do notWithout it, Turkish casing applies and the i of Security becomes a dotted capital İ.
Where the language is declared.
Every page declares its language on the html element.
WhyEnglish pages are lang="en" at the root of the site; Turkish pages are lang="tr" at /tr and below it. The test reads every built page.
TestEnforced by a test on every change.
On Turkish pages, the tagline and framework names that stay English carry lang="en".
WhyThe tagline on the home page, the framework names in its trust strip and the framework names that keep their English form in the lists on the trust and solution pages stay in English. Marking them keeps their capitals and their pronunciation English. Framework names with a Turkish form are written in Turkish and carry no mark.
TestEnforced by a test on every change.
Research articles stay in English, and the Turkish research list says so.
WhyOn a Turkish route the page frame is Turkish and the article body sits in an article element with lang="en". The category, the date, the contents title and the links below the article stay in Turkish and carry lang="tr", so the category is capitalised by Turkish rules. Each entry in the Turkish list carries a badge that names the language.
TestEnforced by a test on every change.
A Turkish document on an English route says it is Turkish.
WhyUntil a legal text has an English version, the English route shows the Turkish body with lang="tr" and a note above it. The document details, the notes and the link back to the index stay in English and carry lang="en".
TestEnforced by a test on every change.
Letters the fonts cover
The New Black files are cut down to a subset that includes every Turkish letter. A few symbols are left out; this page names them instead of drawing them, and where the site uses one, such as the arrow between two figures, the browser draws it from a system font. The table is read from the shipped files when the page is built.
Ç ç Ğ ğ İ ı Ö ö Ş ş Ü ü â î û
Ç ç Ğ ğ İ ı Ö ö Ş ş Ü ü â î û
Turkish letters and the circumflex vowels, in New Black 700 and 400. All 15 are present in every weight.
Symbols in the shipped files
Symbols in the shipped files
Character
Code point
New Black
Geist Mono
En dash
U+2013
Present
Present
Curly quotation mark
U+201C
Present
Present
Ellipsis
U+2026
Present
Present
Bullet
U+2022
Present
Present
Euro sign
U+20AC
Present
Present
Em dash
U+2014
Absent
Present
Rightwards arrow
U+2192
Absent
Present
Backtick (grave accent)
U+0060
Absent
Present
Turkish lira sign
U+20BA
Absent
Absent
Check mark
U+2713
Absent
Absent
Ballot box
U+2610
Absent
Absent
The em dash is not used on the site in any case: a test rejects it in the site dictionaries and a build gate on every built page.
The New Black subset covers every Turkish letter.
WhyTurkish text must never fall back to another face in the middle of a word. The test reads the character map of each shipped weight.
TestEnforced by a test on every change.
Figures and dates
Figures in copy are written by hand in each dictionary, in the convention of that language. Research dates are formatted when the site is built, with the en-GB and tr-TR locales: day first in both, with slashes in English and dots in Turkish. Legal document versions and the Last changed line of these pages are written year first, as ISO 8601.
The interface speaks in the calm register. Of the site's two typefaces it shares one, Geist Mono, for machine text. This is what its code shows.
The site and the product, side by side
The site and the product, side by side
Aspect
This site
Zero Door
Typeface for language
New Black, 4 static weights
Inter, one variable file
Machine text
Geist Mono, 2 weights
Geist Mono, one variable file
Weights in use
400 to 700
400 to 600
Largest heading
74 px, the home headline
24 px, the page title
Body text
16 px, line height 1.5
14 px, line height 20 px
Loading
Self-hosted through Astro
Self-hosted through next/font/local
The typeface and the four roles are read from the source code of the product into a snapshot, published as product-tokens.json. The page title, the weights in use, the tabular figures and the contract test are checked by hand in the product code.