Add design/ contract; archive prior design system as provenance
Establish keysat's durable, vendor-neutral design contract (the standards /design backfill, document-as-is): - design/DESIGN.md — nine-section brand brief distilled from the prior Claude Design system (navy-on-cream-paper identity, sovereignty-first voice, component + motion rules, do's/don'ts). Manrope is canonical display (the README's "Archivo" was a stale placeholder). - design/tokens.tokens.json — W3C DTCG tokens from colors_and_type.css. - design/brand/ — canonical palette.css + logo/mark assets. - design/_imports/2026-06-16-claude-design-system/ — the original system, relocated as dated provenance (nothing imported it). - AGENTS.md — add the Design line (read the contract before UI work); repoint the layout entry. - ROADMAP.md — design-checker cleanup backlog (gold-as-fill + pill-radius blockers, the inline-token-copy consolidation, token gaps).
@@ -25,6 +25,7 @@ its guide** — see the index below.
|
||||
- Before building, bumping the version, or editing the StartOS wrapper, read `docs/guides/startos-packaging.md`.
|
||||
- Before editing the admin SPA (`web/index.html`), read `docs/guides/admin-ui.md`.
|
||||
- Before editing public site/docs copy, read `docs/guides/website-copy.md`.
|
||||
- **Before building or changing any user-facing UI (landing, docs, admin SPA), read `design/DESIGN.md` and `design/tokens.tokens.json` and conform to them** — the brand contract; pull colors/type/space/radii/shadows from the tokens, never hardcode off-scale values.
|
||||
- Before adding/altering tests or relying on lint/CI, read `docs/guides/testing.md`.
|
||||
|
||||
## Build / test / run (quick ref)
|
||||
@@ -50,7 +51,7 @@ licensing-service-startos/ daemon + StartOS wrapper (s9pk package source)
|
||||
keysat-xyz-landing/ keysat-docs/ keysat-registry-landing/ public sites → guides/website-copy.md
|
||||
licensing-client-{rust,ts,python,go}/ the four SDK source repos
|
||||
activate-license-template/ Tauri desktop template for license activation
|
||||
keysat-design-system/ design tokens / brand assets
|
||||
design/ design contract (DESIGN.md + tokens.tokens.json) + brand/ assets; original Claude Design system archived in design/_imports/
|
||||
plans/ design specs (multi-provider-payment-model.md, keysat-smtp-emails.md)
|
||||
tests/crosscheck/ cross-language LIC1 verifier → guides/crypto-wire-format.md
|
||||
```
|
||||
|
||||
@@ -24,3 +24,36 @@ Longer-term backlog. Near-term state lives in `AGENTS.md` → Current state.
|
||||
|
||||
- Re-test `KEYSAT_INTEGRATION.md` against a fresh downstream app to confirm a clean one-shot SDK integration.
|
||||
- End-to-end Zaprite sandbox pass on the multi-merchant-profile webhook routing before relying on it in production.
|
||||
|
||||
## Design (contract conformance)
|
||||
|
||||
The brand contract now lives in `design/DESIGN.md` + `design/tokens.tokens.json` (distilled
|
||||
2026-06-16 from the prior Claude Design system, now archived in `design/_imports/`). A
|
||||
`design-checker` audit (2026-06-16) found high fidelity overall, with these items where the
|
||||
**code contradicts the contract's stated rules** or bypasses the token scale:
|
||||
|
||||
**Blockers (code violates a named "never" rule):**
|
||||
- Gold used as an actionable *fill* (contract: gold is accent/border only, never a fill).
|
||||
(a) admin SPA `.featured-pill-toggle.on` → `web/index.html:418`; (b) admin sidebar
|
||||
upgrade CTA `#tier-banner-cta` → `web/index.html:537-542`. Fix to navy-fill or
|
||||
gold-border/text.
|
||||
- Primary buy CTA uses pill radius `999px` (contract: buttons are `r-md` 8px; pill is
|
||||
badges-only) — `keysat-xyz-landing/index.html:384-385`. Set to 8px.
|
||||
|
||||
**Structural (headline):**
|
||||
- All four surfaces inline their own copy of the CSS variables instead of importing the
|
||||
canonical `design/brand/palette.css` (landing :33-56, registry :11-22, docs.css :7-21,
|
||||
admin :9-25). Copies are currently exact but one edit from drift. Consolidate onto
|
||||
`palette.css`.
|
||||
|
||||
**Token gaps / drift (decide: tokenize the as-built value, or snap to an existing token):**
|
||||
- `14px` card radius used throughout the marketing landing — not a token (between `r-lg` 12
|
||||
and `r-xl` 18). Snap to a token or add one.
|
||||
- Wordmark letter-spacing is `0.30em` (landing) vs `0.28em` (docs/admin) and has no token —
|
||||
pick one value, add a `letterSpacing.wordmark` token.
|
||||
- Semantic badge *text* colors (`#205c47`/`#7a5814`/`#8a2828`) are darker one-offs with no
|
||||
token — add `semantic.*-text` tokens or reference existing ones.
|
||||
- Syntax-highlight colors hardcoded as hex (`#d4b985`=gold-400, `#a6b7cf`=navy-300) — switch
|
||||
to `var()`. One admin hex `#f6f1e7` isn't a token (closest cream-50/100) — reconcile.
|
||||
- Sticky-header backdrop on docs/admin (`blur(10px)`/`blur(8px)`) diverges from the contract's
|
||||
`blur(12px)` — align if a single header treatment is wanted.
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
---
|
||||
project: keysat
|
||||
tagline: Software licensing for Bitcoin creators
|
||||
source: design/_imports/2026-06-16-claude-design-system/ (Claude Design output, May 2025)
|
||||
tokens: design/tokens.tokens.json
|
||||
canonical_css: design/brand/palette.css
|
||||
last_distilled: 2026-06-16
|
||||
---
|
||||
|
||||
# Keysat — design contract
|
||||
|
||||
> The durable, vendor-neutral brand brief. Any agent building or changing a user-facing
|
||||
> surface (the marketing landing, the docs site, the creator admin SPA) reads this and
|
||||
> `design/tokens.tokens.json` first and conforms to them. Token *values* live in the tokens
|
||||
> file; this document is the *intent* and the rules. Distilled from a prior Claude Design
|
||||
> system (now in `design/_imports/`).
|
||||
|
||||
## 1. Visual theme
|
||||
|
||||
Navy ink on cream paper, with a gold accent that whispers. The reference objects are a
|
||||
**certificate of authenticity, a vault deed, a hand-numbered print** — classical trust
|
||||
signaling crossed with modern indie-software practicality. Restrained, archival, precise;
|
||||
**modern in interaction, classical in composition.** The identity is anchored by the logo: a
|
||||
deep-navy key crossing a Bitcoin "B" bow, on cream paper with a gold inner border. The brand
|
||||
reads as **printed, not liquid** — solid surfaces over glass and gradients.
|
||||
|
||||
## 2. Color palette
|
||||
|
||||
Values in `tokens.tokens.json`. Roles:
|
||||
|
||||
- **Navy is the primary brand color.** `navy-800` (`#1E3A5F`) is the wordmark color and
|
||||
dominant ink — primary buttons, headings, key chrome. Full scale `navy-950 → navy-50`.
|
||||
- **Cream is the page background.** `cream-100` (`#F5F1E8`) default; `cream-50` (`#FBF9F2`)
|
||||
for elevated paper. **Pure white (`#FFFFFF`)** is reserved for forms, tables, and code
|
||||
blocks where contrast matters.
|
||||
- **Gold (`gold-500`, `#BFA068`) is the accent, used sparingly** — eyebrow labels, dividers,
|
||||
the inner stroke of premium cards, a verified-badge highlight. **Never a primary button
|
||||
color.**
|
||||
- **Ink scale (`ink-900 → ink-300`)** handles body text, secondary copy, disabled states.
|
||||
- **Semantic:** success `#2D7A5F`, warning `#B8861F`, danger `#B23A3A`, info = `navy-700`,
|
||||
each with a tinted `-bg`.
|
||||
- Borders are **alpha navy**, not solid lines: `border-1` 12%, `border-2` 20%, `border-3` 35%.
|
||||
|
||||
## 3. Typography
|
||||
|
||||
- **Display — Manrope** (`font-display`): geometric sans, mirrors the wordmark. h1–h4, large
|
||||
numerals, the wordmark. Weights 400–700 (500 for h1/h2, 600 for h3/h4). *Note: the original
|
||||
design-system README named "Archivo" as a placeholder substitution, but the shipped CSS and
|
||||
every live surface use Manrope — Manrope is canonical. If a licensed display face is ever
|
||||
supplied, swap `font.display` in the tokens and remove the Google Fonts import.*
|
||||
- **Body — Inter** (`font-body`): humanist sans for prose, UI labels, form fields. Stylistic
|
||||
sets `ss01` + `cv11`.
|
||||
- **Mono — JetBrains Mono** (`font-mono`): license keys, code samples, API responses, tx IDs.
|
||||
- **Type scale** is the `fs-*` tokens (`display-xl` clamp 56–88px down to `meta` 12px).
|
||||
Headings track tight (`-0.02em`); eyebrows track wide (`0.18em`), uppercase.
|
||||
- **Casing:** sentence case for buttons/menus/headings ("Create a license"); ALL CAPS + wide
|
||||
tracking only for eyebrow labels above sections, sparingly. Proper nouns capitalized
|
||||
(Keysat, Start9, StartOS, BTCPay, Bitcoin, Lightning, Ed25519).
|
||||
- **Identifiers:** license keys monospace, hyphen-grouped (`KS-9F2A-7C41-XK22-6D8E`); keys/
|
||||
hashes ellipsized middle (`mz7q8…h3k2p`); amounts default to **sats** under 0.01 BTC.
|
||||
|
||||
## 4. Component styling
|
||||
|
||||
- **Buttons** — radius `r-md` (8px). Primary: `navy-800` bg, `cream-50` text; hover `navy-900`,
|
||||
press `navy-950` + 1px translate-down (no scale). Secondary: `cream-50` bg, alpha-navy
|
||||
border; hover `cream-200`. Ghost: transparent; hover `rgba(14,31,51,0.05)`. **Gold is a
|
||||
text/border treatment only, never a primary button fill.** Danger: red text + faint red
|
||||
border.
|
||||
- **Cards** — radius `r-lg` (12px), on cream with a hairline `border-1` and `shadow-sm`.
|
||||
Premium/featured cards get a **1px gold inner stroke** (`gold-500`) + `shadow-md`.
|
||||
- **Badges** — pill (`r-pill`), tinted semantic bg + matching text; gold/neutral = transparent
|
||||
with a gold border.
|
||||
- **Forms** — white bg, alpha-navy border, radius ~7px; focus = navy border + `ring-focus`
|
||||
halo. Monospace variant for keys.
|
||||
- **Code blocks** — `navy-950` bg, `cream-50` text, `r-lg`, JetBrains Mono; gold/cream syntax.
|
||||
- **Icons — Lucide**, stroke 1.75px, 16/20/24px. **No emoji in product UI. No PNG icons** (the
|
||||
logo mark is SVG; only the source thumbnail is PNG). The logo mark is never recolored —
|
||||
navy, or cream on dark surfaces.
|
||||
|
||||
## 5. Layout
|
||||
|
||||
- **4px base grid**, spacing tokens `sp-1` (4px) → `sp-12` (128px). Use the scale; don't mint
|
||||
magic numbers.
|
||||
- Marketing pages **breathe** — sections often `sp-11` (96px) apart. Dashboard density is
|
||||
moderate: table rows ~52px, card padding `sp-6` (24px).
|
||||
- Max content width on marketing ~1200px; reading width for prose/docs ~680px.
|
||||
- **Backgrounds are cream with a subtle grain** (the `paper-texture` utility — two radial-dot
|
||||
grids at ~2.5% opacity), never flat color. Section bands alternate cream → cream-200 →
|
||||
cream-50 → navy-950 (dark CTAs/footers).
|
||||
|
||||
## 6. Depth / elevation
|
||||
|
||||
A **paper-shadow** system, not a glassy one. `shadow-xs`/`sm` for resting cards; `shadow-md`
|
||||
for elevated/premium cards; `shadow-lg` for popovers/menus; `shadow-xl` for modals/command
|
||||
palettes. `shadow-inset` gives buttons subtle paper relief. Elevation comes from **shadow,
|
||||
not heavy borders**. Transparency/blur is rare — acceptable only for the sticky marketing
|
||||
header (`blur(12px)` over `rgba(245,241,232,0.85)`) and the modal scrim (`rgba(14,31,51,0.55)`).
|
||||
|
||||
## 7. Do's and don'ts
|
||||
|
||||
**Do**
|
||||
- Lead copy with *what the creator owns* — the signing key, the customer list, the payment
|
||||
rails. Sovereignty-first is the brand's center of gravity.
|
||||
- Keep gold rare and accenting; keep motion quiet (200ms standard, 120ms hover).
|
||||
- Use solid surfaces, restrained radii, hairline alpha borders, the paper grain.
|
||||
- Link a one-sentence definition the first time a term appears (Ed25519, BTCPay webhook,
|
||||
`.s9pk`).
|
||||
|
||||
**Don't**
|
||||
- **No hype words:** revolutionary, seamless, unlock, supercharge, leverage, ecosystem,
|
||||
journey, paradigm, game-changing.
|
||||
- **No emoji** in product UI; **no PNG icons**; **no Bitcoin orange** in the UI (navy/cream
|
||||
stands alone); no blue/purple gradients, no glassmorphism.
|
||||
- **Gold is never a primary button fill.** Radii never exceed ~18px (`r-xl`) for surfaces —
|
||||
no 24px+ rounding (reads as a consumer fintech app, which Keysat is not).
|
||||
- No spring physics, no scale-up on hover (cards move at most 1px; buttons darken, not grow).
|
||||
- Never remove focus rings.
|
||||
|
||||
## 8. Responsive behavior
|
||||
|
||||
Breakpoints in use: **980px** (grids collapse), **720px** (sidebar → slide-in drawer),
|
||||
**640px** (phone: tighter chrome, single column). Container padding steps 32px desktop → 20px
|
||||
tablet → 14px phone. Marketing is full-width hero + stacked sections; docs is a 3-column
|
||||
(sidebar | prose | TOC) collapsing to single column at 980px; admin is a 240px sticky sidebar
|
||||
+ main, sidebar drawering at 720px.
|
||||
|
||||
## 9. Agent prompt guide
|
||||
|
||||
When building or changing a Keysat UI:
|
||||
|
||||
- **Pull every color, size, space, radius, and shadow from `design/tokens.tokens.json`** (or a
|
||||
CSS custom property derived from it — see `design/brand/palette.css`). Do not hardcode hex or
|
||||
px values that bypass the scale.
|
||||
- Match the **existing surface** you're editing (landing / docs / admin) — they share the token
|
||||
set; keep them consistent rather than introducing a new look.
|
||||
- Default to **navy primary buttons, cream pages, gold-as-accent-only**, paper shadows, Lucide
|
||||
icons, sentence-case labels, no emoji.
|
||||
- If a needed value genuinely isn't in the tokens, **add it to the tokens file** (and ideally
|
||||
`palette.css`) rather than inlining a one-off — keep the contract the single source of truth.
|
||||
- Known debt (see ROADMAP): the three surfaces currently inline their own copy of the
|
||||
variables. Prefer importing `design/brand/palette.css`; when you touch a surface, move it
|
||||
toward that shared source rather than perpetuating a private copy.
|
||||
|
Before Width: | Height: | Size: 226 B After Width: | Height: | Size: 226 B |
|
Before Width: | Height: | Size: 618 B After Width: | Height: | Size: 618 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 828 B After Width: | Height: | Size: 828 B |
|
Before Width: | Height: | Size: 831 B After Width: | Height: | Size: 831 B |
|
Before Width: | Height: | Size: 843 B After Width: | Height: | Size: 843 B |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none">
|
||||
<text x="16" y="24" text-anchor="middle" font-family="Archivo, Helvetica, sans-serif" font-weight="900" font-size="28" fill="#1E3A5F">₿</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 226 B |
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none">
|
||||
|
||||
<rect width="32" height="32" rx="6" fill="#F5F1E8"></rect>
|
||||
<ellipse cx="16" cy="9" rx="9" ry="1.6" fill="#1E3A5F"></ellipse>
|
||||
<rect x="7" y="9" width="18" height="16" fill="#FBF9F2" stroke="#1E3A5F" stroke-width="1.4"></rect>
|
||||
<ellipse cx="16" cy="25" rx="9" ry="1.6" fill="#1E3A5F"></ellipse>
|
||||
<circle cx="13" cy="17" r="2.6" fill="none" stroke="#BFA068" stroke-width="1.4"></circle>
|
||||
<rect x="15.6" y="16.4" width="6" height="1.5" fill="#BFA068"></rect>
|
||||
<rect x="20" y="17.9" width="0.9" height="1.8" fill="#BFA068"></rect>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 618 B |
@@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 280 80" fill="none">
|
||||
<g transform="translate(0,0)">
|
||||
<ellipse cx="40" cy="20" rx="26" ry="4.5" fill="#1E3A5F"></ellipse>
|
||||
<rect x="14" y="20" width="52" height="52" fill="#FBF9F2" stroke="#1E3A5F" stroke-width="2.8"></rect>
|
||||
<ellipse cx="40" cy="72" rx="26" ry="4.5" fill="#1E3A5F"></ellipse>
|
||||
<line x1="23" y1="33" x2="57" y2="33" stroke="#1E3A5F" stroke-width="1.4" stroke-linecap="round"></line>
|
||||
<line x1="23" y1="40" x2="52" y2="40" stroke="#1E3A5F" stroke-width="1.4" stroke-linecap="round"></line>
|
||||
<circle cx="32" cy="55" r="5.5" fill="none" stroke="#BFA068" stroke-width="2.3"></circle>
|
||||
<rect x="38" y="53.7" width="13" height="2.7" fill="#BFA068"></rect>
|
||||
<rect x="47" y="56.4" width="1.8" height="3.6" fill="#BFA068"></rect>
|
||||
<rect x="51" y="56.4" width="1.8" height="2.7" fill="#BFA068"></rect>
|
||||
</g>
|
||||
<text x="92" y="52" font-family="Manrope, system-ui, sans-serif" font-weight="500" font-size="32" letter-spacing="9" fill="#1E3A5F">KEYSAT</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="none">
|
||||
|
||||
<ellipse cx="50" cy="22" rx="28" ry="5" fill="#1E3A5F"></ellipse>
|
||||
<rect x="22" y="22" width="56" height="56" fill="none" stroke="#1E3A5F" stroke-width="3"></rect>
|
||||
<ellipse cx="50" cy="78" rx="28" ry="5" fill="#1E3A5F"></ellipse>
|
||||
<line x1="32" y1="36" x2="68" y2="36" stroke="#1E3A5F" stroke-width="1.5" stroke-linecap="round"></line>
|
||||
<line x1="32" y1="44" x2="62" y2="44" stroke="#1E3A5F" stroke-width="1.5" stroke-linecap="round"></line>
|
||||
<circle cx="42" cy="60" r="6" fill="none" stroke="#1E3A5F" stroke-width="2.5"></circle>
|
||||
<rect x="48" y="58.5" width="14" height="3" fill="#1E3A5F"></rect>
|
||||
<rect x="58" y="61.5" width="2" height="4" fill="#1E3A5F"></rect>
|
||||
<rect x="62" y="61.5" width="2" height="3" fill="#1E3A5F"></rect>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 828 B |
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="none">
|
||||
|
||||
<ellipse cx="50" cy="22" rx="28" ry="5" fill="#FBF9F2"></ellipse>
|
||||
<rect x="22" y="22" width="56" height="56" fill="#0E1F33" stroke="#FBF9F2" stroke-width="3"></rect>
|
||||
<ellipse cx="50" cy="78" rx="28" ry="5" fill="#FBF9F2"></ellipse>
|
||||
<line x1="32" y1="36" x2="68" y2="36" stroke="#FBF9F2" stroke-width="1.5" stroke-linecap="round"></line>
|
||||
<line x1="32" y1="44" x2="62" y2="44" stroke="#FBF9F2" stroke-width="1.5" stroke-linecap="round"></line>
|
||||
<circle cx="42" cy="60" r="6" fill="none" stroke="#BFA068" stroke-width="2.5"></circle>
|
||||
<rect x="48" y="58.5" width="14" height="3" fill="#BFA068"></rect>
|
||||
<rect x="58" y="61.5" width="2" height="4" fill="#BFA068"></rect>
|
||||
<rect x="62" y="61.5" width="2" height="3" fill="#BFA068"></rect>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 831 B |
@@ -0,0 +1,16 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="none">
|
||||
|
||||
<ellipse cx="50" cy="22" rx="28" ry="5" fill="#1E3A5F"></ellipse>
|
||||
|
||||
<rect x="22" y="22" width="56" height="56" fill="#FBF9F2" stroke="#1E3A5F" stroke-width="3"></rect>
|
||||
|
||||
<ellipse cx="50" cy="78" rx="28" ry="5" fill="#1E3A5F"></ellipse>
|
||||
|
||||
<line x1="32" y1="36" x2="68" y2="36" stroke="#1E3A5F" stroke-width="1.5" stroke-linecap="round"></line>
|
||||
<line x1="32" y1="44" x2="62" y2="44" stroke="#1E3A5F" stroke-width="1.5" stroke-linecap="round"></line>
|
||||
|
||||
<circle cx="42" cy="60" r="6" fill="none" stroke="#BFA068" stroke-width="2.5"></circle>
|
||||
<rect x="48" y="58.5" width="14" height="3" fill="#BFA068"></rect>
|
||||
<rect x="58" y="61.5" width="2" height="4" fill="#BFA068"></rect>
|
||||
<rect x="62" y="61.5" width="2" height="3" fill="#BFA068"></rect>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 843 B |
@@ -0,0 +1,285 @@
|
||||
/* ============================================================
|
||||
Keysat — palette.css (CANONICAL design tokens as CSS custom properties)
|
||||
"Software Licensing for Bitcoin Creators"
|
||||
Navy + cream, paper texture, classical type.
|
||||
|
||||
Single canonical stylesheet, derived from design/tokens.tokens.json.
|
||||
Surfaces (landing, docs, admin SPA) should @import or inline THIS file
|
||||
rather than keep private copies — see the "shared token source" item in
|
||||
ROADMAP.md. Keep this and the tokens JSON in sync; never fork a one-off copy.
|
||||
============================================================ */
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');
|
||||
|
||||
:root {
|
||||
/* ---------- Brand Colors ---------- */
|
||||
/* Primary navy — pulled from the wordmark */
|
||||
--navy-950: #0E1F33;
|
||||
--navy-900: #142A47;
|
||||
--navy-800: #1E3A5F; /* core brand navy */
|
||||
--navy-700: #2A4A75;
|
||||
--navy-600: #3A5C8A;
|
||||
--navy-500: #5074A1;
|
||||
--navy-400: #7892B8;
|
||||
--navy-300: #A6B7CF;
|
||||
--navy-200: #CBD5E2;
|
||||
--navy-100: #E4EAF1;
|
||||
--navy-50: #F2F5F9;
|
||||
|
||||
/* Cream / paper — the background tone of the logo card */
|
||||
--cream-50: #FBF9F2;
|
||||
--cream-100: #F5F1E8; /* core cream */
|
||||
--cream-200: #EDE7D7;
|
||||
--cream-300: #E1D8C0;
|
||||
--cream-400: #C9BC9A;
|
||||
|
||||
/* Gold / tan — the inner key border */
|
||||
--gold-700: #8A6F3D;
|
||||
--gold-600: #A88652;
|
||||
--gold-500: #BFA068; /* core gold accent */
|
||||
--gold-400: #D4B985;
|
||||
--gold-300: #E5CFA5;
|
||||
--gold-200: #F0E2C5;
|
||||
|
||||
/* Ink — dark text */
|
||||
--ink-900: #0E1F33;
|
||||
--ink-700: #2C3E54;
|
||||
--ink-500: #5A6B7F;
|
||||
--ink-400: #7E8C9D;
|
||||
--ink-300: #A4AEBB;
|
||||
|
||||
/* Semantic */
|
||||
--success: #2D7A5F;
|
||||
--success-bg: #E3F0EA;
|
||||
--warning: #B8861F;
|
||||
--warning-bg: #F7EFD7;
|
||||
--danger: #B23A3A;
|
||||
--danger-bg: #F4E0E0;
|
||||
--info: var(--navy-700);
|
||||
--info-bg: var(--navy-100);
|
||||
|
||||
/* ---------- Semantic surface tokens ---------- */
|
||||
--bg-page: var(--cream-100); /* default page bg */
|
||||
--bg-paper: var(--cream-50); /* lighter paper */
|
||||
--bg-elev: #FFFFFF; /* elevated surface (cards on cream) */
|
||||
--bg-inverse: var(--navy-900); /* dark surface */
|
||||
--bg-tint: var(--cream-200); /* tinted band/section */
|
||||
|
||||
--fg-1: var(--ink-900); /* primary text */
|
||||
--fg-2: var(--ink-700); /* secondary text */
|
||||
--fg-3: var(--ink-500); /* tertiary / meta */
|
||||
--fg-4: var(--ink-400); /* disabled / hint */
|
||||
--fg-on-navy: var(--cream-50);
|
||||
--fg-on-gold: var(--navy-900);
|
||||
|
||||
--border-1: rgba(14, 31, 51, 0.12); /* hairline on cream */
|
||||
--border-2: rgba(14, 31, 51, 0.20); /* card border */
|
||||
--border-3: rgba(14, 31, 51, 0.35); /* focus / strong */
|
||||
--border-on-navy: rgba(245, 241, 232, 0.18);
|
||||
|
||||
--accent: var(--navy-800);
|
||||
--accent-hover: var(--navy-900);
|
||||
--accent-press: var(--navy-950);
|
||||
--accent-soft: var(--navy-100);
|
||||
|
||||
--gold: var(--gold-500);
|
||||
--gold-hover: var(--gold-600);
|
||||
|
||||
/* ---------- Type families ---------- */
|
||||
--font-display: 'Manrope', 'Helvetica Neue', Arial, sans-serif;
|
||||
--font-body: 'Inter', 'Helvetica Neue', Arial, sans-serif;
|
||||
--font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;
|
||||
|
||||
/* ---------- Type scale ---------- */
|
||||
--fs-display-xl: clamp(56px, 6vw, 88px);
|
||||
--fs-display: clamp(40px, 4.5vw, 64px);
|
||||
--fs-h1: 44px;
|
||||
--fs-h2: 32px;
|
||||
--fs-h3: 24px;
|
||||
--fs-h4: 20px;
|
||||
--fs-h5: 17px;
|
||||
--fs-body-lg: 18px;
|
||||
--fs-body: 15px;
|
||||
--fs-body-sm: 13.5px;
|
||||
--fs-meta: 12px;
|
||||
--fs-mono: 13px;
|
||||
|
||||
/* ---------- Line heights ---------- */
|
||||
--lh-display: 1.02;
|
||||
--lh-heading: 1.15;
|
||||
--lh-body: 1.55;
|
||||
--lh-tight: 1.25;
|
||||
|
||||
/* ---------- Letter spacing ---------- */
|
||||
--tracking-tight: -0.02em;
|
||||
--tracking-normal: 0;
|
||||
--tracking-wide: 0.04em;
|
||||
--tracking-eyebrow: 0.18em;
|
||||
|
||||
/* ---------- Spacing (4px base) ---------- */
|
||||
--sp-1: 4px;
|
||||
--sp-2: 8px;
|
||||
--sp-3: 12px;
|
||||
--sp-4: 16px;
|
||||
--sp-5: 20px;
|
||||
--sp-6: 24px;
|
||||
--sp-7: 32px;
|
||||
--sp-8: 40px;
|
||||
--sp-9: 56px;
|
||||
--sp-10: 72px;
|
||||
--sp-11: 96px;
|
||||
--sp-12: 128px;
|
||||
|
||||
/* ---------- Radii ---------- */
|
||||
--r-xs: 3px;
|
||||
--r-sm: 5px;
|
||||
--r-md: 8px;
|
||||
--r-lg: 12px;
|
||||
--r-xl: 18px;
|
||||
--r-2xl: 24px;
|
||||
--r-pill: 999px;
|
||||
|
||||
/* ---------- Shadows ---------- */
|
||||
/* Quiet, layered shadows — paper, not glassy */
|
||||
--shadow-xs: 0 1px 1px rgba(14,31,51,0.04);
|
||||
--shadow-sm: 0 1px 2px rgba(14,31,51,0.06), 0 1px 1px rgba(14,31,51,0.03);
|
||||
--shadow-md: 0 2px 4px rgba(14,31,51,0.06), 0 4px 12px rgba(14,31,51,0.06);
|
||||
--shadow-lg: 0 4px 8px rgba(14,31,51,0.07), 0 12px 32px rgba(14,31,51,0.10);
|
||||
--shadow-xl: 0 8px 16px rgba(14,31,51,0.10), 0 24px 64px rgba(14,31,51,0.14);
|
||||
--shadow-inset: inset 0 1px 0 rgba(255,255,255,0.6), inset 0 -1px 0 rgba(14,31,51,0.05);
|
||||
--ring-focus: 0 0 0 3px rgba(30,58,95,0.25);
|
||||
|
||||
/* ---------- Motion ---------- */
|
||||
--ease-standard: cubic-bezier(0.2, 0.7, 0.2, 1);
|
||||
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
|
||||
--ease-in: cubic-bezier(0.7, 0, 0.84, 0);
|
||||
--dur-fast: 120ms;
|
||||
--dur-base: 200ms;
|
||||
--dur-slow: 360ms;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
Paper texture — subtle grain on cream surfaces
|
||||
============================================================ */
|
||||
.paper-texture {
|
||||
background-color: var(--bg-page);
|
||||
background-image:
|
||||
radial-gradient(rgba(14,31,51,0.025) 1px, transparent 1px),
|
||||
radial-gradient(rgba(138,111,61,0.022) 1px, transparent 1px);
|
||||
background-size: 3px 3px, 7px 7px;
|
||||
background-position: 0 0, 1px 1px;
|
||||
}
|
||||
.paper-texture-strong {
|
||||
background-color: var(--bg-page);
|
||||
background-image:
|
||||
radial-gradient(rgba(14,31,51,0.04) 1px, transparent 1.4px),
|
||||
radial-gradient(rgba(138,111,61,0.035) 1px, transparent 1.2px);
|
||||
background-size: 3px 3px, 7px 7px;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
Element defaults — drop these into a body class .keysat
|
||||
============================================================ */
|
||||
.keysat {
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--fs-body);
|
||||
line-height: var(--lh-body);
|
||||
color: var(--fg-1);
|
||||
background: var(--bg-page);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-feature-settings: 'ss01', 'cv11';
|
||||
}
|
||||
|
||||
.keysat h1, .keysat .h1 {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--fs-h1);
|
||||
font-weight: 500;
|
||||
line-height: var(--lh-heading);
|
||||
letter-spacing: var(--tracking-tight);
|
||||
color: var(--fg-1);
|
||||
margin: 0;
|
||||
}
|
||||
.keysat h2, .keysat .h2 {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--fs-h2);
|
||||
font-weight: 500;
|
||||
line-height: var(--lh-heading);
|
||||
letter-spacing: var(--tracking-tight);
|
||||
color: var(--fg-1);
|
||||
margin: 0;
|
||||
}
|
||||
.keysat h3, .keysat .h3 {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--fs-h3);
|
||||
font-weight: 600;
|
||||
line-height: var(--lh-tight);
|
||||
letter-spacing: var(--tracking-tight);
|
||||
margin: 0;
|
||||
}
|
||||
.keysat h4, .keysat .h4 {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--fs-h4);
|
||||
font-weight: 600;
|
||||
line-height: var(--lh-tight);
|
||||
margin: 0;
|
||||
}
|
||||
.keysat h5, .keysat .h5 {
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--fs-h5);
|
||||
font-weight: 600;
|
||||
line-height: var(--lh-tight);
|
||||
margin: 0;
|
||||
}
|
||||
.keysat .display-xl {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--fs-display-xl);
|
||||
font-weight: 500;
|
||||
line-height: var(--lh-display);
|
||||
letter-spacing: -0.022em;
|
||||
}
|
||||
.keysat .display {
|
||||
font-family: var(--font-display);
|
||||
font-size: var(--fs-display);
|
||||
font-weight: 500;
|
||||
line-height: var(--lh-display);
|
||||
letter-spacing: -0.022em;
|
||||
}
|
||||
.keysat .wordmark {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.32em;
|
||||
text-transform: uppercase;
|
||||
color: var(--navy-800);
|
||||
}
|
||||
.keysat .eyebrow {
|
||||
font-family: var(--font-body);
|
||||
font-size: 11.5px;
|
||||
font-weight: 600;
|
||||
letter-spacing: var(--tracking-eyebrow);
|
||||
text-transform: uppercase;
|
||||
color: var(--gold-700);
|
||||
}
|
||||
.keysat p { margin: 0 0 1em 0; color: var(--fg-2); }
|
||||
.keysat .lead {
|
||||
font-size: var(--fs-body-lg);
|
||||
line-height: 1.5;
|
||||
color: var(--fg-2);
|
||||
}
|
||||
.keysat .meta {
|
||||
font-size: var(--fs-meta);
|
||||
color: var(--fg-3);
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.keysat code, .keysat .mono {
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--fs-mono);
|
||||
font-feature-settings: 'ss02';
|
||||
}
|
||||
.keysat a {
|
||||
color: var(--accent);
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: 1px;
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
.keysat a:hover { color: var(--accent-hover); }
|
||||
@@ -0,0 +1,171 @@
|
||||
{
|
||||
"$description": "Keysat design tokens — W3C DTCG-shaped, distilled from design/_imports/2026-06-16-claude-design-system/colors_and_type.css (as-built values). Group-level $type is set where it maps cleanly to a DTCG primitive; a few groups (fontSize, letterSpacing, shadow) carry as-built CSS expressions (clamp(), em, multi-layer shadows) as strings and are intentionally left untyped pending a Style Dictionary build. See design/DESIGN.md for intent and rules.",
|
||||
|
||||
"color": {
|
||||
"$type": "color",
|
||||
"navy": {
|
||||
"950": { "$value": "#0E1F33" },
|
||||
"900": { "$value": "#142A47" },
|
||||
"800": { "$value": "#1E3A5F", "$description": "core brand navy — wordmark, primary buttons, dominant ink" },
|
||||
"700": { "$value": "#2A4A75" },
|
||||
"600": { "$value": "#3A5C8A" },
|
||||
"500": { "$value": "#5074A1" },
|
||||
"400": { "$value": "#7892B8" },
|
||||
"300": { "$value": "#A6B7CF" },
|
||||
"200": { "$value": "#CBD5E2" },
|
||||
"100": { "$value": "#E4EAF1" },
|
||||
"50": { "$value": "#F2F5F9" }
|
||||
},
|
||||
"cream": {
|
||||
"50": { "$value": "#FBF9F2", "$description": "elevated paper / card surface" },
|
||||
"100": { "$value": "#F5F1E8", "$description": "core cream — default page background" },
|
||||
"200": { "$value": "#EDE7D7" },
|
||||
"300": { "$value": "#E1D8C0" },
|
||||
"400": { "$value": "#C9BC9A" }
|
||||
},
|
||||
"gold": {
|
||||
"700": { "$value": "#8A6F3D", "$description": "eyebrow labels / dark gold accent" },
|
||||
"600": { "$value": "#A88652" },
|
||||
"500": { "$value": "#BFA068", "$description": "core gold accent — sparing use only, never a primary button fill" },
|
||||
"400": { "$value": "#D4B985" },
|
||||
"300": { "$value": "#E5CFA5" },
|
||||
"200": { "$value": "#F0E2C5" }
|
||||
},
|
||||
"ink": {
|
||||
"900": { "$value": "#0E1F33", "$description": "primary text" },
|
||||
"700": { "$value": "#2C3E54", "$description": "secondary text" },
|
||||
"500": { "$value": "#5A6B7F", "$description": "tertiary / meta" },
|
||||
"400": { "$value": "#7E8C9D", "$description": "disabled / hint" },
|
||||
"300": { "$value": "#A4AEBB" }
|
||||
},
|
||||
"semantic": {
|
||||
"success": { "$value": "#2D7A5F" },
|
||||
"success-bg": { "$value": "#E3F0EA" },
|
||||
"warning": { "$value": "#B8861F" },
|
||||
"warning-bg": { "$value": "#F7EFD7" },
|
||||
"danger": { "$value": "#B23A3A" },
|
||||
"danger-bg": { "$value": "#F4E0E0" },
|
||||
"info": { "$value": "{color.navy.700}" },
|
||||
"info-bg": { "$value": "{color.navy.100}" }
|
||||
},
|
||||
"surface": {
|
||||
"page": { "$value": "{color.cream.100}", "$description": "default page bg (apply with paper-texture grain)" },
|
||||
"paper": { "$value": "{color.cream.50}" },
|
||||
"elevated":{ "$value": "#FFFFFF", "$description": "forms, tables, code blocks — reserved white" },
|
||||
"inverse": { "$value": "{color.navy.900}" },
|
||||
"tint": { "$value": "{color.cream.200}" }
|
||||
},
|
||||
"fg": {
|
||||
"primary": { "$value": "{color.ink.900}" },
|
||||
"secondary": { "$value": "{color.ink.700}" },
|
||||
"tertiary": { "$value": "{color.ink.500}" },
|
||||
"hint": { "$value": "{color.ink.400}" },
|
||||
"on-navy": { "$value": "{color.cream.50}" },
|
||||
"on-gold": { "$value": "{color.navy.900}" }
|
||||
},
|
||||
"accent": {
|
||||
"default": { "$value": "{color.navy.800}" },
|
||||
"hover": { "$value": "{color.navy.900}" },
|
||||
"press": { "$value": "{color.navy.950}" },
|
||||
"soft": { "$value": "{color.navy.100}" },
|
||||
"gold": { "$value": "{color.gold.500}" }
|
||||
},
|
||||
"border": {
|
||||
"1": { "$value": "rgba(14, 31, 51, 0.12)", "$description": "hairline on cream" },
|
||||
"2": { "$value": "rgba(14, 31, 51, 0.20)", "$description": "card border" },
|
||||
"3": { "$value": "rgba(14, 31, 51, 0.35)", "$description": "focus / strong" },
|
||||
"on-navy": { "$value": "rgba(245, 241, 232, 0.18)" }
|
||||
}
|
||||
},
|
||||
|
||||
"font": {
|
||||
"$type": "fontFamily",
|
||||
"display": { "$value": ["Manrope", "Helvetica Neue", "Arial", "sans-serif"], "$description": "canonical display face (README's 'Archivo' was a stale placeholder)" },
|
||||
"body": { "$value": ["Inter", "Helvetica Neue", "Arial", "sans-serif"] },
|
||||
"mono": { "$value": ["JetBrains Mono", "ui-monospace", "SF Mono", "Menlo", "monospace"] }
|
||||
},
|
||||
|
||||
"fontSize": {
|
||||
"$description": "as-built type scale; display sizes use CSS clamp() and are kept as strings",
|
||||
"display-xl": { "$value": "clamp(56px, 6vw, 88px)" },
|
||||
"display": { "$value": "clamp(40px, 4.5vw, 64px)" },
|
||||
"h1": { "$value": "44px" },
|
||||
"h2": { "$value": "32px" },
|
||||
"h3": { "$value": "24px" },
|
||||
"h4": { "$value": "20px" },
|
||||
"h5": { "$value": "17px" },
|
||||
"body-lg": { "$value": "18px" },
|
||||
"body": { "$value": "15px" },
|
||||
"body-sm": { "$value": "13.5px" },
|
||||
"meta": { "$value": "12px" },
|
||||
"mono": { "$value": "13px" }
|
||||
},
|
||||
|
||||
"lineHeight": {
|
||||
"$type": "number",
|
||||
"display": { "$value": 1.02 },
|
||||
"heading": { "$value": 1.15 },
|
||||
"body": { "$value": 1.55 },
|
||||
"tight": { "$value": 1.25 }
|
||||
},
|
||||
|
||||
"letterSpacing": {
|
||||
"$description": "em values kept as strings",
|
||||
"tight": { "$value": "-0.02em" },
|
||||
"normal": { "$value": "0" },
|
||||
"wide": { "$value": "0.04em" },
|
||||
"eyebrow": { "$value": "0.18em" }
|
||||
},
|
||||
|
||||
"space": {
|
||||
"$type": "dimension",
|
||||
"1": { "$value": "4px" },
|
||||
"2": { "$value": "8px" },
|
||||
"3": { "$value": "12px" },
|
||||
"4": { "$value": "16px" },
|
||||
"5": { "$value": "20px" },
|
||||
"6": { "$value": "24px" },
|
||||
"7": { "$value": "32px" },
|
||||
"8": { "$value": "40px" },
|
||||
"9": { "$value": "56px" },
|
||||
"10": { "$value": "72px" },
|
||||
"11": { "$value": "96px" },
|
||||
"12": { "$value": "128px" }
|
||||
},
|
||||
|
||||
"radius": {
|
||||
"$type": "dimension",
|
||||
"xs": { "$value": "3px" },
|
||||
"sm": { "$value": "5px" },
|
||||
"md": { "$value": "8px", "$description": "buttons" },
|
||||
"lg": { "$value": "12px", "$description": "cards" },
|
||||
"xl": { "$value": "18px", "$description": "max surface rounding — never exceed" },
|
||||
"2xl": { "$value": "24px", "$description": "reserved; avoid in product UI" },
|
||||
"pill": { "$value": "999px", "$description": "tags/badges only" }
|
||||
},
|
||||
|
||||
"shadow": {
|
||||
"$description": "paper-shadow system — multi-layer CSS strings, not glassy",
|
||||
"xs": { "$value": "0 1px 1px rgba(14,31,51,0.04)" },
|
||||
"sm": { "$value": "0 1px 2px rgba(14,31,51,0.06), 0 1px 1px rgba(14,31,51,0.03)" },
|
||||
"md": { "$value": "0 2px 4px rgba(14,31,51,0.06), 0 4px 12px rgba(14,31,51,0.06)" },
|
||||
"lg": { "$value": "0 4px 8px rgba(14,31,51,0.07), 0 12px 32px rgba(14,31,51,0.10)" },
|
||||
"xl": { "$value": "0 8px 16px rgba(14,31,51,0.10), 0 24px 64px rgba(14,31,51,0.14)" },
|
||||
"inset": { "$value": "inset 0 1px 0 rgba(255,255,255,0.6), inset 0 -1px 0 rgba(14,31,51,0.05)" },
|
||||
"ring-focus": { "$value": "0 0 0 3px rgba(30,58,95,0.25)" }
|
||||
},
|
||||
|
||||
"duration": {
|
||||
"$type": "duration",
|
||||
"fast": { "$value": "120ms", "$description": "hover transitions" },
|
||||
"base": { "$value": "200ms", "$description": "default" },
|
||||
"slow": { "$value": "360ms" }
|
||||
},
|
||||
|
||||
"easing": {
|
||||
"$type": "cubicBezier",
|
||||
"standard": { "$value": [0.2, 0.7, 0.2, 1] },
|
||||
"out": { "$value": [0.16, 1, 0.3, 1] },
|
||||
"in": { "$value": [0.7, 0, 0.84, 0] }
|
||||
}
|
||||
}
|
||||