Early Spring · Lab · Guidelines
← Main site
Lab · Guidelines · v1.0

Foundations of Early Spring.

A working spec for building sites in the same style — tokens, voice, type, components, motion, and atmospheric effects. The page itself is the artifact: every pattern documented here is also rendered here.

01 / Thesis

The site should feel like a well-set page, not a brochure.

Editorial calm before everything: paper, sapphire, Lausanne, generous breath. The visitor should feel they've walked into a room — quiet, deliberately arranged, slightly under-lit — not been served a sales surface.

02 / Tokens

Two colors. One typeface. Five sizes of breath.

Color

Paper
#FFFFFF · pure white
Sapphire (Ink)
#0F52BA · the only ink
Faint rule
rgba(15,82,186,0.18)
Faded ink
rgba(15,82,186,0.42)

Two foundational colors: paper and sapphire. The other two entries are sapphire at reduced opacity. No greys. No black. No second hue. Use opacity, not new colors. Faded ink is reserved for soft emphasis (<em>), supporting metadata, and dimmed states.

Marks

Lockup · horizontal
/early-spring-lockup.svg
Primary brand marker. Nav, footer, large placements, partner-style headers — anywhere the page has horizontal room and the wordmark needs to read.
Download
Lockup · stacked
/early-spring-stacked.svg
Square or near-square frames — social tiles, end-cards, app icons inside larger compositions, anything taller than wide.
Download
Isotype · mark only
/early-spring-isotype.svg
Tight slots — favicon, social avatars, watermarks, animated stings. Use only where the brand context is otherwise clear.
Download

Each mark ships as an SVG sized to its native ratio. Render in sapphire via CSS mask so a single asset reproduces in any color the page calls for. Never recolor amber, never composite over photography, never reflow the elements inside the lockup.

Need a file? Each card downloads on demand in sapphire, black, or paper (white) — as SVG (vector, recolored at source) or PNG (~2K longest edge). Every export has a transparent background; the paper variant is for placing on dark surfaces.

/* CSS mask pattern — paints the SVG in any single color */
.mark--lockup {
  width: 124px;
  height: 26px;
  background: var(--ink);
  -webkit-mask: url(/early-spring-lockup.svg) left center/contain no-repeat;
          mask: url(/early-spring-lockup.svg) left center/contain no-repeat;
}

Typography

TWK Lausanne. Self-host all five weights. No fallback typeface mixing — system-ui only as a network-failure backup. Designed by Kasper-Florio in Switzerland.

@font-face { font-family: "TWK Lausanne"; src: url("/fonts/TWKLausanne-300.woff2") format("woff2"); font-weight: 300; font-display: swap; }
@font-face { font-family: "TWK Lausanne"; src: url("/fonts/TWKLausanne-400.woff2") format("woff2"); font-weight: 400; font-display: swap; }
@font-face { font-family: "TWK Lausanne"; src: url("/fonts/TWKLausanne-600.woff2") format("woff2"); font-weight: 600; font-display: swap; }
@font-face { font-family: "TWK Lausanne"; src: url("/fonts/TWKLausanne-800.woff2") format("woff2"); font-weight: 800; font-display: swap; }
@font-face { font-family: "TWK Lausanne"; src: url("/fonts/TWKLausanne-900.woff2") format("woff2"); font-weight: 900; font-display: swap; }

Spacing

:root {
  --gutter: clamp(20px, 3vw, 56px);  /* horizontal padding */
  --max: 1640px;                      /* page max-width */
}

Section vertical padding scales with the section's weight: 56px for utility sections, 64–96px for primary, 96px+ for hero. Always vertical first, horizontal second.

Surfaces

/* Floating panels, cards, the Forecast Builder */
background: rgba(255,255,255,0.62);
backdrop-filter: blur(28px) saturate(140%);
border: 1px solid rgba(15,82,186,0.16);
border-radius: 18px;
box-shadow: 0 32px 80px rgba(15,82,186,0.22);
03 / Voice

Editorial, atmospheric, slightly underwritten. Never corporate.

Naming rules

01
Early Spring  not  Early Spring Studio
The name is the name. Adding "Studio" softens the brand into a category; it isn't one.
02
Engineering Serendipity  not  We Engineer Serendipity
Gerund. The studio is engaged in an ongoing argument with chance, not announcing a method.
03
Brooklyn → Worldwide — never use this trope
The phrase reads as ad-agency boilerplate. Locate the studio (Brooklyn) and let the work speak about reach.
04
Use the interpunct (·) as separator — U+00B7
Cleaner than slash, less aggressive than pipe, more deliberate than comma. ⌥ + ⇧ + 9 on Mac.
05
Em dashes — for breath, not commas in chains
A comma stacks ideas. An em dash inserts space. Use it when the sentence needs to pause without ending.
06
Soft emphasis via <em>, not bold
em is rendered at 42% sapphire — it dims rather than shouts. Used for the second half of a phrase, alternate readings, or quiet asides.
07
"Lab · X" for unlisted experiments
Anything live but not part of the main IA — the Forecast Builder, easter eggs, internal tools — is named Lab followed by an interpunct.

Tone calibration

Default register: a curator describing what they made, addressed to one reader. Avoid the "we" of advertising. Avoid the "you" of self-help. The studio narrates; the reader observes.

Numbers as figures (5 continents, 40 speakers, 2024 Miami Open) — the precision is part of the texture. Spell out when running prose demands it.

04 / Typography

Tight headlines. Open body.

Display Engineering Serendipity.
H2 A two-day sensory journey across five continents.
H3 The Insight
Body For UNWTO World Tourism Day 2023, the Saudi Ministry of Tourism partnered with Early Spring to develop strategy and concept a two-day experience in Riyadh.
Kicker Lab · The Forecast Builder
Mono --ink: #0F52BA;

Sizing

/* Display (hero, page titles) */
font-size: clamp(48px, 7vw, 144px);
font-weight: 800;
line-height: 0.94;
letter-spacing: -0.028em;

/* H2 (section titles) */
font-size: clamp(28px, 3.4vw, 48px);
font-weight: 800;
line-height: 1.04;
letter-spacing: -0.022em;

/* Kicker (above titles, brand markers) */
font-size: 11px;
font-weight: 700;
letter-spacing: 0.10em;
text-transform: uppercase;
opacity: 0.6;

/* Body */
font-size: 15px;
font-weight: 400-500;
line-height: 1.55-1.6;
max-width: 60-64ch;

Letter-spacing curve

Bigger type → tighter letter-spacing (negative tracking pulls the headline together). Smaller type, especially uppercase kickers, opens up to 0.06–0.10em. The visual rhythm is part of the brand.

Italics

True italics for foreign words, cited titles, and the occasional editorial flourish. Soft emphasis is the dimmed sapphire-via-<em> pattern, not italic.

05 / Components

Few. Each one earned.

Nav

Sticky, paper background with backdrop-filter blur, faint rule below. Three-column grid: brand left, page links centered, time + lab pill right. Sapphire on paper, no shadow.

Hero (carousel)

Aspect ratio 21/9 on desktop, taller on mobile. Slides cross-fade by opacity (no slide motion). A muted bubble row at bottom-right; active bubble stretches into a pill.

Work rows

Expandable accordion list. Each row: number / client / title / year. Click expands inline with case-study overview + "See the full case study →" link.

Case study pages

Hero media, 4-column meta dl (Client / Year / Type / Location), 16/9 hero image or video, 2-column overview + insight grid below. No forced "Ask / Solution / Result" format unless the project has it.

Floating panels

Frosted glass at 62–78% paper opacity, 22–28px backdrop blur, faint sapphire border, 18px radius, soft sapphire shadow. Slide in from edge with cubic-bezier easing.

Buttons

Pill (999px radius) for nav and inline actions; rectangle for form primary. Default is paper bg + sapphire 1px border + sapphire text; hover fills with sapphire and reverses to paper.

Form fields

No box border. Underline only — border-bottom: 1px solid rgba(255,255,255,0.32). The whole field is sapphire-on-ink (the contact form sits inside the sapphire footer panel, inverting the page).

Sliders (Forecast Builder pattern)

accent-color: var(--ink). 18px round thumb desktop, 22px mobile. Track: 4px rgba(15,82,186,0.18). No label tick marks.

The "em dimmed" pattern

/* Apply once, use everywhere via <em> in HTML */
em { font-style: normal; color: rgba(15,82,186,0.42); }

/* Italics get an explicit class instead */
.italic { font-style: italic; color: inherit; }

Hijacking <em> for visual dimming (rather than italics) lets editorial copy use semantic markup that produces the brand's signature soft-emphasis treatment automatically.

06 / Motion

Quiet, cubic, never bouncy.

Motion is for confirmation, not entertainment. A hover should acknowledge the cursor; a panel should arrive with weight; an effect should feel meteorological, not animated. No spring overshoot, no easter-egg bounce, no parallax.

Easings

/* Default — for entrances, panel slides, large transitions */
cubic-bezier(0.2, 0.8, 0.2, 1)

/* State changes — hover, focus, color shifts */
ease (or ease-in-out for symmetric)

Durations

Hover defaults

/* Move up by a hair, never sideways */
.element:hover { transform: translateY(-1px); }

/* Or fill with ink, reverse to paper */
.btn:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }
07 / Structure

BEM-ish. Schema-rich. Lab-aware.

CSS naming

.case__title       /* block__element */
.row__client       /* repeated row component */
.fc-panel          /* feature-prefix when isolated subsystem */
.fc-row__value     /* feature-prefix__element */
body.is-windy      /* state class on body for global modes */

HTML conventions

Section IDs (the public IA)

id="top"        — page brand anchor
id="work"       — case study list
id="thinking"   — essays, forecasts, editorial
id="contact"    — form + footer

/about          — about page
/work/[slug]    — case study page
/lab/[slug]     — unlisted experiment

URL conventions

Routing pattern (vercel.json)

{
  "rewrites": [
    { "source": "/about", "has": [{ "type": "host", "value": "earlyspring.nyc" }],
      "destination": "/early-spring-about-lab.html" },
    { "source": "/lab/:slug", "has": [{ "type": "host", "value": "earlyspring.nyc" }],
      "destination": "/lab-:slug.html" }
  ],
  "redirects": [
    { "source": "/early-spring-about-lab.html", "destination": "/about", "permanent": true }
  ]
}
08 / Starter

A barebones page in the style.

Drop into any new site. Renders a top nav, a hero block, and a contact section using the design system above. Replace placeholders, keep the architecture.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>[Page] — [Brand]</title>
<link rel="canonical" href="https://[brand].com/[path]" />
<style>
  @font-face { font-family: "TWK Lausanne"; src: url("/fonts/TWKLausanne-400.woff2") format("woff2"); font-weight: 400; font-display: swap; }
  @font-face { font-family: "TWK Lausanne"; src: url("/fonts/TWKLausanne-800.woff2") format("woff2"); font-weight: 800; font-display: swap; }

  :root {
    --paper: #FFFFFF;
    --ink: #0F52BA;
    --rule: rgba(15,82,186,0.18);
    --gutter: clamp(20px, 3vw, 56px);
    --max: 1640px;
  }
  * { box-sizing: border-box; margin: 0; padding: 0; }
  html, body { background: var(--paper); color: var(--ink); font-family: "TWK Lausanne", system-ui, sans-serif; }
  ::selection { background: var(--ink); color: var(--paper); }
  em { font-style: normal; color: rgba(15,82,186,0.42); }

  .nav { position: sticky; top: 0; z-index: 20; background: rgba(255,255,255,0.92); backdrop-filter: blur(10px); border-bottom: 1px solid var(--rule); }
  .nav__inner { max-width: var(--max); margin: 0 auto; padding: 16px var(--gutter); display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 32px; }

  main { max-width: var(--max); margin: 0 auto; padding: 0 var(--gutter); }
  .hero { padding: 96px 0; }
  .hero h1 { font-size: clamp(48px, 7vw, 144px); font-weight: 800; line-height: 0.94; letter-spacing: -0.028em; max-width: 14ch; }

  .kicker { font-size: 11px; font-weight: 700; letter-spacing: 0.10em; text-transform: uppercase; opacity: 0.6; margin-bottom: 18px; }

  .contact { background: var(--ink); color: var(--paper); padding: 96px var(--gutter); }
  .contact a { color: inherit; }
</style>
</head>
<body>

<header class="nav">
  <div class="nav__inner">
    <a href="/">Brand</a>
    <nav><a href="#work">Work</a> <a href="#thinking">Thinking</a> <a href="/about">About</a></nav>
    <span>[Time] · NYC</span>
  </div>
</header>

<main>
  <section class="hero">
    <div class="kicker">Brand · Tagline</div>
    <h1>The headline. <em>A softer second half.</em></h1>
  </section>
</main>

<section class="contact" id="contact">
  <h2>Let's talk.</h2>
</section>

</body></html>