---
version: alpha
name: "NTS Radio"
description: "Stark white canvas, Univers Condensed in uppercase all the way down, and a raw schedule-grid aesthetic that puts broadcast function before decoration"

colors:
  # Canvas
  background: "#ffffff"
  surface: "#000000"
  surface-muted: "#4c4c4c"

  # Ink
  ink: "#000000"
  ink-muted: "#666666"
  ink-subtle: "#999999"
  on-background: "#000000"
  on-surface: "#ffffff"

  # Brand accent — sky blue used on focus, hover, and the NTS Book Club badge
  primary: "#1eaedb"
  on-primary: "#ffffff"
  primary-container: "#1eaedb" # same hue; no tint variant

  # Interaction states
  text-hover: "#3860be"     # was rgb(56, 96, 190) — link hover
  focus-ring: "#000000"     # 1px solid black outline on focus

  # Borders
  border: "#4c4c4c"         # most common border on dark panels
  border-light: "#cccccc"   # light-context borders

  # Shadow tints (minimal; site is largely flat)
  shadow-soft: "#999999"    # was rgb(153,153,153) — lightest observed shadow

typography:
  display-hero:
    fontFamily: "Univers Condensed, Arial Narrow, Arial, sans-serif"
    fontSize: 42px
    fontWeight: 700
    lineHeight: 1.0
    letterSpacing: 0px
    textTransform: uppercase
  display:
    fontFamily: "Univers Condensed, Arial Narrow, Arial, sans-serif"
    fontSize: 24px
    fontWeight: 700
    lineHeight: 1.17
    letterSpacing: 0px
    textTransform: uppercase
  heading-section:
    fontFamily: "Univers Condensed, Arial Narrow, Arial, sans-serif"
    fontSize: 22px
    fontWeight: 700
    lineHeight: 1.09
    letterSpacing: 0px
    textTransform: uppercase
  heading-sub:
    fontFamily: "Univers Condensed, Arial Narrow, Arial, sans-serif"
    fontSize: 18px
    fontWeight: 700
    lineHeight: 1.11
    letterSpacing: 0px
    textTransform: uppercase
  body-large:
    fontFamily: "Univers Condensed, Arial Narrow, Arial, sans-serif"
    fontSize: 16px
    fontWeight: 400
    lineHeight: 1.4
    letterSpacing: 0px
  body:
    fontFamily: "Univers Condensed, Arial Narrow, Arial, sans-serif"
    fontSize: 14px
    fontWeight: 400
    lineHeight: 1.29
    letterSpacing: 0px
  nav-link:
    fontFamily: "Univers Condensed, Arial Narrow, Arial, sans-serif"
    fontSize: 16px
    fontWeight: 700
    lineHeight: 1.13
    letterSpacing: 0.88px
    textTransform: uppercase
  button-ui:
    fontFamily: "Univers Condensed, Arial Narrow, Arial, sans-serif"
    fontSize: 16px
    fontWeight: 700
    lineHeight: 1.0
    letterSpacing: 0px
    textTransform: uppercase
  label:
    fontFamily: "Univers Condensed, Arial Narrow, Arial, sans-serif"
    fontSize: 14px
    fontWeight: 700
    lineHeight: 1.14
    letterSpacing: 0.144px
    textTransform: uppercase
  caption:
    fontFamily: "Univers Condensed, Arial Narrow, Arial, sans-serif"
    fontSize: 12px
    fontWeight: 400
    lineHeight: 1.42
    letterSpacing: 0px
    textTransform: uppercase
  caption-bold:
    fontFamily: "Univers Condensed, Arial Narrow, Arial, sans-serif"
    fontSize: 12px
    fontWeight: 700
    lineHeight: 1.17
    letterSpacing: 0px
    textTransform: uppercase

spacing:
  xs: 4px
  sm: 8px
  md: 12px
  lg: 16px
  xl: 24px
  2xl: 48px
  3xl: 80px
  4xl: 120px

rounded:
  none: 0px
  xs: 2px
  sm: 4px
  pill: 9999px

components:
  button-primary:
    backgroundColor: "{colors.surface}"
    textColor: "{colors.on-surface}"
    typography: "{typography.button-ui}"
    rounded: "{rounded.none}"
    padding: 13px 24px
    border: 1px solid {colors.surface}
  button-primary-hover:
    backgroundColor: "{colors.ink-muted}"
    textColor: "{colors.on-surface}"
    typography: "{typography.button-ui}"
    rounded: "{rounded.none}"
    padding: 13px 24px

  button-secondary:
    backgroundColor: "{colors.background}"
    textColor: "{colors.ink}"
    typography: "{typography.button-ui}"
    rounded: "{rounded.none}"
    padding: 13px 24px
    border: 1px solid {colors.background}
  button-secondary-hover:
    backgroundColor: "{colors.ink-subtle}"
    textColor: "{colors.on-surface}"
    typography: "{typography.button-ui}"
    rounded: "{rounded.none}"
    padding: 13px 24px

  button-ghost:
    backgroundColor: "{colors.background}"
    textColor: "{colors.ink-subtle}"
    typography: "{typography.caption-bold}"
    rounded: "{rounded.none}"
    padding: 6px 8px
    border: 1px solid {colors.border}
  button-ghost-hover:
    textColor: "{colors.primary}"
    backgroundColor: "{colors.background}"
    typography: "{typography.caption-bold}"
    rounded: "{rounded.none}"
    padding: 6px 8px

  card:
    backgroundColor: "{colors.background}"
    textColor: "{colors.ink}"
    typography: "{typography.body}"
    rounded: "{rounded.none}"
    padding: 8px
    border: 0px solid {colors.border}
  card-dark:
    backgroundColor: "{colors.surface}"
    textColor: "{colors.on-surface}"
    typography: "{typography.body}"
    rounded: "{rounded.none}"
    padding: 8px

  input:
    backgroundColor: "{colors.surface}"  # dark panel context — was {colors.background} #fff giving 1:1 with on-surface text
    textColor: "{colors.on-surface}"
    typography: "{typography.body-large}"
    rounded: "{rounded.none}"
    padding: 0px 0px 6px 0px
    border: 0px 0px 1px 0px solid {colors.on-surface}
  input-focus:
    backgroundColor: "{colors.primary}"
    textColor: "{colors.on-primary}"
    rounded: "{rounded.none}"
    padding: 0px 0px 6px 0px
    border: 1px solid {colors.ink}

  badge-live:
    backgroundColor: "{colors.background}"
    textColor: "{colors.ink}"
    typography: "{typography.caption-bold}"
    rounded: "{rounded.none}"
    padding: 8px
  badge-tag:
    backgroundColor: "{colors.background}"
    textColor: "{colors.ink-subtle}"
    typography: "{typography.caption-bold}"
    rounded: "{rounded.none}"
    padding: 6px 8px
    border: 1px solid {colors.border}
  badge-tag-hover:
    backgroundColor: "{colors.background}"
    textColor: "{colors.primary}"
    typography: "{typography.caption-bold}"
    rounded: "{rounded.none}"
    padding: 6px 8px
    border: 1px solid {colors.primary}

  nav-bar:
    backgroundColor: "{colors.background}"
    textColor: "{colors.ink}"
    typography: "{typography.nav-link}"
    rounded: "{rounded.none}"
    padding: 0px 16px
    border: 0px 0px 1px 0px solid {colors.on-surface}

  schedule-cell:
    backgroundColor: "{colors.background}"
    textColor: "{colors.ink}"
    typography: "{typography.caption}"
    rounded: "{rounded.none}"
    padding: 6px 8px
    border: 1px solid {colors.border}
  schedule-cell-active:
    backgroundColor: "{colors.primary}"
    textColor: "{colors.on-primary}"
    typography: "{typography.caption}"
    rounded: "{rounded.none}"
    padding: 6px 8px
---

# NTS Radio Design System

## Overview

NTS Radio operates at the uncompromising intersection of raw function and editorial conviction. The canvas is pure {colors.background} white held against absolute {colors.surface} black — no off-white, no warm tones, no softening. Every surface is either on or off. The result reads less like a consumer product and more like a printed programme guide: urgent, dense, and proudly undecorated. The colour system is strictly achromatic except for a single electric sky-blue accent ({colors.primary}), which surfaces only under focused states and occasional editorial badges, keeping the palette honest to the brand's underground-radio ethos.

Typography is the entire design. Univers Condensed — a self-hosted, tightly compressed grotesque — runs the show at every level, universally uppercased, with weight toggling between 400 and 700 as the hierarchy mechanism. There are no serifs, no display alternatives, no expressive faces for editorial moments. The condensed geometry packs more information into narrower columns, serving the schedule-grid format where show title, time, and station label must coexist in a 120px cell. Tracking is near-zero on most sizes; the 0.88px letter-spacing on nav items is the only departure, lending the top bar a slightly more composed register.

Layout is dense by design. The homepage presents a full-width featured broadcast panel alongside a column-based schedule grid that scrolls off the right edge — a horizontal data table masquerading as a hero. Spacing is tight: the dominant values are 6px, 8px, and 12px. Borders define structure instead of whitespace; 1px rules in {colors.border} and {colors.on-surface} subdivide the grid rather than any gutter system. The overall impression is of a station that broadcasts constantly and trusts you to navigate it.

**Key Characteristics:**
- Strict binary palette: {colors.background} white canvas with {colors.surface} black fills — zero mid-tones on intentional surfaces
- Single brand accent {colors.primary} (sky blue `#1eaedb`) reserved for hover links, focus rings, and editorial accent moments
- Univers Condensed exclusively — a self-hosted compressed grotesque, all-caps at every hierarchy level
- Weight-only type hierarchy: 400 for secondary info, 700 for anything you should read first
- Near-zero letter-spacing across all sizes — condensed geometry does the work, not tracking
- Dense schedule-grid layout: 8px cells, 1px borders, information packed to the edge
- Horizontal scroll on the schedule panel — radio time-slots demand more horizontal space than mobile affords
- Borders as structure: `1px solid {colors.border}` divides rather than whitespace or shadows
- Motion is minimal and functional: 100ms linear transitions on links and buttons only
- `flickerAnimation` keyframe on the LIVE NOW indicator — the only decorative motion on the site
- No card shadows in primary UI; the flat aesthetic reinforces the broadcast-monitor aesthetic
- Tag/genre chips use `{colors.border}` outline with `{colors.ink-subtle}` text — secondary, not primary affordances

## Colors

### Primary
- **White** (`{colors.background}`): The dominant canvas. Header, all card backgrounds, page surface. NTS uses white as an active design choice, not a default.
- **Black** (`{colors.surface}`): Fills the channel-selector bars, the persistent player bar, modal overlays, and the header-nav bottom border on dark contexts.

### Mid-tones (monochromatic scale)
- **Dark grey** (`{colors.surface-muted}`): Border colour on dark panels and primary button borders. The only grey that takes a structural role.
- **Mid grey** (`{colors.ink-muted}`): Secondary text — show descriptions, secondary nav items, links at rest on dark backgrounds.
- **Light grey** (`{colors.ink-subtle}`): Tag chip text, tertiary labels, schedule-row secondary copy. Sits comfortably on both {colors.background} and {colors.surface}.
- **Silver** (`{colors.border-light}`): Light-context dividers, input bottom borders.

### Brand Accent
- **Sky blue** (`{colors.primary}`): Reserved for hover and focus states across the entire site, and for the NTS Book Club badge. Applying it to any decorative surface would break the chromatic restraint that makes its appearance feel electric.

### Interaction States
- **Link hover** (`{colors.text-hover}`): A deeper cobalt (`#3860be`) distinct from the focus blue — links feel interactive, not selected.
- **Focus ring** (`{colors.focus-ring}`): 1px solid black outline on form inputs in their default state; shifts to `{colors.primary}` tint with blue background on active focus.

### Semantic
No semantic success/warning/error colours appear in the primary UI. The live broadcast indicator uses a `{colors.background}` badge with `{colors.ink}` text rather than red.

## Typography

### Font Family
- **Primary**: Univers Condensed (self-hosted: `UniversCom-57Condensed.woff2`, `UniversLTPro-BoldCond.woff2`), with fallbacks: `Arial Narrow, Arial, sans-serif`
- **Closest Google Fonts equivalent**: Barlow Condensed (weight 400/700) or Oswald (weight 400/700)
- No secondary typeface. No monospace. No serif. One face, two weights, all caps.

### Hierarchy

The complete type scale is in the `typography:` token block above. Use those tokens directly via reference.

| Token | Use |
|---|---|
| `display-hero` | Section headers, show-title overlays at 42px |
| `display` | Channel labels, featured-item headings at 24px |
| `heading-section` | "NTS PICKS", "INFINITE MIXTAPES" labels at 22px |
| `heading-sub` | Card show names, station labels at 18px |
| `body-large` | Show description body copy, about text at 16px |
| `body` | Schedule grid cells, supporting copy at 14px |
| `nav-link` | Top navigation bar items (RADIO, LATEST, EXPLORE…) |
| `button-ui` | CTA buttons: SUBSCRIBE, BECOME AN NTS SUPPORTER |
| `label` | Badge labels, LIVE NOW chip, time stamps |
| `caption` | Genre tags, station sub-labels, timestamps in grid |
| `caption-bold` | Genre chips, tag pills, section eyebrows at 12px/700 |

### Principles
- All-caps is non-negotiable. Univers Condensed is designed for it — the letterforms only feel complete uppercased.
- Weight is binary: 400 (reading mode) or 700 (scanning mode). No 500 or 600 in practice.
- Size steps are small and functional: 42, 24, 22, 18, 16, 14, 12 — a tightly packed utility scale.
- Letter-spacing is intentionally suppressed (0px on almost everything) except the 0.88px on nav items.
- Line-height is tight throughout (1.0–1.4) — the schedule grid demands maximum vertical density.

## Layout

### Spacing System
The complete spacing scale is in the `spacing:` token block above. Base unit: 4px, with 8px as the dominant rhythm.

The spacing personality is extremely tight. NTS treats whitespace as wasted broadcast time — cells pack to the edge, and the most common padding values (6px, 8px, 12px) reflect a data-dense grid interface rather than a marketing site's generous breathing room.

### Grid & Container
- Max content width: 1920px (fluid to viewport, no fixed container)
- Schedule panel: horizontal-scroll grid, ~120px column cells for each time slot
- Featured hero panel: full-width with an image/editorial card overlaid with title and description
- Right sidebar: fixed-width scrollable show queue (~200px), always visible on desktop
- Mobile: single-column stacked layout; schedule grid becomes vertically scrollable

### Whitespace Philosophy
- Borders separate, whitespace does not. Structure comes from 1px rules, not gaps.
- Padding is 6–12px within cells; 16–24px between major page sections.
- The dense layout signals live broadcast immediacy — space would imply editorial leisure.

## Elevation & Depth

| Level | Treatment | Use |
|---|---|---|
| Flat (Level 0) | No shadow | Schedule grid, nav bar, all primary surfaces |
| Subtle (Level 1) | `rgba(0,0,0,0.05) 0px 1px 20px 0px` | Dropdown menus, floating schedule overlays |
| Card (Level 2) | `rgba(0,0,0,0.24) 0px 6px 6px 0px, rgba(0,0,0,0.12) 0px 0px 6px 0px` | Modals, the persistent player panel |
| Overlay (Level 3) | `rgba(0,0,0,0.5)` scrim | Full-screen overlays, mobile drawer |
| Focus Ring | `1px solid {colors.focus-ring}` | All interactive elements; shifts to `{colors.primary}` background on input focus |

**Shadow Philosophy**: NTS is functionally flat. The dominant aesthetic is the 1px border, not the shadow — surfaces are either separated by a line or they aren't. Shadows appear only in genuine overlay contexts (modals, dropdowns) and use opaque-black rgba rather than brand-tinted values. This keeps the interface feeling like a signal board, not a layered document.

## Shapes

The complete radius scale is in the `rounded:` token block above. The system uses:

| Token | Value | Use |
|---|---|---|
| `none` | 0px | Everything primary: buttons, cards, inputs, schedule cells, badges |
| `xs` | 2px | Occasional button microradius found in UI controls |
| `sm` | 4px | Checkbox and toggle elements |
| `pill` | 9999px | Profile avatars, circular play buttons |

NTS is almost entirely square-cornered. The design ethos rejects softening — a radio schedule is a grid of information, not a consumer-friendly product carousel. `{rounded.none}` applies to 95% of interactive elements.

## Components

The complete component spec lives in the `components:` token block above. Reference component tokens directly.

### Button variants

- **`button-primary`** — Full black fill, white uppercase text, square corners. Used for "Subscribe" and "Become an NTS Supporter". The most prominent CTA sits inside a dark context panel.
- **`button-secondary`** — White fill with white border on dark panels. Visually subordinate to primary but still high contrast.
- **`button-ghost`** — Transparent with `{colors.border}` outline and `{colors.ink-subtle}` text. Used for genre/tag filter chips (e.g. "Detroit Techno"). Hover lifts colour to `{colors.primary}`.

### Cards
Schedule cards and show tiles are square-cornered (`{rounded.none}`), 8px padding, with the show artwork filling the full card face. Title and station metadata sit below in `{typography.body}` and `{typography.caption}`. Dark cards use `{colors.surface}` background with `{colors.on-surface}` text.

### Inputs
Bottom-border-only inputs (no surrounding box) on dark subscription panels. Default state: transparent background, white text, white bottom border. Focus state: `{colors.primary}` background with `{colors.on-primary}` text and 1px black outline — an unusually bold focus treatment that makes the active field unmissable against the dark panel.

### Badges / Tags
`badge-live`: plain white badge, bold black caps — "LIVE NOW" in `{typography.caption-bold}`. No colour, no animation (other than the `flickerAnimation` opacity pulse on the indicator dot). `badge-tag`: outline chip with grey text for genre labels; hover turns the border and text to `{colors.primary}`.

### Navigation
Full-width sticky header: white background, `{typography.nav-link}` at 700 weight, 0.88px tracking. Nav items are bare text links — no dividers, no underlines at rest, no icons. Separator is a white `0px 0px 1px solid` bottom-border when sitting above a dark channel bar.

### Schedule Grid
The signature NTS component: a dense horizontal time-table where each cell is `{typography.caption}` on `{colors.background}` white, bordered with `{colors.border}`. Active/live cells fill with `{colors.primary}` — the only context where the accent colour floods a surface rather than appearing as a text colour or line.

## Do's and Don'ts

### Do
- Use `{colors.background}` as the default surface for all content areas — reserve `{colors.surface}` black for channel bars, player, and promotional panels
- Set all type in uppercase — `text-transform: uppercase` is not stylistic, it is structural to the Univers Condensed system
- Apply `{rounded.none}` to all interactive elements; square corners are a brand commitment, not a default
- Use `{colors.primary}` only for hover/focus states and genuinely live/active UI indicators — its rarity makes it feel like a broadcast signal
- Pair `{typography.button-ui}` (700 weight, uppercase) with all CTAs regardless of context — buttons never use a lighter weight
- Set `1px solid {colors.border}` rules to define structure rather than padding/margin gaps
- Keep the `flickerAnimation` keyframe exclusive to LIVE NOW indicators — any other flicker would dilute the broadcast connotation
- Preserve 0px letter-spacing on all display and body sizes; the condensed geometry handles visual density

### Don't
- Don't introduce rounded corners on cards, buttons, or input boxes — even `{rounded.sm}` 4px would soften the grid aesthetic into something generic
- Don't use `{colors.primary}` as a fill for decorative panels or large background areas — it's a signal colour, not a brand colour
- Don't mix Univers Condensed with any secondary typeface for editorial moments — the single-face discipline is the discipline
- Don't add whitespace padding beyond 24px between sections — generous spacing would contradict the broadcast-grid density
- Don't use mid-range grey (#aaaaaa, #bbbbbb) as new tokens — the system uses the exact five achromatic steps already declared
- Don't shadow cards or schedule cells — flat 1px borders only; shadows imply hierarchy the schedule grid doesn't have
- Don't use title-case or sentence-case for any UI label — lowercase is never correct in NTS's system
- Don't tint shadows with brand colour — the only brand tint in the system is `{colors.primary}`, and it doesn't belong in shadows

---

## Responsive Behavior

### Breakpoints
| Name | Width | Key Changes |
|---|---|---|
| Mobile Small | <400px | Single-column layout; schedule grid hidden or collapsed to current show only |
| Mobile | 400–767px | Single-column; featured hero stacks vertically; nav collapses to hamburger |
| Tablet | 768–1023px | Two-column grid begins; schedule shows 3–4 time slots visible |
| Desktop | 1024–1499px | Full schedule grid with horizontal scroll; right sidebar at ~200px fixed |
| Large Desktop | ≥1500px | Grid expands to fill viewport; more time slots visible without scroll |

### Touch Targets
- Buttons maintain `13px 24px` padding — at 16px body font this gives adequate touch height (~42px) on mobile
- Genre tag chips (6px 8px) are smaller than ideal on touch; acceptable as secondary filters rather than primary navigation
- The player bar persists at the bottom on mobile with full-width touch targets for play/pause/skip

### Collapsing Strategy
- Navigation collapses to hamburger at ≤767px; the top nav becomes a slide-in drawer on `{colors.surface}` black background
- Schedule grid is the most complex responsive element: collapses from horizontal time-table to a stacked list of upcoming shows
- The featured hero loses its editorial overlay at ≤530px; image goes full-width, text drops below
- Right sidebar collapses entirely on mobile; accessible via a dedicated "INFINITE MIXTAPES" tab

### Image Behavior
- Show artwork is always square-cropped via `object-fit: cover`
- Hero images fill 100% of their container width at all breakpoints with no max-width
- Thumbnails in the schedule grid maintain aspect ratio via `padding-top: 56.25%` (16:9 basis)

---

## Agent Prompt Guide

### Quick Color Reference
- Background: `{colors.background}` (#ffffff)
- Primary text: `{colors.ink}` (#000000)
- Secondary text: `{colors.ink-muted}` (#666666)
- Tertiary text: `{colors.ink-subtle}` (#999999)
- Brand accent / focus: `{colors.primary}` (#1eaedb)
- Dark panel fill: `{colors.surface}` (#000000)
- Border: `{colors.border}` (#4c4c4c)
- CTA background: `{colors.surface}` (#000000)

### Example Component Prompts

- "Build a schedule-grid cell using `{colors.background}` fill, `1px solid {colors.border}` on all sides, `{typography.caption}` text in `{colors.ink}`, 6px 8px padding, `{rounded.none}`. Active/live state: swap fill to `{colors.primary}` and text to `{colors.on-primary}`."

- "Create an NTS-style show card: `{rounded.none}`, `{colors.background}` background, 8px padding, square artwork image above, show title in `{typography.heading-sub}` at `{colors.ink}`, station label in `{typography.caption}` at `{colors.ink-subtle}`. Border: none on card itself — the grid provides the boundary."

- "Build a primary CTA button: `{colors.surface}` background, `{colors.on-surface}` text, `{typography.button-ui}` (700 weight, uppercase, 16px), `{rounded.none}`, `13px 24px` padding, `1px solid {colors.surface}` border. Hover: `{colors.ink-muted}` background."

- "Create a genre tag chip: transparent background, `{colors.ink-subtle}` text, `{typography.caption-bold}` (12px, 700, uppercase), `{rounded.none}`, `6px 8px` padding, `1px solid {colors.border}` outline. Hover: swap border and text to `{colors.primary}`."

- "Build the NTS sticky nav bar: full-width `{colors.background}` white, `{typography.nav-link}` (700, uppercase, 0.88px tracking) for each item in `{colors.ink}`, `{rounded.none}`, `0px 16px` horizontal padding, bottom border `1px solid {colors.on-surface}`. Logo in `{colors.ink}` uppercase wordmark flush-left. Live channel indicators as white badges (`badge-live`) to the left of the wordmark."

### Iteration Guide

1. Start with `{colors.background}` white as the base canvas. The page is white until a structural element demands black.
2. Apply `{colors.surface}` black sparingly and intentionally — player bar, channel selector rows, promotional panels only.
3. All type is uppercase Univers Condensed. Set `text-transform: uppercase` on every text element from the outset.
4. Use weight 700 for anything that should be read first (headings, labels, nav, buttons); 400 for supporting copy.
5. Structure with `1px solid {colors.border}` rules, not whitespace. Keep padding tight: 6–12px inside cells.
6. Keep `{rounded.none}` on everything interactive. No softening.
7. Introduce `{colors.primary}` only as a hover/active signal or for a live-broadcast indicator — never as ambient colour.

---

## Attribution

Independent design analysis from [Design Swatches](https://designmd.santiagoalonso.com) by [Santiago Alonso](https://santiagoalonso.com). Based on publicly observable interface patterns. Not affiliated with or endorsed by NTS Radio. Brand names and trademarks belong to their respective owners.
