# Halden design system

Inter Tight on a warm paper ground (#f1f0ec), English Red as the one accent, pill buttons, frosted glass panels. Built for: Applied AI lab.

> **For AI coding agents.** Build UI from this file alone. Paste the tokens (section 11) into the global stylesheet and load the fonts (section 2). Use only the colours, fonts, sizes, radii, shadows and easings defined here: no new hex values, no other fonts, no Tailwind default palette. When something is not covered, derive it from the nearest rule here and keep it quieter, not louder.

## 1. At a glance

| | |
|---|---|
| Theme | Light first; dark included via `data-theme="dark"` |
| Palette | Sanzo Wada combination 190: Ivory Buff `#ebd3a2` · English Red `#d96629` · Black `#111314` |
| Type | Inter Tight (display) · Newsreader (accent) |
| Shape | cards 31.68px, buttons pill; soft shadows, 1px hairline rings |
| Motion | 350ms / 700ms / 1200ms; `cubic-bezier(.76, 0, .24, 1)`, `cubic-bezier(.16, 1, .3, 1)` |
| Layout | 1680px max width, clamp(18px, 2.4vw, 36px) gutter, clamp(88px, 10vw, 168px) section padding, left-aligned headings |

**The designer's notes** (from the template's token file):

- Wada combination 190: Ivory Buff #ebd3a2 · English Red #d96629 · Black #111314.
- Neutrals (paper, greys, white) are free. Every chromatic colour on the site is one of the three, or an alpha of one.
- Type: Inter Tight (display, UI, body) + Newsreader upright light (the one serif voice, used for a word or a line, never a paragraph).
- Shape: inset cards with large radius, pills for actions, crosshair marks on dark grounds.
- Motion: one easing family; things rise out of their own line masks, cards open from a band, grain moves on film.

## 2. Setup

Fonts (put in `<head>`):

```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter+Tight:wght@400;500;600;700&family=Newsreader:wght@400&display=swap">
```

Base styles, after the tokens:

```css
*, *::before, *::after { box-sizing: border-box; }
body {
  margin: 0;
  background: var(--color-background);
  color: var(--color-body);
  font-family: var(--font-body);
  font-size: var(--text-body, 16px);
  line-height: var(--text-body--line-height, 1.5);
  letter-spacing: var(--text-body--letter-spacing, 0);
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4 { margin: 0; font-family: var(--font-display); color: var(--color-foreground); text-wrap: balance; }
p { margin: 0; text-wrap: pretty; }
.container { max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.section { padding-block: var(--section-y); }
:focus-visible { outline: 2px solid var(--color-ring); outline-offset: 2px; }
```

## 3. Colour

### Palette

| Name | Hex | Token |
|---|---|---|
| Ivory Buff (Wada) | `#ebd3a2` | `--palette-ivory-buff` |
| English Red (Wada) | `#d96629` | `--palette-english-red` |
| Black (Wada) | `#111314` | `--palette-black` |

### Roles

| Role | Light | Dark | Use for |
|---|---|---|---|
| `--color-background` | `#f1f0ec` | `#111314` | Page background |
| `--color-foreground` | `#111314` | `#f1f0ec` | Headings and primary text |
| `--color-body` | `#111314` | `#f1f0ec` | Body copy |
| `--color-surface` | `#fbfaf7` | `#1b1d1e` | Cards and raised panels |
| `--color-surface-foreground` | `#111314` | `#f1f0ec` | Text on surfaces |
| `--color-muted` | `#e9e7e1` | `#1a1c1d` | Quiet bands, wells, hover rows |
| `--color-muted-foreground` | `#63645f` | `#a3a49f` | Secondary text, captions, labels |
| `--color-border` | `#1010101f` | `#efefef1f` | Hairlines, dividers, card edges |
| `--color-border-strong` | `#0f141433` | `#f0f0eb33` | Emphasised rules, input edges on hover |
| `--color-input` | `#1010101f` | `#efefef1f` | Input borders |
| `--color-input-background` | `#fbfaf7` | `#1b1d1e` | Input fill |
| `--color-ring` | `#d96629` | `#d96629` | Focus ring |
| `--color-primary` | `#111314` | `#f1f0ec` | Primary action fill |
| `--color-primary-foreground` | `#f1f0ec` | `#111314` | Text on primary |
| `--color-secondary` | `#d96629` | `#111314` | Secondary action fill |
| `--color-secondary-foreground` | `#111314` | `#ffffff` | Text on secondary |
| `--color-accent` | `#d96629` | `#d96629` | Brand accent: highlights, links, active states |
| `--color-accent-foreground` | `#111314` | `#111314` | Text on accent |
| `--color-inverse` | `#111314` | `#fbfaf7` | Inverted band (dark on light pages, light on dark) |
| `--color-inverse-foreground` | `#f1f0ec` | `#111314` | Text on inverse |
| `--color-inverse-muted` | `#9c9c9a` | `#6a6b6a` | Secondary text on inverse |
| `--color-inverse-border` | `#f1f0ec29` | `#11131429` | Hairlines on inverse |
| `--color-destructive` | `#dd4027` | `#e15741` | Errors and destructive actions |
| `--color-destructive-foreground` | `#ffffff` | `#111111` | Text on destructive |

### How the colours are used

- Share of page area by surface colour, measured on the home page: Black 45.3%, #f1f0ec 37.7%, Ivory Buff 5.8%, #fbfaf7 5.1%. Keep roughly that balance; the accent's share is about where it is used, not how much.
- `--color-accent` (#d96629) is the brand's accent: use it sparingly, on one thing per view: a primary action, a highlight, an active state, an underline. Never use it for body text.
- `--color-primary` (#111314) fills the main action; its text is `--color-primary-foreground` (#f1f0ec), contrast 16.34:1.
- Text: headings `--color-foreground` (16.34:1 on the page), secondary text `--color-muted-foreground` (5.23:1). Don't go lighter than muted for anything people must read.
- Surfaces step: `background` → `muted` for quiet bands → `surface` for cards → `inverse` (#111314) for one contrasting band or card per page.
- Borders are hairlines: `--color-border` (#1010101f), often drawn as `box-shadow: 0 0 0 1px` (inset on cards) rather than `border`.
- Every chromatic colour is one of the Wada combination's colours or a tint, shade or alpha of one. Neutrals (white, greys, near-black) are free. No other hues, no rainbow gradients.
- Gradients appear (23 on the measured pages), only between this palette's colours.

## 4. Typography

| Family | Role | Weights | Licence | Source |
|---|---|---|---|---|
| Inter Tight | display | 400, 500, 600, 700 | SIL Open Font License 1.1 | Google Fonts |
| Newsreader | accent | 400 | SIL Open Font License 1.1 | Google Fonts |

| Token | Desktop (1440) | Phone (390) | CSS size | Weight | Line height | Tracking | Case | Font |
|---|---|---|---|---|---|---|---|---|
| `--text-display` | 178.56px | 64px | `clamp(64px, 1.34rem + 10.91vw, 178.56px)` | 600 | 0.82 | -0.065em | as written | `--font-display` |
| `--text-h2` | 63.36px | 36px | `clamp(36px, 4.4vw, 68px)` | 500 | 0.98 | -0.045em | as written | `--font-display` |
| `--text-h3` | 18.72px | 18.72px | `18.72px` | 700 | 1.55 | -0.009em | as written | `--font-display` |
| `--text-lead` | 20.88px | 18px | `clamp(18px, 1.45vw, 22px)` | 400 | 1.4 | -0.015em | as written | `--font-body` |
| `--text-body` | 16px | 16px | `16px` | 400 | 1.55 | -0.01em | as written | `--font-body` |
| `--text-small` | 14px | 14px | `14px` | 400 | 1.55 | -0.011em | as written | `--font-body` |
| `--text-eyebrow` | 13px | 13px | `13px` | 400 | 1.55 | -0.012em | as written | `--font-body` |

Rules:
- Headlines: weight 500, tracking -0.045em, line height 0.98. Tight tracking is part of the look; keep it on every heading above 32px. Use only the weights listed above.
- Headlines are one voice: no mixed fonts, no coloured or italic accent words inside a headline.
- Eyebrow above section titles: 13px, weight 400, sentence case, var(--color-muted-foreground), in Inter Tight.
- Body copy: 16px in `--color-body`, lines no longer than 65 characters (`max-width: 60ch`).
- No uppercase text except where a token above says so.
- No italics anywhere.

## 5. Layout and spacing

- Container: max 1680px, centred, side padding `--gutter` (clamp(18px, 2.4vw, 36px); 34.55px at 1440, 18px on phones).
- Sections: `--section-y` top and bottom (clamp(88px, 10vw, 168px); 144px at 1440, 88px on phones). Adjacent sections on the same ground share the rhythm; don't add extra margins.
- Spacing scale (base 4px): `--space-1` 2px, `--space-2` 4px, `--space-3` 6px, `--space-4` 10px, `--space-5` 14px, `--space-6` 22px, `--space-7` 32px, `--space-8` 48px, `--space-9` 64px. Most common gaps on the pages: 36px, 8px, 10px, 14px, 12px, 6px.
- Section headings are left-aligned, on the container edge; never centre long text.
- Breakpoints (desktop-first): 860px, 900px, 960px, 1000px. Phone layout (the phone gutter and section padding above) applies below 860px.
- Grids of three or four equal items: all in one row above 1000px, two columns down to 860px, one column below. Items in a row share the same height and their actions align at the bottom.

## 6. Shape and depth

- Radii: `--radius-sm` 12px, `--radius-md` 18px, `--radius-lg` clamp(20px, 2.2vw, 32px) (32px at 1440), `--radius-pill` 9999px.
- Rules and borders: 1px var(--color-border), 1px #0d0d1a14, 1px #ffffff24.
- Buttons: pill (`--radius-pill`). Cards: 31.68px. Inputs: 12px.
- Shadows: `--shadow-sm`; used on floating elements only (menus, popovers, lifted cards); cards themselves use the 1px ring.
- Frosted glass: `backdrop-filter: blur(18px) saturate(1.4)` on sticky bars and overlays, over a translucent surface.

## 7. Motion

- Durations: `--duration-fast` 350ms, `--duration-base` 700ms, `--duration-slow` 1200ms.
- Easings: `--ease-in-out` `cubic-bezier(.76, 0, .24, 1)`, `--ease-out` `cubic-bezier(.16, 1, .3, 1)`. Use `--ease-out` for things that enter, lift or underline; `--ease-in-out` for things that move across or open and close.
- What animates: background, color, opacity, box-shadow and transform. Hover changes colour or fill, never layout.
- The template uses 15 keyframe animations: content rises or fades in once as it enters the viewport.
- Respect `prefers-reduced-motion`: drop to opacity changes or none.

## 8. Components

Exact recipes, measured from the template. Use these classes or translate them 1:1 into your framework.

### Buttons

Primary: (“Try Halden”) var(--color-foreground) fill, 40px tall. Secondary: var(--color-accent) fill. 

```css
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 40px;
  padding-inline: 18px 18px;
  border-radius: var(--radius-pill);
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background 400ms var(--ease-out), color 400ms var(--ease-out), box-shadow 400ms var(--ease-out), transform 400ms var(--ease-out);
}
.btn-primary {
  background: var(--color-primary);
  color: var(--color-primary-foreground);
  border: none;
}
.btn-primary:hover { background: #2a2c2d }
.btn-secondary {
  background: var(--color-secondary);
  color: var(--color-secondary-foreground);
  border: none;
  font-weight: 600;
}
.btn-secondary:hover { background: var(--color-accent); box-shadow: rgba(17, 19, 20, 0.08) 0px 0px 0px 100px inset }
.btn-outline { background: transparent; color: var(--color-foreground); border: 1px solid var(--color-border-strong); }
.btn-outline:hover { border-color: var(--color-foreground); }
.card-inverse .btn-outline { color: var(--color-inverse-foreground); border-color: var(--color-inverse-border); }
```

Use one `.btn-primary` per view. Actions beside it, and the actions of the other items in a set, use `.btn-outline`; `.btn-secondary` is for the one featured item (see Cards). All buttons share the `.btn` base height and padding.

```html
<a class="btn btn-primary" href="#">Try Halden</a>
<a class="btn btn-secondary" href="#">Learn more</a>
```

### Inputs

```css
.field { display: grid; gap: 8px; }
.label { font-size: 14px; font-weight: 500; color: var(--color-foreground); }
.input {
  height: 53px;
  padding: 0 16px;
  background: #ffffff;
  color: var(--color-foreground);
  border: none;
  box-shadow: rgba(17, 19, 20, 0.12) 0px 0px 0px 1px inset;
  border-radius: 12px;
  font: inherit; font-size: 16px;
}
.input::placeholder { color: #8b8c87; }
.input:focus { outline: none; box-shadow: 0 0 0 2px var(--color-ring); }
textarea.input { height: auto; min-height: 140px; padding-block: 14px; }
```

### Cards

Cards sit on the page in grids with 36px gaps. One card or band per page may be inverse (`.card-inverse`) to anchor the eye.

```css
.card {
  background: #ffffff;
  color: var(--color-surface-foreground);
  border-radius: 31.68px;
  padding: 34.56px 34.56px;
  box-shadow: rgba(17, 19, 20, 0.12) 0px 0px 0px 1px inset;
}
.card-inverse {
  background: var(--color-inverse);
  color: var(--color-inverse-foreground);
  border-radius: 31.68px;
  padding: 34.56px 34.56px;
}
.card h3 { font-size: var(--text-h3, 24px); font-weight: var(--text-h3--font-weight, 500); letter-spacing: var(--text-h3--letter-spacing, 0); line-height: var(--text-h3--line-height, 1.2); }
.card p { color: var(--color-muted-foreground); }
[data-theme="dark"] .card { background: var(--color-surface); }
.card-inverse h3 { color: var(--color-inverse-foreground); }
.card-inverse p { color: var(--color-inverse-muted); }
.card-inverse hr, .card-inverse li { border-color: var(--color-inverse-border); }
.card-inverse .btn-primary { background: var(--color-accent); color: var(--color-accent-foreground); }
.card ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.card li { display: flex; gap: 10px; align-items: baseline; }
.card li::before { content: ""; flex: none; width: 8px; height: 1px; background: currentColor; opacity: .5; transform: translateY(-4px); }
```

To feature one item in a set (a recommended plan, the main offer), make it `.card-inverse` with the `.btn-secondary` action; the others stay `.card` with `.btn-outline`. Mark it with a small text label in the eyebrow style, not a badge.

Big figures (prices, stats): `--font-display` at `--text-h2` size, weight and tracking, `font-variant-numeric: tabular-nums`; the unit or period beside it in `--text-small` and the muted colour.

### Navigation bar

72px tall, sticky to the top, transparent over the page. Logo left, 5 links (15px, weight 500), then one primary button (“Try Halden”) on the right.

```css
.nav { position: sticky; top: 0; z-index: 50; height: 72px; display: flex; align-items: center; justify-content: space-between; gap: 24px; padding-inline: var(--gutter); }
.nav a.link { font-size: 15px; font-weight: 500; color: var(--color-foreground); text-decoration: none; opacity: .8; }
.nav a.link:hover { opacity: 1; }
```

### Footer

On the page ground, link columns, links 15px in `--color-muted-foreground`, the wordmark and a one-line note, legal links last.

### Section header

```html
<section class="section">
  <div class="container">
    <header class="section-head">
      <p class="eyebrow">Section label</p>
      <h2>Short statement headline.</h2>
      <p class="lead">One sentence that says what the section gives the reader.</p>
    </header>
    <!-- grid of .card -->
  </div>
</section>
```

```css
.section-head { display: grid; gap: 10px; max-width: 720px; margin-bottom: 32px; }
.section-head h2 { font-size: var(--text-h2); line-height: var(--text-h2--line-height); letter-spacing: var(--text-h2--letter-spacing); font-weight: var(--text-h2--font-weight); }
.lead { font-size: var(--text-lead, 18px); color: var(--color-muted-foreground); max-width: 56ch; }
.eyebrow { font-family: var(--font-display); font-size: 13px; font-weight: 400; letter-spacing: -0.012em; color: var(--color-muted-foreground); }
```

## 9. Voice and copy

Written for: Applied AI lab. Halden is an independent AI lab building agents for work that has to be right: contracts, audits, grid plans and clinical paperwork.

- Headlines: 4 words typical (1–8), sentence case, ending with a full stop. Plain statements, no hype words.
- Buttons: two words or fewer, a verb first (“Try Halden”, “Talk to us”, “About the lab”, “How we keep it safe”).
- Minimal text: every word earns its place. One idea per section, one sentence of support under a headline.
- No decorative micro-text, fake data readouts, “LIVE” chips or invented metrics.

Headlines from the template, for tone:

- Agents that show their work.
- Eight teams, checked twice.
- Two products, one standard.
- Right, or flagged.
- Measured, not promised, every release.
- Told by the people who use it daily.

## 10. Do and don’t

**Do**

- Use `--color-background` for the page and step to `--color-muted` / `--color-surface` for bands and cards.
- Give the main action `--color-primary` in a pill; one primary button per view. Other actions beside it use `.btn-outline`.
- Keep headings in `--font-display` at the scale above, with its tracking.
- Use hairline borders (`--color-border`) to separate, and generous `--section-y` spacing between sections.
- Animate with `--ease-*` and `--duration-*` only.

**Don’t**

- Introduce colours outside the palette and roles (no Tailwind blue/indigo/gray defaults).
- Use a font other than Inter Tight and Newsreader.
- Stack heavy shadows; one level of lift at most.
- Use gradients between colours that are not in the palette.
- Use italics.
- Use uppercase text, except for tiny labels if the scale says so.
- Mix radii: actions are pills, cards 31.68px, inputs 12px.
- Centre long paragraphs, or let text run wider than 65 characters.
- Add emoji, icons in every heading, or decorative badges.

## 11. Tokens

The complete set. Paste into your global stylesheet before anything else.

```css
:root {
  color-scheme: light;
  /* palette */
  --palette-ivory-buff:          #ebd3a2;
  --palette-english-red:         #d96629;
  --palette-black:               #111314;

  /* colour roles (light) */
  --color-background:            #f1f0ec;
  --color-foreground:            #111314;
  --color-body:                  #111314;
  --color-surface:               #fbfaf7;
  --color-surface-foreground:    #111314;
  --color-muted:                 #e9e7e1;
  --color-muted-foreground:      #63645f;
  --color-border:                #1010101f;
  --color-border-strong:         #0f141433;
  --color-input:                 #1010101f;
  --color-input-background:      #fbfaf7;
  --color-ring:                  #d96629;
  --color-primary:               #111314;
  --color-primary-foreground:    #f1f0ec;
  --color-secondary:             #d96629;
  --color-secondary-foreground:  #111314;
  --color-accent:                #d96629;
  --color-accent-foreground:     #111314;
  --color-inverse:               #111314;
  --color-inverse-foreground:    #f1f0ec;
  --color-inverse-muted:         #9c9c9a;
  --color-inverse-border:        #f1f0ec29;
  --color-destructive:           #dd4027;
  --color-destructive-foreground: #ffffff;

  /* type */
  --font-display:                "Inter Tight", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-body:                   "Inter Tight", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-accent:                 "Newsreader", ui-serif, Georgia, serif;
  --text-display:                clamp(64px, 1.34rem + 10.91vw, 178.56px);
  --text-display--line-height:   0.82;
  --text-display--letter-spacing: -0.065em;
  --text-display--font-weight:   600;
  --text-h2:                     clamp(36px, 4.4vw, 68px);
  --text-h2--line-height:        0.98;
  --text-h2--letter-spacing:     -0.045em;
  --text-h2--font-weight:        500;
  --text-h3:                     18.72px;
  --text-h3--line-height:        1.55;
  --text-h3--letter-spacing:     -0.009em;
  --text-h3--font-weight:        700;
  --text-lead:                   clamp(18px, 1.45vw, 22px);
  --text-lead--line-height:      1.4;
  --text-lead--letter-spacing:   -0.015em;
  --text-lead--font-weight:      400;
  --text-body:                   16px;
  --text-body--line-height:      1.55;
  --text-body--letter-spacing:   -0.01em;
  --text-body--font-weight:      400;
  --text-small:                  14px;
  --text-small--line-height:     1.55;
  --text-small--letter-spacing:  -0.011em;
  --text-small--font-weight:     400;
  --text-eyebrow:                13px;
  --text-eyebrow--line-height:   1.55;
  --text-eyebrow--letter-spacing: -0.012em;
  --text-eyebrow--font-weight:   400;

  /* space and layout */
  --space-1:                     2px;
  --space-2:                     4px;
  --space-3:                     6px;
  --space-4:                     10px;
  --space-5:                     14px;
  --space-6:                     22px;
  --space-7:                     32px;
  --space-8:                     48px;
  --space-9:                     64px;
  --container:                   1680px;
  --gutter:                      clamp(18px, 2.4vw, 36px);
  --section-y:                   clamp(88px, 10vw, 168px);

  /* shape */
  --radius-sm:                   12px;
  --radius-md:                   18px;
  --radius-lg:                   clamp(20px, 2.2vw, 32px);
  --radius-pill:                 9999px;
  --shadow-sm:                   rgba(0, 0, 0, 0.8) 0px 40px 70px -30px;
  --shadow-ring:                 rgba(17, 19, 20, 0.12) 0px 0px 0px 1px inset;
  --backdrop:                    blur(18px) saturate(1.4);

  /* motion */
  --ease-in-out:                 cubic-bezier(.76, 0, .24, 1);
  --ease-out:                    cubic-bezier(.16, 1, .3, 1);
  --duration-fast:               350ms;
  --duration-base:               700ms;
  --duration-slow:               1200ms;
}

/* dark theme: <html data-theme="dark"> or class="dark" */
[data-theme="dark"], .dark {
  color-scheme: dark;
  --color-background:            #111314;
  --color-foreground:            #f1f0ec;
  --color-body:                  #f1f0ec;
  --color-surface:               #1b1d1e;
  --color-surface-foreground:    #f1f0ec;
  --color-muted:                 #1a1c1d;
  --color-muted-foreground:      #a3a49f;
  --color-border:                #efefef1f;
  --color-border-strong:         #f0f0eb33;
  --color-input:                 #efefef1f;
  --color-input-background:      #1b1d1e;
  --color-ring:                  #d96629;
  --color-primary:               #f1f0ec;
  --color-primary-foreground:    #111314;
  --color-secondary:             #111314;
  --color-secondary-foreground:  #ffffff;
  --color-accent:                #d96629;
  --color-accent-foreground:     #111314;
  --color-inverse:               #fbfaf7;
  --color-inverse-foreground:    #111314;
  --color-inverse-muted:         #6a6b6a;
  --color-inverse-border:        #11131429;
  --color-destructive:           #e15741;
  --color-destructive-foreground: #111111;
}

/* breakpoints (desktop-first): 860px, 900px, 960px, 1000px */
@media (prefers-reduced-motion: reduce) {
  :root { --duration-fast: 1ms; --duration-base: 1ms; --duration-slow: 1ms; }
}
```

---
Source: the Halden template, measured from its rendered pages (2026-10-08).
