+```
+
+4. **Title text color:**
+```tsx
+// BEFORE:
+text-[var(--color-neo-text-on-bright)]
+
+// AFTER:
+text-[var(--color-neo-text)]
+```
+
+---
+
+## 2. Playwright Browser Configuration
+
+### Overview
+
+Changed default Playwright settings for better performance:
+- **Default browser:** Firefox (lower CPU usage)
+- **Default mode:** Headless (saves resources)
+
+### Modified Files
+
+#### `client.py`
+
+**Changes:**
+
+```python
+# BEFORE:
+DEFAULT_PLAYWRIGHT_HEADLESS = False
+
+# AFTER:
+DEFAULT_PLAYWRIGHT_HEADLESS = True
+DEFAULT_PLAYWRIGHT_BROWSER = "firefox"
+```
+
+**New function added:**
+```python
+def get_playwright_browser() -> str:
+ """
+ Get the browser to use for Playwright.
+ Options: chrome, firefox, webkit, msedge
+ Firefox is recommended for lower CPU usage.
+ """
+ return os.getenv("PLAYWRIGHT_BROWSER", DEFAULT_PLAYWRIGHT_BROWSER).lower()
+```
+
+**Playwright args updated:**
+```python
+playwright_args = [
+ "@playwright/mcp@latest",
+ "--viewport-size", "1280x720",
+ "--browser", browser, # NEW: configurable browser
+]
+```
+
+---
+
+#### `.env.example`
+
+**Updated documentation:**
+```bash
+# PLAYWRIGHT_BROWSER: Which browser to use for testing
+# - firefox: Lower CPU usage, recommended (default)
+# - chrome: Google Chrome
+# - webkit: Safari engine
+# - msedge: Microsoft Edge
+# PLAYWRIGHT_BROWSER=firefox
+
+# PLAYWRIGHT_HEADLESS: Run browser without visible window
+# - true: Browser runs in background, saves CPU (default)
+# - false: Browser opens a visible window (useful for debugging)
+# PLAYWRIGHT_HEADLESS=true
+```
+
+---
+
+## 3. SQLite Robust Connection Handling
+
+### Overview
+
+Added robust SQLite connection handling to prevent database corruption from concurrent access (MCP server, FastAPI server, progress tracking).
+
+**Features Added:**
+- WAL mode for better concurrency
+- Busy timeout (30 seconds)
+- Retry logic with exponential backoff
+- Database health check endpoint
+
+### Modified Files
+
+#### `api/database.py`
+
+**New functions added:**
+
+```python
+def get_robust_connection(db_path: str) -> sqlite3.Connection:
+ """
+ Create a SQLite connection with robust settings:
+ - WAL mode for concurrent access
+ - 30 second busy timeout
+ - Foreign keys enabled
+ """
+
+@contextmanager
+def robust_db_connection(db_path: str):
+ """Context manager for robust database connections."""
+
+def execute_with_retry(conn, sql, params=None, max_retries=3):
+ """Execute SQL with exponential backoff retry for transient errors."""
+
+def check_database_health(db_path: str) -> dict:
+ """
+ Check database integrity and return health status.
+ Returns: {healthy: bool, message: str, details: dict}
+ """
+```
+
+---
+
+#### `progress.py`
+
+**Changed from raw sqlite3 to robust connections:**
+
+```python
+# BEFORE:
+conn = sqlite3.connect(db_path)
+
+# AFTER:
+from api.database import robust_db_connection, execute_with_retry
+
+with robust_db_connection(db_path) as conn:
+ execute_with_retry(conn, sql, params)
+```
+
+---
+
+#### `server/routers/projects.py`
+
+**New endpoint added:**
+
+```python
+@router.get("/{project_name}/db-health")
+async def get_database_health(project_name: str) -> DatabaseHealth:
+ """
+ Check the health of the project's features database.
+ Useful for diagnosing corruption issues.
+ """
+```
+
+---
+
+#### `server/schemas.py`
+
+**New schema added:**
+
+```python
+class DatabaseHealth(BaseModel):
+ healthy: bool
+ message: str
+ details: dict = {}
+```
+
+---
+
+## Update Checklist
+
+When updating AutoCoder from upstream, verify these items:
+
+### UI Changes
+- [ ] `ui/src/styles/custom-theme.css` is preserved
+- [ ] `ui/src/components/KanbanColumn.tsx` changes are preserved
+- [ ] Run `npm run build` in `ui/` directory
+- [ ] Test both light and dark modes
+
+### Backend Changes
+- [ ] `client.py` - Playwright browser/headless defaults preserved
+- [ ] `.env.example` - Documentation updates preserved
+- [ ] `api/database.py` - Robust connection functions preserved
+- [ ] `progress.py` - Uses robust_db_connection
+- [ ] `server/routers/projects.py` - db-health endpoint preserved
+- [ ] `server/schemas.py` - DatabaseHealth schema preserved
+
+### General
+- [ ] Test database operations under concurrent load
+- [ ] Verify Playwright uses Firefox by default
+- [ ] Check that browser runs headless by default
+
+---
+
+## Reverting to Defaults
+
+### UI Only
+```bash
+rm ui/src/styles/custom-theme.css
+git checkout ui/src/components/KanbanColumn.tsx
+cd ui && npm run build
+```
+
+### Backend Only
+```bash
+git checkout client.py .env.example api/database.py progress.py
+git checkout server/routers/projects.py server/schemas.py
+```
+
+---
+
+## Files Summary
+
+| File | Type | Change Description |
+|------|------|-------------------|
+| `ui/src/styles/custom-theme.css` | UI | Twitter-style theme |
+| `ui/src/components/KanbanColumn.tsx` | UI | Themeable kanban columns |
+| `client.py` | Backend | Firefox + headless defaults |
+| `.env.example` | Config | Updated documentation |
+| `api/database.py` | Backend | Robust SQLite connections |
+| `progress.py` | Backend | Uses robust connections |
+| `server/routers/projects.py` | Backend | db-health endpoint |
+| `server/schemas.py` | Backend | DatabaseHealth schema |
+
+---
+
+## Last Updated
+
+**Date:** January 2026
+**Commits:**
+- `1910b96` - SQLite robust connection handling
+- `e014b04` - Custom theme override system
diff --git a/client.py b/client.py
index 7994f64..80573a9 100644
--- a/client.py
+++ b/client.py
@@ -21,9 +21,14 @@ from security import bash_security_hook
load_dotenv()
# Default Playwright headless mode - can be overridden via PLAYWRIGHT_HEADLESS env var
-# When True, browser runs invisibly in background
-# When False, browser window is visible (default - useful for monitoring agent progress)
-DEFAULT_PLAYWRIGHT_HEADLESS = False
+# When True, browser runs invisibly in background (default - saves CPU)
+# When False, browser window is visible (useful for monitoring agent progress)
+DEFAULT_PLAYWRIGHT_HEADLESS = True
+
+# Default browser for Playwright - can be overridden via PLAYWRIGHT_BROWSER env var
+# Options: chrome, firefox, webkit, msedge
+# Firefox is recommended for lower CPU usage
+DEFAULT_PLAYWRIGHT_BROWSER = "firefox"
# Environment variables to pass through to Claude CLI for API configuration
# These allow using alternative API endpoints (e.g., GLM via z.ai) without
@@ -42,12 +47,27 @@ def get_playwright_headless() -> bool:
"""
Get the Playwright headless mode setting.
- Reads from PLAYWRIGHT_HEADLESS environment variable, defaults to False.
+ Reads from PLAYWRIGHT_HEADLESS environment variable, defaults to True.
Returns True for headless mode (invisible browser), False for visible browser.
"""
- value = os.getenv("PLAYWRIGHT_HEADLESS", "false").lower()
- # Accept various truthy/falsy values
- return value in ("true", "1", "yes", "on")
+ value = os.getenv("PLAYWRIGHT_HEADLESS", str(DEFAULT_PLAYWRIGHT_HEADLESS).lower()).strip().lower()
+ truthy = {"true", "1", "yes", "on"}
+ falsy = {"false", "0", "no", "off"}
+ if value not in truthy | falsy:
+ print(f" - Warning: Invalid PLAYWRIGHT_HEADLESS='{value}', defaulting to {DEFAULT_PLAYWRIGHT_HEADLESS}")
+ return DEFAULT_PLAYWRIGHT_HEADLESS
+ return value in truthy
+
+
+def get_playwright_browser() -> str:
+ """
+ Get the browser to use for Playwright.
+
+ Reads from PLAYWRIGHT_BROWSER environment variable, defaults to firefox.
+ Options: chrome, firefox, webkit, msedge
+ Firefox is recommended for lower CPU usage.
+ """
+ return os.getenv("PLAYWRIGHT_BROWSER", DEFAULT_PLAYWRIGHT_BROWSER).lower()
# Feature MCP tools for feature/test management
@@ -228,10 +248,16 @@ def create_client(
}
if not yolo_mode:
# Include Playwright MCP server for browser automation (standard mode only)
- # Headless mode is configurable via PLAYWRIGHT_HEADLESS environment variable
- playwright_args = ["@playwright/mcp@latest", "--viewport-size", "1280x720"]
+ # Browser and headless mode configurable via environment variables
+ browser = get_playwright_browser()
+ playwright_args = [
+ "@playwright/mcp@latest",
+ "--viewport-size", "1280x720",
+ "--browser", browser,
+ ]
if get_playwright_headless():
playwright_args.append("--headless")
+ print(f" - Browser: {browser} (headless={get_playwright_headless()})")
# Browser isolation for parallel execution
# Each agent gets its own isolated browser context to prevent tab conflicts
diff --git a/ui/src/components/KanbanColumn.tsx b/ui/src/components/KanbanColumn.tsx
index 340f64f..191ac5a 100644
--- a/ui/src/components/KanbanColumn.tsx
+++ b/ui/src/components/KanbanColumn.tsx
@@ -18,9 +18,9 @@ interface KanbanColumnProps {
}
const colorMap = {
- pending: 'var(--color-neo-pending)',
- progress: 'var(--color-neo-progress)',
- done: 'var(--color-neo-done)',
+ pending: 'kanban-header-pending',
+ progress: 'kanban-header-progress',
+ done: 'kanban-header-done',
}
export function KanbanColumn({
@@ -43,18 +43,16 @@ export function KanbanColumn({
)
return (
{/* Header */}
-
+
{title}
- {count}
+ {count}
{(onAddFeature || onExpandProject) && (
diff --git a/ui/src/main.tsx b/ui/src/main.tsx
index e8d9888..0420f66 100644
--- a/ui/src/main.tsx
+++ b/ui/src/main.tsx
@@ -3,6 +3,7 @@ import { createRoot } from 'react-dom/client'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import App from './App'
import './styles/globals.css'
+import './styles/custom-theme.css' // Custom theme overrides (safe from upstream conflicts)
const queryClient = new QueryClient({
defaultOptions: {
diff --git a/ui/src/styles/custom-theme.css b/ui/src/styles/custom-theme.css
new file mode 100644
index 0000000..1d7a032
--- /dev/null
+++ b/ui/src/styles/custom-theme.css
@@ -0,0 +1,437 @@
+/*
+ * Clean Twitter-Style Theme
+ * =========================
+ * Based on user's exact design system values
+ */
+
+:root {
+ /* Core colors */
+ --color-neo-bg: oklch(1.0000 0 0);
+ --color-neo-card: oklch(0.9784 0.0011 197.1387);
+ --color-neo-text: oklch(0.1884 0.0128 248.5103);
+ --color-neo-text-secondary: oklch(0.1884 0.0128 248.5103);
+ --color-neo-text-muted: oklch(0.5637 0.0078 247.9662);
+ --color-neo-text-on-bright: oklch(1.0000 0 0);
+
+ /* Primary accent - Twitter blue */
+ --color-neo-accent: oklch(0.6723 0.1606 244.9955);
+
+ /* Status colors - all use accent blue except danger */
+ --color-neo-pending: oklch(0.6723 0.1606 244.9955);
+ --color-neo-progress: oklch(0.6723 0.1606 244.9955);
+ --color-neo-done: oklch(0.6723 0.1606 244.9955);
+ --color-neo-danger: oklch(0.6188 0.2376 25.7658);
+
+ /* Borders and neutrals */
+ --color-neo-border: oklch(0.9317 0.0118 231.6594);
+ --color-neo-neutral-50: oklch(0.9809 0.0025 228.7836);
+ --color-neo-neutral-100: oklch(0.9392 0.0166 250.8453);
+ --color-neo-neutral-200: oklch(0.9222 0.0013 286.3737);
+ --color-neo-neutral-300: oklch(0.9317 0.0118 231.6594);
+
+ /* No shadows */
+ --shadow-neo-sm: none;
+ --shadow-neo-md: none;
+ --shadow-neo-lg: none;
+ --shadow-neo-xl: none;
+ --shadow-neo-left: none;
+ --shadow-neo-inset: none;
+
+ /* Typography */
+ --font-neo-sans: Open Sans, sans-serif;
+ --font-neo-mono: Menlo, monospace;
+
+ /* Radius - 1.3rem base */
+ --radius-neo-sm: calc(1.3rem - 4px);
+ --radius-neo-md: calc(1.3rem - 2px);
+ --radius-neo-lg: 1.3rem;
+ --radius-neo-xl: calc(1.3rem + 4px);
+}
+
+.dark {
+ /* Core colors - dark mode (Twitter dark style) */
+ --color-neo-bg: oklch(0.08 0 0);
+ --color-neo-card: oklch(0.16 0.005 250);
+ --color-neo-text: oklch(0.95 0 0);
+ --color-neo-text-secondary: oklch(0.75 0 0);
+ --color-neo-text-muted: oklch(0.55 0 0);
+ --color-neo-text-on-bright: oklch(1.0 0 0);
+
+ /* Primary accent */
+ --color-neo-accent: oklch(0.6692 0.1607 245.0110);
+
+ /* Status colors - all use accent blue except danger */
+ --color-neo-pending: oklch(0.6692 0.1607 245.0110);
+ --color-neo-progress: oklch(0.6692 0.1607 245.0110);
+ --color-neo-done: oklch(0.6692 0.1607 245.0110);
+ --color-neo-danger: oklch(0.6188 0.2376 25.7658);
+
+ /* Borders and neutrals - better contrast */
+ --color-neo-border: oklch(0.30 0 0);
+ --color-neo-neutral-50: oklch(0.20 0 0);
+ --color-neo-neutral-100: oklch(0.25 0.01 250);
+ --color-neo-neutral-200: oklch(0.22 0 0);
+ --color-neo-neutral-300: oklch(0.30 0 0);
+
+ /* No shadows */
+ --shadow-neo-sm: none;
+ --shadow-neo-md: none;
+ --shadow-neo-lg: none;
+ --shadow-neo-xl: none;
+ --shadow-neo-left: none;
+ --shadow-neo-inset: none;
+}
+
+/* ===== GLOBAL OVERRIDES ===== */
+
+* {
+ box-shadow: none !important;
+}
+
+/* ===== CARDS ===== */
+.neo-card,
+[class*="neo-card"] {
+ border: 1px solid var(--color-neo-border) !important;
+ box-shadow: none !important;
+ transform: none !important;
+ border-radius: var(--radius-neo-lg) !important;
+ background-color: var(--color-neo-card) !important;
+}
+
+.neo-card:hover,
+[class*="neo-card"]:hover {
+ transform: none !important;
+ box-shadow: none !important;
+}
+
+/* ===== BUTTONS ===== */
+.neo-btn,
+[class*="neo-btn"],
+button {
+ border-width: 1px !important;
+ box-shadow: none !important;
+ text-transform: none !important;
+ font-weight: 500 !important;
+ transform: none !important;
+ border-radius: var(--radius-neo-lg) !important;
+ font-family: var(--font-neo-sans) !important;
+}
+
+.neo-btn:hover,
+[class*="neo-btn"]:hover,
+button:hover {
+ transform: none !important;
+ box-shadow: none !important;
+}
+
+.neo-btn:active,
+[class*="neo-btn"]:active {
+ transform: none !important;
+}
+
+/* Primary button */
+.neo-btn-primary {
+ background-color: var(--color-neo-accent) !important;
+ border-color: var(--color-neo-accent) !important;
+ color: white !important;
+}
+
+/* Success button - use accent blue instead of green */
+.neo-btn-success {
+ background-color: var(--color-neo-accent) !important;
+ border-color: var(--color-neo-accent) !important;
+ color: white !important;
+}
+
+/* Danger button - subtle red */
+.neo-btn-danger {
+ background-color: var(--color-neo-danger) !important;
+ border-color: var(--color-neo-danger) !important;
+ color: white !important;
+}
+
+/* ===== INPUTS ===== */
+.neo-input,
+.neo-textarea,
+input,
+textarea,
+select {
+ border: 1px solid var(--color-neo-border) !important;
+ box-shadow: none !important;
+ border-radius: var(--radius-neo-md) !important;
+ background-color: var(--color-neo-neutral-50) !important;
+}
+
+.neo-input:focus,
+.neo-textarea:focus,
+input:focus,
+textarea:focus,
+select:focus {
+ box-shadow: none !important;
+ border-color: var(--color-neo-accent) !important;
+ outline: none !important;
+}
+
+/* ===== KEYBOARD ACCESSIBILITY ===== */
+/* Focus-visible styles for keyboard navigation */
+.neo-btn:focus-visible,
+[class*="neo-btn"]:focus-visible,
+button:focus-visible {
+ outline: 2px solid var(--color-neo-accent) !important;
+ outline-offset: 2px !important;
+}
+
+.neo-input:focus-visible,
+.neo-textarea:focus-visible,
+input:focus-visible,
+textarea:focus-visible,
+select:focus-visible {
+ outline: 2px solid var(--color-neo-accent) !important;
+ outline-offset: 2px !important;
+ border-color: var(--color-neo-accent) !important;
+}
+
+a:focus-visible,
+[role="button"]:focus-visible,
+[tabindex]:focus-visible {
+ outline: 2px solid var(--color-neo-accent) !important;
+ outline-offset: 2px !important;
+}
+
+/* ===== BADGES ===== */
+.neo-badge,
+[class*="neo-badge"] {
+ border: 1px solid var(--color-neo-border) !important;
+ box-shadow: none !important;
+ border-radius: var(--radius-neo-lg) !important;
+ font-weight: 500 !important;
+ text-transform: none !important;
+}
+
+/* ===== PROGRESS BAR ===== */
+.neo-progress {
+ border: none !important;
+ box-shadow: none !important;
+ border-radius: var(--radius-neo-lg) !important;
+ background-color: var(--color-neo-neutral-100) !important;
+ overflow: hidden !important;
+ height: 0.75rem !important;
+}
+
+.neo-progress-fill {
+ background-color: var(--color-neo-accent) !important;
+ border-radius: var(--radius-neo-lg) !important;
+}
+
+.neo-progress-fill::after {
+ display: none !important;
+}
+
+/* ===== KANBAN COLUMNS ===== */
+.kanban-column {
+ border: 1px solid var(--color-neo-border) !important;
+ border-radius: var(--radius-neo-lg) !important;
+ overflow: hidden;
+ background-color: var(--color-neo-bg) !important;
+ border-left: none !important;
+}
+
+/* Left accent border on the whole column */
+.kanban-column.kanban-header-pending {
+ border-left: 3px solid var(--color-neo-accent) !important;
+}
+
+.kanban-column.kanban-header-progress {
+ border-left: 3px solid var(--color-neo-accent) !important;
+}
+
+.kanban-column.kanban-header-done {
+ border-left: 3px solid var(--color-neo-accent) !important;
+}
+
+.kanban-header {
+ background-color: var(--color-neo-card) !important;
+ border-bottom: 1px solid var(--color-neo-border) !important;
+ border-left: none !important;
+}
+
+/* ===== MODALS & DROPDOWNS ===== */
+.neo-modal,
+[class*="neo-modal"],
+[role="dialog"] {
+ border: 1px solid var(--color-neo-border) !important;
+ border-radius: var(--radius-neo-xl) !important;
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.1) !important;
+}
+
+.neo-dropdown,
+[class*="dropdown"],
+[role="menu"],
+[data-radix-popper-content-wrapper] {
+ border: 1px solid var(--color-neo-border) !important;
+ border-radius: var(--radius-neo-lg) !important;
+ box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.08) !important;
+}
+
+/* ===== STATUS BADGES ===== */
+[class*="bg-neo-pending"],
+.bg-\[var\(--color-neo-pending\)\] {
+ background-color: var(--color-neo-neutral-100) !important;
+ color: var(--color-neo-text-secondary) !important;
+}
+
+[class*="bg-neo-progress"],
+.bg-\[var\(--color-neo-progress\)\] {
+ background-color: oklch(0.9392 0.0166 250.8453) !important;
+ color: var(--color-neo-accent) !important;
+}
+
+[class*="bg-neo-done"],
+.bg-\[var\(--color-neo-done\)\] {
+ background-color: oklch(0.9392 0.0166 250.8453) !important;
+ color: var(--color-neo-accent) !important;
+}
+
+/* ===== REMOVE NEO EFFECTS ===== */
+[class*="shadow-neo"],
+[class*="shadow-"] {
+ box-shadow: none !important;
+}
+
+[class*="hover:translate"],
+[class*="hover:-translate"],
+[class*="translate-x"],
+[class*="translate-y"] {
+ transform: none !important;
+}
+
+/* ===== TEXT STYLING ===== */
+h1, h2, h3, h4, h5, h6,
+[class*="heading"],
+[class*="title"],
+[class*="font-display"] {
+ text-transform: none !important;
+ font-family: var(--font-neo-sans) !important;
+}
+
+.uppercase {
+ text-transform: none !important;
+}
+
+strong, b,
+[class*="font-bold"],
+[class*="font-black"] {
+ font-weight: 600 !important;
+}
+
+/* ===== SPECIFIC ELEMENT FIXES ===== */
+
+/* Green badges should use accent color */
+[class*="bg-green"],
+[class*="bg-emerald"],
+[class*="bg-lime"] {
+ background-color: oklch(0.9392 0.0166 250.8453) !important;
+ color: var(--color-neo-accent) !important;
+}
+
+/* Category badges */
+[class*="FUNCTIONAL"],
+[class*="functional"] {
+ background-color: oklch(0.9392 0.0166 250.8453) !important;
+ color: var(--color-neo-accent) !important;
+}
+
+/* Live/Status indicators - use accent instead of green */
+.text-\[var\(--color-neo-done\)\] {
+ color: var(--color-neo-accent) !important;
+}
+
+/* Override any remaining borders to be thin */
+[class*="border-3"],
+[class*="border-b-3"] {
+ border-width: 1px !important;
+}
+
+/* ===== DARK MODE SPECIFIC FIXES ===== */
+
+.dark .neo-card,
+.dark [class*="neo-card"] {
+ background-color: var(--color-neo-card) !important;
+ border-color: var(--color-neo-border) !important;
+}
+
+.dark .kanban-column {
+ background-color: var(--color-neo-card) !important;
+}
+
+.dark .kanban-header {
+ background-color: var(--color-neo-neutral-50) !important;
+}
+
+/* Feature cards in dark mode */
+.dark .neo-card .neo-card {
+ background-color: var(--color-neo-neutral-50) !important;
+}
+
+/* Badges in dark mode - lighter background for visibility */
+.dark .neo-badge,
+.dark [class*="neo-badge"] {
+ background-color: var(--color-neo-neutral-100) !important;
+ color: var(--color-neo-text) !important;
+ border-color: var(--color-neo-border) !important;
+}
+
+/* Status badges in dark mode */
+.dark [class*="bg-neo-done"],
+.dark .bg-\[var\(--color-neo-done\)\] {
+ background-color: oklch(0.25 0.05 245) !important;
+ color: var(--color-neo-accent) !important;
+}
+
+.dark [class*="bg-neo-progress"],
+.dark .bg-\[var\(--color-neo-progress\)\] {
+ background-color: oklch(0.25 0.05 245) !important;
+ color: var(--color-neo-accent) !important;
+}
+
+/* Green badges in dark mode */
+.dark [class*="bg-green"],
+.dark [class*="bg-emerald"],
+.dark [class*="bg-lime"] {
+ background-color: oklch(0.25 0.05 245) !important;
+ color: var(--color-neo-accent) !important;
+}
+
+/* Category badges in dark mode */
+.dark [class*="FUNCTIONAL"],
+.dark [class*="functional"] {
+ background-color: oklch(0.25 0.05 245) !important;
+ color: var(--color-neo-accent) !important;
+}
+
+/* Buttons in dark mode - better visibility */
+.dark .neo-btn,
+.dark button {
+ border-color: var(--color-neo-border) !important;
+}
+
+.dark .neo-btn-primary,
+.dark .neo-btn-success {
+ background-color: var(--color-neo-accent) !important;
+ border-color: var(--color-neo-accent) !important;
+ color: white !important;
+}
+
+/* Toggle buttons - fix "Graph" visibility */
+.dark [class*="text-neo-text"] {
+ color: var(--color-neo-text) !important;
+}
+
+/* Inputs in dark mode */
+.dark input,
+.dark textarea,
+.dark select {
+ background-color: var(--color-neo-neutral-50) !important;
+ border-color: var(--color-neo-border) !important;
+ color: var(--color-neo-text) !important;
+}