Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added web/public/favicon-512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions web/public/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/src/app/apple-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
208 changes: 208 additions & 0 deletions web/src/app/brand-tokens.css
Original file line number Diff line number Diff line change
@@ -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);
}

Binary file modified web/src/app/favicon.ico
Binary file not shown.
16 changes: 16 additions & 0 deletions web/src/app/globals.css
Original file line number Diff line number Diff line change
@@ -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); }
}
4 changes: 4 additions & 0 deletions web/src/app/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 25 additions & 2 deletions web/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,39 @@
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.',
}

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className="dark">
<body className="min-h-screen bg-zinc-950 text-zinc-100 antialiased">
<html
lang="en"
className={`dark ${archivo.variable} ${plexSans.variable} ${plexMono.variable}`}
>
<body className="min-h-screen">
<SiteNav />
<main className="mx-auto max-w-5xl px-6 py-10">{children}</main>
</body>
Expand Down
10 changes: 7 additions & 3 deletions web/src/components/site-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ const links = [
export function SiteNav() {
const pathname = usePathname()
return (
<nav className="flex gap-6 border-b border-zinc-800 px-6 py-4 text-sm">
<Link href="/" className="font-semibold tracking-tight">
<nav className="flex items-center gap-6 border-b border-[var(--mm-border)] bg-[var(--mm-surface)] px-6 text-sm h-[var(--mm-nav-height)]">
<Link href="/" className="font-semibold tracking-tight text-[var(--mm-accent)]">
Modular Mind
</Link>
{links.map((l) => {
Expand All @@ -25,7 +25,11 @@ export function SiteNav() {
key={l.href}
href={l.href}
aria-current={isActive ? 'page' : undefined}
className="text-zinc-400 hover:text-zinc-100"
className={
isActive
? 'text-[var(--mm-text)]'
: 'text-[var(--mm-text-dim)] hover:text-[var(--mm-text)]'
}
>
{l.label}
</Link>
Expand Down
Loading