# Futureproof design system

Futureproof is the umbrella brand for Colin Taylor's software products, operated at futureproof.dev and developed by Colt Consulting. This document holds the rules. The tokens live in `src/styles/global.css`, and the page at [futureproof.dev/brand](https://futureproof.dev/brand) renders every token and component from that stylesheet. The reasons behind the identity are recorded in `docs/brand-and-products.md`.

When this document and the stylesheet disagree, the stylesheet is what ships. Fix whichever is wrong in the same change.

## Principles

- **Ink and ultramarine on white.** The identity is quiet, with one accent. Ultramarine marks the brand dot and the things you can act on.
- **The dot is the signature.** It appears as the full stop in the wordmark, in the icon, and grown into the rising dot. Nothing else is decorated.
- **Flat and ruled.** Surfaces separate by colour and 1px lines. Shadows are reserved for things that float over the page.
- **The products keep their colours.** The umbrella frames them without competing.
- **External CSS only.** The Content Security Policy forbids inline styles and scripts. Every visual rule is a class in an external stylesheet.

## Logo

![Futureproof logo](https://futureproof.dev/brand/futureproof-logo.svg)

The wordmark reads `futureproof.` in constructed lowercase letters built from stems, bars and ring sectors in `scripts/brand/lettering.mjs`. It is not set in a font. `npm run build:brand` regenerates every logo, icon and the social card. Never edit the generated SVGs by hand.

| Artwork (`public/brand/`) | Use |
|---|---|
| `futureproof-logo.svg` | Primary: ink letters and ultramarine dot on white or cloud |
| `futureproof-logo-reverse.svg` | White letters and ultramarine dot on ink |
| `futureproof-logo-mono.svg` | One colour, where colour is unavailable |
| `futureproof-dev-logo.svg`, `futureproof-dev-logo-reverse.svg` | Domain lockup |
| `favicon.svg`, `icon-*.png` | The `f.` icon: white f and ultramarine dot on an ink tile |
| `social.png` | Domain lockup and tagline on ink, with the rising dot |

### Wordmark or domain lockup

Use `futureproof.` by default, including in the site header. Use `futureproof.dev` only where the address matters: the social card, slides, print, and product sites that link back to Futureproof. Leaving `.dev` out of the everyday logo stops the consumer products looking like developer tools. In running text write Futureproof. Page titles, the footer and structured data say Futureproof.dev.

### Clear space

Keep clear space of one **x-height** on every side of any logo. The x-height is the height of the o: 100 of the wordmark's 1077 font units, which is 9.3% of the wordmark's width or 53% of its height. Measure from the artwork's box. Nothing enters that space: text, rules, other logos or the edge of the frame.

### Minimum sizes

| Artwork | Screen | Print |
|---|---|---|
| Wordmark `futureproof.` | 120px wide | 30mm wide |
| Domain lockup `futureproof.dev` | 160px wide | 40mm wide |
| Icon `f.` | 16px as a favicon; 24px anywhere else | 8mm |

Below these sizes the dot shrinks under about 3.5px and stops reading as a signature. The site header uses the wordmark at 212px, or 184px at phone width.

### Icon

The icon is the wordmark's `f.` on an ink tile with rounded corners (radius 56 of 256). The tile is always ink. A white lowercase f on a blue tile reads as Facebook. The 180px touch icon is square because iOS rounds it.

### Don't

- Set the wordmark in a font, capitalise it, or recolour the letters.
- Change the dot to any colour but ultramarine. In the one-colour version it takes the letter colour.
- Place the logo on ultramarine, on a product colour or over a photograph.
- Stretch, outline, shadow, rotate or animate the logo.
- Combine the logo with a mascot or a product's mark. Futureproof has no mascot.

## Colour

| Name | Token | Value | Use |
|---|---|---|---|
| Ink | `--color-ink` | `#0A0E27` | Text, wordmark, homepage header and hero, icon tile |
| Ultramarine | `--color-ultramarine` | `#3843FF` | The dot, links, primary buttons, focus rings, the rising dot |
| Deep ultramarine | `--color-ultramarine-deep` | `#2A34E0` | Hover state of ultramarine controls |
| White | `--color-white` | `#FFFFFF` | Page background; text on ink and ultramarine |
| Cloud | `--color-cloud` | `#F4F5F9` | Secondary surfaces, callouts, footer |
| Tint | `--color-tint` | `#ECEEFF` | Inline code and quiet highlights |
| Slate | `--color-slate` | `#4B5068` | Secondary text |
| Mist | `--color-mist` | `#AEB3CC` | Secondary text on ink only |

The supporting neutrals are line `#E4E6EE` and line strong `#C5C9D8` for dividers, borders and underlines, and faint `#5F6480` for the legal line and metadata.

Components use **role tokens**, not palette names: `--color-bg`, `--color-bg-1` (cloud), `--color-bg-2` (tint), `--color-text` (ink), `--color-dim` (slate), `--color-faint`, `--color-line`, `--color-line-2`, `--color-accent` (ultramarine), `--color-accent-strong` (deep ultramarine) and `--color-accent-ink` (white). Each role points to a palette token, so a palette value is defined once. Tailwind utilities such as `bg-ink` and `text-slate` come from the same `@theme` block.

### Contrast

Text needs at least 4.5:1.

| Pairing | Ratio | Use |
|---|---|---|
| Ink on white | 19.0:1 | Headings and body text |
| Slate on white | 7.9:1 | Secondary text |
| Slate on cloud | 7.3:1 | Secondary text on callouts and the footer |
| Faint on white | 5.8:1 | Fine print only |
| Ultramarine on white | 6.2:1 | Links and accent text |
| Ultramarine on cloud | 5.6:1 | Links on quiet surfaces |
| White on ultramarine | 6.2:1 | Primary button label |
| White on deep ultramarine | 7.9:1 | Primary button on hover |
| White on ink | 19.0:1 | Text on ink |
| Mist on ink | 9.2:1 | Secondary text on ink |
| **Ultramarine on ink** | **3.1:1** | **Never text.** The dot, the rising dot and large shapes only |

On ink, links and ghost buttons are white. The `.surface-ink` class sets this, and overrides the global rule that turns links ultramarine on hover. The 3px ultramarine focus ring meets the 3:1 non-text minimum on ink, but only just (3.09:1). Don't thin the ring or lighten the ink.

Mist is for ink only. On white it is 2.1:1.

## Type

Manrope (400–800) sets headings. Source Sans 3 (400–700) sets body text and interface text. JetBrains Mono sets code. All three are self-hosted OFL fonts in `public/fonts/`. The included Manrope Bold TTF supplies the social card's tagline.

| Token | Size | Face | Use |
|---|---|---|---|
| `--type-display-l` | 35–58px | Manrope 600, −0.05em | Brochure headline |
| `--type-display-m` | 32–48px | Manrope 600, −0.05em | Page title |
| `--type-display-s` | 28–42px | Manrope 500, −0.05em | Homepage tagline |
| `--type-title` | 24px | Manrope 600, −0.04em | Catalogue product names, callout and section headings |
| `--type-lead` | 20px | Manrope 600 or Source Sans 3 | Feature headings, brochure summary, page introductions |
| `--type-base` | 18px | Source Sans 3 | Body text (line height 1.6) |
| `--type-body` | 16.8px | Source Sans 3 | Long-form prose and brochure features (line height 1.7) |
| `--type-ui` | 16px | Source Sans 3 | Navigation, buttons, footer |
| `--type-meta` | 14.4px | Source Sans 3 | Category, status, diagram captions |
| `--type-caption` | 12.8px | Source Sans 3 | Legal line, figure captions |

Display sizes are `clamp()`s that scale with the viewport. Headings use `text-wrap: balance`. Keep running text within `--measure` (44rem).

## Space, radius and elevation

**Spacing** steps on a 4px base: `--space-1` to `--space-10` are 4, 8, 12, 16, 24, 32, 40, 48, 64 and 96px. Lay out siblings with `gap`. Content sits in the `wrap` utility: a 74rem (`--page-max`) column with a `--gutter` of `clamp(1rem, 4vw, 2.5rem)`.

**Radius** follows the object's role:

| Token | Value | Use |
|---|---|---|
| `--radius-inline` | 4px | Inline code, focus-ring corners |
| `--radius-control` | 6px | Buttons |
| `--radius-surface` | 8px | Monograms, callouts, link panels, code blocks |
| `--radius-screen` | 12px | Product screenshots |
| `--radius-panel` | 18px | Diagram and artwork panels |
| `--radius-tile` | 12.5% | Square product icon grounds |
| `--radius-round` | 50% | The dot, status dots, the rising dot |

**Elevation** is flat. The levels are white (the page), cloud (a surface), tint (a highlight) and ink (inverse). `--shadow-overlay` is the only shadow, and it is for things that float over the page, such as the skip link. Don't put shadows on cards or rows, or give them a coloured rail or accent border.

## Components

The components live in `src/components/`, with their CSS in `src/styles/global.css`.

**Buttons** (`.btn`). `.btn-primary` is ultramarine with a white label and turns deep ultramarine on hover. Use one per view. `.btn-ghost` has a line-strong border and ink text that turn ultramarine on hover. On ink it has a mist border and white text. Buttons are 16px, 600 weight, with 6px corners.

**Links.** Body links are ink with a line-strong underline that turns ultramarine on hover. `.link-accent` is ultramarine for a single onward action: Explore, All products, About Futureproof. On ink, links are white with a mist underline.

**Navigation** (`SiteNav.astro`, wrapped by `Nav.astro`). On white it has the primary logo, slate links, and the current page in ink with an underline offset by 8px. On the homepage the header joins the ink hero: `surface-ink`, the reverse logo, mist links, and the current page in white. The header carries a skip link that floats in with the overlay shadow.

**Product catalogue** (`ProductRow.astro`). Each product is a row divided by lines, not a card. The monogram is 52×58px with 8px corners in the product's own colours, showing its initials in Manrope 700. Then come the name (title size), category and platform (meta size, slash separator in line strong), summary, release status (meta, 600) and an Explore accent link with an arrow. The row has three columns, two below 850px, and stacks below 600px.

**Brochure hero** (`BrochureHero.astro`). Product name in Manrope, then the headline (display-l), summary (lead), status with an ultramarine status dot and the platform, and one primary action. The artwork is one of three things:
- a real product screen in a cloud frame (`--radius-panel`), with the screenshot at `--radius-screen`
- the product's icon on its own ground (`--radius-tile`)
- a diagram

Never use an invented interface screenshot.

**Diagram** (`ProductDiagram.astro`). Three steps: input, product, result. Titles are Manrope, and the product step is the largest. Captions are meta size in slate. The steps are joined by 1px connectors in the product's `--diagram-line`, on the product's ground.

**Callout** (`Callout.astro`). One offer on cloud with 8px corners, a title, body in slate, an optional note, and one primary button. The consulting callout's action always goes to `https://coltconsulting.nz/contact`. It stacks below 600px.

**Footer** (`Footer.astro`). On cloud: Futureproof.dev as Manrope text (not the logo), a one-line description, slate links, and `© <year> Colt Consulting Ltd` in faint caption size.

**Prose** (`.prose-fp`). Markdown pages. Code blocks are on cloud with a line border. Inline code is on tint. Blockquotes have a 3px ultramarine rule. Syntax highlighting uses only umbrella colours: keywords ultramarine, functions and literals deep ultramarine, strings and punctuation slate, comments faint italic.

## The rising dot

On the homepage and the social card, the dot grows into a large ultramarine disc rising from the lower right of an ink ground. Colt Consulting's signature is the horizon rule; Futureproof's dot rises from it, with the horizon implied by the frame's lower edge rather than drawn. Apply it with the `.rising-dot` class on an ink (`surface-ink`) element, and size it with `--rising-dot-size`. The disc's right and bottom offsets are fixed fractions of its size (−0.27 and −0.77), so about a quarter of its diameter shows.

- Use it once per view, on ink only, from the lower right.
- Keep it clear of text. The homepage sets 38rem, 28rem below 850px and 22rem below 600px, and adds bottom padding so the disc sits beneath the copy.
- Keep it flat ultramarine: no gradients, glows, rings, outlines or extra shapes.
- Never show it as a whole circle, which reads as a second dot. Never put it on white, animate it on load, or use it on product brochures.

## Product colours

The products own the warm and green hues, and Colt Consulting owns teal. The umbrella uses ultramarine because no product has claimed it.

| Product | Hue | Ground | Ink |
|---|---|---|---|
| Next Game | Orange (brand `#FF5A00`) | `#FFF0E3` | `#A34A13` |
| Next Book | Green | `#E6F3EB` | `#246044` |
| Decision Fieldbook | Grey slate | `#ECEFF2` | `#394756` |
| Launch Pane | Cerulean | `#E4F0FA` | `#1F5A94` |
| Secret Pane | Amber | `#FDF3D0` | `#6B5200` |
| AsOf | Phosphor on ink (diagram on `#E6EBF0`, connectors `#3FB876`) | `#0B0F14` | `#5BF09E` |
| Click Dog | Amber brown (the product also uses violet) | `#FBEFDA` | `#7B4F14` |

- A product's colours appear only in its own elements: the monogram, the artwork ground, diagram lines. They are set by `.product-<slug>` as `--product-ground`, `--product-ink` and `--diagram-line`.
- A product's ink needs at least 4.5:1 on its ground.
- A product blue stays at least 20° of hue away from ultramarine (237°) and is less saturated. Launch Pane's cerulean is at 210°.
- The umbrella never uses orange, amber, violet or green, nor teal, which belongs to Colt Consulting, the parent company. That covers page chrome, code highlighting, charts and illustrations.
- A product with a dark ground (AsOf) sets a light `--diagram-ground` so diagram captions stay legible, and its monogram never takes an ultramarine dot, so it can't read as the `f.` icon. Phosphor is never text on a light ground.
- Futureproof never draws a horizon line. The horizon rule is Colt Consulting's signature.
- On a brochure, the status dot, buttons, links and focus rings stay ultramarine.
- Product websites keep their own designs. When they link back to Futureproof they use the domain lockup.

## Adding or changing a token

1. Change `src/styles/global.css`. For a colour, update `src/lib/brand.ts` too, because `npm run check:site` fails when the `/brand` swatches drift from the stylesheet.
2. For a brand colour, update `scripts/build-brand-assets.mjs` and run `npm run build:brand`.
3. Update this document, then run `npm run check`, `npm run build` and `npm run check:site`.
