diff --git a/web/public/favicon-512.png b/web/public/favicon-512.png new file mode 100755 index 0000000..779af48 Binary files /dev/null and b/web/public/favicon-512.png differ diff --git a/web/public/logo.svg b/web/public/logo.svg new file mode 100755 index 0000000..158748a --- /dev/null +++ b/web/public/logo.svg @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/web/src/app/apple-icon.png b/web/src/app/apple-icon.png new file mode 100755 index 0000000..26bce16 Binary files /dev/null and b/web/src/app/apple-icon.png differ diff --git a/web/src/app/brand-tokens.css b/web/src/app/brand-tokens.css new file mode 100755 index 0000000..da3ee35 --- /dev/null +++ b/web/src/app/brand-tokens.css @@ -0,0 +1,208 @@ +/* Modular Mind brand tokens — ported from the design system. + * Fonts are loaded via next/font in layout.tsx, which supplies the + * --font-archivo / --font-plex-sans / --font-plex-mono variables used below. */ + +/* Modular Mind — Color tokens + * Direction: Rack Panel — warm graphite anthracite surfaces (eurorack front panels), + * a single amber "signal" accent, and a tight verdict semantic set. + * Base scales first, then semantic aliases. */ +:root { + /* ---- Graphite neutrals (warm-tinted anthracite) ---- */ + --mm-black: #0c0a07; /* deepest, page void */ + --mm-graphite-950: #131009; /* page background */ + --mm-graphite-900: #1c1811; /* recessed surface */ + --mm-graphite-800: #26221b; /* panel */ + --mm-graphite-700: #302a20; /* raised panel / hover */ + --mm-graphite-600: #3a3428; /* hairline / border */ + --mm-graphite-500: #4a4335; /* strong border / divider */ + --mm-graphite-400: #6f6656; /* faint text, ticks */ + --mm-graphite-300: #a2988a; /* dim text */ + --mm-graphite-200: #d6cdbe; /* secondary text */ + --mm-graphite-100: #f2ece0; /* primary text (warm bone) */ + + /* ---- Amber signal (the one accent) ---- */ + --mm-amber-bright: #ffbe5c; /* hover / peak highlight */ + --mm-amber: #f2a63c; /* primary accent, waveform, active */ + --mm-amber-deep: #c77f22; /* pressed */ + --mm-amber-dim: #4a3418; /* accent fill / tint background */ + --mm-amber-ink: #17140f; /* text/glyph on amber fills */ + + /* ---- Verdict semantics ---- */ + --mm-caution: #f2c14e; /* dc_offset, near_silent */ + --mm-caution-dim: #40350f; + --mm-fault: #e8664e; /* clipping */ + --mm-fault-dim: #3d1c14; + --mm-good: #f2a63c; /* passing / makes sound — reuses signal amber */ + + /* ---- Semantic aliases ---- */ + --mm-bg: var(--mm-graphite-950); + --mm-surface: var(--mm-graphite-900); + --mm-panel: var(--mm-graphite-800); + --mm-panel-raised: var(--mm-graphite-700); + --mm-border: var(--mm-graphite-600); + --mm-border-strong: var(--mm-graphite-500); + --mm-divider: var(--mm-graphite-600); + + --mm-text: var(--mm-graphite-100); + --mm-text-secondary: var(--mm-graphite-200); + --mm-text-dim: var(--mm-graphite-300); + --mm-text-faint: var(--mm-graphite-400); + + --mm-accent: var(--mm-amber); + --mm-accent-hover: var(--mm-amber-bright); + --mm-accent-active: var(--mm-amber-deep); + --mm-accent-tint: var(--mm-amber-dim); + --mm-on-accent: var(--mm-amber-ink); + + --mm-signal: var(--mm-amber); /* waveform trace */ + --mm-signal-grid: rgba(242,166,60,.08); /* scope grid lines */ + + --mm-link: var(--mm-amber); + --mm-link-hover: var(--mm-amber-bright); +} + +/* Modular Mind — Typography tokens + * Archivo → display / headings (mechanical grotesque, tight) + * IBM Plex Sans → body / prose + * IBM Plex Mono → data, labels, slugs, metrics, "readouts" + * Mono carries the instrument-panel voice; use it for anything numeric or code-like. */ +:root { + /* ---- Families ---- */ + --mm-font-display: var(--font-archivo), 'Helvetica Neue', Arial, sans-serif; + --mm-font-body: var(--font-plex-sans), system-ui, sans-serif; + --mm-font-mono: var(--font-plex-mono), ui-monospace, 'SF Mono', Menlo, monospace; + + /* ---- Weights ---- */ + --mm-weight-regular: 400; + --mm-weight-medium: 500; + --mm-weight-semibold:600; + --mm-weight-bold: 700; + --mm-weight-display: 800; + + /* ---- Type scale (px) ---- */ + --mm-size-3xs: 9px; /* micro caps / ticks */ + --mm-size-2xs: 10px; /* mono labels */ + --mm-size-xs: 11px; /* meta, slugs */ + --mm-size-sm: 13px; /* body small, table */ + --mm-size-md: 15px; /* body */ + --mm-size-lg: 17px; /* card title */ + --mm-size-xl: 21px; /* section title */ + --mm-size-2xl: 28px; /* stat number / page sub */ + --mm-size-3xl: 40px; /* page title */ + --mm-size-4xl: 60px; /* hero */ + + /* ---- Line heights ---- */ + --mm-leading-tight: 1.1; + --mm-leading-snug: 1.3; + --mm-leading-normal: 1.55; + + /* ---- Letter spacing ---- */ + --mm-tracking-tight: -0.02em; /* display */ + --mm-tracking-normal: 0; + --mm-tracking-label: 0.08em; /* mono labels */ + --mm-tracking-caps: 0.14em; /* micro caps */ + + /* ---- Semantic roles ---- */ + --mm-text-hero: var(--mm-weight-display) var(--mm-size-4xl)/var(--mm-leading-tight) var(--mm-font-display); + --mm-text-title: var(--mm-weight-bold) var(--mm-size-3xl)/var(--mm-leading-tight) var(--mm-font-display); + --mm-text-section: var(--mm-weight-bold) var(--mm-size-xl)/var(--mm-leading-snug) var(--mm-font-display); + --mm-text-card: var(--mm-weight-bold) var(--mm-size-lg)/var(--mm-leading-snug) var(--mm-font-display); + --mm-text-body: var(--mm-weight-regular) var(--mm-size-md)/var(--mm-leading-normal) var(--mm-font-body); + --mm-text-body-sm: var(--mm-weight-regular) var(--mm-size-sm)/var(--mm-leading-normal) var(--mm-font-body); + --mm-text-mono: var(--mm-weight-regular) var(--mm-size-xs)/var(--mm-leading-snug) var(--mm-font-mono); + --mm-text-readout: var(--mm-weight-bold) var(--mm-size-2xl)/var(--mm-leading-tight) var(--mm-font-mono); +} + +/* Modular Mind — Spacing tokens. 4px base grid; dense instrument-panel rhythm. */ +:root { + --mm-space-0: 0; + --mm-space-1: 2px; + --mm-space-2: 4px; + --mm-space-3: 6px; + --mm-space-4: 8px; + --mm-space-5: 10px; + --mm-space-6: 12px; + --mm-space-7: 14px; + --mm-space-8: 16px; + --mm-space-10: 20px; + --mm-space-12: 24px; + --mm-space-14: 28px; + --mm-space-16: 32px; + --mm-space-20: 40px; + --mm-space-24: 48px; + --mm-space-32: 64px; + + /* Semantic */ + --mm-pad-badge: 4px 8px; + --mm-pad-button: 8px 14px; + --mm-pad-card: 16px 18px; + --mm-pad-panel: 18px; + --mm-gap-tight: 6px; + --mm-gap: 12px; + --mm-gap-loose: 20px; + + /* Layout */ + --mm-content-max: 1120px; + --mm-nav-height: 52px; +} + +/* Modular Mind — Radius tokens. Tight & technical — machined panel corners, never pill-soft. */ +:root { + --mm-radius-none: 0; + --mm-radius-xs: 2px; /* ticks, inner elements */ + --mm-radius-sm: 3px; /* badges, buttons, inputs */ + --mm-radius-md: 5px; /* cards, panels */ + --mm-radius-lg: 8px; /* large surfaces, modals */ + --mm-radius-full: 999px; /* status dots, play button only */ + + /* Semantic */ + --mm-radius-badge: var(--mm-radius-sm); + --mm-radius-button: var(--mm-radius-sm); + --mm-radius-card: var(--mm-radius-md); + --mm-radius-panel: var(--mm-radius-md); +} + +/* Modular Mind — Elevation & effects. + * Panels read like machined front panels: a faint top light-line, a seated drop shadow, + * a hairline border. The amber glow is reserved for live signal (waveform, play, active). */ +:root { + /* Border-as-elevation hairlines are the primary separation; shadows are subtle. */ + --mm-shadow-panel: + inset 0 1px 0 rgba(255,255,255,.035), + 0 1px 2px rgba(0,0,0,.5); + --mm-shadow-raised: + inset 0 1px 0 rgba(255,255,255,.05), + 0 4px 14px rgba(0,0,0,.45); + --mm-shadow-pop: + inset 0 1px 0 rgba(255,255,255,.06), + 0 12px 34px rgba(0,0,0,.55); + --mm-shadow-inset: + inset 0 1px 3px rgba(0,0,0,.5); + + /* Signal glow — amber phosphor */ + --mm-glow-signal: 0 0 12px rgba(242,166,60,.45); + --mm-glow-signal-sm:0 0 6px rgba(242,166,60,.5); + --mm-glow-fault: 0 0 10px rgba(232,102,78,.45); + + /* Focus ring */ + --mm-ring: 0 0 0 2px var(--mm-graphite-950), 0 0 0 3.5px var(--mm-amber); +} + +/* Modular Mind — Motion. Mechanical and fast: linear-ish snaps, no bounce, no float. + * Hover brightens borders/fills; press seats the element down 1px. */ +:root { + --mm-dur-instant: 80ms; /* @kind other */ + --mm-dur-fast: 130ms; /* @kind other */ + --mm-dur: 200ms; /* @kind other */ + --mm-dur-slow: 340ms; /* @kind other */ + + --mm-ease: cubic-bezier(.2,.7,.3,1); /* @kind other */ + --mm-ease-out: cubic-bezier(.16,1,.3,1); /* @kind other */ + --mm-ease-linear: linear; /* @kind other */ + + --mm-transition-control: background var(--mm-dur-fast) var(--mm-ease), + border-color var(--mm-dur-fast) var(--mm-ease), + color var(--mm-dur-fast) var(--mm-ease), + transform var(--mm-dur-instant) var(--mm-ease); +} + diff --git a/web/src/app/favicon.ico b/web/src/app/favicon.ico index 718d6fe..816a7ea 100644 Binary files a/web/src/app/favicon.ico and b/web/src/app/favicon.ico differ diff --git a/web/src/app/globals.css b/web/src/app/globals.css index f1d8c73..ef11850 100644 --- a/web/src/app/globals.css +++ b/web/src/app/globals.css @@ -1 +1,17 @@ @import "tailwindcss"; +@import "./brand-tokens.css"; + +/* Brand ground + base type (was: bg-zinc-950 / text-zinc-100 utility classes). + * These must live in @layer base: unlayered rules outrank every Tailwind + * utility, which would make `a` here override any text-colour class. */ +@layer base { + body { + background: var(--mm-bg); + color: var(--mm-text); + font: var(--mm-text-body); + -webkit-font-smoothing: antialiased; + } + a { color: var(--mm-link); text-decoration: none; } + a:hover { color: var(--mm-link-hover); } + ::selection { background: var(--mm-amber-dim); color: var(--mm-amber-bright); } +} diff --git a/web/src/app/icon.svg b/web/src/app/icon.svg new file mode 100755 index 0000000..158748a --- /dev/null +++ b/web/src/app/icon.svg @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/web/src/app/layout.tsx b/web/src/app/layout.tsx index 2db9bc7..35e5d7a 100644 --- a/web/src/app/layout.tsx +++ b/web/src/app/layout.tsx @@ -1,7 +1,27 @@ import type { Metadata } from 'next' +import { Archivo, IBM_Plex_Sans, IBM_Plex_Mono } from 'next/font/google' import { SiteNav } from '@/components/site-nav' import './globals.css' +const archivo = Archivo({ + subsets: ['latin'], + variable: '--font-archivo', +}) + +const plexSans = IBM_Plex_Sans({ + subsets: ['latin'], + weight: ['400', '500', '600'], + variable: '--font-plex-sans', +}) + +// IBM Plex Mono has no variable cut on Google Fonts — weights must be listed. +// 700 is required by --mm-text-readout. +const plexMono = IBM_Plex_Mono({ + subsets: ['latin'], + weight: ['400', '500', '600', '700'], + variable: '--font-plex-mono', +}) + export const metadata: Metadata = { title: 'Modular Mind', description: 'An AI that learns to build modular synth patches — explore the pipeline and listen to what it makes.', @@ -9,8 +29,11 @@ export const metadata: Metadata = { export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - -
+ +