diff --git a/README.md b/README.md
index f21bcd4..b9b1159 100644
--- a/README.md
+++ b/README.md
@@ -57,6 +57,11 @@ Under the hood: FastAPI + SQLite + Jinja2 on Python 3.13. AMQP-mediated cluster
## What's New in v0.22.0
+### Color Theme System
+- Palette icon in topbar opens dropdown with 6 themes: IBM Blue, Green Ln (Matrix terminal), Dark, Light, Amber (fallout terminal), Trippin (neon rave).
+- All CSS variables are dynamically swapped; choice persists in `localStorage`.
+- Existing `:root` CSS variable architecture made this trivial — no CSS changes needed beyond the dropdown styles.
+
### RAG Corpus Management UI (B4)
- New admin modal (RAG button in drawer header) to browse, search, edit, and delete individual RAG corpus entries.
- **Endpoints**: `GET /api/rag/points` (paginated list with semantic search and source filter), `GET /api/rag/point/{id}` (single point detail), `DELETE /api/rag/point/{id}` (single point deletion), `PATCH /api/rag/point/{id}` (edit text with re-embed).
diff --git a/templates/index.html b/templates/index.html
index 77c10fb..4fa7d3c 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -110,6 +110,19 @@ body { font-family: var(--font-body); background: var(--bg-primary); color: var(
.badge.off { border-color:rgba(255,51,85,0.3); color:var(--danger); background:rgba(255,51,85,0.08); }
.badge.admin { border-color:var(--border); color:var(--text-muted); }
.badge.admin:hover { border-color:var(--accent-dim); color:var(--accent); }
+.theme-picker { position:relative; }
+.theme-dropdown { display:none; position:absolute; top:100%; right:0; margin-top:6px; background:var(--bg-secondary); border:1px solid var(--border); border-radius:var(--radius); min-width:150px; z-index:2000; box-shadow:0 4px 20px rgba(0,0,0,0.4); }
+.theme-dropdown.open { display:block; }
+.theme-option { display:flex; align-items:center; gap:8px; padding:8px 14px; font-size:12px; font-family:var(--font-mono); color:var(--text-secondary); cursor:pointer; transition:background 0.12s; }
+.theme-option:hover { background:var(--bg-hover); color:var(--text-primary); }
+.theme-option.active { color:var(--accent); background:var(--accent-glow); }
+.theme-swatch { width:14px; height:14px; border-radius:3px; border:1px solid var(--border); flex-shrink:0; }
+.swatch-ibm { background:#0088bb; }
+.swatch-green { background:#00cc33; }
+.swatch-dark { background:#7b68ee; }
+.swatch-light { background:#e0e0e0; }
+.swatch-amber { background:#cc8800; }
+.swatch-trip { background:linear-gradient(135deg,#ff00ff,#ffff00,#00ff66); }
.info-icon { display:inline-flex; align-items:center; justify-content:center; width:18px; height:18px; border-radius:50%; border:1px solid var(--border); color:var(--text-muted); font-size:11px; cursor:pointer; user-select:none; transition:all 0.15s; }
.info-icon:hover { border-color:var(--accent-dim); color:var(--accent); }
.info-popup-overlay { position:fixed; inset:0; background:rgba(0,0,0,0.6); z-index:1000; display:flex; align-items:center; justify-content:center; }
@@ -517,6 +530,17 @@ body { font-family: var(--font-body); background: var(--bg-primary); color: var(
+
+
+
+
IBM Blue
+
Green Ln
+
Dark
+
Light
+
Amber
+
Trippin
+
+
@@ -788,6 +812,8 @@ function startHeartbeat() {
async function initializeMainApp() {
if (appInitialized) return;
appInitialized = true;
+ const savedTheme = localStorage.getItem('caic-theme') || 'ibm-blue';
+ applyTheme(savedTheme);
await loadModels();
await loadSettings();
await loadSkills();
@@ -1022,6 +1048,85 @@ function togglePrivateInfo(e) {
overlay.style.display = overlay.style.display === 'none' ? 'flex' : 'none';
}
+// ---------- Theme system ----------
+const THEMES = {
+ 'ibm-blue': {
+ '--bg-primary': '#080c1a', '--bg-secondary': '#0d1428', '--bg-tertiary': '#121d3a', '--bg-hover': '#182850',
+ '--text-primary': '#a0b8ff', '--text-secondary': '#7890d8', '--text-muted': '#5068a0',
+ '--accent': '#33ddff', '--accent-dim': '#0088bb', '--accent-glow': 'rgba(51,221,255,0.1)',
+ '--danger': '#ff3355', '--danger-hover': '#cc0022', '--success': '#00ff66', '--warning': '#ffaa00',
+ '--border': '#1a3060',
+ },
+ 'green-bg': {
+ '--bg-primary': '#000000', '--bg-secondary': '#0a0a0a', '--bg-tertiary': '#111111', '--bg-hover': '#1a1a1a',
+ '--text-primary': '#00ff41', '--text-secondary': '#00cc33', '--text-muted': '#008800',
+ '--accent': '#00ff41', '--accent-dim': '#00aa22', '--accent-glow': 'rgba(0,255,65,0.1)',
+ '--danger': '#ff3355', '--danger-hover': '#cc0022', '--success': '#00ff66', '--warning': '#ffaa00',
+ '--border': '#003300',
+ },
+ 'dark': {
+ '--bg-primary': '#1a1a2e', '--bg-secondary': '#222240', '--bg-tertiary': '#2a2a50', '--bg-hover': '#333366',
+ '--text-primary': '#e0e0ff', '--text-secondary': '#b0b0dd', '--text-muted': '#8080aa',
+ '--accent': '#bb86fc', '--accent-dim': '#8855cc', '--accent-glow': 'rgba(187,134,252,0.1)',
+ '--danger': '#cf6679', '--danger-hover': '#aa4455', '--success': '#00ff66', '--warning': '#ffaa00',
+ '--border': '#333366',
+ },
+ 'light': {
+ '--bg-primary': '#f5f5f5', '--bg-secondary': '#ffffff', '--bg-tertiary': '#e8e8e8', '--bg-hover': '#dddddd',
+ '--text-primary': '#222222', '--text-secondary': '#555555', '--text-muted': '#888888',
+ '--accent': '#0066cc', '--accent-dim': '#004488', '--accent-glow': 'rgba(0,102,204,0.1)',
+ '--danger': '#cc0033', '--danger-hover': '#990022', '--success': '#008844', '--warning': '#cc8800',
+ '--border': '#cccccc',
+ },
+ 'amber': {
+ '--bg-primary': '#000000', '--bg-secondary': '#0d0800', '--bg-tertiary': '#1a1000', '--bg-hover': '#2a1a00',
+ '--text-primary': '#ffb000', '--text-secondary': '#cc8800', '--text-muted': '#886600',
+ '--accent': '#ffb000', '--accent-dim': '#cc8800', '--accent-glow': 'rgba(255,176,0,0.1)',
+ '--danger': '#ff3355', '--danger-hover': '#cc0022', '--success': '#00ff66', '--warning': '#ffaa00',
+ '--border': '#332200',
+ },
+ 'trippin': {
+ '--bg-primary': '#0a001a', '--bg-secondary': '#15002a', '--bg-tertiary': '#20003a', '--bg-hover': '#300050',
+ '--text-primary': '#ff66ff', '--text-secondary': '#ff33cc', '--text-muted': '#cc0099',
+ '--accent': '#ff00ff', '--accent-dim': '#cc00cc', '--accent-glow': 'rgba(255,0,255,0.15)',
+ '--danger': '#ff0066', '--danger-hover': '#cc0044', '--success': '#00ff66', '--warning': '#ffff00',
+ '--border': '#440066',
+ },
+};
+
+function applyTheme(name) {
+ const theme = THEMES[name];
+ if (!theme) return;
+ const root = document.documentElement;
+ for (const [key, val] of Object.entries(theme)) {
+ root.style.setProperty(key, val);
+ }
+ const scrollbarTrack = name === 'light' ? 'rgba(0,68,136,0.08)' :
+ name === 'trippin' ? 'rgba(204,0,204,0.08)' :
+ name === 'green-bg' ? 'rgba(0,170,34,0.08)' :
+ name === 'amber' ? 'rgba(204,136,0,0.08)' :
+ name === 'dark' ? 'rgba(136,85,204,0.08)' :
+ 'rgba(0,136,187,0.08)';
+ root.style.setProperty('--scrollbar-track', scrollbarTrack);
+ root.style.setProperty('--scrollbar', theme['--accent-dim']);
+ root.style.setProperty('--scrollbar-hover', theme['--accent']);
+
+ document.querySelectorAll('.theme-option').forEach(el => {
+ el.classList.toggle('active', el.dataset.theme === name);
+ });
+ localStorage.setItem('caic-theme', name);
+}
+
+function toggleThemePicker() {
+ document.getElementById('themeDropdown').classList.toggle('open');
+}
+document.addEventListener('click', function(e) {
+ const picker = document.querySelector('.theme-picker');
+ if (picker && !picker.contains(e.target)) {
+ document.getElementById('themeDropdown')?.classList.remove('open');
+ }
+});
+
function updateProfileUI() {
const badge = document.getElementById('profileBadge');
const toggle = document.getElementById('profileToggle');