diff --git a/packages/web/src/app/globals.css b/packages/web/src/app/globals.css index 4d617244d..0f8e6a365 100644 --- a/packages/web/src/app/globals.css +++ b/packages/web/src/app/globals.css @@ -54,31 +54,33 @@ --color-text-muted: #8b8b93; --color-text-inverse: #ffffff; - /* Interactive accent — Linear indigo */ - --color-accent: #5e6ad2; - --color-accent-hover: #4850b8; - --color-accent-subtle: rgba(94, 106, 210, 0.08); + /* Interactive accent */ + --color-accent: #5B7EF8; + --color-accent-hover: #7a96ff; + --color-accent-subtle: rgba(91, 126, 248, 0.12); /* Status / semantic colors */ - --color-status-working: #5e6ad2; - --color-status-ready: #1a7f37; + --color-status-working: #16a34a; + --color-status-ready: #5B7EF8; + --color-status-respond: #ca8a04; + --color-status-review: #0891b2; --color-status-attention: #9a6700; --color-status-idle: #8b949e; --color-status-done: #d0d7de; - --color-status-error: #cf222e; + --color-status-error: #dc2626; /* Semantic aliases */ - --color-accent-blue: #5e6ad2; - --color-accent-green: #1a7f37; + --color-accent-blue: #5B7EF8; + --color-accent-green: #16a34a; --color-accent-yellow: #9a6700; --color-accent-orange: #bc4c00; - --color-accent-red: #cf222e; + --color-accent-red: #dc2626; --color-accent-violet: #8250df; --color-accent-purple: #8250df; /* Theme-adaptive tints (bg tints behind status pills) */ - --color-tint-blue: rgba(94, 106, 210, 0.08); - --color-tint-green: rgba(26, 127, 55, 0.08); + --color-tint-blue: rgba(91, 126, 248, 0.08); + --color-tint-green: rgba(22, 163, 74, 0.08); --color-tint-yellow: rgba(154, 103, 0, 0.08); --color-tint-red: rgba(207, 34, 46, 0.08); --color-tint-violet: rgba(130, 80, 223, 0.08); @@ -157,8 +159,8 @@ --color-alert-comment-bg: #c2410c; /* Activity pulse glow */ - --color-activity-pulse: rgba(94, 106, 210, 0.45); - --color-activity-pulse-end: rgba(94, 106, 210, 0); + --color-activity-pulse: rgba(22, 163, 74, 0.45); + --color-activity-pulse-end: rgba(22, 163, 74, 0); } /* ── Dark mode ────────────────────────────────────────────────────── */ @@ -186,30 +188,32 @@ --color-text-inverse: #0a0d12; /* Interactive accent */ - --color-accent: #8fb4ff; - --color-accent-hover: #b4ccff; - --color-accent-subtle: rgba(143, 180, 255, 0.16); + --color-accent: #5B7EF8; + --color-accent-hover: #7a96ff; + --color-accent-subtle: rgba(91, 126, 248, 0.12); /* Status */ - --color-status-working: #6e8fff; - --color-status-ready: #73e0aa; + --color-status-working: #22c55e; + --color-status-ready: #5B7EF8; + --color-status-respond: #f1be64; + --color-status-review: #06b6d4; --color-status-attention: #f1be64; --color-status-idle: #293142; --color-status-done: #202838; - --color-status-error: #ff7b72; + --color-status-error: #ef4444; /* Semantic aliases */ - --color-accent-blue: #8fb4ff; - --color-accent-green: #5fd39a; + --color-accent-blue: #5B7EF8; + --color-accent-green: #22c55e; --color-accent-yellow: #f1be64; --color-accent-orange: #ff9d57; - --color-accent-red: #ff7b72; + --color-accent-red: #ef4444; --color-accent-violet: #b59cff; --color-accent-purple: #b59cff; /* Theme-adaptive tints */ - --color-tint-blue: rgba(143, 180, 255, 0.12); - --color-tint-green: rgba(95, 211, 154, 0.12); + --color-tint-blue: rgba(91, 126, 248, 0.12); + --color-tint-green: rgba(34, 197, 94, 0.12); --color-tint-yellow: rgba(241, 190, 100, 0.12); --color-tint-red: rgba(255, 123, 114, 0.12); --color-tint-violet: rgba(181, 156, 255, 0.12); @@ -292,16 +296,17 @@ --color-alert-comment-bg: rgba(200, 138, 46, 0.25); /* Activity pulse glow */ - --color-activity-pulse: rgba(94, 106, 210, 0.45); - --color-activity-pulse-end: rgba(94, 106, 210, 0); + --color-activity-pulse: rgba(34, 197, 94, 0.45); + --color-activity-pulse-end: rgba(34, 197, 94, 0); } @theme { /* ── Typography ──────────────────────────────────────────────────── */ - --font-sans: var(--font-ibm-plex-sans), "SF Pro Text", -apple-system, system-ui, sans-serif; - --font-mono: var(--font-ibm-plex-mono), "SF Mono", "Menlo", "Consolas", monospace; + --font-sans: var(--font-geist-sans), "SF Pro Text", -apple-system, system-ui, sans-serif; + --font-mono: var(--font-jetbrains-mono), "SF Mono", "Menlo", "Consolas", monospace; /* ── Border radius ───────────────────────────────────────────────── */ + --radius-base: 2px; --radius-sm: 4px; --radius-md: 6px; --radius-lg: 8px; @@ -532,14 +537,14 @@ html.light .xterm .xterm-viewport:hover::-webkit-scrollbar-thumb:active { opacity: 1; } 50% { - opacity: 0.4; + opacity: 0.6; } } @keyframes slide-up { from { opacity: 0; - transform: translateY(4px); + transform: translateY(8px); } to { opacity: 1; @@ -547,34 +552,17 @@ html.light .xterm .xterm-viewport:hover::-webkit-scrollbar-thumb:active { } } -@keyframes ready-sheen { +@keyframes dot-ring { 0% { - transform: translateX(-160%); - opacity: 0; - } - 18% { - opacity: 0.28; + transform: scale(0.8); + opacity: 0.5; } 100% { - transform: translateX(220%); + transform: scale(1.3); opacity: 0; } } -@keyframes ready-rail-breathe { - 0%, - 100% { - box-shadow: - inset 0 1px 0 color-mix(in srgb, white 4%, transparent), - 0 18px 34px color-mix(in srgb, var(--color-status-ready) 10%, transparent); - } - 50% { - box-shadow: - inset 0 1px 0 color-mix(in srgb, white 4%, transparent), - 0 18px 38px color-mix(in srgb, var(--color-status-ready) 14%, transparent); - } -} - @keyframes ready-dot-pulse { 0%, 100% { @@ -587,16 +575,33 @@ html.light .xterm .xterm-viewport:hover::-webkit-scrollbar-thumb:active { } } +/* ── Status dot pulse — GPU-composited pseudo-element ring ────────────── + Use .dot-pulse on the dot element. Parent must be position:relative. */ + +.dot-pulse { + position: relative; +} + +.dot-pulse::after { + content: ""; + position: absolute; + inset: -3px; + border-radius: inherit; + background: inherit; + will-change: transform, opacity; + animation: dot-ring 2s ease-in-out infinite; +} + /* ── Kanban card entrance ─────────────────────────────────────────────── */ .kanban-card-enter { - animation: slide-up 0.18s ease forwards; + animation: slide-up 0.2s cubic-bezier(0.16, 1, 0.3, 1) forwards; } /* ── Orchestrator button ──────────────────────────────────────────────── */ .orchestrator-btn { - border-radius: 0; + border-radius: 2px; color: var(--color-accent); background: linear-gradient( 175deg, @@ -606,12 +611,16 @@ html.light .xterm .xterm-viewport:hover::-webkit-scrollbar-thumb:active { border: 1px solid color-mix(in srgb, var(--color-accent) 25%, transparent); box-shadow: var(--btn-shadow); transition: - transform 0.12s ease, + transform 0.16s ease-out, box-shadow 0.12s ease, background 0.12s ease, border-color 0.12s ease; } +.orchestrator-btn:active { + transform: scale(0.97); +} + .orchestrator-btn:hover { transform: translateY(-1px); background: linear-gradient( @@ -627,8 +636,8 @@ html.light .xterm .xterm-viewport:hover::-webkit-scrollbar-thumb:active { .nav-glass { background: var(--color-nav-bg); - backdrop-filter: blur(16px); - -webkit-backdrop-filter: blur(16px); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); } /* ── Dashboard shell ───────────────────────────────────────────────── */ @@ -1031,10 +1040,11 @@ html.light .xterm .xterm-viewport:hover::-webkit-scrollbar-thumb:active { .session-card { background: var(--card-bg); border-color: var(--card-border); - border-radius: 0; + border-radius: 2px; box-shadow: inset 0 1px 0 color-mix(in srgb, white 4%, transparent), 0 10px 20px rgba(0, 0, 0, 0.08); + contain: layout style paint; transition: transform 0.12s ease, border-color 0.12s ease, @@ -1210,7 +1220,6 @@ html.light .xterm .xterm-viewport:hover::-webkit-scrollbar-thumb:active { box-shadow: inset 0 1px 0 color-mix(in srgb, white 4%, transparent), 0 22px 40px color-mix(in srgb, var(--color-status-ready) 18%, transparent); - animation: ready-rail-breathe 3.8s ease-in-out infinite; } .session-card.card-merge-ready::before { diff --git a/packages/web/src/app/layout.tsx b/packages/web/src/app/layout.tsx index a78f259c2..ae789ffe3 100644 --- a/packages/web/src/app/layout.tsx +++ b/packages/web/src/app/layout.tsx @@ -1,22 +1,14 @@ import type { Metadata, Viewport } from "next"; -import { IBM_Plex_Sans, IBM_Plex_Mono, JetBrains_Mono } from "next/font/google"; +import { Geist, JetBrains_Mono } from "next/font/google"; import { ThemeProvider } from "next-themes"; import { getProjectName } from "@/lib/project-name"; import { ServiceWorkerRegistrar } from "@/components/ServiceWorkerRegistrar"; import "./globals.css"; -const ibmPlexSans = IBM_Plex_Sans({ +const geistSans = Geist({ subsets: ["latin"], - variable: "--font-ibm-plex-sans", + variable: "--font-geist-sans", display: "swap", - weight: ["300", "400", "500", "600", "700"], -}); - -const ibmPlexMono = IBM_Plex_Mono({ - subsets: ["latin"], - variable: "--font-ibm-plex-mono", - display: "swap", - weight: ["300", "400", "500"], }); const jetbrainsMono = JetBrains_Mono({ @@ -54,7 +46,7 @@ export async function generateMetadata(): Promise { export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - + {children} diff --git a/packages/web/src/components/ActivityDot.tsx b/packages/web/src/components/ActivityDot.tsx index d2abab4b7..a20c6aa0e 100644 --- a/packages/web/src/components/ActivityDot.tsx +++ b/packages/web/src/components/ActivityDot.tsx @@ -37,10 +37,7 @@ export function ActivityDot({ activity, dotOnly = false, size = 6 }: ActivityDot if (dotOnly) { return (
); @@ -52,10 +49,7 @@ export function ActivityDot({ activity, dotOnly = false, size = 6 }: ActivityDot style={{ background: c.bg }} >