diff --git a/.env.example b/.env.example index a62d8a6..bc49c38 100644 --- a/.env.example +++ b/.env.example @@ -1,16 +1,80 @@ -# Keycloak Configuration (frontend public client) +# ============================================================================ +# CERTifAI Dashboard - Environment Variables +# ============================================================================ +# Copy this file to .env and fill in the values. +# Variables marked [REQUIRED] must be set; others have sensible defaults. + +# --------------------------------------------------------------------------- +# Keycloak Configuration (frontend public client) [REQUIRED] +# --------------------------------------------------------------------------- KEYCLOAK_URL=http://localhost:8080 KEYCLOAK_REALM=certifai KEYCLOAK_CLIENT_ID=certifai-dashboard -# Application Configuration +# Keycloak admin / service-account client (server-to-server calls) [OPTIONAL] +KEYCLOAK_ADMIN_CLIENT_ID= +KEYCLOAK_ADMIN_CLIENT_SECRET= + +# --------------------------------------------------------------------------- +# Application Configuration [REQUIRED] +# --------------------------------------------------------------------------- APP_URL=http://localhost:8000 REDIRECT_URI=http://localhost:8000/auth/callback ALLOWED_ORIGINS=http://localhost:8000 -# SearXNG meta-search engine +# --------------------------------------------------------------------------- +# MongoDB [OPTIONAL - defaults shown] +# --------------------------------------------------------------------------- +MONGODB_URI=mongodb://localhost:27017 +MONGODB_DATABASE=certifai + +# --------------------------------------------------------------------------- +# SearXNG meta-search engine [OPTIONAL - default: http://localhost:8888] +# --------------------------------------------------------------------------- SEARXNG_URL=http://localhost:8888 -# Ollama LLM instance (used for article summarization and chat) -OLLAMA_URL=http://mac-mini-von-benjamin-2:11434 -OLLAMA_MODEL=qwen3:30b-a3b +# --------------------------------------------------------------------------- +# Ollama LLM instance [OPTIONAL - defaults shown] +# --------------------------------------------------------------------------- +OLLAMA_URL=http://localhost:11434 +OLLAMA_MODEL=llama3.1:8b + +# --------------------------------------------------------------------------- +# LLM Providers (comma-separated list) [OPTIONAL] +# --------------------------------------------------------------------------- +LLM_PROVIDERS=ollama + +# --------------------------------------------------------------------------- +# SMTP (transactional email) [OPTIONAL] +# --------------------------------------------------------------------------- +SMTP_HOST= +SMTP_PORT=587 +SMTP_USERNAME= +SMTP_PASSWORD= +SMTP_FROM_ADDRESS= + +# --------------------------------------------------------------------------- +# Stripe billing [OPTIONAL] +# --------------------------------------------------------------------------- +STRIPE_SECRET_KEY= +STRIPE_WEBHOOK_SECRET= +STRIPE_PUBLISHABLE_KEY= + +# --------------------------------------------------------------------------- +# LangChain / LangGraph / Langfuse [OPTIONAL] +# --------------------------------------------------------------------------- +LANGCHAIN_URL= +LANGGRAPH_URL= +LANGFUSE_URL= + +# --------------------------------------------------------------------------- +# Vector database [OPTIONAL] +# --------------------------------------------------------------------------- +VECTORDB_URL= + +# --------------------------------------------------------------------------- +# S3-compatible object storage [OPTIONAL] +# --------------------------------------------------------------------------- +S3_URL= +S3_ACCESS_KEY= +S3_SECRET_KEY= diff --git a/Cargo.toml b/Cargo.toml index b5dd7e5..2064284 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,12 @@ maud = { version = "0.27", default-features = false } url = { version = "2.5.4", default-features = false, optional = true } web-sys = { version = "0.3", optional = true, features = [ "Clipboard", + "Document", + "Element", + "HtmlElement", "Navigator", + "Storage", + "Window", ] } tracing = "0.1.40" # Debug @@ -93,6 +98,8 @@ server = [ "dep:sha2", "dep:base64", "dep:scraper", + "dep:secrecy", + "dep:petname", ] [[bin]] diff --git a/README.md b/README.md index 9bba058..dd70de3 100644 --- a/README.md +++ b/README.md @@ -1,64 +1,132 @@ -# CERTifAI +
+
+
+ Self-hosted, GDPR-compliant GenAI infrastructure dashboard +
-## Overview + -The SaaS application dashboard is the landing page for the company admin to view, edit and manage the company internal GenAI tools. The following tasks can be performed by the administrator: + - - User management: Can add, remove, set roles, permissions and add restrictions for other users. - - SSO/Oauth/LDAP: Can connect to company internal SSO/LDAP or other identity provider to load users and their respective permissions. - - Turn features on/off: Turn off/on different GenAI features - - Billing: View the current seats being used and token usage per seat for any given billing cycle - - Request support: Request support or new features using feedback form - - GenAI: View currently running LLMs, Agents, MCP Servers. Modify or add more resources, switch to a different model, launch tools like Langchain + Langfuse for creating new agents,tavily for internet search or more complex tools for use with GenAI. View endpoints and generate API Keys for integrations in other applications. +--- + +## About + +CERTifAI is a SaaS dashboard for administering self-hosted private GenAI infrastructure. It gives companies and individuals a single pane of glass to manage LLMs, Agents, MCP Servers, and other GenAI-related services -- without sending data to non-EU cloud providers. + +> **Why?** Protect your intellectual property from being used as training data. Stay fully GDPR-compliant with infrastructure you own. + +## Features + +| Area | Capabilities | +|------|-------------| +| **User Management** | Add, remove, set roles, permissions, and restrictions | +| **SSO / OAuth / LDAP** | Connect to company identity providers and sync users | +| **Feature Flags** | Toggle GenAI features on or off per-org | +| **Billing** | View seat usage and token consumption per billing cycle | +| **Support** | Request support or new features via feedback form | +| **GenAI Tools** | Manage LLMs, Agents, MCP Servers; launch Langchain, Langfuse, Tavily; view endpoints and generate API keys | ## Dashboard -The main dashboard provides a news feed powered by SearXNG and Ollama: +The main dashboard provides a news feed powered by **SearXNG** and **Ollama**: -- **Topic-based search**: Browse AI, Technology, Science, Finance and custom topics. Add or remove topics on the fly; selections persist in localStorage. -- **Article detail + AI summary**: Click any card to open a split-view panel. The full article is fetched, summarized by Ollama, and a follow-up chat lets you ask questions. +- **Topic-based search** -- Browse AI, Technology, Science, Finance, and custom topics. Add or remove topics on the fly; selections persist in localStorage. +- **Article detail + AI summary** -- Click any card to open a split-view panel. The full article is fetched, summarized by Ollama, and a follow-up chat lets you ask questions. - **Sidebar** (visible when no article is selected): - - **Ollama Status** -- green/red indicator with the list of loaded models. - - **Trending** -- keywords extracted from recent news headlines via SearXNG. - - **Recent Searches** -- last 10 topics you searched, persisted in localStorage. + - **Ollama Status** -- green/red indicator with the list of loaded models + - **Trending** -- keywords extracted from recent news headlines via SearXNG + - **Recent Searches** -- last 10 topics you searched, persisted in localStorage -## Development environment +## Tech Stack -This project is written in Dioxus 0.7 with fullstack and router features. MongoDB is used as a database for maintaining user state. Keycloak is used as identity provider for user management. +| Layer | Technology | +|-------|-----------| +| Frontend | [Dioxus 0.7](https://dioxuslabs.com/) (fullstack + router), Tailwind CSS 4, DaisyUI 5 | +| Backend | Axum, tower-sessions, Dioxus server functions | +| Database | MongoDB | +| Auth | Keycloak 26+ (OAuth2 + PKCE, Organizations) | +| Search | SearXNG (meta-search) | +| LLM | Ollama (local inference) | -### External services +## Getting Started -| Service | Purpose | Default URL | -|----------|--------------------------------|----------------------------| -| Keycloak | Identity provider / SSO | `http://localhost:8080` | -| SearXNG | Meta-search engine for news | `http://localhost:8888` | -| Ollama | Local LLM for summarization | `http://localhost:11434` | +### Prerequisites -Copy `.env.example` to `.env` and adjust the URLs and model name to match your setup. +- Rust 1.89+ +- [Dioxus CLI](https://dioxuslabs.com/learn/0.7/getting_started) (`dx`) +- MongoDB +- Keycloak +- SearXNG (optional) +- Ollama (optional) + +### Setup + +```bash +# Clone the repository +git clone https://gitea.meghsakha.com/sharang/certifai.git +cd certifai + +# Configure environment +cp .env.example .env +# Edit .env with your Keycloak, MongoDB, and service URLs + +# Run the dev server +dx serve +``` + +### External Services + +| Service | Purpose | Default URL | +|---------|---------|-------------| +| Keycloak | Identity provider / SSO | `http://localhost:8080` | +| MongoDB | User data and preferences | `mongodb://localhost:27017` | +| SearXNG | Meta-search engine for news | `http://localhost:8888` | +| Ollama | Local LLM for summarization | `http://localhost:11434` | + +## Project Structure + +``` +src/ + components/ Frontend-only reusable UI components + infrastructure/ Server-side: auth, config, DB, server functions + models/ Shared data models (web + server) + pages/ Full page views composing components + models +assets/ Static assets (CSS, icons, manifest) +styles/ Tailwind/DaisyUI input stylesheet +bin/ Binary entrypoint +``` -## Code structure - The following folder structure is maintained for separation of concerns: - - src/components/*.rs : All components that are required to be rendered are placed here. These are frontend only, reusable components that are specific for the application. - - src/infrastructure/*.rs : All backend related functions from the dioxus fullstack are placed here. This entire module is behind the feature "server". - - src/models/*.rs : All data models for use by the frontend pages and components. - - src/pages/*.rs : All view pages for the website, which utilize components, models to render the entire page. The pages are more towards the user as they group user-centered functions together in one view. - - ## Git Workflow -We follow feature branch workflow for Git and bringing in new features. The `main` branch is the default and protected branch. -Conventional commits MUST be used for writing commit messages. We follow semantic versioning as per [SemVer](https://semver.org) +We follow the **feature branch workflow**. The `main` branch is the default and protected branch. + +- [Conventional Commits](https://www.conventionalcommits.org/) are required for all commit messages +- We follow [SemVer](https://semver.org/) for versioning ## CI -The CI is run on gitea actions with runner tags `docker`. +CI runs on Gitea Actions with runner tag `docker`. + +--- + ++ Built with Rust, Dioxus, and a commitment to data sovereignty. +
diff --git a/assets/main.css b/assets/main.css index 80e702a..c283179 100644 --- a/assets/main.css +++ b/assets/main.css @@ -1,8 +1,43 @@ +/* ===== Theme Variables ===== */ +:root { + --bg-body: #0f1116; + --bg-sidebar: #0a0c10; + --bg-card: #1a1d26; + --bg-surface: #1e222d; + --text-primary: #e2e8f0; + --text-heading: #f1f5f9; + --text-muted: #8892a8; + --text-faint: #5a6478; + --text-dimmest: #3d4556; + --border-primary: #1e222d; + --border-secondary: #2a2f3d; + --accent: #91a4d2; + --accent-secondary: #6d85c6; + --avatar-text: #0a0c10; +} + +[data-theme="certifai-light"] { + --bg-body: #f4f6f9; + --bg-sidebar: #ffffff; + --bg-card: #ffffff; + --bg-surface: #e9ecf2; + --text-primary: #1e293b; + --text-heading: #0f172a; + --text-muted: #64748b; + --text-faint: #94a3b8; + --text-dimmest: #cbd5e1; + --border-primary: #e2e8f0; + --border-secondary: #cbd5e1; + --accent: #5570b8; + --accent-secondary: #3d5aaf; + --avatar-text: #ffffff; +} + /* ===== Fonts ===== */ body { font-family: 'Inter', sans-serif; - background-color: #0f1116; - color: #e2e8f0; + background-color: var(--bg-body); + color: var(--text-primary); margin: 0; padding: 0; } @@ -26,8 +61,8 @@ h6 { .sidebar { width: 260px; min-width: 260px; - background-color: #0a0c10; - border-right: 1px solid #1e222d; + background-color: var(--bg-sidebar); + border-right: 1px solid var(--border-primary); display: flex; flex-direction: column; height: 100vh; @@ -41,7 +76,7 @@ h6 { align-items: center; gap: 12px; padding: 24px 20px 20px; - border-bottom: 1px solid #1e222d; + border-bottom: 1px solid var(--border-primary); } .avatar-circle { @@ -49,7 +84,7 @@ h6 { height: 38px; min-width: 38px; border-radius: 50%; - background: linear-gradient(135deg, #91a4d2, #6d85c6); + background: linear-gradient(135deg, var(--accent), var(--accent-secondary)); display: flex; align-items: center; justify-content: center; @@ -59,12 +94,28 @@ h6 { font-family: 'Space Grotesk', sans-serif; font-size: 14px; font-weight: 600; - color: #0a0c10; + color: var(--avatar-text); +} + +.sidebar-user-info { + display: flex; + flex-direction: column; + min-width: 0; +} + +.sidebar-name { + font-size: 14px; + font-weight: 600; + color: var(--text-heading); + line-height: 1.3; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .sidebar-email { - font-size: 13px; - color: #8892a8; + font-size: 12px; + color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -85,7 +136,7 @@ h6 { gap: 12px; padding: 10px 14px; border-radius: 8px; - color: #8892a8; + color: var(--text-muted); text-decoration: none; font-size: 14px; font-weight: 500; @@ -93,23 +144,26 @@ h6 { } .sidebar-link:hover { - background-color: #1e222d; - color: #e2e8f0; + background-color: var(--bg-surface); + color: var(--text-primary); } .sidebar-link.active { background-color: rgba(145, 164, 210, 0.12); - color: #91a4d2; + color: var(--accent); } -/* -- Sidebar Logout -- */ -.sidebar-logout { +/* -- Sidebar Bottom Actions (Logout + Theme Toggle) -- */ +.sidebar-bottom-actions { + display: flex; + align-items: center; + justify-content: space-between; padding: 4px 10px; - border-top: 1px solid #1e222d; + border-top: 1px solid var(--border-primary); } .logout-btn { - color: #8892a8; + color: var(--text-muted); } .logout-btn:hover { @@ -117,10 +171,29 @@ h6 { background-color: rgba(248, 113, 113, 0.08); } +.theme-toggle-btn { + display: flex; + align-items: center; + justify-content: center; + width: 34px; + height: 34px; + border: none; + border-radius: 8px; + background: transparent; + color: var(--text-muted); + cursor: pointer; + transition: color 0.2s, background-color 0.2s; +} + +.theme-toggle-btn:hover { + color: var(--accent); + background-color: var(--bg-surface); +} + /* -- Sidebar Footer -- */ .sidebar-footer { padding: 16px 20px; - border-top: 1px solid #1e222d; + border-top: 1px solid var(--border-primary); display: flex; flex-direction: column; align-items: center; @@ -133,18 +206,18 @@ h6 { } .social-link { - color: #5a6478; + color: var(--text-faint); transition: color 0.15s ease; text-decoration: none; } .social-link:hover { - color: #91a4d2; + color: var(--accent); } .sidebar-version { font-size: 11px; - color: #3d4556; + color: var(--text-dimmest); font-family: 'Inter', monospace; } @@ -164,7 +237,7 @@ h6 { .overview-heading { font-size: 28px; font-weight: 700; - color: #f1f5f9; + color: var(--text-heading); margin-bottom: 32px; } @@ -187,33 +260,33 @@ h6 { flex-direction: column; gap: 12px; padding: 24px; - background-color: #1e222d; - border: 1px solid #2a2f3d; + background-color: var(--bg-surface); + border: 1px solid var(--border-secondary); border-radius: 12px; text-decoration: none; - color: #e2e8f0; + color: var(--text-primary); transition: border-color 0.2s ease, box-shadow 0.2s ease; } .dashboard-card:hover { - border-color: #91a4d2; + border-color: var(--accent); box-shadow: 0 0 20px rgba(145, 164, 210, 0.10); } .card-icon { - color: #91a4d2; + color: var(--accent); } .card-title { font-size: 18px; font-weight: 600; - color: #f1f5f9; + color: var(--text-heading); margin: 0; } .card-description { font-size: 14px; - color: #8892a8; + color: var(--text-muted); margin: 0; } @@ -229,9 +302,9 @@ h6 { position: sticky; top: 0; z-index: 100; - background-color: rgba(15, 17, 22, 0.85); + background-color: color-mix(in srgb, var(--bg-body) 85%, transparent); backdrop-filter: blur(12px); - border-bottom: 1px solid #1e222d; + border-bottom: 1px solid var(--border-primary); } .landing-nav-inner { @@ -250,12 +323,12 @@ h6 { font-family: 'Space Grotesk', sans-serif; font-size: 20px; font-weight: 700; - color: #f1f5f9; + color: var(--text-heading); text-decoration: none; } .landing-logo-icon { - color: #91a4d2; + color: var(--accent); display: flex; align-items: center; } @@ -267,7 +340,7 @@ h6 { } .landing-nav-links a { - color: #8892a8; + color: var(--text-muted); text-decoration: none; font-size: 14px; font-weight: 500; @@ -275,7 +348,7 @@ h6 { } .landing-nav-links a:hover { - color: #e2e8f0; + color: var(--text-primary); } .landing-nav-actions { @@ -305,7 +378,7 @@ h6 { .hero-badge { font-size: 13px; font-weight: 500; - color: #91a4d2; + color: var(--accent); border-color: rgba(145, 164, 210, 0.3); margin-bottom: 24px; } @@ -314,13 +387,13 @@ h6 { font-size: 52px; font-weight: 700; line-height: 1.1; - color: #f1f5f9; + color: var(--text-heading); margin: 0 0 24px; width: 100%; } .hero-title-accent { - background: linear-gradient(135deg, #91a4d2, #6d85c6); + background: linear-gradient(135deg, var(--accent), var(--accent-secondary)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; @@ -329,7 +402,7 @@ h6 { .hero-subtitle { font-size: 18px; line-height: 1.7; - color: #8892a8; + color: var(--text-muted); margin: 0 0 36px; max-width: 520px; width: 100%; @@ -351,20 +424,20 @@ h6 { /* -- Social Proof -- */ .social-proof { - border-top: 1px solid #1e222d; - border-bottom: 1px solid #1e222d; + border-top: 1px solid var(--border-primary); + border-bottom: 1px solid var(--border-primary); padding: 40px 32px; text-align: center; } .social-proof-text { font-size: 16px; - color: #8892a8; + color: var(--text-muted); margin: 0 0 28px; } .social-proof-highlight { - color: #91a4d2; + color: var(--accent); font-weight: 600; } @@ -387,12 +460,12 @@ h6 { font-family: 'Space Grotesk', sans-serif; font-size: 24px; font-weight: 700; - color: #f1f5f9; + color: var(--text-heading); } .proof-stat-label { font-size: 13px; - color: #5a6478; + color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.05em; } @@ -400,21 +473,21 @@ h6 { .proof-divider { width: 1px; height: 40px; - background-color: #1e222d; + background-color: var(--bg-surface); } /* -- Section Titles -- */ .section-title { font-size: 36px; font-weight: 700; - color: #f1f5f9; + color: var(--text-heading); text-align: center; margin: 0 0 12px; } .section-subtitle { font-size: 18px; - color: #8892a8; + color: var(--text-muted); text-align: center; margin: 0 0 48px; } @@ -433,34 +506,34 @@ h6 { } .feature-card { - background-color: #1a1d26; - border: 1px solid #2a2f3d; + background-color: var(--bg-card); + border: 1px solid var(--border-secondary); border-radius: 12px; padding: 32px 28px; transition: border-color 0.2s ease, transform 0.2s ease; } .feature-card:hover { - border-color: #91a4d2; + border-color: var(--accent); transform: translateY(-2px); } .feature-card-icon { - color: #91a4d2; + color: var(--accent); margin-bottom: 16px; } .feature-card-title { font-size: 18px; font-weight: 600; - color: #f1f5f9; + color: var(--text-heading); margin: 0 0 8px; } .feature-card-desc { font-size: 14px; line-height: 1.6; - color: #8892a8; + color: var(--text-muted); margin: 0; } @@ -486,7 +559,7 @@ h6 { font-family: 'Space Grotesk', sans-serif; font-size: 48px; font-weight: 700; - background: linear-gradient(135deg, #91a4d2, #6d85c6); + background: linear-gradient(135deg, var(--accent), var(--accent-secondary)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; @@ -497,14 +570,14 @@ h6 { .step-title { font-size: 22px; font-weight: 600; - color: #f1f5f9; + color: var(--text-heading); margin: 0 0 12px; } .step-desc { font-size: 15px; line-height: 1.6; - color: #8892a8; + color: var(--text-muted); margin: 0; } @@ -524,13 +597,13 @@ h6 { .cta-title { font-size: 32px; font-weight: 700; - color: #f1f5f9; + color: var(--text-heading); margin: 0 0 12px; } .cta-subtitle { font-size: 18px; - color: #8892a8; + color: var(--text-muted); margin: 0 0 32px; } @@ -542,7 +615,7 @@ h6 { /* -- Landing Footer -- */ .landing-footer { - border-top: 1px solid #1e222d; + border-top: 1px solid var(--border-primary); padding: 60px 32px 0; margin-top: auto; } @@ -563,7 +636,7 @@ h6 { .footer-tagline { font-size: 14px; - color: #5a6478; + color: var(--text-faint); margin: 0; max-width: 280px; } @@ -577,7 +650,7 @@ h6 { .footer-links-heading { font-size: 13px; font-weight: 600; - color: #8892a8; + color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 4px; @@ -585,26 +658,26 @@ h6 { .footer-links-group a { font-size: 14px; - color: #5a6478; + color: var(--text-faint); text-decoration: none; transition: color 0.15s ease; } .footer-links-group a:hover { - color: #91a4d2; + color: var(--accent); } .footer-bottom { max-width: 1200px; margin: 48px auto 0; padding: 20px 0; - border-top: 1px solid #1e222d; + border-top: 1px solid var(--border-primary); text-align: center; } .footer-bottom p { font-size: 13px; - color: #3d4556; + color: var(--text-dimmest); margin: 0; } @@ -617,7 +690,7 @@ h6 { .legal-nav { padding: 20px 32px; - border-bottom: 1px solid #1e222d; + border-bottom: 1px solid var(--border-primary); } .legal-content { @@ -630,21 +703,21 @@ h6 { .legal-content h1 { font-size: 36px; font-weight: 700; - color: #f1f5f9; + color: var(--text-heading); margin: 0 0 32px; } .legal-content h2 { font-size: 22px; font-weight: 600; - color: #f1f5f9; + color: var(--text-heading); margin: 40px 0 12px; } .legal-content p { font-size: 15px; line-height: 1.7; - color: #8892a8; + color: var(--text-muted); margin: 0 0 16px; } @@ -656,19 +729,19 @@ h6 { .legal-content li { font-size: 15px; line-height: 1.7; - color: #8892a8; + color: var(--text-muted); margin-bottom: 8px; } .legal-updated { font-size: 14px; - color: #5a6478; + color: var(--text-faint); font-style: italic; } .legal-footer { padding: 20px 32px; - border-top: 1px solid #1e222d; + border-top: 1px solid var(--border-primary); display: flex; gap: 24px; justify-content: center; @@ -676,13 +749,13 @@ h6 { .legal-footer a { font-size: 14px; - color: #5a6478; + color: var(--text-faint); text-decoration: none; transition: color 0.15s ease; } .legal-footer a:hover { - color: #91a4d2; + color: var(--accent); } /* ===== Responsive: Landing Page ===== */ @@ -771,8 +844,8 @@ h6 { align-items: center; justify-content: center; padding: 10px 20px; - background: linear-gradient(135deg, #91a4d2, #6d85c6); - color: #0a0c10; + background: linear-gradient(135deg, var(--accent), var(--accent-secondary)); + color: var(--avatar-text); border: none; border-radius: 8px; font-size: 14px; @@ -796,9 +869,9 @@ h6 { align-items: center; justify-content: center; padding: 10px 20px; - background-color: #1e222d; - color: #e2e8f0; - border: 1px solid #2a2f3d; + background-color: var(--bg-surface); + color: var(--text-primary); + border: 1px solid var(--border-secondary); border-radius: 8px; font-size: 14px; font-weight: 500; @@ -808,7 +881,7 @@ h6 { } .btn-secondary:hover { - border-color: #91a4d2; + border-color: var(--accent); } .btn-icon { @@ -818,8 +891,8 @@ h6 { width: 36px; height: 36px; background-color: transparent; - color: #8892a8; - border: 1px solid #2a2f3d; + color: var(--text-muted); + border: 1px solid var(--border-secondary); border-radius: 8px; cursor: pointer; font-size: 16px; @@ -827,8 +900,8 @@ h6 { } .btn-icon:hover { - color: #e2e8f0; - border-color: #91a4d2; + color: var(--text-primary); + border-color: var(--accent); } .btn-danger { @@ -857,16 +930,16 @@ h6 { .form-group label { font-size: 13px; font-weight: 500; - color: #8892a8; + color: var(--text-muted); } .form-select, .form-input { padding: 10px 14px; - background-color: #1a1d26; - border: 1px solid #2a2f3d; + background-color: var(--bg-card); + border: 1px solid var(--border-secondary); border-radius: 8px; - color: #e2e8f0; + color: var(--text-primary); font-size: 14px; font-family: 'Inter', sans-serif; outline: none; @@ -875,7 +948,7 @@ h6 { .form-select:focus, .form-input:focus { - border-color: #91a4d2; + border-color: var(--accent); } .form-success { @@ -900,13 +973,13 @@ h6 { .page-title { font-size: 28px; font-weight: 700; - color: #f1f5f9; + color: var(--text-heading); margin: 0 0 4px; } .page-subtitle { font-size: 15px; - color: #8892a8; + color: var(--text-muted); margin: 0; } @@ -915,7 +988,7 @@ h6 { display: flex; gap: 4px; padding: 0 0 20px; - border-bottom: 1px solid #1e222d; + border-bottom: 1px solid var(--border-primary); margin-bottom: 24px; } @@ -924,19 +997,19 @@ h6 { border-radius: 8px; font-size: 14px; font-weight: 500; - color: #8892a8; + color: var(--text-muted); text-decoration: none; transition: background-color 0.15s ease, color 0.15s ease; } .sub-nav-item:hover { - background-color: #1e222d; - color: #e2e8f0; + background-color: var(--bg-surface); + color: var(--text-primary); } .sub-nav-item--active { background-color: rgba(145, 164, 210, 0.12); - color: #91a4d2; + color: var(--accent); } /* ===== Shell Content (Developer/Org) ===== */ @@ -964,9 +1037,9 @@ h6 { .filter-tab { padding: 6px 16px; border-radius: 20px; - border: 1px solid #2a2f3d; + border: 1px solid var(--border-secondary); background-color: transparent; - color: #8892a8; + color: var(--text-muted); font-size: 13px; font-weight: 500; cursor: pointer; @@ -975,14 +1048,14 @@ h6 { } .filter-tab:hover { - border-color: #91a4d2; - color: #e2e8f0; + border-color: var(--accent); + color: var(--text-primary); } .filter-tab--active { background-color: rgba(145, 164, 210, 0.12); - border-color: #91a4d2; - color: #91a4d2; + border-color: var(--accent); + color: var(--accent); } /* ===== News Card ===== */ @@ -993,8 +1066,8 @@ h6 { } .news-card { - background-color: #1a1d26; - border: 1px solid #2a2f3d; + background-color: var(--bg-card); + border: 1px solid var(--border-secondary); border-radius: 12px; overflow: hidden; transition: border-color 0.2s ease, transform 0.2s ease; @@ -1003,7 +1076,7 @@ h6 { } .news-card:hover { - border-color: #91a4d2; + border-color: var(--accent); transform: translateY(-2px); } @@ -1046,7 +1119,7 @@ h6 { letter-spacing: 0.05em; /* Default badge color for any topic */ background-color: rgba(99, 132, 210, 0.15); - color: #91a4d2; + color: var(--accent); } .news-badge--llm { @@ -1076,18 +1149,18 @@ h6 { .news-card-source { font-size: 12px; - color: #5a6478; + color: var(--text-faint); } .news-card-date { font-size: 12px; - color: #3d4556; + color: var(--text-dimmest); } .news-card-title { font-size: 16px; font-weight: 600; - color: #f1f5f9; + color: var(--text-heading); margin: 0 0 8px; line-height: 1.3; } @@ -1098,18 +1171,18 @@ h6 { } .news-card-title a:hover { - color: #91a4d2; + color: var(--accent); } .news-card-summary { font-size: 13px; line-height: 1.5; - color: #8892a8; + color: var(--text-muted); margin: 0; } .news-card--selected { - border-color: #91a4d2; + border-color: var(--accent); background-color: rgba(145, 164, 210, 0.08); } @@ -1178,7 +1251,7 @@ h6 { .dashboard-loading { text-align: center; padding: 24px; - color: #8892a8; + color: var(--text-muted); font-size: 14px; } @@ -1192,7 +1265,7 @@ h6 { .topic-remove { background: none; border: none; - color: #5a6478; + color: var(--text-faint); font-size: 12px; cursor: pointer; padding: 2px 4px; @@ -1207,9 +1280,9 @@ h6 { .topic-add-btn { padding: 6px 14px; border-radius: 20px; - border: 1px dashed #2a2f3d; + border: 1px dashed var(--border-secondary); background-color: transparent; - color: #5a6478; + color: var(--text-faint); font-size: 16px; cursor: pointer; transition: all 0.15s ease; @@ -1218,8 +1291,8 @@ h6 { } .topic-add-btn:hover { - border-color: #91a4d2; - color: #91a4d2; + border-color: var(--accent); + color: var(--accent); } .topic-input-wrapper { @@ -1231,9 +1304,9 @@ h6 { .topic-input { padding: 5px 12px; border-radius: 20px; - border: 1px solid #2a2f3d; - background-color: #1a1d26; - color: #e2e8f0; + border: 1px solid var(--border-secondary); + background-color: var(--bg-card); + color: var(--text-primary); font-size: 13px; font-family: 'Inter', sans-serif; outline: none; @@ -1241,19 +1314,19 @@ h6 { } .topic-input:focus { - border-color: #91a4d2; + border-color: var(--accent); } .topic-cancel-btn { background: none; border: none; - color: #5a6478; + color: var(--text-faint); font-size: 12px; cursor: pointer; } .topic-cancel-btn:hover { - color: #e2e8f0; + color: var(--text-primary); } /* ===== Settings Panel ===== */ @@ -1262,8 +1335,8 @@ h6 { } .settings-panel { - background-color: #1a1d26; - border: 1px solid #2a2f3d; + background-color: var(--bg-card); + border: 1px solid var(--border-secondary); border-radius: 12px; padding: 20px; margin-bottom: 24px; @@ -1272,7 +1345,7 @@ h6 { .settings-panel-title { font-size: 15px; font-weight: 600; - color: #e2e8f0; + color: var(--text-primary); margin: 0 0 16px; } @@ -1283,7 +1356,7 @@ h6 { .settings-field label { display: block; font-size: 12px; - color: #8892a8; + color: var(--text-muted); margin-bottom: 4px; font-weight: 500; } @@ -1293,16 +1366,16 @@ h6 { max-width: 400px; padding: 8px 12px; border-radius: 8px; - border: 1px solid #2a2f3d; - background-color: #0f1116; - color: #e2e8f0; + border: 1px solid var(--border-secondary); + background-color: var(--bg-body); + color: var(--text-primary); font-size: 13px; font-family: 'Inter', sans-serif; outline: none; } .settings-input:focus { - border-color: #91a4d2; + border-color: var(--accent); } .settings-hint { @@ -1317,8 +1390,8 @@ h6 { /* ===== Article Detail Panel ===== */ .article-detail-panel { - background-color: #1a1d26; - border: 1px solid #2a2f3d; + background-color: var(--bg-card); + border: 1px solid var(--border-secondary); border-radius: 12px; padding: 24px; position: relative; @@ -1329,8 +1402,8 @@ h6 { top: 16px; right: 16px; background: none; - border: 1px solid #2a2f3d; - color: #8892a8; + border: 1px solid var(--border-secondary); + color: var(--text-muted); width: 32px; height: 32px; border-radius: 8px; @@ -1355,7 +1428,7 @@ h6 { .article-detail-title { font-size: 22px; font-weight: 700; - color: #f1f5f9; + color: var(--text-heading); margin: 0 0 12px; line-height: 1.3; } @@ -1370,7 +1443,7 @@ h6 { .article-detail-source { font-size: 13px; - color: #8892a8; + color: var(--text-muted); display: inline-flex; align-items: center; gap: 6px; @@ -1385,7 +1458,7 @@ h6 { .article-detail-date { font-size: 13px; - color: #5a6478; + color: var(--text-faint); } .article-detail-body { @@ -1395,21 +1468,21 @@ h6 { .article-detail-body p { font-size: 14px; line-height: 1.7; - color: #c8d0e0; + color: var(--text-primary); margin: 0; } .article-detail-link { display: inline-block; font-size: 13px; - color: #91a4d2; + color: var(--accent); text-decoration: none; margin-bottom: 20px; transition: color 0.15s ease; } .article-detail-link:hover { - color: #b4c4e8; + color: var(--accent-secondary); } /* ---- AI Summary Bubble ---- */ @@ -1425,7 +1498,7 @@ h6 { .ai-summary-bubble-text { font-size: 14px; line-height: 1.65; - color: #c8d0e0; + color: var(--text-primary); margin: 0; white-space: pre-wrap; } @@ -1434,7 +1507,7 @@ h6 { display: block; font-size: 11px; font-weight: 600; - color: #91a4d2; + color: var(--accent); margin-top: 12px; text-transform: uppercase; letter-spacing: 0.05em; @@ -1446,7 +1519,7 @@ h6 { align-items: center; gap: 10px; font-size: 14px; - color: #91a4d2; + color: var(--accent); font-style: italic; } @@ -1469,7 +1542,7 @@ h6 { width: 6px; height: 6px; border-radius: 50%; - background: #91a4d2; + background: var(--accent); animation: dotPulse 1.2s infinite ease-in-out; } @@ -1495,7 +1568,7 @@ h6 { /* ---- Follow-up Chat ---- */ .article-chat { margin-top: 16px; - border-top: 1px solid #2a2f3d; + border-top: 1px solid var(--border-secondary); padding-top: 16px; } @@ -1526,7 +1599,7 @@ h6 { align-self: flex-end; background: rgba(99, 132, 210, 0.2); border: 1px solid rgba(99, 132, 210, 0.3); - color: #d0d8ee; + color: var(--text-primary); border-bottom-right-radius: 4px; } @@ -1534,7 +1607,7 @@ h6 { align-self: flex-start; background: rgba(145, 164, 210, 0.08); border: 1px solid rgba(145, 164, 210, 0.15); - color: #c8d0e0; + color: var(--text-primary); border-bottom-left-radius: 4px; } @@ -1549,18 +1622,18 @@ h6 { .article-chat-textbox { flex: 1; - background: #1a1e2e; - border: 1px solid #2a2f3d; + background: var(--bg-card); + border: 1px solid var(--border-secondary); border-radius: 8px; padding: 10px 14px; font-size: 14px; - color: #c8d0e0; + color: var(--text-primary); outline: none; transition: border-color 0.2s; } .article-chat-textbox:focus { - border-color: #6384d2; + border-color: var(--accent-secondary); } .article-chat-textbox:disabled { @@ -1568,8 +1641,8 @@ h6 { } .article-chat-send { - background: #6384d2; - color: #fff; + background: var(--accent-secondary); + color: var(--avatar-text); border: none; border-radius: 8px; padding: 10px 18px; @@ -1581,7 +1654,7 @@ h6 { } .article-chat-send:hover:not(:disabled) { - background: #5270b8; + background: var(--accent); } .article-chat-send:disabled { @@ -1601,8 +1674,8 @@ h6 { } .providers-form { - background-color: #1a1d26; - border: 1px solid #2a2f3d; + background-color: var(--bg-card); + border: 1px solid var(--border-secondary); border-radius: 12px; padding: 28px; } @@ -1616,13 +1689,13 @@ h6 { .providers-status h3 { font-size: 18px; font-weight: 600; - color: #f1f5f9; + color: var(--text-heading); margin: 0 0 16px; } .status-card { - background-color: #1a1d26; - border: 1px solid #2a2f3d; + background-color: var(--bg-card); + border: 1px solid var(--border-secondary); border-radius: 12px; padding: 20px; } @@ -1631,7 +1704,7 @@ h6 { display: flex; justify-content: space-between; padding: 10px 0; - border-bottom: 1px solid #1e222d; + border-bottom: 1px solid var(--border-primary); } .status-row:last-child { @@ -1640,13 +1713,13 @@ h6 { .status-label { font-size: 13px; - color: #8892a8; + color: var(--text-muted); } .status-value { font-size: 13px; font-weight: 500; - color: #e2e8f0; + color: var(--text-primary); } /* ===== Chat Page ===== */ @@ -1660,10 +1733,10 @@ h6 { .chat-sidebar-panel { width: 260px; min-width: 260px; - border-right: 1px solid #1e222d; + border-right: 1px solid var(--border-primary); display: flex; flex-direction: column; - background-color: #0d0f14; + background-color: var(--bg-sidebar); } .chat-sidebar-header { @@ -1671,13 +1744,13 @@ h6 { align-items: center; justify-content: space-between; padding: 20px; - border-bottom: 1px solid #1e222d; + border-bottom: 1px solid var(--border-primary); } .chat-sidebar-header h3 { font-size: 16px; font-weight: 600; - color: #f1f5f9; + color: var(--text-heading); margin: 0; } @@ -1700,7 +1773,7 @@ h6 { } .chat-session-item:hover { - background-color: #1e222d; + background-color: var(--bg-surface); } .chat-session-item--active { @@ -1710,13 +1783,13 @@ h6 { .chat-session-title { font-size: 14px; font-weight: 500; - color: #e2e8f0; + color: var(--text-primary); margin-bottom: 4px; } .chat-session-date { font-size: 12px; - color: #5a6478; + color: var(--text-faint); } .chat-main-panel { @@ -1739,7 +1812,7 @@ h6 { display: flex; align-items: center; justify-content: center; - color: #5a6478; + color: var(--text-faint); } /* ===== Chat Bubble ===== */ @@ -1754,22 +1827,22 @@ h6 { .chat-bubble--user { align-self: flex-end; background-color: rgba(145, 164, 210, 0.15); - color: #e2e8f0; + color: var(--text-primary); border-bottom-right-radius: 4px; } .chat-bubble--assistant { align-self: flex-start; - background-color: #1a1d26; - border: 1px solid #2a2f3d; - color: #e2e8f0; + background-color: var(--bg-card); + border: 1px solid var(--border-secondary); + color: var(--text-primary); border-bottom-left-radius: 4px; } .chat-bubble--system { align-self: center; background-color: transparent; - color: #5a6478; + color: var(--text-faint); font-size: 13px; font-style: italic; } @@ -1784,12 +1857,12 @@ h6 { .chat-bubble-role { font-size: 12px; font-weight: 600; - color: #91a4d2; + color: var(--accent); } .chat-bubble-time { font-size: 11px; - color: #5a6478; + color: var(--text-faint); } .chat-bubble-content { @@ -1808,7 +1881,7 @@ h6 { padding: 4px 10px; background-color: rgba(145, 164, 210, 0.1); border-radius: 4px; - color: #91a4d2; + color: var(--accent); } /* -- Chat Input Bar -- */ @@ -1817,24 +1890,24 @@ h6 { align-items: center; gap: 8px; padding: 16px 24px; - border-top: 1px solid #1e222d; - background-color: #0d0f14; + border-top: 1px solid var(--border-primary); + background-color: var(--bg-sidebar); } .chat-input { flex: 1; padding: 10px 14px; - background-color: #1a1d26; - border: 1px solid #2a2f3d; + background-color: var(--bg-card); + border: 1px solid var(--border-secondary); border-radius: 8px; - color: #e2e8f0; + color: var(--text-primary); font-size: 14px; font-family: 'Inter', sans-serif; outline: none; } .chat-input:focus { - border-color: #91a4d2; + border-color: var(--accent); } .chat-attach-btn { @@ -1858,15 +1931,15 @@ h6 { /* ===== Tool Card ===== */ .tool-card { - background-color: #1a1d26; - border: 1px solid #2a2f3d; + background-color: var(--bg-card); + border: 1px solid var(--border-secondary); border-radius: 12px; padding: 24px; transition: border-color 0.2s ease; } .tool-card:hover { - border-color: #91a4d2; + border-color: var(--accent); } .tool-card-header { @@ -1878,7 +1951,7 @@ h6 { .tool-card-icon { font-size: 24px; - color: #91a4d2; + color: var(--accent); } .tool-status { @@ -1892,7 +1965,7 @@ h6 { } .tool-status--inactive { - background-color: #5a6478; + background-color: var(--text-faint); } .tool-status--error { @@ -1902,14 +1975,14 @@ h6 { .tool-card-name { font-size: 16px; font-weight: 600; - color: #f1f5f9; + color: var(--text-heading); margin: 0 0 6px; } .tool-card-desc { font-size: 13px; line-height: 1.5; - color: #8892a8; + color: var(--text-muted); margin: 0 0 16px; } @@ -1922,7 +1995,7 @@ h6 { .tool-card-category { font-size: 11px; font-weight: 500; - color: #5a6478; + color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.05em; } @@ -1944,8 +2017,8 @@ h6 { } .tool-toggle--off { - background-color: #1e222d; - color: #5a6478; + background-color: var(--bg-surface); + color: var(--text-faint); } /* ===== Knowledge Base Page ===== */ @@ -1973,19 +2046,19 @@ h6 { .knowledge-table thead th { font-size: 12px; font-weight: 600; - color: #5a6478; + color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.05em; padding: 12px 16px; text-align: left; - border-bottom: 1px solid #1e222d; + border-bottom: 1px solid var(--border-primary); } .knowledge-table tbody td { font-size: 14px; - color: #e2e8f0; + color: var(--text-primary); padding: 14px 16px; - border-bottom: 1px solid #1e222d; + border-bottom: 1px solid var(--border-primary); } .file-row:hover { @@ -2000,7 +2073,7 @@ h6 { } .file-row-icon { - color: #91a4d2; + color: var(--accent); font-size: 12px; } @@ -2017,8 +2090,8 @@ h6 { text-align: center; max-width: 480px; padding: 48px 40px; - background-color: #1a1d26; - border: 1px solid #2a2f3d; + background-color: var(--bg-card); + border: 1px solid var(--border-secondary); border-radius: 16px; } @@ -2034,20 +2107,20 @@ h6 { font-family: 'Space Grotesk', sans-serif; font-size: 28px; font-weight: 700; - color: #91a4d2; + color: var(--accent); } .placeholder-card h2 { font-size: 24px; font-weight: 700; - color: #f1f5f9; + color: var(--text-heading); margin: 0 0 12px; } .placeholder-desc { font-size: 14px; line-height: 1.6; - color: #8892a8; + color: var(--text-muted); margin: 0 0 24px; } @@ -2056,7 +2129,7 @@ h6 { margin-top: 12px; font-size: 12px; font-weight: 600; - color: #91a4d2; + color: var(--accent); padding: 4px 12px; border: 1px solid rgba(145, 164, 210, 0.3); border-radius: 20px; @@ -2077,8 +2150,8 @@ h6 { align-items: center; gap: 2px; padding: 16px; - background-color: #1a1d26; - border: 1px solid #2a2f3d; + background-color: var(--bg-card); + border: 1px solid var(--border-secondary); border-radius: 12px; } @@ -2086,12 +2159,12 @@ h6 { font-family: 'Space Grotesk', sans-serif; font-size: 22px; font-weight: 700; - color: #f1f5f9; + color: var(--text-heading); } .analytics-stat-label { font-size: 12px; - color: #5a6478; + color: var(--text-faint); } .analytics-stat-change { @@ -2121,8 +2194,8 @@ h6 { /* ===== Pricing Card ===== */ .pricing-card { - background-color: #1a1d26; - border: 1px solid #2a2f3d; + background-color: var(--bg-card); + border: 1px solid var(--border-secondary); border-radius: 16px; padding: 32px 28px; text-align: center; @@ -2134,7 +2207,7 @@ h6 { } .pricing-card--highlighted { - border-color: #91a4d2; + border-color: var(--accent); box-shadow: 0 0 30px rgba(145, 164, 210, 0.1); position: relative; } @@ -2142,7 +2215,7 @@ h6 { .pricing-card-name { font-size: 20px; font-weight: 700; - color: #f1f5f9; + color: var(--text-heading); margin: 0 0 16px; } @@ -2154,17 +2227,17 @@ h6 { font-family: 'Space Grotesk', sans-serif; font-size: 40px; font-weight: 700; - color: #f1f5f9; + color: var(--text-heading); } .pricing-card-period { font-size: 14px; - color: #5a6478; + color: var(--text-faint); } .pricing-card-seats { font-size: 13px; - color: #8892a8; + color: var(--text-muted); margin: 0 0 24px; } @@ -2177,9 +2250,9 @@ h6 { .pricing-card-features li { font-size: 14px; - color: #8892a8; + color: var(--text-muted); padding: 6px 0; - border-bottom: 1px solid #1e222d; + border-bottom: 1px solid var(--border-primary); } .pricing-card-features li:last-child { @@ -2189,8 +2262,8 @@ h6 { .pricing-card-cta { width: 100%; padding: 12px; - background: linear-gradient(135deg, #91a4d2, #6d85c6); - color: #0a0c10; + background: linear-gradient(135deg, var(--accent), var(--accent-secondary)); + color: var(--avatar-text); border: none; border-radius: 8px; font-size: 14px; @@ -2222,8 +2295,8 @@ h6 { align-items: center; gap: 4px; padding: 20px; - background-color: #1a1d26; - border: 1px solid #2a2f3d; + background-color: var(--bg-card); + border: 1px solid var(--border-secondary); border-radius: 12px; } @@ -2231,12 +2304,12 @@ h6 { font-family: 'Space Grotesk', sans-serif; font-size: 24px; font-weight: 700; - color: #f1f5f9; + color: var(--text-heading); } .org-stat-label { font-size: 12px; - color: #5a6478; + color: var(--text-faint); } /* ===== Organization Table ===== */ @@ -2252,19 +2325,19 @@ h6 { .org-table thead th { font-size: 12px; font-weight: 600; - color: #5a6478; + color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.05em; padding: 12px 16px; text-align: left; - border-bottom: 1px solid #1e222d; + border-bottom: 1px solid var(--border-primary); } .org-table tbody td { font-size: 14px; - color: #e2e8f0; + color: var(--text-primary); padding: 14px 16px; - border-bottom: 1px solid #1e222d; + border-bottom: 1px solid var(--border-primary); } .member-row:hover { @@ -2273,17 +2346,17 @@ h6 { .member-role-select { padding: 6px 10px; - background-color: #1a1d26; - border: 1px solid #2a2f3d; + background-color: var(--bg-card); + border: 1px solid var(--border-secondary); border-radius: 6px; - color: #e2e8f0; + color: var(--text-primary); font-size: 13px; font-family: 'Inter', sans-serif; outline: none; } .member-role-select:focus { - border-color: #91a4d2; + border-color: var(--accent); } /* ===== Modal ===== */ @@ -2301,8 +2374,8 @@ h6 { } .modal-content { - background-color: #1a1d26; - border: 1px solid #2a2f3d; + background-color: var(--bg-card); + border: 1px solid var(--border-secondary); border-radius: 16px; padding: 32px; min-width: 400px; @@ -2312,7 +2385,7 @@ h6 { .modal-content h3 { font-size: 20px; font-weight: 700; - color: #f1f5f9; + color: var(--text-heading); margin: 0 0 20px; } @@ -2333,7 +2406,7 @@ h6 { align-self: flex-start; max-height: 80vh; overflow-y: auto; - border-left: 1px solid #1e222d; + border-left: 1px solid var(--border-primary); padding-left: 24px; display: flex; flex-direction: column; @@ -2349,7 +2422,7 @@ h6 { .sidebar-section-title { font-size: 12px; font-weight: 600; - color: #5a6478; + color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.05em; margin: 0; @@ -2380,7 +2453,7 @@ h6 { .sidebar-status-label { font-size: 13px; - color: #e2e8f0; + color: var(--text-primary); font-weight: 500; } @@ -2397,7 +2470,7 @@ h6 { padding: 3px 10px; border-radius: 12px; background-color: rgba(145, 164, 210, 0.1); - color: #91a4d2; + color: var(--accent); border: 1px solid rgba(145, 164, 210, 0.2); } @@ -2411,14 +2484,14 @@ h6 { border-radius: 6px; font-size: 13px; font-family: 'Inter', sans-serif; - color: #8892a8; + color: var(--text-muted); cursor: pointer; transition: background-color 0.15s ease, color 0.15s ease; } .sidebar-topic-link:hover { background-color: rgba(145, 164, 210, 0.08); - color: #e2e8f0; + color: var(--text-primary); } /* ===== Responsive: Dashboard Pages ===== */ @@ -2493,7 +2566,7 @@ h6 { min-width: unset; max-height: 200px; border-right: none; - border-bottom: 1px solid #1e222d; + border-bottom: 1px solid var(--border-primary); } .page-header { diff --git a/assets/tailwind.css b/assets/tailwind.css index 7177b28..173cc7e 100644 --- a/assets/tailwind.css +++ b/assets/tailwind.css @@ -1290,9 +1290,6 @@ } } } - .fixed { - position: fixed; - } .relative { position: relative; } diff --git a/src/app.rs b/src/app.rs index 8964acc..79be24e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -93,6 +93,17 @@ pub fn App() -> Element { "# } - div { "data-theme": "certifai-dark", Router::