diff --git a/.changeset/beetroot-soft-rebrand.md b/.changeset/beetroot-soft-rebrand.md new file mode 100644 index 0000000..e83f627 --- /dev/null +++ b/.changeset/beetroot-soft-rebrand.md @@ -0,0 +1,10 @@ +--- +"@btravstack/theme": minor +--- + +Beetroot Soft rebrand — warm, rounded, soft-but-exact. + +- Light warm near-white paper (`oklch(97.5% 0.008 353)`) is now the canonical scheme; dark stays the soft plum, activated on `.dark`. +- Typography: Plus Jakarta Sans (display) + Geist (body) + JetBrains Mono (code) replace Montserrat. +- Rounded, friendly shape language: 12px card corners, pill buttons, soft accent-tinted shadows, hairline rules, hover-lifts; solid ink hero name (no gradient text), gently floating hero mark preserved as the one character moment. +- Non-breaking: all semantic tokens keep their names and the one-knob rebrand contract is unchanged — consumer sites still override only `--accent` (`:root:root { --accent: … }`) and every derived shade follows via `color-mix`. diff --git a/.hallmark/log.json b/.hallmark/log.json new file mode 100644 index 0000000..feba200 --- /dev/null +++ b/.hallmark/log.json @@ -0,0 +1,26 @@ +[ + { + "date": "2026-07-16", + "scope": "app", + "macrostructure": "Bento Grid", + "theme": "custom", + "theme_axes": "light / rounded-sans / chromatic-other (beetroot-pink ~353)", + "vibe": "warm, rounded, charming beetroot, soft-but-exact", + "enrichment": "H9 original gradient mascot (Tier-B, restored)", + "nav": "N1b", + "footer": "Ft8 marquee", + "brief": "btravstack org rebrand v2 — Beetroot Soft (playful), replaces rejected Beetroot Riso" + }, + { + "date": "2026-07-16", + "scope": "app", + "macrostructure": "Manifesto", + "theme": "custom", + "theme_axes": "light / display-heavy / chromatic-other (beetroot-pink ~353)", + "vibe": "beetroot risograph, contract-as-document, raw, technical", + "enrichment": "Tier-B hand-built SVG (flat riso logo set)", + "nav": "N7 brutal slab", + "footer": "Ft4 dense colophon", + "brief": "btravstack org rebrand v1 — Beetroot Riso (REJECTED by user; PR #27)" + } +] diff --git a/apps/website/.vitepress/theme/Landing.vue b/apps/website/.vitepress/theme/Landing.vue index 5c7c877..e4a64ff 100644 --- a/apps/website/.vitepress/theme/Landing.vue +++ b/apps/website/.vitepress/theme/Landing.vue @@ -106,354 +106,392 @@ onMounted(() => { diff --git a/design.md b/design.md new file mode 100644 index 0000000..eff9c56 --- /dev/null +++ b/design.md @@ -0,0 +1,128 @@ +# Design — btravstack · "Beetroot Soft" + +A locked design system for the btravstack org (landing + `@btravstack/theme` + +the four docs sites). Every page redesign reads this file before emitting code. +Do not regenerate per page — extend or amend this file when the system needs to +grow. + +**The idea.** Software can be soft and exact at once. The beetroot is a +charming character, not a logo stamp — one gentle character moment per page +(the floating mascot), warm near-white paper, white card lifts, 12px corners, +soft accent-tinted shadows, and a friendly-but-precise voice. Never childish, +never quirk-for-quirk. (Supersedes the rejected "Beetroot Riso" brutalist +system — hard edges, flat riso marks, and all-caps declarations are out.) + +## Genre + +playful (post-Linear soft, Hum-adjacent). Vibe: *"warm, rounded, charming +beetroot, soft-but-exact"*. + +## Macrostructure family + +- Marketing pages (landing): **Bento Grid** — compact centered hero (mascot + + one-line headline + pill CTAs), then one friendly mosaic of mixed-span tiles + (intro, principles, packages, "why now" cluster, inspirations, CTA tile). +- Docs sites (VitePress content): default VitePress layout wearing the theme + tokens — rounded cards, soft hover-lifts, floating hero mark, tinted icon + chips. +- Nav: **N1b** — wordmark left, centered links, toggle + filled pill CTA + right, soft frost. +- Footer: **Ft8 marquee** — gently scrolling brand line (static under + `prefers-reduced-motion`), with a compact meta/link row beneath. + +## Theme + +Light is canonical; dark is the soft plum the brand grew up on. +Only lightness/chroma move between schemes — the hue family never changes. + +| Token | Light (canonical) | Dark — plum | +| --- | --- | --- | +| `--bg` (paper) | `oklch(97.5% 0.008 353)` | `oklch(18.5% 0.025 310)` | +| `--card` | `oklch(99.5% 0.003 353)` white lift | `oklch(22% 0.026 310)` | +| `--text` (ink) | `oklch(24% 0.020 350)` | `oklch(94% 0.008 340)` | +| `--accent` | **#E0589A** = `oklch(65.5% 0.182 353)` — THE knob | same (fills stay vibrant) | +| `--text-accent` | accent darkened 28% (AA on paper) | `var(--accent)` | +| rules | soft hairlines (12–20% ink mix) | same recipe | +| focus ring | accent darkened 16% — instant, never animated | same | + +**One-knob rebrand is contractual.** Docs sites override only `--accent` +(`:root:root { --accent: … }`); every derived shade flows via `color-mix`. +amqp `#FF6600` · temporal `#6B76F2` · unthrown `#46B86C` · demesne `#B45CE0`. + +Accent discipline: fills ≤ 5% of any viewport; large accent areas are washes +(`--accent-wash`, ≤ 14% opacity), never solid blocks. + +## Typography + +- Display: **Plus Jakarta Sans**, 700–800, roman, tracking −0.025em. Never + italic headers, never all-caps headlines. +- Body: **Geist**, 400. +- Mono (outlier): **JetBrains Mono** — code, package identifiers, small labels. +- Hero headline ≤ 50 chars at `--fs-hero: clamp(38px, 5.5vw, 60px)`. +- 2+1 rule: exactly these three families. + +## Spacing / shape + +Named px scale in `packages/theme/src/tokens.css` (`--space-1…6`). Corners: +cards `--radius-lg` (12px) max, inputs 8px, pills/chips 999px. Shadows: soft +accent-tinted (`0 8px 24px -10px`), no hard offsets, no glow halos, no +glassmorphism-as-decoration (frosted sticky nav is the one functional frost). + +## Motion + +- Easings: `--ease: cubic-bezier(0.16, 1, 0.3, 1)`; durations ≤ 0.2s. +- Three primitives per page, max: the mascot idle bob (the character moment), + card hover-lift (`translateY(-2px)` + shadow expansion), and the copy toast. +- No spring/overshoot on UI state. No scroll-triggered reveals. +- Reduced-motion: bob/marquee stop; state changes stay ≤ 150ms. + +## Microinteractions stance + +- Silent success (copy button → small toast, 1.6s, no celebration). +- Hover tooltips 800ms · focus 0ms. Focus rings instant, 2px `--focus`. + +## CTA voice + +- Primary: filled pill, accent fill + ink text, Plus Jakarta 700. One per view. +- Secondary: outlined pill or text link with arrow. No gradients, no springs. + +## Logos + +The original soft gradient marks are the canon — the beetroot mascot, rabbit, +hourglass, no-throw and shield keep their gradient warmth and rounded +construction. Do NOT flatten them into hard-edged print marks. Refinements +stay within the soft register. + +## What pages MUST share + +- The wordmark voice (Plus Jakarta 700+, "btrav" in accent-as-text + "stack" in ink). +- The one-knob accent architecture and its footprint discipline. +- The three fonts, 12px card corners, soft-shadow language, pill CTAs. +- The single character moment per page (never more than one). + +## What pages MAY differ on + +- Macrostructure within the family (a future marketing page may be Split + Studio or Conversational FAQ — still wearing this theme). +- Per-site accent hue (docs sites). +- Docs sites keep VitePress's functional layout; only the dress is shared. + +## Exports + +### tokens.css +The canonical export lives at `packages/theme/src/tokens.css` (published as +`@btravstack/theme`). It is the single source of truth; do not duplicate here. + +### Quick-reference (other projects) +```css +:root { + --color-paper: oklch(97.5% 0.008 353); + --color-ink: oklch(24% 0.020 350); + --color-accent: #E0589A; + --font-display: "Plus Jakarta Sans", sans-serif; + --font-body: "Geist", sans-serif; + --font-mono: "JetBrains Mono", monospace; + --ease: cubic-bezier(0.16, 1, 0.3, 1); + --radius-card: 12px; +} +``` diff --git a/packages/theme/src/fonts.css b/packages/theme/src/fonts.css index 39e3d6d..c2d9c98 100644 --- a/packages/theme/src/fonts.css +++ b/packages/theme/src/fonts.css @@ -1,6 +1,8 @@ /* - * btravstack web fonts. Montserrat (display/UI) + JetBrains Mono (code). - * Loaded from Google Fonts so the system stays zero-asset and portable; - * the weights here match the --fw-* tokens in tokens.css. + * btravstack web fonts — the "Beetroot Soft" pairing (2+1 rule). + * Plus Jakarta Sans (display, 600–800) — rounded humanist, warm, never childish + * Geist (body/UI, 400–700) + * JetBrains Mono (code + labels) + * Loaded from Google Fonts so the system stays zero-asset and portable. */ -@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@600;700;800&family=Geist:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap'); diff --git a/packages/theme/src/style.css b/packages/theme/src/style.css index 3afaf6d..778a22b 100644 --- a/packages/theme/src/style.css +++ b/packages/theme/src/style.css @@ -1,13 +1,16 @@ +/* Hallmark · genre: playful · design-system: design.md · designed-as-app + * scope: VitePress chrome mapping for all btravstack sites (docs + landing shell) + */ + /* - * @btravstack/theme — VitePress style entry. + * @btravstack/theme — VitePress style entry ("Beetroot Soft"). * * Maps the btravstack design tokens onto VitePress's own `--vp-*` variables so * every btravstack site (the website + the docs sites) shares one look. * Because the semantic tokens (--bg, --text, --text-accent, …) flip per scheme * in tokens.css, this mapping is written once and adapts automatically: - * - dark → the btravstack plum surfaces, pink links - * - light → clean warm-white surfaces, brand darkened to deep beetroot - * (--text-accent) so links/headings pass WCAG-AA on white + * - light → warm near-white paper, deep-beet links (WCAG-AA on white) + * - dark → the soft plum surfaces, vibrant pink links * * The `:root:root` selector raises specificity to (0,2,0) so the mapping wins * over VitePress's own `:root` / `.dark` defaults regardless of load order. @@ -38,8 +41,7 @@ --vp-c-brand-soft: color-mix(in srgb, var(--accent) 16%, transparent); --vp-c-brand: var(--text-accent); /* legacy alias */ - /* Inline code — accent-as-text so it stays readable on the (scheme-aware) - code inset: pink on dark, deep beetroot on the light code wash */ + /* Inline code — accent-as-text on the scheme-aware code inset */ --vp-code-color: var(--text-accent); --vp-code-bg: var(--code-bg); @@ -65,52 +67,43 @@ --vp-custom-block-warning-border: var(--accent-line); --vp-custom-block-warning-text: var(--text-accent); - /* Home hero: gradient name + accent glow behind the logo */ - --vp-home-hero-name-color: transparent; - --vp-home-hero-name-background: linear-gradient(120deg, var(--text-accent), var(--accent-soft)); - --vp-home-hero-image-background-image: radial-gradient(closest-side, color-mix(in srgb, var(--accent) 30%, transparent), transparent 72%); + /* Home hero: solid ink name (no gradient text) + a soft accent halo that + tracks the logo wherever the layout places it. */ + --vp-home-hero-name-color: var(--vp-c-text-1); + --vp-home-hero-name-background: none; + --vp-home-hero-image-background-image: radial-gradient(closest-side, color-mix(in srgb, var(--accent) 22%, transparent), transparent 72%); --vp-home-hero-image-filter: blur(8px); } -/* ── Playful flourishes — shared across every btravstack site ───── */ - -/* Home hero: a gently floating logo. The beetroot glow lives BEHIND the hero - image itself (--vp-home-hero-image-background-image, set above) so it tracks - the logo wherever the layout places it. We deliberately do NOT add a separate - centered ::before glow: the landing page uses its own bespoke layout (not - .VPHome), and on the docs sites the default home hero is left-aligned with the - image on the right — a hard-centered glow there just floats, disconnected. */ -.VPHome { overflow-x: clip; } -.VPHero .image-container { animation: btv-floaty 6s ease-in-out infinite; } -.VPHero .image-src { filter: drop-shadow(0 18px 40px color-mix(in srgb, var(--accent-deep) 45%, transparent)); } -@keyframes btv-floaty { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-10px); } } - -/* Navbar logo: VitePress's 24px mark reads as a speck next to the wordmark. - Size it up through VitePress's own variable (--vp-nav-logo-height) rather than - overriding `.logo { height }` — VitePress sets the height from a scoped - `:deep(.logo)` selector that out-specifies a plain `.VPNavBarTitle .logo`, so - the variable is the only reliable lever. Spacing stays on the logo's default - margin (no extra `gap`, which would compound with it). - Light/dark switching is left to VitePress: with `logo: { light, dark }` per - repo, its native .VPImage class toggle already shows the right artwork. */ -:root:root { --vp-nav-logo-height: 34px; } -@media (max-width: 768px) { :root:root { --vp-nav-logo-height: 28px; } } - -/* Home hero: tighten the display type. (The hero `image:` is one file per repo, - set in index.md frontmatter; for a crisp mark in both schemes make it - scheme-aware there — hero.image.light / hero.image.dark.) */ -.VPHero .name { letter-spacing: -1.5px; } -.VPHero .text { letter-spacing: -0.5px; } - -/* Feature cards: hairline border + a soft lift on hover, echoing the landing - page's package cards; brand-tinted icon chip. */ +/* ── Soft chrome — shared across every btravstack site ─────────── */ + +/* No horizontal scroll, ever (clip — not hidden — so sticky keeps working). */ +html, body { overflow-x: clip; } + +/* Display voice on the big headings; docs body stays Geist for reading. */ +.VPHero .name, +.VPHero .text { font-family: var(--display); letter-spacing: var(--tracking-hero); } +.VPHero .name { font-weight: 800; } +.VPFeature .title { font-family: var(--display); letter-spacing: var(--tracking-snug); } + +/* Home hero: the gently floating mark — the one character moment per page. */ +.VPHome { overflow-x: clip; position: relative; } +.VPHero .image-container { animation: btv-floaty 6s var(--ease) infinite; } +.VPHero .image-src { filter: drop-shadow(0 14px 30px color-mix(in srgb, var(--accent-deep) 40%, transparent)); } +@keyframes btv-floaty { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-9px); } } + +/* Navbar logo sizing via VitePress's own variable. */ +:root:root { --vp-nav-logo-height: 32px; } +@media (max-width: 768px) { :root:root { --vp-nav-logo-height: 26px; } } + +/* Feature cards: hairline border + a soft lift on hover; brand-tinted icon chip. */ .VPFeature { border: 1px solid var(--vp-c-divider); border-radius: var(--radius-lg); - transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease; + transition: transform var(--speed) var(--ease), border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease); } .VPFeature:hover { - transform: translateY(-4px); + transform: translateY(-2px); border-color: var(--vp-c-border); box-shadow: var(--shadow-card); } @@ -119,47 +112,38 @@ background: var(--vp-c-brand-soft) !important; border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent); } -.VPFeature .title { letter-spacing: -0.3px; } -/* Brand buttons: springy hover */ -.VPButton.brand { transition: filter 0.15s, transform 0.15s; } -.VPButton.brand:hover { filter: brightness(1.07); transform: translateY(-1px); } +/* Brand buttons: soft lift, no bounce. */ +.VPButton { font-weight: 600; } +.VPButton.brand { transition: background-color var(--speed-fast) var(--ease), transform var(--speed-fast) var(--ease); } +.VPButton.brand:hover { transform: translateY(-1px); } +.VPButton.brand:active { transform: translateY(0); } -/* Rounder, pinker code */ -.vp-doc :not(pre) > code { border-radius: 7px; } +/* Rounded code + admonitions. */ +.vp-doc :not(pre) > code { border-radius: var(--radius-sm); } .vp-doc div[class*="language-"] { border-radius: var(--radius); } .vp-doc .custom-block { border-radius: var(--radius); } -/* ── Home hero: a bigger mark + a large accent glow shape ───────── - The default hero only has a small glow hugging the logo; the docs - mockups show a broad accent wash bleeding off the top-right. */ -/* position: relative only — the existing `.VPHome { overflow-x: clip }` above - handles the glow's horizontal bleed; a full `overflow: clip` here would also - clip vertical content like the feature-card hover-lift shadows. */ -.VPHome { position: relative; } -.VPHome::before { - content: ""; - position: absolute; top: -40px; right: -120px; z-index: 0; - width: 620px; height: 620px; max-width: 85vw; - background: radial-gradient(closest-side, color-mix(in srgb, var(--accent) 16%, transparent), transparent 70%); - pointer-events: none; -} -.VPHome .VPHero, -.VPHome .VPFeatures { position: relative; z-index: 1; } +/* Hero mark sizing on docs homes (all project logos are square). */ @media (min-width: 960px) { - .VPHero .image-container { width: 340px; height: 340px; } + .VPHero .image-container { width: 320px; height: 320px; } .VPHero .image-bg { width: 100%; height: 100%; } - /* the mark ships at a small intrinsic size, so stretch it explicitly - (all project logos are square) rather than relying on max-width */ .VPHero .image-src { - width: 300px; - height: 300px; - max-width: 300px; - max-height: 300px; + width: 280px; + height: 280px; + max-width: 280px; + max-height: 280px; object-fit: contain; } } +/* Focus is a first-class state — instant, visible, never animated. */ +:focus-visible { + outline: 2px solid var(--focus); + outline-offset: 2px; + transition: none; +} + /* ── "Part of btravstack" org-attribution strip (layout-bottom slot) ── */ .btv-partof { border-top: 1px solid var(--vp-c-divider); @@ -176,18 +160,18 @@ font-weight: 600; color: var(--vp-c-text-3); text-decoration: none; - transition: color 0.15s; + transition: color var(--speed-fast) var(--ease); } .btv-partof-link:hover { color: var(--vp-c-text-2); } .btv-partof-link:focus-visible { - outline: 2px solid var(--accent); + outline: 2px solid var(--focus); outline-offset: 3px; - border-radius: 4px; + border-radius: var(--radius-sm); color: var(--vp-c-text-1); } .btv-partof-mark { display: inline-flex; line-height: 0; } -.btv-partof-word { font-weight: 700; letter-spacing: -0.3px; color: var(--vp-c-text-2); } -.btv-partof-pink { color: var(--bt-pink); } +.btv-partof-word { font-family: var(--display); font-weight: 700; letter-spacing: -0.01em; color: var(--vp-c-text-2); } +.btv-partof-pink { color: var(--text-accent); } @media (prefers-reduced-motion: reduce) { .VPHero .image-container { animation: none; } diff --git a/packages/theme/src/tokens.css b/packages/theme/src/tokens.css index 5167a3a..13914e2 100644 --- a/packages/theme/src/tokens.css +++ b/packages/theme/src/tokens.css @@ -1,18 +1,28 @@ +/* Hallmark · genre: playful · macrostructure: Bento Grid · design-system: design.md · designed-as-app + * theme: custom · vibe: "warm, rounded, charming beetroot, soft-but-exact" + * paper: oklch(97.5% 0.008 353) · accent: #E0589A (oklch(65.5% 0.182 353)) + * display: Plus Jakarta Sans 700/800 · body: Geist · outlier: JetBrains Mono + * axes: light / rounded-sans / chromatic-other (beetroot-pink ~353°) + * nav: N1b · footer: Ft8 marquee · studied: no · context: user-provided + * pre-emit critique: P4 H4 E4 S4 R4 V5 + */ + /* - * btravstack design tokens — the single source of truth. + * btravstack design tokens — the single source of truth ("Beetroot Soft"). * - * Mirrors the btravstack Design System (tokens/colors.css, themes.css, - * typography.css, spacing.css). Reference the SEMANTIC tokens (--bg, --text, + * Soft and exact at once: warm near-white paper, white card lifts, 12px + * corners, soft accent-tinted shadows, and the beetroot as a charming + * character rather than a mark. Reference the SEMANTIC tokens (--bg, --text, * --accent, --text-accent, --radius, --space-*) in app code; the raw --bt-* * palette underneath is the provenance, exposed so adapters (e.g. VitePress) * can remap freely. * - * Dark is the canonical default. The light scheme overrides the semantic - * tokens; it activates on `:root:not(.dark)` (VitePress's appearance system) - * as well as an explicit `.light` / `[data-theme="light"]`. + * LIGHT is the canonical scheme. The dark "plum" scheme overrides the + * semantic tokens on `.dark` / `[data-theme="dark"]` (VitePress's + * appearance system). * - * Fills vs text: keep --accent / --green for FILLS (buttons, glow, washes); - * use --text-accent / --text-green for COLORED TEXT and icons — light mode + * Fills vs text: keep --accent / --green for FILLS (buttons, washes); use + * --text-accent / --text-green for COLORED TEXT and icons — light mode * darkens those to deep beetroot so links/labels pass WCAG-AA on white. */ :root { @@ -26,60 +36,62 @@ --bt-leaf-dark: #2C8B4E; /* side leaves */ --bt-green: #46B86C; /* "success" green (checks) */ --bt-cream: #F5EFF3; /* near-white text */ - --bt-ink: #1a0c14; /* near-black, for text on accent */ + --bt-ink: #1F1218; /* near-black, for text on accent */ - /* ── Surfaces (dark scheme — the canonical btravstack look) ───── */ - --bg: #15101c; - --bg-grad: #1b1326; /* radial hero glow inner stop */ - --card: #1d1726; - --card-soft: #241c31; + /* ── Surfaces (LIGHT scheme — canonical) ─────────────────────── */ + --bg: oklch(97.5% 0.008 353); /* warm near-white ≈ #faf4f6 */ + --bg-grad: oklch(95.5% 0.014 353); /* soft wash (legacy alias) */ + --card: oklch(99.5% 0.003 353); /* white lift */ + --card-soft: oklch(95% 0.012 353); - /* ── Borders / dividers ──────────────────────────────────────── */ - --border: rgba(255, 255, 255, 0.08); - --border-2: rgba(255, 255, 255, 0.12); + /* ── Borders / dividers — soft hairlines ─────────────────────── */ + --border: color-mix(in srgb, var(--text) 12%, transparent); + --border-2: color-mix(in srgb, var(--text) 20%, transparent); /* ── Text ────────────────────────────────────────────────────── */ - --text: #F5EFF3; - --muted: #AEA1BC; - --faint: #7E7290; + --text: oklch(24% 0.020 350); /* ≈ #291d23 */ + --muted: oklch(42% 0.018 350); /* ~7:1 on paper */ + --faint: oklch(52% 0.015 350); /* ~4.6:1 on paper */ - /* ── Accent — the single brand knob (v1.4.0) ───────────────────── + /* ── Accent — the single brand knob ────────────────────────────── Set ONE value, `--accent`, and the whole theme rebrands: the deep, hover, soft and wash shades below all derive from it via color-mix, - so a consumer site needs only `:root { --accent: #FF6600 }`. The - beetroot pink here is the default (the btravstack org brand); the - docs sites override it with their own accent. - (Optional finer knobs: override --text-accent for a hand-tuned - light-mode AA link color, or --accent-contrast for ink on the fill.) */ + so a consumer site needs only `:root:root { --accent: #FF6600 }`. + The beetroot pink here is the default (the btravstack org brand); + the docs sites override it with their own accent. */ --accent: #E0589A; /* ← the knob */ - --accent-contrast: #1a0c14; /* readable text on an accent fill */ - --accent-deep: color-mix(in srgb, var(--accent), #000 42%); /* deep root — active/press */ - --accent-hover: color-mix(in srgb, var(--accent), #000 18%); /* button hover fill */ - --accent-soft: color-mix(in srgb, var(--accent) 62%, #fff); /* highlight vein / brand-2 */ - --accent-rgb: 224, 88, 154; /* legacy: prefer color-mix over rgba(var(--accent-rgb),…) */ + --accent-contrast: #1F1218; /* readable text on an accent fill */ + --accent-deep: color-mix(in srgb, var(--accent), #000 42%); /* active/press */ + --accent-hover: color-mix(in srgb, var(--accent), #000 14%); /* button hover fill */ + --accent-soft: color-mix(in srgb, var(--accent) 62%, #fff); /* highlight vein */ + --accent-rgb: 224, 88, 154; /* legacy: prefer color-mix */ --green: #46B86C; --green-rgb: 70, 184, 108; - /* ── Accent-as-TEXT (split from fill; light darkens it for AA) ─── */ - --text-accent: var(--accent); /* on dark plum the accent reads fine as text */ - --text-green: #46B86C; + /* ── Accent-as-TEXT (split from fill; light darkens it for AA) ── */ + --text-accent: color-mix(in srgb, var(--accent), #000 28%); /* deep beet, AA on paper */ + --text-green: #15683A; - /* Display accent — the accent as the LARGE display wordmark color in - both schemes (large text only needs 3:1, so it can stay fully vibrant). */ - --display-accent: var(--accent); + /* Display accent — the accent as the LARGE display wordmark color + (large text only needs 3:1, so it can stay vibrant). */ + --display-accent: color-mix(in srgb, var(--accent), #000 12%); + + /* ── Focus — instant, never animated ─────────────────────────── */ + --focus: color-mix(in srgb, var(--accent), #000 16%); /* ── Glass (frosted sticky headers) ──────────────────────────── */ - --glass: rgba(21, 16, 28, 0.66); + --glass: color-mix(in srgb, var(--bg) 76%, transparent); /* ── Accent washes / lines / code inset ──────────────────────── */ - --accent-wash: color-mix(in srgb, var(--accent) 10%, transparent); - --accent-wash-2: color-mix(in srgb, var(--accent) 16%, transparent); - --accent-line: color-mix(in srgb, var(--accent) 25%, transparent); - --code-bg: rgba(0, 0, 0, 0.35); /* inline code inset */ + --accent-wash: color-mix(in srgb, var(--accent) 9%, transparent); + --accent-wash-2: color-mix(in srgb, var(--accent) 14%, transparent); + --accent-line: color-mix(in srgb, var(--accent-deep) 35%, transparent); + --code-bg: color-mix(in srgb, var(--accent) 7%, #fff); - /* ── Typography: families ────────────────────────────────────── */ - --sans: 'Montserrat', 'Segoe UI', system-ui, sans-serif; - --mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace; + /* ── Typography: families (2+1 — see design.md) ──────────────── */ + --display: 'Plus Jakarta Sans', 'Segoe UI', system-ui, sans-serif; + --sans: 'Geist', 'Segoe UI', system-ui, sans-serif; + --mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace; /* ── Typography: weights ─────────────────────────────────────── */ --fw-medium: 500; @@ -88,14 +100,14 @@ --fw-extrabold: 800; /* ── Typography: display / heading scale (clamped, fluid) ─────── */ - --fs-hero: clamp(52px, 9vw, 92px); + --fs-hero: clamp(38px, 5.5vw, 60px); --fs-h1: clamp(34px, 6vw, 56px); - --fs-h2: clamp(28px, 4vw, 40px); - --fs-h3: 23px; - --fs-h4: 18px; + --fs-h2: clamp(24px, 3.2vw, 32px); + --fs-h3: 20px; + --fs-h4: 17px; /* ── Typography: body / UI scale ─────────────────────────────── */ - --fs-lead: clamp(20px, 2.6vw, 26px); + --fs-lead: clamp(18px, 2.2vw, 22px); --fs-body-lg: 17px; --fs-body: 15px; --fs-body-sm: 14px; @@ -103,20 +115,20 @@ --fs-eyebrow: 13px; /* ── Typography: tracking + line heights ─────────────────────── */ - --tracking-hero: -2.5px; - --tracking-tight: -1px; - --tracking-snug: -0.5px; - --tracking-eyebrow: 1.5px; - --lh-tight: 1.08; + --tracking-hero: -0.025em; + --tracking-tight: -0.015em; + --tracking-snug: -0.01em; + --tracking-eyebrow: 0.08em; + --lh-tight: 1.12; --lh-snug: 1.3; --lh-body: 1.6; - /* ── Radii ───────────────────────────────────────────────────── */ - --radius-sm: 9px; - --radius: 12px; - --radius-lg: 16px; - --radius-xl: 20px; - --radius-2xl: 24px; + /* ── Radii — rounded, friendly (12px cards, 999px pills) ─────── */ + --radius-sm: 8px; + --radius: 10px; + --radius-lg: 12px; + --radius-xl: 14px; + --radius-2xl: 16px; --radius-pill: 999px; /* ── Spacing scale ───────────────────────────────────────────── */ @@ -127,51 +139,48 @@ --space-5: 22px; --space-6: 28px; - /* ── Shadows ─────────────────────────────────────────────────── */ - --shadow-card: 0 24px 60px -28px rgba(0, 0, 0, 0.8); - --shadow-accent: 0 12px 30px -10px color-mix(in srgb, var(--accent) 60%, transparent); - --shadow-toast: 0 18px 40px -16px rgba(0, 0, 0, 0.8); + /* ── Shadows — soft, accent-tinted, restrained ───────────────── */ + --shadow-card: 0 8px 24px -10px color-mix(in srgb, var(--accent-deep) 40%, transparent); + --shadow-accent: 0 8px 20px -8px color-mix(in srgb, var(--accent) 55%, transparent); + --shadow-toast: 0 12px 28px -12px color-mix(in srgb, var(--accent-deep) 45%, transparent); /* ── Layout & motion ─────────────────────────────────────────── */ --container: 1180px; --speed-fast: 0.15s; --speed: 0.2s; - --ease: ease; + --ease: cubic-bezier(0.16, 1, 0.3, 1); } /* - * Light scheme — warm, faintly beetroot-tinted near-whites. Only the semantic - * tokens flip; the raw --bt-* palette is unchanged. Brand FILLS stay vibrant - * pink (dark ink on top); accent-as-TEXT darkens to deep beetroot for AA. + * Dark scheme — the soft plum the brand grew up on. Only the semantic tokens + * flip; the raw --bt-* palette and the hue family never change. Brand FILLS + * stay vibrant (dark ink on top); accent-as-TEXT reads fine on plum. */ -:root:not(.dark), -[data-theme="light"], -.light { - --bg: #FBF7F9; - --bg-grad: #FCEDF4; - --card: #FFFFFF; - --card-soft: #F0E6EC; - - --border: rgba(26, 12, 20, 0.16); - --border-2: rgba(26, 12, 20, 0.24); - - --text: #1F0E18; - --muted: #5E5159; /* ~7.1:1 on white */ - --faint: #766678; /* ~5.0:1 on white (was #978996, 3.1:1 — failed AA) */ - - /* --accent (the FILL) is NOT overridden here — it stays the site's vibrant - accent in both schemes (dark ink sits on top). Only accent-as-TEXT darkens - for WCAG-AA on white. */ - --text-accent: color-mix(in srgb, var(--accent), #000 22%); /* darkened accent, ~AA on white */ - --text-green: #15683A; /* ~5.2:1 on white — affirmative text */ - - --accent-wash: color-mix(in srgb, var(--accent) 12%, transparent); +.dark, +[data-theme="dark"] { + --bg: oklch(18.5% 0.025 310); /* plum ≈ #171021 */ + --bg-grad: oklch(21% 0.028 310); + --card: oklch(22% 0.026 310); + --card-soft: oklch(25.5% 0.027 310); + + --border: color-mix(in srgb, var(--text) 10%, transparent); + --border-2: color-mix(in srgb, var(--text) 16%, transparent); + + --text: oklch(94% 0.008 340); + --muted: oklch(76% 0.014 330); + --faint: oklch(62% 0.014 320); + + --text-accent: var(--accent); + --text-green: #46B86C; + --display-accent: var(--accent); + + --glass: color-mix(in srgb, var(--bg) 72%, transparent); + --accent-wash: color-mix(in srgb, var(--accent) 11%, transparent); --accent-wash-2: color-mix(in srgb, var(--accent) 16%, transparent); - --accent-line: color-mix(in srgb, var(--accent-deep) 40%, transparent); - --glass: rgba(255, 255, 255, 0.72); - --code-bg: color-mix(in srgb, var(--accent) 8%, #fff); + --accent-line: color-mix(in srgb, var(--accent) 26%, transparent); + --code-bg: rgba(0, 0, 0, 0.35); - --shadow-card: 0 18px 44px -26px color-mix(in srgb, var(--accent-deep) 55%, transparent); - --shadow-accent: 0 12px 26px -10px color-mix(in srgb, var(--accent) 45%, transparent); - --shadow-toast: 0 18px 40px -16px rgba(40, 16, 30, 0.18); + --shadow-card: 0 12px 32px -14px rgba(0, 0, 0, 0.7); + --shadow-accent: 0 8px 22px -8px color-mix(in srgb, var(--accent) 45%, transparent); + --shadow-toast: 0 12px 30px -12px rgba(0, 0, 0, 0.7); }