# ProductLed Design System — V2 ("silent alpha")

The system the ProductLed site and app actually run on. Ink surfaces, Geist, one
rationed yellow. Extracted from the live prototype, not from the 2020 brand guide.

**V1 is not deleted.** The 2020 system — Montserrat, white surfaces, yellow pill
buttons, hard offset shadows — remains valid for print, book covers, and anything
that has to sit on white. Everything on screen uses V2.

---

## Company context

**ProductLed** helps SaaS founders build products that sell themselves. The public
keyword is "product-led growth" — the go-to-market motion. What ProductLed actually
builds with a client is a **product-led business**. PLG is the entry, the business is
the outcome.

The brand pillar is **generosity**: lead by giving value away (a free assessment,
public books, free tiers) and inspire founders to let people try before they buy.
Working slogan: *Generosity wins.* Framed as the growth strategy, never as charity.

### Surfaces in this system

| Surface | What it is |
|---|---|
| **Marketing site** | productled.com. Dominant surface. Dark, typographic, one conversion action per viewport. |
| **The ProductLed Assessment** | The free product. Paste a URL, get your home / pricing / signup pages scored and your three biggest opportunities. |
| **The ProductLed Agent** | The AI that reads the funnel. |
| **Full Funnel Audit** | The paid deep version of the assessment. |
| **Implementation** | The service. Premium, selective, from $10,000/month. Delivered by **ProductLed Operators** (never "implementers" or "consultants"). CTA is "Contact us" only. |
| **The workspace** | Where a customer's report, opportunities and roadmap live. |
| **Books** | *Product-Led Growth* 2nd ed = Step 1, the strategy. *The Product-Led Playbook* = Step 2, the execution. A two-door pair. |

### The Market Ascent

Five levels, bottom to top. Every engagement is a climb up one level.

1. **Commodity** — build something differentiated
2. **Contender** — build a product that sells itself
3. **Cusp** — build a business that runs itself
4. **Chosen** — become the obvious choice
5. **Crusader** — give away what you sold, own the markets next to yours

### The ten agents

One **Orchestrator agent** you talk to, nine component agents that do the work:
Strategy, User, Model, Offer, Pricing, Onboarding, Data, Growth process, Team.
Referred to as "<Component> agent", sentence case. **Always tagged IN DEVELOPMENT
or PRODUCT DIRECTION in mono. Never presented as shipped.**

### Anchor lines

> The best products are meant to be experienced, not explained.

> Generosity wins.

Proof stat: **97 of 100 buyers would rather try than talk to sales** (Independent
survey, ProductLed, 2026). Drawn as a 10×10 dot grid, never printed as a giant numeral.

---

## Sources

- `ProductLed Site Prototype v2.dc.html` — the live click-through prototype of the
  whole site and app. **The source of truth for this system.** Lives in the
  *ProductLed Site & App 2026 Design* project.
- `ProductLed Design System v2.dc.html` — the written V2 spec document in that same
  project. This design system is its compiled form.
- `CLAUDE.md` in that project — the standing brand rules.
- V1 design system (2020 Brand Quick Reference, Montserrat, three-color palette) —
  superseded on screen, still current for print.

---

## Index

| Path | Purpose |
|---|---|
| `00-START-HERE.md` | Read first. What this is, how to read it, the five rules. |
| `readme.md` | This file. The full system. |
| `CHANGELOG.md` | What changed, and when. |
| `index.html` | Browsable index. Open it after downloading the folder. |
| `SKILL.md` | Agent-skill entry point. |
| `styles.css` | Global entry point. Import list only. |
| `tokens/` | `fonts` · `colors` · `typography` · `spacing` · `geometry` · `motion` · `base` |
| `components/core/` | Button, Card, Kicker, Chip, StatusTag |
| `components/data/` | ScoreRing, DotGrid, ProgressBar, OpportunityRow, MetricRow |
| `components/marketing/` | SectionHead, ScanLockup, Testimonial, Headshot, PullQuote, AscentMark, **AscentLogo** |
| `components/product/` | PanelShell, AgentTile, OperatorFloat |
| `ui_kits/website/` | productled.com — nav, home, pricing, report, footer |
| `ui_kits/app/` | The workspace — assessment, opportunities, agents, roadmap |
| `snippets/` | Copy-paste HTML for Webflow, WordPress, and hand-built pages |
| `guidelines/` | 21 foundation specimen cards |
| `assets/` | Logos, the Ascent mark, icon tiles, book covers, Wes headshot |

---

## CONTENT FUNDAMENTALS

ProductLed V2 writes like a **silent alpha**. State the claim once, small, and let the
numbers and the product do the bragging. V1's voice was urgent and loud ("Right now."
"The window is closing."). V2 is quieter and more certain.

### Voice

- **No superlatives.** Never "the best", "world-class", "revolutionary". The work is
  the argument.
- **Say it once.** A claim repeated is a claim being defended.
- **Short sentences. Periods over commas.** Stacked fragments are fine.
- **Specific over general.** Claim a number: 60,202 people, 400+ companies, 97 of 100,
  $1B+ in self-serve revenue, from $10,000/month.
- **Anti-jargon.** "sells itself" not "drives organic pipeline". "your product" not
  "your platform".
- **Generosity, not charity-speak.** "The expensive part is having us in the room. The
  knowledge is not."

### Mechanics

- **No em dashes.** Ever. Use a period or a comma.
- **Sentence case everywhere** — headlines, buttons, nav. Not Title Case. Not ALL CAPS
  except mono kickers and status tags.
- **The name:** always `ProductLed`. Capital P, capital L, no space, no hyphen.
- **No emoji.** Arrows (→) welcome, and encouraged in link CTAs and before/after lockups.
- **The yellow terminal period** closes a headline. It is the brand's signature move.

### Say this / not this

| Say | Not |
|---|---|
| ProductLed Assessment | WARP Week, ProductLed MBA, Masterclass |
| the ProductLed Agent | the bot, the tool |
| Full Funnel Audit | premium report |
| Implementation · ProductLed Operators | implementers, consultants, coaches |
| Opportunities | recommendations |
| "We reply within the week" | "No sales call" |
| Nav: Product · Advisory · Books · Pricing · Mission | Services, Resources, Blog |

**Canonical CTA labels:** "Start my free analysis" and "Talk to the team".
Implementation's CTA is "Contact us", only.

### Rhetorical moves

1. **Before / after with an arrow** — "11 min → 90 sec".
2. **Deletion language** — "They didn't shorten the path to value. They deleted it."
3. **Numbered takeaways** — three opportunities, five levels, ten agents.
4. **Problem / pattern / prescription** — name the pain, name the cause, prescribe the fix.
5. **Effort honesty** — every fix carries a relative effort in mono caps: HOURS,
   A FEW DAYS, WEEKS, A QUARTER.

---

## VISUAL FOUNDATIONS

V2 is **ink, five greys, and one rationed yellow**. The confidence comes from what is
absent: no gradients as decoration, no illustrations, no stock photography, no
graph-paper grids, no offset shadows. Typography and value shifts do all the work.

### Colors

- **Surfaces:** `#08090A` page, `#0E0F11` panel, `#16181B` float, plus a card wash
  (`white 0.03 → 0.006` gradient).
- **Text:** a five-step grey ramp, `#F7F8F8 → #8A8F98 → #62666D → #494D53 → #33363B`.
  Body is `#8A8F98`; anything that matters is `#F7F8F8`. **Only the first two steps
  are text colours at any size**. See Contrast duties below.

#### Contrast duties

Measured against `--pl-page` `#08090A` on 2026-09-17. WCAG AA asks 4.5:1 for normal
text and 3:1 at 24px and above.

| Token | Hex | On page | May carry text |
|---|---|---|---|
| `--pl-text` | `#F7F8F8` | 18.73:1 | any size |
| `--pl-text-2` | `#8A8F98` | 6.13:1 | any size, **and this is the label colour** |
| `--pl-text-3` | `#62666D` | 3.45:1 | 24px and up only |
| `--pl-text-4` | `#494D53` | 2.34:1 | **never** |
| `--pl-text-5` | `#33363B` | 1.64:1 | **never** |

Steps 4 and 5 are not text colours. They are for disabled states, decorative rules
and marks nobody has to read. Step 3 is for large quiet figures, a ghost numeral at
56px, not for labels, meta or mono kickers.

**This replaces the old role split.** The ramp used to read "labels, meta, mono
kickers" on step 3, "fine print, captions, placeholders" on step 4 and "footnotes"
on step 5. Those roles put small text at 3.45:1 and 2.34:1 wherever the system was
followed correctly.
**Every label, caption, kicker, byline and piece of fine print takes `--pl-text-2`.**

Panel `#0E0F11` and float `#16181B` sit about 4% and 11% lower again, so a value
that is borderline on the page ground is worse on a panel. Re-check anything near
the line rather than assuming the page figure carries.

**One signal needs the same care:** `--pl-next` `#62666D` is 3.45:1, so a "next /
not started" label in running text fails. Use it for the large numeral or the bar,
and set its caption in `--pl-text-2`.
- **One yellow, `#F3CA07`, with exactly six jobs:** the terminal period, mono kickers,
  status and bottleneck signals, headshot plates and the rank-one numeral, the 2px
  pull-quote rule, thin 3px progress fills. **One accent per viewport.**
- **Never:** a yellow page or section fill, a resting yellow button, the V1 highlight
  marker, two yellow elements competing, white text on yellow.
- **Text on yellow is always `#08090A`.**
- **Signals are desaturated on purpose** — `#5FA36B` strong, `#F3CA07` average,
  `#D98A45` fix-first, `#62666D` next. No pure red: a failing score is a next step,
  not an alarm.
- **Light plates** (`#FFFFFF`, `#08090A` text, `#C9C9C3` hairlines) appear only inside
  product mocks and in print. The site is dark; the screenshots inside it are not.

### Typography

- **Geist** for everything, **Geist Mono** for every label, number, timestamp and
  kicker. Montserrat and Source Sans 3 are retired.
- **Display never exceeds weight 500.** Confidence comes from size and negative
  tracking, not from bold.
- Tracking tightens as size grows: `-0.036em` at 84px, `-0.032em` at 64px,
  `-0.022em` at 32px, `-0.016em` at 17px.
- Body 15.5px / 1.7 on marketing, 13-14px inside product panels. Measure capped
  460-620px.
- **Mono has a size floor: 12px for anything a reader reads as words.** Captions,
  multi-word labels, figure titles and step names are 12px at `--pl-text-2`. 11px
  takes one or two words; 10.5px a single word or a numeral; 9.5px numerals and
  glyphs only. Uppercase strips the word-shape a reader recognises and kicker
  tracking removes what is left, so the two compound: a tracked uppercase phrase at
  10.5px is not a small label, it is an unreadable one.
- Tracking follows the same logic: `0.14em` kicker is for **one word**. A label that
  reads as a phrase takes `0.06em`. `0.12em` sits between, for short labels.

### Spacing and layout

- **1120px container, 24px edge padding.** Built for 1120px and up.
- **A section starts with a hairline:** `border-top: rgba(255,255,255,0.06)`, then
  48-56px of padding, then the kicker and headline.
- **Vertical rhythm 96 / 104 / 120px** between sections; 128px above the first headline.
- **Hero rule.** Above the fold is headline, subtitle and call to action only. Any
  visual goes BELOW the fold at full container width, never beside the headline.
- **CTA width rule.** A call to action is never wider than the headline or subtitle
  above it. Hero input lockups cap at about 470px.
- **Nav is 56px** on `rgba(8,9,10,0.8)` with a 16px backdrop blur.

### Backgrounds and imagery

- Ink, flat. No repeating patterns, no textures, no graph-paper grids, no decorative
  gradients.
- The only gradients allowed: the card wash, the panel bottom fade into the page, and
  yellow radial glows at 0.05-0.07 opacity behind hero content (pointer-events none).
- **Imagery:** real photos only. Headshots are cropped square, face centred, plain
  background, on a yellow plate. B&W is fine; duotones are not. No stock, no illustration.
- **Never draw brand imagery as SVG.** Use a placeholder and ask for the real asset.

### Borders, elevation, corners

- **Hairlines at four opacities:** 0.05 rows, 0.06 section rules and panel edges, 0.08
  cards that must read as objects, 0.12 outline buttons. Surfaces separate by value
  shift, not by heavy borders.
- **The 2px yellow left rule on a pull quote is the only 2px border in the system.**
- **One elevation:** `#16181B` + `0 12px 32px rgba(0,0,0,0.45)`, used for the
  `OperatorFloat` chip. Nothing else gets a shadow. Book covers get a
  `drop-shadow(0 24px 32px rgba(0,0,0,0.5))` filter.
- **Radii:** chips 5-9, buttons and inputs 8-11, cards 12-16, hero shells 18, discs 50%.
  **Never a pill.** The V1 999px button is retired.

### Animation and interaction

- **Elements change value, not size.** No hover scale, no spring, no overshoot, no
  parallax, no bounce.
- Durations: 160ms color and background, 200ms border warm-up, 320ms panel cross-fade,
  240ms page enter (fade + 6px rise). Easing `cubic-bezier(0.16, 1, 0.3, 1)` throughout.
- **Hover:** primaries flip white → yellow (text stays black). Outline buttons warm
  their border to `rgba(243,202,7,0.6)`. Text links grey up to `#F7F8F8`. Logo rows
  lift from 0.72 to 1 opacity.
- **Press:** 1px down. No shrink.
- **Focus:** 2px yellow ring, 2px offset from the page ground.
- **The one exception to "minimal": the logo climb.** Hovering the lockup runs the
  Market Ascent up the mark, level 1 to level 5, holds the full pyramid 2s, clears,
  repeats on a 6s loop. It is still value-only, no scale or rotation. See
  `components/marketing/AscentLogo.prompt.md`. Because it lights four extra yellow
  levels, never animate the mark next to another yellow element.

### Transparency and blur

Used twice: the sticky nav (`rgba(8,9,10,0.8)` + 16px blur) and the card wash. No
frosted glass inside page content.

### Card vocabulary

| Variant | Use |
|---|---|
| `wash` | Default. Gradient fill, 0.08 hairline, 16px radius. |
| `flat` | Testimonials and quote cards. `rgba(255,255,255,0.025)`. |
| `plain` | Hairline only, no fill. Sidebar and inline groupings. |
| `panel` | Product chrome. `#0E0F11`. Pair with `PanelShell`. |
| `shell` | End-CTA blocks. 18px radius, 0.09 hairline. |
| `float` | The one shadowed element. `#16181B`. |

### The product-panel pattern

Every product specimen on the site sits in the same shell: three grey dots, a mono
context label (usually a URL), then hairline-separated rows. In a hero the panel is
cropped by a bottom fade into the page ground. This is the system's signature
compositional move — it proves the product exists without a full screenshot.

---

## ICONOGRAPHY

V2 is close to icon-free. The prototype uses almost none, and that restraint is the
look. What stands in for icons:

- **Two-letter mono codes in a disc** — the ten agents (`Or`, `St`, `Us`, `Mo`, `Of`,
  `Pr`, `On`, `Da`, `Gr`, `Te`). 24px disc, 9.5px Geist Mono.
- **Unicode arrows** — `→` in link CTAs, before/after lockups and list bullets; `↻` for
  loops. Never an icon font for these.
- **Small geometry** — 3px dots as separators, 8px grey discs as panel chrome, 22px
  numerals for rank, conic-gradient rings for scores, dot grids for stats.
- **Brand logos** in the proof row come from `cdn.simpleicons.org` tinted `8A8F98`.
- **No emoji.** Ever.

If a genuine icon is ever needed, use **Lucide** (2px stroke, rounded caps, 24px grid)
at `--pl-text-2`, paired with a text label. **Flagged:** no in-house icon set was
provided for V2 — Lucide is a substitution, not a brand asset. Ask before shipping one.

### The mark

The **Market Ascent pyramid** — four bars narrowing to a yellow `#F3CA07` triangle
summit, one level per Ascent stage. Black bars on light, white bars on dark, the summit
is always yellow. Rounded-square tile for favicons, browser tabs, app icons, avatars.

The **wordmark** (with its yellow period) stays primary wherever there is room; the mark
covers the small squares. In the nav they sit together, mark then wordmark, 9px apart.

The mark has two forms. **`AscentMark`** is static, for anywhere the mark must not
move: favicons, tiles, avatars, print. **`AscentLogo`** is the lockup with the hover
climb, for the nav, hero and footer. The climb is the only motion the brand mark is
ever allowed.

Sources: `assets/mark-ascent.svg` (bare, currentColor bars — for inline SVG only; `currentColor`
does not resolve inside an `<img>`, so use `mark-ascent-white.svg` / `mark-ascent-black.svg`
there), `assets/icon-tile-light.svg`,
`assets/icon-tile-dark.svg`, `assets/logo-white.png`, `assets/logo-black.png`.

Don'ts, carried over from the 2020 guide: no rotate, no recolor, no shadow, no crop, no
tint or opacity, no stretch, no placing on a similar color.

---

## Intentional additions

Everything here is grounded in the prototype. Two notes on judgement calls:

- **`Headshot` falls back to a dark disc** when no photo is supplied. The brand rule is
  "real photos only, initials are not an acceptable shipping state" — so rather than
  render initials on yellow, a missing photo goes visibly unfinished. The yellow plate
  is earned by a real face.
- **`ProgressBar`** appears in the prototype only as inline markup. Extracted as a
  component because thin 3px fills recur across scores and roadmaps.

## Known gaps

- **The logo climb has not been reviewed on light plates.** On white the bars are
  `#08090A` and the same climb applies (`.pl-logo--on-light`), but that combination is
  unapproved. Ask before shipping the climb on a light surface.
- **Light mode is incomplete.** Only the light-plate tokens exist (the palette used
  inside product mocks and print). A full light-mode surface and text ramp has not been
  designed.
- **Three headshots are missing** — Christopher Gimmer, Ian Robertson, Gergana Berman.
  Testimonials in the kits render the dark-disc fallback until real photos land.
- **Nav naming.** The prototype says *Advisory* and *Books*; `CLAUDE.md` says
  *Implementation* and *Library*. This system follows the prototype. Needs a decision.
- **Fonts are loaded from Google Fonts.** If licensed Geist files are preferred, drop
  them into `assets/fonts/` and replace the `@import` in `tokens/fonts.css` with
  `@font-face` rules.
- **Component and UI-kit cards resolve the bundle namespace at runtime** (a small
  `plNamespace()` helper) because the namespace is derived from the project id. Once
  compiled, hard-code `window.<Namespace>` if you prefer.
