diff --git a/.claude/launch.json b/.claude/launch.json index 2d574100..4d94ad28 100644 --- a/.claude/launch.json +++ b/.claude/launch.json @@ -3,12 +3,9 @@ "configurations": [ { "name": "dev", - "runtimeExecutable": "/Users/yantr/.nvm/versions/node/v24.13.1/bin/node", - "runtimeArgs": [ - "/Users/yantr/Desktop/HoundShield.Online-main/compliance-firewall-agent/node_modules/next/dist/bin/next", - "dev", - "/Users/yantr/Desktop/HoundShield.Online-main/compliance-firewall-agent" - ], + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "dev"], + "cwd": "/Users/yantr/Desktop/HoundShield-main/.claude/worktrees/beautiful-wiles-b5b879/compliance-firewall-agent", "port": 3000 } ] diff --git a/.claude/rules/frontend.md b/.claude/rules/frontend.md index 498ee7dd..2906c208 100644 --- a/.claude/rules/frontend.md +++ b/.claude/rules/frontend.md @@ -8,17 +8,23 @@ paths: # Frontend Rules — Hound Shield ## Design System (NEVER violate) -- Homepage bg: `bg-[#07070b]` — never `bg-white`, `bg-surface`, `bg-slate-*` -- Alt section bg: `bg-[#0d0d14]` -- Brand gold: `brand-400` CSS variable — NEVER `amber-*`, `yellow-*`, `indigo-*`, `blue-*` -- Cards: `bg-white/[0.03]` + `border border-white/[0.08]` for glass surfaces +- **Theme default: LIGHT.** `` — no `dark` class on root. +- Homepage bg: `bg-[#FBF8F2]` (cream-50) — never `bg-black`, `bg-slate-900`, raw dark hexes +- Alt section bg: `bg-[#F3E3D0]` (cream-100, palette Cream) +- Surface accent: `bg-[#E5D2BD]` (cream-200) / `bg-[#D2C4B4]` (palette Beige) +- Brand blue: `brand-500` (#81A6C6, palette Blue) — primary CTA, focus, accents +- Brand text on light bg: `text-brand-700` or darker (brand-500 fails WCAG AA on cream) +- CTAs: `bg-brand-500 text-white hover:bg-brand-600` +- NEVER `amber-*`, `yellow-*`, `orange-*`, `indigo-*` — use `brand-*` (which is now blue) +- Semantic colors stay: `success` (emerald), `danger` (red), `warning` (amber DEFAULT token only via theme — not raw `amber-400`) +- Cards: `bg-white/80` + `border border-slate-200` for glass on light - Typography: `font-editorial` (display headers), `font-mono` (metrics/code) -- Dark mode always: `` +- Dark mode = optional inverse via theme toggle; `.dark` class on `html` enables it ## Styling - Tailwind CSS ONLY — no inline styles (exception: radial-gradient as `style` prop only) - `cn()` for conditional class merging -- No flat black — use gradients, glass borders, glows for depth +- Soft pastel surfaces; no flat white — use cream/beige gradients, subtle blue glows for depth ## Components - Functional components + hooks only @@ -28,5 +34,6 @@ paths: - `transformStyle: "preserve-3d"` + Framer Motion `motion.div` = crash — never combine - Components max 500 lines — split into co-located files if larger - Every new feature: error boundary + loading state -- `next/image` for all images -- Custom cursor `CursorGlow` on `pointer:fine` — never break it +- `next/image` for all images (including the logo) +- Logo: `` component renders `/houndshield-logo.png` via `next/image` +- Custom cursor `CursorGlow` on `pointer:fine` — never break it; tint = `brand-500` rgba diff --git a/CLAUDE.md b/CLAUDE.md index 8741e02e..f211c6f1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,156 +1,44 @@ -# HoundShield — Project Brain (HERMES Doctrine, compass-corrected 2026-05-26) +# HoundShield — Project Brain (HERMES Doctrine) ## Product -OpenAI-compatible compliance proxy. Intercepts prompts before they reach ChatGPT, Copilot, or Claude. Local scan <10ms. 16 detection engines (CUI/PHI/PII/IP/ITAR). SHA-256 hash-chained audit log. Generates PDF mapped to NIST 800-171 Rev 2 controls. +Local-only AI compliance firewall. Intercepts every AI prompt before it leaves the network. Enforces CMMC Level 2, SOC 2, HIPAA. 16 detection engines. <10ms latency. One proxy URL change to deploy. -## Three Deployment Modes (NEVER conflate) -| Mode | Stack | CUI-safe? | Audience | -|------|-------|-----------|----------| -| A | `proxy.houndshield.com` (Vercel) | NO — not FedRAMP-authorized | Demo, non-CUI evaluation only | -| B | Self-hosted Docker on customer infra | YES — data never leaves boundary | CUI-handling contractors | -| C | Air-gapped customer network | YES | Enterprise, IL-5+ | - -**Architecture truth:** Marketing/dashboard plane runs on Vercel. Vercel is NOT FedRAMP-authorized. Any C3PAO assessor will flag this if shown the hosted endpoint as the production CUI path. Mode B (Docker) is the answer. The CUI-safe claim is true ONLY in Mode B/C. Be explicit about this distinction before every sales conversation. - -**Brain AI restriction:** Brain AI routes through OpenRouter → commercial LLM endpoints (not FedRAMP-authorized). Any CUI input to Brain AI is a CMMC spillage event. Must display: "Do not input CUI. This feature routes to a commercial cloud endpoint." If warning is not yet live, Brain AI is removed from the homepage. - ---- - -## Prime Objective — Stage 1 (by June 25, 2026) - -- **3 paid $499 CMMC AI Risk Reports** closed (any vertical: healthcare/defense/legal) -- **1 RPO or CMMC-focused MSP** signed referral agreement (40–50% rev share on $499 co-brand) - -The "10 SaaS customers by June 10" goal is dead — median B2B SaaS cycle is 84 days. The above is the revised, arithmetic-honest milestone. - ---- - -## Session Start Protocol - -Output this block first thing every session: - -``` -HERMES BRIEFING — [DATE] -DAYS TO JUNE 25 CHECKPOINT: [X] -PAID GAP REPORTS CLOSED: [X] / 3 -RPO/MSP REFERRAL AGREEMENTS: [X] / 1 -ARCHITECTURE STATUS: Vercel (trial) / Docker (CUI-safe) / [customer's stack] -BRAIN AI STATUS: ON (non-CUI only, warning live) / OFF -TODAY'S PRIORITY: [derive from stage] -``` - -Then ask: "What are we shipping today?" +Target buyer: Jordan — IT Security Manager at 50-250 person DoD contractor facing CMMC Level 2 deadline. +Pricing: Free → $199 Pro → $499 Growth → $999 Enterprise → $2,499 Agency/mo. +**Prime objective: $5,000 MRR in 30 days → $10K MRR → YC S26/W27.** --- ## HERMES AI Swarm — Agent Roster -Each agent runs OODA loop (Observe → Orient → Decide → Act). Self-corrects via `tasks/lessons.md`. Self-terminates if KPI missed 3 cycles. No agent overrides prime objective. No agent works outside its domain without team-lead escalation. +Each agent runs OODA loop (Observe → Orient → Decide → Act). Self-corrects via `tasks/lessons.md`. Self-terminates if KPI missed 3 cycles. | Agent | Role | Owns | |----------|-----------------------|-------------------------------------------------------------------| | ATLAS | Backend + Infra | Supabase schema, API routes, migrations, Stripe wiring | -| FORGE | Frontend + UI | Design system, all components, landing page | -| CIPHER | LLM Orchestration | OpenRouter routing, Brain AI (with CUI warning gate), prompt chains | -| STRIKER | Revenue + Growth | RPO outreach, $499 gap-report funnel, pricing coherence | +| FORGE | Frontend + UI | Design system, all components, landing page, light-mode rebuild | +| CIPHER | LLM Orchestration | OpenRouter routing, Brain AI, prompt chains | +| STRIKER | Revenue + Growth | Pricing coherence, onboarding funnel, MRR tracking | | GUARDIAN | QA + Testing | Test coverage gates, pre-commit hooks, E2E | -| SCRIBE | Docs | CLAUDE.md, PRD, README, docs/ folder, SEO articles | +| SCRIBE | Docs | CLAUDE.md, PRD, README, docs/ folder | | ORACLE | Research | Market research, competitor mapping, product ideas | ---- - -## Three Buyers (sales-cycle speed order) - -1. **Rachel H. — Healthcare Privacy Officer / CISO** (30–90 days, fastest) - - 50–300-person physician group or clinic - - Pain: nurses pasting patient data into ChatGPT (not HIPAA-compliant without BAA) - - Budget: $299–$799/month. No FedRAMP requirement on vendor. - - Evidence: 81% of healthcare data policy violations involve regulated data (Netskope, May 2025). - -2. **Jordan M. — Defense IT Security Manager** (90–180 days) - - 50–500-person DoD subcontractor - - Pain: employees pasting CUI into ChatGPT. No audit trail. C3PAO assessment due. - - Budget: $500–$1,500/month. Needs: Mode B (Docker), SHA-256 log, C3PAO PDF. - - Blocker: SOC 2 Type I before mid-market DIB will sign. - -3. **Marcus T. — Law Firm IT Director** (45–90 days) - - 50–500-attorney firm - - Pain: attorneys pasting privileged comms into ChatGPT (state bar AI ethics opinions, 2024–2025) - - Budget: $500–$2,000/month. - -**Sequence:** Lead with Rachel. Use Jordan wins as CMMC validation. Add Marcus when bandwidth exists. +**No agent overrides prime objective. No agent works outside its domain without team-lead escalation.** --- -## Lead Product — $499 CMMC AI Risk Assessment Report +## Manager Mode (ACTIVE) -**What it is:** 14-day proxy deployment in customer's environment. SHA-256-signed PDF showing every AI prompt event risk-scored against NIST 800-171 controls. No subscription. No MSA needed for a $499 PO. Bypasses procurement. +Before every task: +1. Is this in the active sprint in `tasks/todo.md`? +2. Does it serve Jordan (the CMMC buyer) directly? +3. Are we building a feature or building distribution? -**Who buys it:** Jordan and Rachel both buy this before they buy a subscription. +If unclear → **[MANAGER CHECK]** This looks like [X]. Sprint goal is [Y]. Deliberately shifting? -**Why it works:** RPOs charge $5K–$15K for gap assessments. $499 is impulse. Report becomes evidence of both problem AND solution. +**Drift indicators:** UI polish before paying customers · features for hypothetical buyers · refactoring without a failing test · non-Jordan work before Sprint 2 complete. -**RPO white-label:** $299 wholesale → RPO charges client $499–$999. - -**DO NOT** lead with $199/mo SaaS subscription. Subscription requires procurement review. $499 PO does not. - ---- - -## Pricing (revised) - -**Stage 1 (now — June 25):** -- CMMC AI Risk Assessment Report: $499 one-time (primary product) -- RPO co-brand wholesale: $299 → RPO marks up - -**Stage 2 (July–September 2026, only after Stage 1 triggers hit):** -- Starter: $299/mo — quarterly gap report, basic monitoring -- Pro: $799/mo — continuous detection, Slack alerts, C3PAO PDF -- Enterprise: $1,499/mo — on-prem Docker, dedicated CSM, air-gapped option -- Audit Pack: $999 one-time — SSP + POA&M + 14 policy templates + 1-hr expert review - -Annual discount: 17%. 30-day money-back. ONE pricing grid. No Federal tier until SOC 2 lands. - ---- - -## Channel — RPOs and MSPs ONLY - -**NEVER C3PAOs.** C3PAOs are legally prohibited from product recommendations to clients they assess (32 CFR Part 170, CMMC CoPC, ISO 17020 cooling-off). - -**Target list:** 50 RPOs from Cyber AB Marketplace. -**Top names:** Summit 7, MAD Security, CyberSheath, CompliancePoint, BEMO, Steel Root, Etactics. -**Offer:** 40–50% rev share on $499 gap-report co-brand. - ---- - -## Manager Mode — Counter-Intelligence Protocol - -Before executing ANY new request: -1. Does this help close 1+ paid gap report or RPO agreement by June 25? -2. Does it map to a NIST 800-171 / HIPAA control the buyer needs evidence for? -3. Under $500 and under 8 hours of solo founder time? -4. Is it on the NEVER DO list? -5. Does it expose the Vercel/OpenRouter stack issue to a buyer before we've addressed it? - -If any check fails: **[HERMES CHALLENGE]** [reason] / Cost: [tradeoff] / Recommendation: [drop/defer/modify] / Override? Y/N - -**Drift indicators:** UI polish before paying customers · features for hypothetical buyers · refactoring without a failing test · subscription-first pitches · C3PAO outreach. - ---- - -## NEVER DO List - -- ✗ Claim "10 customers by June 10" — impossible with 84-day median B2B SaaS cycle -- ✗ Pitch C3PAOs as referral/endorsement channel — legally prohibited -- ✗ Lead with $199/mo SaaS before proving $499 gap report sells -- ✗ Claim hosted endpoint (Vercel) is CUI-safe — NOT FedRAMP-authorized -- ✗ Allow Brain AI to process CUI without explicit warning + user consent -- ✗ Publish fictional metrics ("500+ teams," "2M+ scans") — defense/healthcare buyers verify -- ✗ Mobile app before 50 customers -- ✗ Israel / Mossad / foreign defense (12–24 month motion) -- ✗ Generic "AI security" positioning — always: "AI prompt compliance for CMMC / HIPAA" -- ✗ Features without NIST 800-171 or HIPAA control mapping -- ✗ Lower gap report below $499 — anchors value -- ✗ A second pricing grid +**Current sprint:** Sprint 2 — 10 paying customers by Day 14, $5K MRR by Day 30. --- @@ -172,7 +60,7 @@ Rules: ## Task Management -- All tasks in `tasks/todo.md`. Stage 1 → `## Stage 1`. Done → `## Done`. +- All tasks in `tasks/todo.md`. Active → `## Active`. Done → `## Done`. - Add to backlog before starting. Never work from memory. - Corrections → dated entry in `tasks/lessons.md`. @@ -180,52 +68,44 @@ Rules: ## Core Principles -1. **Local-only data boundary is sacred** — in Mode B/C only. Mode A is trial, not CUI-safe. Any code or copy implying otherwise is CRITICAL. +1. **Local-only data boundary is sacred.** Prompt content never leaves the customer's machine. Only license key hash + prompt count go external. Any violation is CRITICAL. 2. **Compliance accuracy over features.** 16 CUI patterns, 110 NIST 800-171 Rev 2 controls, SPRS weights must be correct. Run `compliance-specialist` before any engine change. -3. **Sequenced beachhead.** Lead with healthcare (Rachel — fastest). Layer in defense (Jordan) and legal (Marcus). One vertical landing page per stage gate. -4. **Revenue before polish.** If a feature doesn't close a $499 gap report or sign an RPO, it waits. - ---- - -## Architecture Critical Path - -| Timeline | Action | -|----------|--------| -| Now | Add explicit "Mode B (Docker) required for CUI workloads" warning everywhere | -| Stage 1 | Publish `houndshield/proxy:latest` to Docker Hub + 60-second deploy video | -| Stage 2 | Begin SOC 2 Type I (Vanta/Drata, ~$5K–$15K, 60–90 days) | -| Stage 3 | Begin AWS GovCloud deployment option for larger DIB contracts | - ---- - -## Kill Criteria (September 1, 2026) - -If ANY TWO are true → shut down or pivot: -- Fewer than 5 paid customers (any product, any price) -- No signed channel partner generating leads -- CMMC Phase 2 enforcement officially extended ≥6 months by DoD +3. **One beachhead.** Lead with CMMC only. SOC 2 and HIPAA are upsells. +4. **Revenue before polish.** If a feature doesn't close Jordan, it waits. --- ## Design System -Landing = light mode. Dashboard = dark mode. Both coexist via `html.dark` class toggle. - -**Landing (light):** -- Body bg: `#ffffff` / `#f0f4f8` (slate-50) -- Primary text: `#0f172a` (slate-900) · Secondary: `#475569` (slate-600) -- Brand accent: `brand-400` CSS var — never raw `amber-*`, `yellow-*`, `indigo-*` -- Cards: light glass, `border-slate-200`, white bg +**Theme: LIGHT default everywhere.** Dark mode = optional inverse via toggle (`.dark` class on ``). + +Palette (soft blue + cream pastels): +- `#81A6C6` Blue — `brand-500` PRIMARY +- `#AACDDC` Sea — `brand-300` +- `#F3E3D0` Cream — `cream-100` +- `#D2C4B4` Beige — `cream-300` + +**Landing + Dashboard (light, no `.dark` on ``):** +- Body bg: `#FBF8F2` (cream-50) +- Section alt bg: `#F3E3D0` (cream-100, palette Cream) +- Surface accent: `#D2C4B4` (cream-300, palette Beige) +- Primary text: `#0f172a` (slate-900) +- Secondary text: `#475569` (slate-600) +- Brand text on light bg: `text-brand-700`+ (brand-500 fails WCAG AA on cream) +- CTA: `bg-brand-500 text-white hover:bg-brand-600` +- Cards: `bg-white/80` + `border-slate-200` - Fonts: `font-editorial` (headers), `font-mono` (metrics) +- NEVER `amber-*`, `yellow-*`, `orange-*`, `indigo-*` — use `brand-*` (blue) -**Dashboard (dark, `.dark` on wrapper):** -- Background: `#07070b` (home), `#0d0d14` (alt sections) -- Brand gold: `brand-400` — never raw color names +**Logo:** +- File: `compliance-firewall-agent/public/houndshield-logo.png` (black doberman + shield) +- Component: `` renders via `next/image` +- Favicon: same file referenced in `app/layout.tsx` -**Both:** +**Both modes:** - No inline styles (radial-gradient `style` prop OK) - Components max 500 lines — split if larger -- Custom cursor `CursorGlow` on `pointer:fine` — never break it +- Custom cursor `CursorGlow` on `pointer:fine` — never break it; tint = blue rgba --- @@ -235,6 +115,5 @@ Landing = light mode. Dashboard = dark mode. Both coexist via `html.dark` class - `transformStyle: "preserve-3d"` + Framer Motion `motion.div` = crash. - HMR error: `rm -rf .next` then restart. - Never `git push origin main`. Never `vercel --prod` without explicit approval. -- Never claim CUI-safety for Mode A (Vercel-hosted endpoint). → Stack details: `.claude/rules/stack.md` · API rules: `.claude/rules/api.md` diff --git a/compliance-firewall-agent/app/agents/page.tsx b/compliance-firewall-agent/app/agents/page.tsx index 0d10076f..4f2c851f 100644 --- a/compliance-firewall-agent/app/agents/page.tsx +++ b/compliance-firewall-agent/app/agents/page.tsx @@ -343,7 +343,7 @@ export default function AgentsPage() { amber: { bg: "bg-brand-500/5", border: "border-brand-500/30", text: "text-brand-400", iconBg: "bg-brand-500/10 border-brand-500/20" }, rose: { bg: "bg-rose-500/5", border: "border-rose-500/30", text: "text-rose-400", iconBg: "bg-rose-500/10 border-rose-500/20" }, cyan: { bg: "bg-cyan-500/5", border: "border-cyan-500/30", text: "text-cyan-400", iconBg: "bg-cyan-500/10 border-cyan-500/20" }, - orange: { bg: "bg-orange-500/5", border: "border-orange-500/30", text: "text-orange-400", iconBg: "bg-orange-500/10 border-orange-500/20" }, + orange: { bg: "bg-brand-500/5", border: "border-brand-500/30", text: "text-brand-400", iconBg: "bg-brand-500/10 border-brand-500/20" }, teal: { bg: "bg-teal-500/5", border: "border-teal-500/30", text: "text-teal-400", iconBg: "bg-teal-500/10 border-teal-500/20" }, indigo: { bg: "bg-brand-500/5", border: "border-brand-500/30", text: "text-brand-400", iconBg: "bg-brand-500/10 border-brand-500/20" }, }; diff --git a/compliance-firewall-agent/app/command-center/agents/page.tsx b/compliance-firewall-agent/app/command-center/agents/page.tsx index 7b835141..51426587 100644 --- a/compliance-firewall-agent/app/command-center/agents/page.tsx +++ b/compliance-firewall-agent/app/command-center/agents/page.tsx @@ -43,7 +43,7 @@ const EDGES: SimEdge[] = [ ]; const STATUS_ICON: Record> = { idle: Clock, thinking: Brain, interacting: Zap }; -const STATUS_COLOR: Record = { idle: "text-slate-400", thinking: "text-amber-400", interacting: "text-brand-400" }; +const STATUS_COLOR: Record = { idle: "text-slate-400", thinking: "text-brand-400", interacting: "text-brand-400" }; export default function AgentsPage() { const [selectedGroup, setSelectedGroup] = useState(null); @@ -69,7 +69,7 @@ export default function AgentsPage() { {[ { label: "Active", value: activeCount, icon: Activity, color: "text-brand-400" }, { label: "Interacting", value: interactingCount, icon: Zap, color: "text-brand-400" }, - { label: "Thinking", value: thinkingCount, icon: Brain, color: "text-amber-400" }, + { label: "Thinking", value: thinkingCount, icon: Brain, color: "text-brand-400" }, ].map(({ label, value, icon: Icon, color }) => (
diff --git a/compliance-firewall-agent/app/command-center/rules/page.tsx b/compliance-firewall-agent/app/command-center/rules/page.tsx index 0cff4e02..3c3306a5 100644 --- a/compliance-firewall-agent/app/command-center/rules/page.tsx +++ b/compliance-firewall-agent/app/command-center/rules/page.tsx @@ -211,14 +211,14 @@ const PREDEFINED_PATTERNS: { label: string; category: RuleCategory; pattern: str const RISK_COLORS: Record = { LOW: "text-sky-400 bg-sky-400/10 border-sky-400/20", - MEDIUM: "text-amber-400 bg-amber-400/10 border-amber-400/20", - HIGH: "text-orange-400 bg-orange-400/10 border-orange-400/20", + MEDIUM: "text-brand-400 bg-brand-400/10 border-brand-400/20", + HIGH: "text-brand-400 bg-brand-400/10 border-brand-400/20", CRITICAL: "text-red-400 bg-red-400/10 border-red-400/20", }; const ACTION_COLORS: Record = { ALLOW: "text-emerald-400 bg-emerald-400/10 border-emerald-400/20", - WARN: "text-yellow-400 bg-yellow-400/10 border-yellow-400/20", + WARN: "text-brand-400 bg-brand-400/10 border-brand-400/20", BLOCK: "text-red-400 bg-red-400/10 border-red-400/20", QUARANTINE: "text-brand-400 bg-brand-400/10 border-brand-400/20", }; diff --git a/compliance-firewall-agent/app/command-center/sdk/page.tsx b/compliance-firewall-agent/app/command-center/sdk/page.tsx index d8285e1e..34e55e5b 100644 --- a/compliance-firewall-agent/app/command-center/sdk/page.tsx +++ b/compliance-firewall-agent/app/command-center/sdk/page.tsx @@ -192,7 +192,7 @@ export default function SDKPage() { label: "Block Rate", value: `${blockRate}%`, icon: BarChart3, - color: "text-amber-400", + color: "text-brand-400", trend: "stable", up: null, }, diff --git a/compliance-firewall-agent/app/command-center/security/page.tsx b/compliance-firewall-agent/app/command-center/security/page.tsx index 61a82ca7..177fa540 100644 --- a/compliance-firewall-agent/app/command-center/security/page.tsx +++ b/compliance-firewall-agent/app/command-center/security/page.tsx @@ -99,8 +99,8 @@ const CATEGORY_BAR = [ const RISK_COLORS: Record = { CRITICAL: "text-red-400 bg-red-400/10 border-red-400/20", - HIGH: "text-orange-400 bg-orange-400/10 border-orange-400/20", - MEDIUM: "text-amber-400 bg-amber-400/10 border-amber-400/20", + HIGH: "text-brand-400 bg-brand-400/10 border-brand-400/20", + MEDIUM: "text-brand-400 bg-brand-400/10 border-brand-400/20", LOW: "text-sky-400 bg-sky-400/10 border-sky-400/20", NONE: "text-white/30 bg-white/5 border-white/10", }; @@ -195,7 +195,7 @@ export default function SecurityDashboardPage() {
- +
diff --git a/compliance-firewall-agent/app/command-center/settings/page.tsx b/compliance-firewall-agent/app/command-center/settings/page.tsx index 32be9593..315a3765 100644 --- a/compliance-firewall-agent/app/command-center/settings/page.tsx +++ b/compliance-firewall-agent/app/command-center/settings/page.tsx @@ -225,7 +225,7 @@ export default function SettingsPage() { free: 'bg-white/[0.05] text-slate-500 border-white/10', pro: 'bg-brand-500/10 text-brand-500 border-brand-200', enterprise: 'bg-purple-500/10 text-purple-400 border-purple-500/20', - agency: 'bg-amber-500/100/10 text-amber-400 border-amber-500/20', + agency: 'bg-brand-500/100/10 text-brand-400 border-brand-500/20', }; return ( diff --git a/compliance-firewall-agent/app/command-center/shield/assessment/page.tsx b/compliance-firewall-agent/app/command-center/shield/assessment/page.tsx index 3d22c573..f6aef26a 100644 --- a/compliance-firewall-agent/app/command-center/shield/assessment/page.tsx +++ b/compliance-firewall-agent/app/command-center/shield/assessment/page.tsx @@ -32,7 +32,7 @@ import type { // ─── Tailwind safelist for dynamic colors (rendered at build time) ─────────── const _TW_SAFELIST = [ "bg-emerald-500/100/20", "text-emerald-400", "border-emerald-500", "border-emerald-500/30", - "bg-amber-500/100/20", "text-amber-400", "border-amber-500", "border-amber-500/30", + "bg-brand-500/100/20", "text-brand-400", "border-brand-500", "border-brand-500/30", "bg-red-500/20", "text-red-400", "border-red-500", "border-red-500/30", "bg-white/[0.03]0/20", "text-slate-400", "border-slate-500", "border-slate-500/30", "shadow-[0_0_12px_rgba(0,0,0,0.3)]", @@ -190,12 +190,12 @@ export default function AssessmentPage() {
{/* Demo mode save warning */} {isDemo && ( -
- +
+ - Results are not being saved.{" "} + Results are not being saved.{" "} Complete your account setup to persist assessment data across sessions.{" "} - + Go to Settings → diff --git a/compliance-firewall-agent/app/command-center/shield/gaps/page.tsx b/compliance-firewall-agent/app/command-center/shield/gaps/page.tsx index f19ea89b..a67a4fa4 100644 --- a/compliance-firewall-agent/app/command-center/shield/gaps/page.tsx +++ b/compliance-firewall-agent/app/command-center/shield/gaps/page.tsx @@ -24,7 +24,7 @@ import type { AssessmentResponse, NISTControl, RiskPriority } from "@/lib/shield const PRIORITY_COLORS: Record = { CRITICAL: { bg: "bg-red-500/10", text: "text-red-400", border: "border-red-500/30" }, - HIGH: { bg: "bg-amber-500/100/10", text: "text-amber-400", border: "border-amber-500/30" }, + HIGH: { bg: "bg-brand-500/100/10", text: "text-brand-400", border: "border-brand-500/30" }, MEDIUM: { bg: "bg-brand-500/100/10", text: "text-brand-400", border: "border-brand-500/30" }, LOW: { bg: "bg-white/[0.03]0/10", text: "text-slate-400", border: "border-slate-500/30" }, }; @@ -199,7 +199,7 @@ export default function GapsPage() { {control.estimatedHours}h diff --git a/compliance-firewall-agent/app/command-center/shield/onboarding/page.tsx b/compliance-firewall-agent/app/command-center/shield/onboarding/page.tsx index d3944732..d9b23ba1 100644 --- a/compliance-firewall-agent/app/command-center/shield/onboarding/page.tsx +++ b/compliance-firewall-agent/app/command-center/shield/onboarding/page.tsx @@ -221,7 +221,7 @@ export default function OnboardingPage() { onClick={() => setHandlesCUI(!handlesCUI)} className={`flex items-center gap-3 p-4 rounded-xl border transition-all ${ handlesCUI - ? "bg-amber-500/100/10 border-amber-500/50 text-amber-400" + ? "bg-brand-500/100/10 border-brand-500/50 text-brand-400" : "bg-white/[0.05]/50 border-slate-600 text-slate-400 hover:border-slate-500" }`} > @@ -410,11 +410,11 @@ const response = await client.chat.completions.create({
{handlesCUI && cmmcLevel === 1 && ( -
- +
+
-

CUI requires Level 2

-

+

CUI requires Level 2

+

You indicated your organization handles CUI. CMMC Level 2 is required for CUI protection.

diff --git a/compliance-firewall-agent/app/command-center/shield/reports/page.tsx b/compliance-firewall-agent/app/command-center/shield/reports/page.tsx index f6380b4e..0bb0d0e8 100644 --- a/compliance-firewall-agent/app/command-center/shield/reports/page.tsx +++ b/compliance-firewall-agent/app/command-center/shield/reports/page.tsx @@ -140,7 +140,7 @@ export default function ReportsPage() { {/* PDF error / upgrade nudge */} {pdfError && ( -
+
{pdfError} Upgrade → @@ -221,7 +221,7 @@ export default function ReportsPage() { Family Controls Met - Partial + Partial Unmet Score Max @@ -242,7 +242,7 @@ export default function ReportsPage() { {family.controlCount} {stats?.met ?? 0} - {stats?.partial ?? 0} + {stats?.partial ?? 0} {stats?.unmet ?? 0} {stats?.score ?? 0} {-maxForFamily} @@ -255,7 +255,7 @@ export default function ReportsPage() { TOTAL {ALL_CONTROLS.length} {statusCounts.met} - {statusCounts.partial} + {statusCounts.partial} {statusCounts.unmet} {sprs.total} -203 diff --git a/compliance-firewall-agent/app/globals.css b/compliance-firewall-agent/app/globals.css index 7b6b5b8d..5035f80b 100644 --- a/compliance-firewall-agent/app/globals.css +++ b/compliance-firewall-agent/app/globals.css @@ -2,118 +2,93 @@ @tailwind components; @tailwind utilities; -/* JetBrains Mono via CSS — next/font doesn't support it without self-hosting */ -@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap'); - -/* ═══════════════════════════════════════════════════════════════════ - HOUND SHIELD — BEAST UI v3.0 - Palette: Steel (#81A6C6) · Sky (#AACDDC) · Cream (#F3E3D0) · Sand (#D2C4B4) - Light mode only (landing). Dashboard uses direct dark Tailwind classes. - ═══════════════════════════════════════════════════════════════════ */ +/* Google Fonts — v3 stack: Fraunces (display), DM Sans (body), JetBrains Mono (code) */ +@import url('https://fonts.googleapis.com/css2?family=Anton&family=Condiment&family=DM+Sans:opsz,wght@9..40,300;9..40,400;9..40,500;9..40,600;9..40,700&family=Fraunces:opsz,wght@9..144,400;9..144,500;9..144,600;9..144,700;9..144,800&family=Inter:wght@100..900&family=JetBrains+Mono:wght@400;500;600;700&family=Outfit:wght@600;700;800;900&family=Playfair+Display:ital,wght@0,700;0,800;1,700;1,800&display=swap'); :root { - /* ── Brand Palette ────────────────────────────────── */ - --hs-steel: #81A6C6; - --hs-sky: #AACDDC; - --hs-cream: #F3E3D0; - --hs-sand: #D2C4B4; - - /* ── Extended Brand ───────────────────────────────── */ - --hs-white: #FAFCFF; - --hs-ink: #0F1E2E; - --hs-ink-secondary: #3D5166; - --hs-ink-tertiary: #6B8299; - --hs-navy: #0D1B2A; - --hs-steel-dark: #5A86A8; - --hs-steel-light: #C5DAE9; - --hs-cream-deep: #EDD5BC; - --hs-sand-light: #E8DDD1; - - /* ── Surfaces ─────────────────────────────────────── */ - --hs-surface-0: #FAFCFF; - --hs-surface-1: #F5F8FB; - --hs-surface-2: #F3E3D0; - --hs-surface-3: #EDD5BC; - - /* ── Borders ──────────────────────────────────────── */ - --hs-border-subtle: rgba(129, 166, 198, 0.12); - --hs-border: rgba(129, 166, 198, 0.22); - --hs-border-strong: rgba(129, 166, 198, 0.45); - --hs-border-ink: rgba(15, 30, 46, 0.10); - - /* ── Overlays ─────────────────────────────────────── */ - --hs-mist: rgba(129, 166, 198, 0.06); - --hs-mist-md: rgba(129, 166, 198, 0.10); - --hs-glow: rgba(129, 166, 198, 0.18); - --hs-glow-strong: rgba(129, 166, 198, 0.28); - - /* ── Semantic ─────────────────────────────────────── */ - --hs-success: #059669; - --hs-success-bg: #ECFDF5; - --hs-danger: #DC2626; - --hs-danger-bg: #FEF2F2; - --hs-warn: #D97706; - --hs-warn-bg: #FFFBEB; - - /* ── Typography ───────────────────────────────────── */ - --font-display: 'Fraunces', Georgia, serif; - --font-body: 'DM Sans', system-ui, sans-serif; - --font-mono: 'JetBrains Mono', 'Fira Code', monospace; - - /* ── Motion ───────────────────────────────────────── */ - --ease-out: cubic-bezier(0.16, 1, 0.3, 1); - --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); - --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); - --duration-fast: 150ms; - --duration-base: 250ms; - --duration-slow: 400ms; - --duration-enter: 600ms; - - /* ── Shadows ──────────────────────────────────────── */ - --shadow-sm: 0 1px 2px rgba(15,30,46,0.04), 0 1px 3px rgba(129,166,198,0.06); - --shadow-md: 0 4px 16px rgba(15,30,46,0.06), 0 1px 4px rgba(129,166,198,0.08); - --shadow-lg: 0 8px 32px rgba(15,30,46,0.08), 0 2px 8px rgba(129,166,198,0.10); - --shadow-xl: 0 16px 48px rgba(15,30,46,0.10), 0 4px 12px rgba(129,166,198,0.12); - --shadow-card: 0 2px 8px rgba(15,30,46,0.06), 0 0 1px rgba(129,166,198,0.20); - --shadow-cta: 0 4px 24px rgba(129,166,198,0.35), 0 1px 4px rgba(129,166,198,0.20); - --shadow-logo: 0 2px 12px rgba(15,30,46,0.12), 0 1px 3px rgba(15,30,46,0.08); - - /* ── Radii ────────────────────────────────────────── */ - --radius-sm: 6px; - --radius-md: 10px; - --radius-lg: 14px; - --radius-xl: 20px; - --radius-pill: 999px; + --font-inter: 'Inter', ui-sans-serif, system-ui, sans-serif; + --font-outfit: 'Outfit', ui-sans-serif, system-ui, sans-serif; + --font-playfair: 'Playfair Display', ui-serif, Georgia, serif; + --font-fraunces: 'Fraunces', Georgia, ui-serif, serif; + --font-dm-sans: 'DM Sans', 'Inter', ui-sans-serif, system-ui, sans-serif; + --font-jetbrains: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace; + --font-sans: var(--font-dm-sans); + --font-display: var(--font-fraunces); + + /* v3 design tokens (HoundShield Landing Page v3) */ + --hs-blue: #81A6C6; + --hs-sky: #AACDDC; + --hs-cream: #F3E3D0; + --hs-sand: #D2C4B4; + --hs-white: #FAFCFF; + --hs-ink: #0F1E2E; + --hs-ink-secondary:#3D5166; + --hs-ink-tertiary: #6B8299; + --hs-navy: #0D1B2A; + --hs-steel-dark: #5A86A8; + --hs-steel-light: #C5DAE9; + --hs-cream-deep: #EDD5BC; + --hs-sand-light: #E8DDD1; + --hs-surface-0: #FAFCFF; + --hs-surface-1: #F5F8FB; + --hs-surface-2: #F3E3D0; + --hs-surface-3: #EDD5BC; + --hs-border-subtle:rgba(129,166,198,0.12); + --hs-border: rgba(129,166,198,0.22); + --hs-border-strong:rgba(129,166,198,0.45); + --hs-border-ink: rgba(15,30,46,0.10); + --hs-mist: rgba(129,166,198,0.06); + --hs-mist-md: rgba(129,166,198,0.10); + --hs-glow: rgba(129,166,198,0.18); + --hs-glow-strong: rgba(129,166,198,0.28); + --hs-shadow-sm: 0 1px 2px rgba(15,30,46,0.04), 0 1px 3px rgba(129,166,198,0.06); + --hs-shadow-md: 0 4px 16px rgba(15,30,46,0.06), 0 1px 4px rgba(129,166,198,0.08); + --hs-shadow-lg: 0 8px 32px rgba(15,30,46,0.08), 0 2px 8px rgba(129,166,198,0.10); /* Cursor position for section spotlights */ --mx: -500px; --my: -500px; } -/* ── Section spotlight ────────────────────────────────────────── */ -.spotlight { position: relative; } +/* ── Section spotlight — add .spotlight class to any section ── */ +.spotlight { + position: relative; +} .spotlight::before { content: ''; position: absolute; inset: 0; - background: radial-gradient(500px circle at var(--mx) var(--my), var(--hs-mist), transparent 40%); + background: radial-gradient( + 500px circle at var(--mx) var(--my), + rgba(129, 166, 198, 0.06), + transparent 40% + ); opacity: 0; transition: opacity 0.4s ease; pointer-events: none; z-index: 1; } -.spotlight:hover::before { opacity: 1; } +.spotlight:hover::before { + opacity: 1; +} -/* ── Card glow ────────────────────────────────────────────────── */ +/* ── Card glow on hover (border lights up from cursor) ──────── */ .card-glow { position: relative; transition: border-color 0.3s ease, box-shadow 0.3s ease; } .card-glow:hover { - border-color: var(--hs-border-strong) !important; - box-shadow: var(--shadow-lg); + border-color: rgba(129, 166, 198, 0.25) !important; + box-shadow: 0 0 0 1px rgba(129, 166, 198, 0.15), 0 8px 32px rgba(0, 0, 0, 0.4); } +/* ═══════════════════════════════════════════════════════════════════ + HOUND SHIELD — Light Pastel Design System + Dual theme · Soft blue + cream accents · Calm authority + Palette: #FAFCFF · #F3E3D0 · #81A6C6 · #AACDDC · #0F1E2E (v3) + Light = default. Dark = optional via .dark class. + ═══════════════════════════════════════════════════════════════════ */ + @layer base { html { scroll-behavior: smooth; @@ -122,93 +97,154 @@ } body { - background-color: var(--hs-surface-0); - color: var(--hs-ink); - font-family: var(--font-body); + background-color: #FAFCFF; + color: #0F1E2E; + font-family: var(--font-dm-sans); line-height: 1.6; } + .dark body, + html.dark body { + background-color: #0a0a0a; + color: #FBF8F2; + } + *:focus-visible { - outline: 2px solid var(--hs-steel); + outline: 2px solid #81A6C6; outline-offset: 2px; border-radius: 4px; } ::selection { - background-color: rgba(129, 166, 198, 0.20); - color: var(--hs-ink); + background-color: rgba(129, 166, 198, 0.18); + color: #0a0a0a; } - ::-webkit-scrollbar { width: 6px; height: 6px; } - ::-webkit-scrollbar-track { background: transparent; } + .dark ::selection { + background-color: rgba(170, 205, 220, 0.22); + color: #FBF8F2; + } + + ::-webkit-scrollbar { + width: 6px; + height: 6px; + } + ::-webkit-scrollbar-track { + background: transparent; + } ::-webkit-scrollbar-thumb { - background: var(--hs-border); + background: rgba(129, 166, 198, 0.25); border-radius: 3px; } - ::-webkit-scrollbar-thumb:hover { background: var(--hs-border-strong); } + ::-webkit-scrollbar-thumb:hover { + background: rgba(129, 166, 198, 0.45); + } + .dark ::-webkit-scrollbar-thumb { + background: rgba(170, 205, 220, 0.15); + } + .dark ::-webkit-scrollbar-thumb:hover { + background: rgba(170, 205, 220, 0.3); + } } @layer components { - /* ===== GLASS CARD ===== */ + /* ===== LIQUID GLASS (NFT-style) ===== */ + .liquid-glass { + background: rgba(255, 255, 255, 0.01); + background-blend-mode: luminosity; + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); + border: none; + box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.1); + position: relative; + overflow: hidden; + } + .liquid-glass::before { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + padding: 1.4px; + background: linear-gradient(180deg, + rgba(255,255,255,0.45) 0%, rgba(255,255,255,0.15) 20%, + rgba(255,255,255,0) 40%, rgba(255,255,255,0) 60%, + rgba(255,255,255,0.15) 80%, rgba(255,255,255,0.45) 100%); + -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; + } + + /* ===== GLASS CARDS ===== */ .glass-card { background: white; - border: 1px solid var(--hs-border); + border: 1px solid rgba(214, 196, 170, 0.7); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-card); - transition: border-color 200ms var(--ease-in-out), - box-shadow 200ms var(--ease-in-out), - transform 200ms var(--ease-in-out); + border-radius: 1rem; + box-shadow: 0 1px 3px rgba(82, 122, 159, 0.06), + 0 1px 2px rgba(82, 122, 159, 0.04); + transition: border-color 200ms cubic-bezier(0.4, 0, 0.2, 1), + box-shadow 200ms cubic-bezier(0.4, 0, 0.2, 1), + transform 200ms cubic-bezier(0.4, 0, 0.2, 1); } .glass-card:hover { - border-color: var(--hs-border-strong); - box-shadow: var(--shadow-lg); + border-color: rgba(129, 166, 198, 0.3); + box-shadow: 0 10px 25px -5px rgba(82, 122, 159, 0.1), + 0 0 15px -3px rgba(129, 166, 198, 0.1); transform: translateY(-2px); } .glass-card-glow { background: white; - border: 1px solid var(--hs-border); + border: 1px solid rgba(214, 196, 170, 0.7); backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-card); - transition: border-color 300ms var(--ease-in-out), - box-shadow 300ms var(--ease-in-out), - transform 300ms var(--ease-in-out); + border-radius: 1rem; + box-shadow: 0 1px 3px rgba(82, 122, 159, 0.06); + transition: border-color 300ms cubic-bezier(0.4, 0, 0.2, 1), + box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1), + transform 300ms cubic-bezier(0.4, 0, 0.2, 1); } .glass-card-glow:hover { - border-color: var(--hs-border-strong); - box-shadow: var(--shadow-xl), 0 0 24px var(--hs-glow); + border-color: rgba(129, 166, 198, 0.3); + box-shadow: 0 0 30px -5px rgba(129, 166, 198, 0.12), + 0 10px 25px -5px rgba(82, 122, 159, 0.08); transform: translateY(-4px); } /* ===== FROSTED NAVBAR ===== */ .nav-frosted { - background: rgba(250, 252, 255, 0.88); + background: rgba(7, 7, 11, 0.88); backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%); - border-bottom: 1px solid var(--hs-border-subtle); + border-bottom: 1px solid rgba(255, 255, 255, 0.06); } /* ===== TEXT GRADIENTS ===== */ .text-gradient-brand { - background: linear-gradient(135deg, var(--hs-steel-dark), var(--hs-steel), var(--hs-sky)); + background: linear-gradient(135deg, #81A6C6, #527A9F, #AACDDC); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + } + + .text-gradient-arctic { + background: linear-gradient(135deg, #AACDDC 0%, #81A6C6 50%, #527A9F 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; } - .text-gradient-sky { - background: linear-gradient(135deg, var(--hs-steel) 0%, var(--hs-sky) 60%, var(--hs-steel-light) 100%); + .text-gradient-fire { + background: linear-gradient(135deg, #AACDDC, #81A6C6, #527A9F); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; } - .text-gradient-warm { - background: linear-gradient(135deg, var(--hs-cream-deep), var(--hs-cream), var(--hs-sand)); + .text-gradient-aurora { + background: linear-gradient(135deg, #AACDDC, #81A6C6, #527A9F); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; @@ -216,32 +252,31 @@ /* ===== BACKGROUNDS ===== */ .bg-dot-grid { - background-image: radial-gradient(var(--hs-border) 1px, transparent 1px); + background-image: radial-gradient(rgba(129, 166, 198, 0.12) 1px, transparent 1px); background-size: 24px 24px; } .bg-grid-lines { - background-image: linear-gradient(var(--hs-border-subtle) 1px, transparent 1px), - linear-gradient(90deg, var(--hs-border-subtle) 1px, transparent 1px); + background-image: linear-gradient(rgba(214, 196, 170, 0.5) 1px, transparent 1px), + linear-gradient(90deg, rgba(214, 196, 170, 0.5) 1px, transparent 1px); background-size: 64px 64px; } .bg-mesh-gradient { - background-image: - radial-gradient(at 27% 37%, rgba(129,166,198,0.08) 0px, transparent 50%), - radial-gradient(at 97% 21%, rgba(170,205,220,0.06) 0px, transparent 50%), - radial-gradient(at 52% 99%, rgba(211,196,180,0.05) 0px, transparent 50%); + background-image: radial-gradient(at 27% 37%, rgba(129, 166, 198, 0.07) 0px, transparent 50%), + radial-gradient(at 97% 21%, rgba(170, 205, 220, 0.05) 0px, transparent 50%), + radial-gradient(at 52% 99%, rgba(82, 122, 159, 0.04) 0px, transparent 50%); } .bg-hero-glow { - background-image: radial-gradient(ellipse 80% 50% at 50% -20%, rgba(129,166,198,0.12), transparent); + background-image: radial-gradient(ellipse 80% 50% at 50% -20%, rgba(129, 166, 198, 0.1), transparent); } .bg-aurora { background-image: - radial-gradient(ellipse 50% 80% at 20% 40%, rgba(129,166,198,0.08), transparent), - radial-gradient(ellipse 50% 80% at 80% 50%, rgba(170,205,220,0.06), transparent), - radial-gradient(ellipse 40% 50% at 50% 80%, rgba(211,196,180,0.06), transparent); + radial-gradient(ellipse 50% 80% at 20% 40%, rgba(170, 205, 220, 0.07), transparent), + radial-gradient(ellipse 50% 80% at 80% 50%, rgba(129, 166, 198, 0.05), transparent), + radial-gradient(ellipse 40% 50% at 50% 80%, rgba(82, 122, 159, 0.05), transparent); } /* ===== STATUS DOT ===== */ @@ -251,14 +286,14 @@ width: 8px; height: 8px; border-radius: 50%; - background-color: var(--hs-success); + background-color: #10B981; } .status-dot::before { content: ""; position: absolute; inset: -3px; border-radius: 50%; - background-color: rgba(5, 150, 105, 0.3); + background-color: rgba(16, 185, 129, 0.3); animation: status-pulse 2s ease-in-out infinite; } @@ -272,10 +307,11 @@ font-weight: 600; font-size: 0.875rem; color: white; - background: linear-gradient(135deg, var(--hs-steel-dark), var(--hs-steel)); - border-radius: var(--radius-md); - transition: all 200ms var(--ease-in-out); - box-shadow: var(--shadow-cta); + background: linear-gradient(135deg, #81A6C6, #527A9F); + border-radius: 0.75rem; + transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: 0 4px 14px -3px rgba(129, 166, 198, 0.45), + inset 0 1px 0 rgba(255, 255, 255, 0.15); position: relative; overflow: hidden; letter-spacing: -0.01em; @@ -284,13 +320,14 @@ content: ""; position: absolute; inset: 0; - background: linear-gradient(135deg, transparent 30%, rgba(255,255,255,0.12) 50%, transparent 70%); + background: linear-gradient(135deg, transparent 30%, rgba(170, 205, 220, 0.2) 50%, transparent 70%); transform: translateX(-100%); transition: transform 400ms ease; } .btn-primary:hover::before { transform: translateX(100%); } .btn-primary:hover { - box-shadow: 0 8px 32px rgba(129,166,198,0.45); + box-shadow: 0 8px 25px -5px rgba(129, 166, 198, 0.55), + inset 0 1px 0 rgba(255, 255, 255, 0.2); transform: translateY(-2px); } .btn-primary:active { transform: translateY(0); } @@ -303,18 +340,19 @@ padding: 0.75rem 1.75rem; font-weight: 500; font-size: 0.875rem; - color: var(--hs-ink); - border: 1px solid var(--hs-border); - border-radius: var(--radius-md); - transition: all 200ms var(--ease-in-out); + color: #0a0a0a; + border: 1px solid rgba(214, 196, 170, 0.8); + border-radius: 0.75rem; + transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1); letter-spacing: -0.01em; background: white; } .btn-ghost:hover { - border-color: var(--hs-border-strong); - background: var(--hs-mist); + color: #0a0a0a; + border-color: rgba(129, 166, 198, 0.3); + background: rgba(129, 166, 198, 0.05); transform: translateY(-1px); - box-shadow: var(--shadow-sm); + box-shadow: 0 4px 12px -4px rgba(82, 122, 159, 0.12); } /* ===== SIDEBAR NAVIGATION ===== */ @@ -324,19 +362,19 @@ gap: 0.75rem; padding: 0.5rem 0.75rem; font-size: 0.875rem; - color: var(--hs-ink-tertiary); + color: #888888; border-radius: 0.5rem; - transition: all 150ms var(--ease-in-out); + transition: all 150ms cubic-bezier(0.4, 0, 0.2, 1); border: 1px solid transparent; } .nav-item:hover { - color: var(--hs-ink); - background: var(--hs-mist); + color: #0a0a0a; + background: rgba(129, 166, 198, 0.06); } .nav-item-active { - color: var(--hs-steel-dark) !important; - background: rgba(129,166,198,0.10) !important; - border-color: var(--hs-border) !important; + color: #81A6C6 !important; + background: rgba(129, 166, 198, 0.1) !important; + border-color: rgba(129, 166, 198, 0.15) !important; font-weight: 500; } @@ -347,20 +385,51 @@ padding: 0.125rem 0.5rem; font-size: 0.75rem; font-weight: 500; - border-radius: var(--radius-pill); + border-radius: 9999px; letter-spacing: 0.01em; } + /* ===== 3D FLOATING SHAPES ===== */ + .shape-3d { + position: absolute; + border-radius: 24px; + opacity: 0.5; + filter: blur(0px); + pointer-events: none; + } + .shape-sphere { + border-radius: 50%; + background: linear-gradient(135deg, rgba(129, 166, 198, 0.14), rgba(82, 122, 159, 0.08)); + box-shadow: inset -8px -8px 20px rgba(129, 166, 198, 0.08), + inset 4px 4px 10px rgba(255, 255, 255, 0.8), + 0 20px 40px -10px rgba(129, 166, 198, 0.12); + } + .shape-cube { + background: linear-gradient(135deg, rgba(129, 166, 198, 0.1), rgba(170, 205, 220, 0.07)); + border: 1px solid rgba(129, 166, 198, 0.12); + box-shadow: inset -4px -4px 12px rgba(129, 166, 198, 0.05), + inset 2px 2px 6px rgba(255, 255, 255, 0.6), + 0 15px 30px -8px rgba(129, 166, 198, 0.1); + transform: rotate(45deg); + } + .shape-torus { + border-radius: 50%; + border: 8px solid rgba(129, 166, 198, 0.12); + background: transparent; + box-shadow: inset 0 0 20px rgba(129, 166, 198, 0.07), + 0 10px 25px -8px rgba(129, 166, 198, 0.1); + } + /* ===== CODE BLOCK ===== */ .code-block { - background: var(--hs-navy); - border: 1px solid rgba(129,166,198,0.2); - border-radius: var(--radius-lg); - font-family: var(--font-mono); + background: #0a0a0a; + border: 1px solid rgba(82, 122, 159, 0.6); + border-radius: 1rem; + font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, monospace; font-size: 0.8125rem; line-height: 1.7; overflow: hidden; - box-shadow: var(--shadow-xl); + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.2); } .code-header { @@ -368,8 +437,8 @@ align-items: center; gap: 0.5rem; padding: 0.75rem 1rem; - border-bottom: 1px solid rgba(129,166,198,0.15); - background: rgba(13,27,42,0.5); + border-bottom: 1px solid rgba(82, 122, 159, 0.4); + background: rgba(10, 10, 10, 0.5); } .code-dot { width: 10px; height: 10px; border-radius: 50%; } @@ -383,17 +452,19 @@ width: 56px; height: 56px; border-radius: 50%; - background: linear-gradient(135deg, var(--hs-steel-dark), var(--hs-steel)); - box-shadow: var(--shadow-cta); + background: linear-gradient(135deg, #81A6C6, #527A9F); + box-shadow: 0 4px 20px -4px rgba(129, 166, 198, 0.55), + 0 8px 24px -8px rgba(0, 0, 0, 0.2); display: flex; align-items: center; justify-content: center; cursor: pointer; - transition: all 300ms var(--ease-in-out); + transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1); } .chat-widget-trigger:hover { transform: scale(1.1); - box-shadow: 0 8px 32px rgba(129,166,198,0.5); + box-shadow: 0 8px 30px -4px rgba(129, 166, 198, 0.65), + 0 12px 32px -8px rgba(0, 0, 0, 0.25); } .chat-window { @@ -403,15 +474,16 @@ z-index: 100; width: 400px; max-height: 540px; - background: rgba(250, 252, 255, 0.97); - border: 1px solid var(--hs-border); - border-radius: var(--radius-xl); + background: rgba(255, 255, 255, 0.97); + border: 1px solid rgba(214, 196, 170, 0.8); + border-radius: 1.25rem; backdrop-filter: blur(24px); - box-shadow: var(--shadow-xl); + box-shadow: 0 25px 60px -15px rgba(82, 122, 159, 0.15), + 0 0 0 1px rgba(214, 196, 170, 0.5); display: flex; flex-direction: column; overflow: hidden; - animation: chat-slide-up 300ms var(--ease-out); + animation: chat-slide-up 300ms cubic-bezier(0.16, 1, 0.3, 1); } .counter-value { font-variant-numeric: tabular-nums; transition: all 0.3s ease; } @@ -422,39 +494,39 @@ left: 0; right: 0; height: 2px; - background: linear-gradient(90deg, transparent, var(--hs-steel), transparent); + background: linear-gradient(90deg, transparent, #81A6C6, transparent); animation: scan-sweep 3s ease-in-out infinite; } .section-divider { height: 1px; - background: linear-gradient(90deg, transparent, var(--hs-border) 50%, transparent); + background: linear-gradient(90deg, transparent, rgba(214, 196, 170, 0.8) 50%, transparent); } .glow-ring { - box-shadow: 0 0 0 1px var(--hs-border), - 0 0 20px -5px var(--hs-glow); + box-shadow: 0 0 0 1px rgba(129, 166, 198, 0.18), + 0 0 20px -5px rgba(129, 166, 198, 0.12); } /* ===== METRIC CARDS ===== */ .metric-card { background: white; - border: 1px solid var(--hs-border); - border-radius: var(--radius-lg); + border: 1px solid #E8DDD0; + border-radius: 1rem; padding: 1.5rem; transition: all 200ms ease; - box-shadow: var(--shadow-sm); + box-shadow: 0 1px 3px rgba(82, 122, 159, 0.05); } .metric-card:hover { - box-shadow: var(--shadow-md); - border-color: var(--hs-border-strong); + box-shadow: 0 8px 25px -8px rgba(129, 166, 198, 0.12); + border-color: rgba(129, 166, 198, 0.2); transform: translateY(-2px); } /* ===== SIDEBAR ===== */ .sidebar-arctic { - background: var(--hs-surface-1); - border-right: 1px solid var(--hs-border-subtle); + background: white; + border-right: 1px solid #E8DDD0; width: 240px; min-height: 100vh; } @@ -465,87 +537,173 @@ height: 44px; padding: 0 0.875rem; background: white; - border: 1px solid var(--hs-border); - border-radius: var(--radius-md); + border: 1px solid #E8DDD0; + border-radius: 0.5rem; font-size: 0.875rem; - color: var(--hs-ink); + color: #0a0a0a; transition: all 150ms ease; - font-family: var(--font-body); } - .input-arctic::placeholder { color: var(--hs-ink-tertiary); } + .input-arctic::placeholder { color: #999999; } .input-arctic:focus { outline: none; - border-color: var(--hs-steel); - box-shadow: 0 0 0 3px rgba(129,166,198,0.15); + border-color: #81A6C6; + box-shadow: 0 0 0 3px rgba(129, 166, 198, 0.12); } - /* ===== LIQUID GLASS ===== */ - .liquid-glass { - background: rgba(250,252,255,0.6); - backdrop-filter: blur(4px); - -webkit-backdrop-filter: blur(4px); - border: none; - box-shadow: inset 0 1px 1px rgba(255,255,255,0.7); - position: relative; - overflow: hidden; + /* ═══════════════════════════════════════════════════════════════ + DARK THEME OVERRIDES + ═══════════════════════════════════════════════════════════════ */ + + /* Glass cards */ + .dark .glass-card { + background: rgba(10, 10, 10, 0.65); + border-color: rgba(82, 122, 159, 0.4); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4), + 0 1px 2px rgba(0, 0, 0, 0.3); } - .liquid-glass::before { - content: ''; - position: absolute; - inset: 0; - border-radius: inherit; - padding: 1.4px; - background: linear-gradient(180deg, - rgba(255,255,255,0.6) 0%, rgba(255,255,255,0.2) 20%, - rgba(255,255,255,0) 40%, rgba(255,255,255,0) 60%, - rgba(255,255,255,0.2) 80%, rgba(255,255,255,0.6) 100%); - -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); - -webkit-mask-composite: xor; - mask-composite: exclude; - pointer-events: none; + .dark .glass-card:hover { + border-color: rgba(170, 205, 220, 0.3); + box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5), + 0 0 15px -3px rgba(170, 205, 220, 0.12); } - /* ===== 3D SHAPES ===== */ - .shape-3d { position: absolute; border-radius: 24px; opacity: 0.5; pointer-events: none; } - .shape-sphere { - border-radius: 50%; - background: linear-gradient(135deg, rgba(129,166,198,0.15), rgba(90,134,168,0.08)); - box-shadow: inset -8px -8px 20px rgba(129,166,198,0.08), - inset 4px 4px 10px rgba(255,255,255,0.8), - 0 20px 40px -10px rgba(129,166,198,0.12); + .dark .glass-card-glow { + background: rgba(10, 10, 10, 0.75); + border-color: rgba(82, 122, 159, 0.4); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4); } - .shape-cube { - background: linear-gradient(135deg, rgba(129,166,198,0.10), rgba(170,205,220,0.07)); - border: 1px solid var(--hs-border-subtle); - box-shadow: inset -4px -4px 12px rgba(129,166,198,0.05), - inset 2px 2px 6px rgba(255,255,255,0.6), - 0 15px 30px -8px rgba(129,166,198,0.10); - transform: rotate(45deg); + .dark .glass-card-glow:hover { + border-color: rgba(170, 205, 220, 0.3); + box-shadow: 0 0 30px -5px rgba(170, 205, 220, 0.18), + 0 10px 25px -5px rgba(0, 0, 0, 0.5); } - .shape-torus { - border-radius: 50%; - border: 8px solid var(--hs-border-subtle); - background: transparent; - box-shadow: inset 0 0 20px rgba(129,166,198,0.07), - 0 10px 25px -8px rgba(129,166,198,0.10); + + /* Navbar */ + .dark .nav-frosted { + background: rgba(10, 10, 10, 0.88); + border-bottom-color: rgba(82, 122, 159, 0.35); } - /* ===== SHIMMER BADGE ===== */ - .badge-shimmer { - background: linear-gradient(90deg, - rgba(129,166,198,0) 0%, rgba(129,166,198,0.06) 40%, - rgba(170,205,220,0.12) 50%, rgba(129,166,198,0.06) 60%, - rgba(129,166,198,0) 100%); - background-size: 200% 100%; - animation: badge-shimmer 4s ease-in-out infinite; + /* Backgrounds */ + .dark .bg-dot-grid { + background-image: radial-gradient(rgba(170, 205, 220, 0.07) 1px, transparent 1px); + } + .dark .bg-grid-lines { + background-image: linear-gradient(rgba(82, 122, 159, 0.25) 1px, transparent 1px), + linear-gradient(90deg, rgba(82, 122, 159, 0.25) 1px, transparent 1px); + } + .dark .bg-mesh-gradient { + background-image: radial-gradient(at 27% 37%, rgba(170, 205, 220, 0.09) 0px, transparent 50%), + radial-gradient(at 97% 21%, rgba(129, 166, 198, 0.07) 0px, transparent 50%), + radial-gradient(at 52% 99%, rgba(82, 122, 159, 0.07) 0px, transparent 50%); + } + .dark .bg-hero-glow { + background-image: radial-gradient(ellipse 80% 50% at 50% -20%, rgba(170, 205, 220, 0.14), transparent); + } + .dark .bg-aurora { + background-image: + radial-gradient(ellipse 50% 80% at 20% 40%, rgba(170, 205, 220, 0.1), transparent), + radial-gradient(ellipse 50% 80% at 80% 50%, rgba(129, 166, 198, 0.07), transparent), + radial-gradient(ellipse 40% 50% at 50% 80%, rgba(82, 122, 159, 0.07), transparent); } - /* ===== ANIMATED BORDER ===== */ - .animated-border { - position: relative; - background: linear-gradient(var(--bg, white), var(--bg, white)) padding-box, - linear-gradient(135deg, rgba(129,166,198,0.4), rgba(170,205,220,0.2), rgba(129,166,198,0.1)) border-box; - border: 1px solid transparent; + /* Buttons */ + .dark .btn-ghost { + color: #AACDDC; + border-color: rgba(82, 122, 159, 0.5); + background: rgba(10, 10, 10, 0.5); + } + .dark .btn-ghost:hover { + color: #FBF8F2; + border-color: rgba(170, 205, 220, 0.3); + background: rgba(15, 8, 4, 0.6); + } + + /* Sidebar navigation */ + .dark .nav-item { + color: #999999; + } + .dark .nav-item:hover { + color: #FBF8F2; + background: rgba(170, 205, 220, 0.07); + } + .dark .nav-item-active { + color: #AACDDC !important; + background: rgba(170, 205, 220, 0.12) !important; + border-color: rgba(170, 205, 220, 0.2) !important; + } + + /* Metric cards */ + .dark .metric-card { + background: rgba(10, 10, 10, 0.65); + border-color: rgba(82, 122, 159, 0.4); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4); + } + .dark .metric-card:hover { + box-shadow: 0 8px 25px -8px rgba(170, 205, 220, 0.18); + border-color: rgba(170, 205, 220, 0.25); + } + + /* Sidebar */ + .dark .sidebar-arctic { + background: rgba(10, 10, 10, 0.97); + border-right-color: rgba(82, 122, 159, 0.35); + } + + /* Input fields */ + .dark .input-arctic { + background: rgba(10, 10, 10, 0.65); + border-color: rgba(82, 122, 159, 0.45); + color: #FBF8F2; + } + .dark .input-arctic::placeholder { color: #888888; } + .dark .input-arctic:focus { + border-color: #AACDDC; + box-shadow: 0 0 0 3px rgba(170, 205, 220, 0.15); + } + + /* Chat window */ + .dark .chat-window { + background: rgba(10, 10, 10, 0.97); + border-color: rgba(82, 122, 159, 0.55); + box-shadow: 0 25px 60px -15px rgba(0, 0, 0, 0.6), + 0 0 0 1px rgba(82, 122, 159, 0.3); + } + + /* Scan line */ + .dark .scan-line { + background: linear-gradient(90deg, transparent, #AACDDC, transparent); + } + + /* Section divider */ + .dark .section-divider { + background: linear-gradient(90deg, transparent, rgba(82, 122, 159, 0.5) 50%, transparent); + } + + /* Glow ring */ + .dark .glow-ring { + box-shadow: 0 0 0 1px rgba(170, 205, 220, 0.2), + 0 0 20px -5px rgba(170, 205, 220, 0.15); + } + + /* 3D shapes */ + .dark .shape-sphere { + background: linear-gradient(135deg, rgba(170, 205, 220, 0.18), rgba(129, 166, 198, 0.1)); + box-shadow: inset -8px -8px 20px rgba(170, 205, 220, 0.09), + inset 4px 4px 10px rgba(255, 255, 255, 0.04), + 0 20px 40px -10px rgba(170, 205, 220, 0.18); + } + .dark .shape-cube { + background: linear-gradient(135deg, rgba(170, 205, 220, 0.1), rgba(129, 166, 198, 0.08)); + border-color: rgba(170, 205, 220, 0.15); + box-shadow: inset -4px -4px 12px rgba(170, 205, 220, 0.07), + inset 2px 2px 6px rgba(255, 255, 255, 0.03), + 0 15px 30px -8px rgba(170, 205, 220, 0.14); + } + .dark .shape-torus { + border-color: rgba(170, 205, 220, 0.15); + box-shadow: inset 0 0 20px rgba(170, 205, 220, 0.09), + 0 10px 25px -8px rgba(170, 205, 220, 0.14); } } @@ -553,6 +711,7 @@ KEYFRAMES ═══════════════════════════════════════════════════════════════════ */ +/* ── Respect user motion preference ─────────────────────────── */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; @@ -568,38 +727,69 @@ @keyframes count-up { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } @keyframes slide-in-right { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: translateX(0); } } @keyframes pulse-ring { 0% { transform: scale(0.33); opacity: 1; } 80%, 100% { transform: scale(2.5); opacity: 0; } } -@keyframes badge-shimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } } -@keyframes border-glow { 0%, 100% { border-color: var(--hs-border-subtle); } 50% { border-color: var(--hs-border-strong); } } - +@keyframes shimmer-slide { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } } +@keyframes border-glow { 0%, 100% { border-color: rgba(129, 166, 198, 0.1); } 50% { border-color: rgba(129, 166, 198, 0.3); } } @keyframes marquee { 0% { transform: translateX(0); } 100% { transform: translateX(-50%); } } -@keyframes marquee-reverse { 0% { transform: translateX(-50%); } 100% { transform: translateX(0); } } - @keyframes grid-fade { 0%, 100% { opacity: 0.3; } 50% { opacity: 0.5; } } -@keyframes reveal-up { 0% { opacity: 0; transform: translateY(40px) skewY(1deg); } 100% { opacity: 1; transform: translateY(0) skewY(0deg); } } -@keyframes reveal-left { 0% { opacity: 0; transform: translateX(-40px); } 100% { opacity: 1; transform: translateX(0); } } -@keyframes reveal-right { 0% { opacity: 0; transform: translateX(40px); } 100% { opacity: 1; transform: translateX(0); } } -@keyframes clip-reveal { 0% { clip-path: inset(0 100% 0 0); } 100% { clip-path: inset(0 0% 0 0); } } -@keyframes line-grow { 0% { transform: scaleX(0); transform-origin: left; } 100% { transform: scaleX(1); transform-origin: left; } } -@keyframes counter-blur-in { 0% { opacity: 0; filter: blur(8px); transform: scale(0.92); } 100% { opacity: 1; filter: blur(0px); transform: scale(1); } } -@keyframes float-slow { 0%, 100% { transform: translateY(0px) rotate(0deg); } 33% { transform: translateY(-10px) rotate(0.5deg); } 66% { transform: translateY(5px) rotate(-0.5deg); } } -@keyframes gradient-pan { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } } -@keyframes shimmer-slide { 0% { transform: translateX(-200%); } 100% { transform: translateX(200%); } } -@keyframes scan-line { 0% { top: 0; opacity: 0.6; } 100% { top: 100%; opacity: 0; } } -@keyframes particle-flow { 0% { transform: translateX(0) translateY(0); opacity: 0; } 10% { opacity: 1; } 90% { opacity: 1; } 100% { transform: translateX(200px) translateY(-10px); opacity: 0; } } -@keyframes fadeSlideIn { from { opacity: 0; transform: translateX(-8px); } to { opacity: 1; transform: none; } } -@keyframes slideInLeft { from { opacity: 0; transform: translateX(-10px); } to { opacity: 1; transform: translateX(0); } } -@keyframes stagger-fade-in-up { 0% { opacity: 0; transform: translateY(20px); } 100% { opacity: 1; transform: translateY(0); } } -@keyframes particle-float-0 { 0%, 100% { transform: translateY(0px) translateX(0px); opacity: 0.15; } 50% { transform: translateY(-18px) translateX(6px); opacity: 0.35; } } -@keyframes particle-float-1 { 0%, 100% { transform: translateY(0px) translateX(0px); opacity: 0.10; } 50% { transform: translateY(-12px) translateX(-8px); opacity: 0.28; } } -@keyframes particle-float-2 { 0%, 100% { transform: translateY(0px) translateX(0px); opacity: 0.08; } 33% { transform: translateY(-22px) translateX(4px); opacity: 0.20; } 66% { transform: translateY(-8px) translateX(-6px); opacity: 0.15; } } -@keyframes particle-float-3 { 0%, 100% { transform: translateY(0px) translateX(0px); opacity: 0.12; } 50% { transform: translateY(-15px) translateX(10px); opacity: 0.30; } } +@keyframes badge-shimmer { + 0% { background-position: -200% 0; } + 100% { background-position: 200% 0; } +} +.badge-shimmer { + background: linear-gradient( + 90deg, + rgba(129, 166, 198, 0) 0%, + rgba(129, 166, 198, 0.05) 40%, + rgba(170, 205, 220, 0.1) 50%, + rgba(129, 166, 198, 0.05) 60%, + rgba(129, 166, 198, 0) 100% + ); + background-size: 200% 100%; + animation: badge-shimmer 4s ease-in-out infinite; +} + +@keyframes particle-flow { + 0% { transform: translateX(0) translateY(0); opacity: 0; } + 10% { opacity: 1; } + 90% { opacity: 1; } + 100% { transform: translateX(200px) translateY(-10px); opacity: 0; } +} -/* ── Utility classes ───────────────────────────────────────────── */ .animate-marquee { animation: marquee 30s linear infinite; } -.animate-marquee-reverse { animation: marquee-reverse 30s linear infinite; } .animate-grid-fade { animation: grid-fade 8s ease-in-out infinite; } -.animate-gradient { background-size: 200% 200%; animation: gradient-pan 6s ease infinite; } + +/* Landing page CSS particle animations */ +@keyframes particle-float-0 { + 0%, 100% { transform: translateY(0px) translateX(0px); opacity: 0.15; } + 50% { transform: translateY(-18px) translateX(6px); opacity: 0.35; } +} +@keyframes particle-float-1 { + 0%, 100% { transform: translateY(0px) translateX(0px); opacity: 0.1; } + 50% { transform: translateY(-12px) translateX(-8px); opacity: 0.28; } +} +@keyframes particle-float-2 { + 0%, 100% { transform: translateY(0px) translateX(0px); opacity: 0.08; } + 33% { transform: translateY(-22px) translateX(4px); opacity: 0.2; } + 66% { transform: translateY(-8px) translateX(-6px); opacity: 0.15; } +} +@keyframes particle-float-3 { + 0%, 100% { transform: translateY(0px) translateX(0px); opacity: 0.12; } + 50% { transform: translateY(-15px) translateX(10px); opacity: 0.3; } +} + +/* Pipeline log entry animation */ +@keyframes fadeSlideIn { + from { opacity: 0; transform: translateX(-8px); } + to { opacity: 1; transform: none; } +} + +/* Dashboard event slide-in */ +@keyframes slideInLeft { + from { opacity: 0; transform: translateX(-10px); } + to { opacity: 1; transform: translateX(0); } +} +.animate-gradient { background-size: 200% 200%; animation: gradient-shift 6s ease infinite; } .stagger-children > * { opacity: 0; animation: stagger-fade-in-up 0.6s ease-out forwards; } .stagger-children > *:nth-child(1) { animation-delay: 0ms; } @@ -609,20 +799,209 @@ .stagger-children > *:nth-child(5) { animation-delay: 400ms; } .stagger-children > *:nth-child(6) { animation-delay: 500ms; } -/* ── Text reveal mask ──────────────────────────────────────────── */ -.text-reveal-mask { overflow: hidden; display: inline-block; } -.text-reveal-inner { display: inline-block; transform: translateY(110%); transition: transform 0.75s var(--ease-out); } -.text-reveal-inner.revealed { transform: translateY(0); } +@keyframes stagger-fade-in-up { 0% { opacity: 0; transform: translateY(20px); } 100% { opacity: 1; transform: translateY(0); } } + +.chat-messages::-webkit-scrollbar { width: 4px; } +.chat-messages::-webkit-scrollbar-thumb { background: rgba(129, 166, 198, 0.2); border-radius: 2px; } + +@media (max-width: 640px) { + .chat-window { right: 0.75rem; left: 0.75rem; width: auto; bottom: 5rem; max-height: 70vh; } +} + +/* ═══════════════════════════════════════════════════════════════════ + LIGHT MODE OVERRIDES + Targets dark-first hardcoded Tailwind classes via attribute selectors. + html.dark = dark mode (default). No .dark class = light mode. + ═══════════════════════════════════════════════════════════════════ */ + +html:not(.dark) body { + background-color: #FAFCFF !important; + color: #0f172a !important; + transition: background-color 0.3s ease, color 0.3s ease; +} + +/* Override hardcoded dark section/div backgrounds */ +html:not(.dark) [class*="[#0a0a0a]"], +html:not(.dark) [class*="[#0a0a10]"], +html:not(.dark) [class*="[#0a0a12]"], +html:not(.dark) [class*="[#07070b]"], +html:not(.dark) [class*="[#0e0e18]"], +html:not(.dark) [class*="[#0a0a0a]/"] { + background-color: #FAFCFF !important; +} + +/* Section wrappers */ +html:not(.dark) section { + background-color: #FAFCFF !important; +} -/* ── Scroll pin ────────────────────────────────────────────────── */ -.scroll-pin-container { position: sticky; top: 0; height: 100vh; overflow: hidden; } -.horizontal-track { display: flex; flex-direction: row; will-change: transform; } +/* Alternating cream surfaces for visual rhythm */ +html:not(.dark) section:nth-of-type(even) { + background-color: #F3E3D0 !important; +} + +/* White / near-white text → dark in light mode */ +html:not(.dark) [class*="text-white"] { + color: #0f172a !important; +} +html:not(.dark) [class*="text-slate-100"], +html:not(.dark) [class*="text-slate-200"], +html:not(.dark) [class*="text-slate-300"] { + color: #334155 !important; +} +html:not(.dark) [class*="text-slate-400"], +html:not(.dark) [class*="text-slate-500"] { + color: #475569 !important; +} + +/* Frosted navbar → light glass */ +html:not(.dark) .nav-frosted { + background: rgba(251, 248, 242, 0.92) !important; + border-bottom-color: rgba(82, 122, 159, 0.12) !important; +} +html:not(.dark) [class*="bg-\\[#0a0a0a\\]\\/85"], +html:not(.dark) [class*="/85"] [class*="backdrop-blur"] { + background-color: rgba(251, 248, 242, 0.92) !important; +} + +/* Border colors: white/5, white/8, white/10 → slate/20 */ +html:not(.dark) [class*="border-white/"] { + border-color: rgba(100, 116, 139, 0.2) !important; +} -/* ── Line clamp ────────────────────────────────────────────────── */ +/* Dark card backgrounds */ +html:not(.dark) [class*="bg-black"], +html:not(.dark) [class*="bg-black/"] { + background-color: rgba(15, 23, 42, 0.06) !important; +} +html:not(.dark) [class*="bg-white/"][class*="0.02"], +html:not(.dark) [class*="bg-white/0"] { + background-color: rgba(255, 255, 255, 0.7) !important; +} + +/* Dark-on-dark text (white/30, white/50 etc.) → readable */ +html:not(.dark) [class*="text-white/20"], +html:not(.dark) [class*="text-white/25"], +html:not(.dark) [class*="text-white/30"] { + color: rgba(71, 85, 105, 0.7) !important; +} +html:not(.dark) [class*="text-white/40"], +html:not(.dark) [class*="text-white/50"], +html:not(.dark) [class*="text-white/60"] { + color: rgba(71, 85, 105, 0.85) !important; +} + +/* ═══════════════════════════════════════════════════════════════════ + MARQUEE KEYFRAMES — defined here as a bulletproof fallback + (Tailwind JIT may not emit @keyframes on cold starts) + ═══════════════════════════════════════════════════════════════════ */ +@keyframes marquee { + 0% { transform: translateX(0); } + 100% { transform: translateX(-50%); } +} + +@keyframes marquee-reverse { + 0% { transform: translateX(-50%); } + 100% { transform: translateX(0); } +} + +/* ═══════════════════════════════════════════════════════════════════ + SCROLL-DRIVEN REVEAL ANIMATIONS + ═══════════════════════════════════════════════════════════════════ */ +@keyframes reveal-up { + 0% { opacity: 0; transform: translateY(40px) skewY(1deg); } + 100% { opacity: 1; transform: translateY(0) skewY(0deg); } +} + +@keyframes reveal-left { + 0% { opacity: 0; transform: translateX(-40px); } + 100% { opacity: 1; transform: translateX(0); } +} + +@keyframes reveal-right { + 0% { opacity: 0; transform: translateX(40px); } + 100% { opacity: 1; transform: translateX(0); } +} + +@keyframes clip-reveal { + 0% { clip-path: inset(0 100% 0 0); } + 100% { clip-path: inset(0 0% 0 0); } +} + +@keyframes line-grow { + 0% { transform: scaleX(0); transform-origin: left; } + 100% { transform: scaleX(1); transform-origin: left; } +} + +@keyframes counter-blur-in { + 0% { opacity: 0; filter: blur(8px); transform: scale(0.92); } + 100% { opacity: 1; filter: blur(0px); transform: scale(1); } +} + +@keyframes float-slow { + 0%, 100% { transform: translateY(0px) rotate(0deg); } + 33% { transform: translateY(-10px) rotate(0.5deg); } + 66% { transform: translateY(5px) rotate(-0.5deg); } +} + +@keyframes gradient-pan { + 0% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } + 100% { background-position: 0% 50%; } +} + +@keyframes shimmer-slide { + 0% { transform: translateX(-200%); } + 100% { transform: translateX(200%); } +} + +@keyframes scan-line { + 0% { top: 0; opacity: 0.6; } + 100% { top: 100%; opacity: 0; } +} + +@keyframes status-pulse { + 0%, 100% { opacity: 1; transform: scale(1); } + 50% { opacity: 0.5; transform: scale(1.5); } +} + +/* ═══════════════════════════════════════════════════════════════════ + SCROLL SECTION PINNING UTILITIES + ═══════════════════════════════════════════════════════════════════ */ +.scroll-pin-container { + position: sticky; + top: 0; + height: 100vh; + overflow: hidden; +} + +.horizontal-track { + display: flex; + flex-direction: row; + will-change: transform; +} + +/* Text reveal mask pattern (Terminal Industries style) */ +.text-reveal-mask { + overflow: hidden; + display: inline-block; +} + +.text-reveal-inner { + display: inline-block; + transform: translateY(110%); + transition: transform 0.75s cubic-bezier(0.16, 1, 0.3, 1); +} + +.text-reveal-inner.revealed { + transform: translateY(0); +} + +/* Line clamp utilities */ .line-clamp-2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .line-clamp-3 { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; } -/* ── Noise overlay ─────────────────────────────────────────────── */ +/* Noise texture overlay */ .noise-overlay::after { content: ''; position: absolute; @@ -636,63 +1015,17 @@ mix-blend-mode: overlay; } -/* ── Chat mobile ────────────────────────────────────────────────── */ -.chat-messages::-webkit-scrollbar { width: 4px; } -.chat-messages::-webkit-scrollbar-thumb { background: var(--hs-border); border-radius: 2px; } -@media (max-width: 640px) { - .chat-window { right: 0.75rem; left: 0.75rem; width: auto; bottom: 5rem; max-height: 70vh; } -} - -/* ── Logo rendering ─────────────────────────────────────────────── */ -.logo-img { mix-blend-mode: multiply; } -.logo-on-dark { filter: brightness(0) invert(1); mix-blend-mode: normal; } - -/* ── Feature card hover-expand ──────────────────────────────────── */ -.feature-card { - background: white; - border: 1px solid var(--hs-border); - border-radius: var(--radius-lg); - padding: 1.5rem; - cursor: default; - transition: border-color 250ms var(--ease-out), - box-shadow 250ms var(--ease-out), - transform 250ms var(--ease-out); -} -.feature-card:hover { - border-color: var(--hs-border-strong); - box-shadow: var(--shadow-lg); - transform: translateY(-2px); -} -@media (max-width: 768px) { +/* Gradient animated border */ +.animated-border { + position: relative; + background: linear-gradient(var(--bg, #0a0a0a), var(--bg, #0a0a0a)) padding-box, + linear-gradient(135deg, rgba(170, 205, 220,0.4), rgba(129, 166, 198,0.2), rgba(170, 205, 220,0.1)) border-box; + border: 1px solid transparent; } -/* ── Decorative background orbs (pricing page) ──────────────────── */ -.orb { - position: absolute; - border-radius: 50%; - filter: blur(100px); - pointer-events: none; - z-index: 0; -} -.orb-1 { - width: 700px; - height: 700px; - background: radial-gradient(ellipse, rgba(218, 119, 86, 0.07), transparent 70%); - top: -200px; - left: -150px; -} -.orb-2 { - width: 600px; - height: 600px; - background: radial-gradient(ellipse, rgba(88, 28, 135, 0.05), transparent 70%); - top: 30%; - right: -200px; -} -.orb-3 { - width: 500px; - height: 500px; - background: radial-gradient(ellipse, rgba(218, 119, 86, 0.04), transparent 70%); - bottom: 15%; - left: 50%; - transform: translateX(-50%); +/* Cinematic section divider */ +.section-divider { + width: 100%; + height: 1px; + background: linear-gradient(to right, transparent, rgba(170, 205, 220,0.15), rgba(129, 166, 198,0.1), transparent); } diff --git a/compliance-firewall-agent/app/page.tsx b/compliance-firewall-agent/app/page.tsx index 317dbccd..48f864d6 100644 --- a/compliance-firewall-agent/app/page.tsx +++ b/compliance-firewall-agent/app/page.tsx @@ -1,436 +1,5 @@ -import Link from 'next/link' -import { ArrowRight, CheckCircle } from 'lucide-react' -import { NavV3 } from '@/components/layout/NavV3' -import { FooterV3 } from '@/components/layout/FooterV3' -import { CountdownTimer } from '@/components/ui/CountdownTimer' -import { PlatformDashboardClient } from '@/components/landing/PlatformDashboardClient' -import { ComparisonFlow } from '@/components/ui/ComparisonFlow' -import { FaqAccordion, type FaqItem } from '@/components/ui/FaqAccordion' -import { CodeBlock } from '@/components/ui/CodeBlock' -import { FeaturesGrid } from '@/components/landing/FeaturesGrid' -import { DeploymentModes } from '@/components/landing/DeploymentModes' -import { ScannerDemo } from '@/components/landing/ScannerDemo' - -/* ─── Static data ──────────────────────────────────────────────── */ - -const STATS = [ - { value: '16', label: 'Detection engines', sub: 'CUI · PHI · PII · IP' }, - { value: '~80,000', label: 'contractors at risk', sub: 'Need CMMC Level 2' }, - { value: 'Nov 2026', label: 'CMMC deadline', sub: 'For DoD prime contractors' }, - { value: '<10ms', label: 'Scan latency', sub: 'Median, fully local' }, -] - -const STEPS = [ - { - n: '01', - title: 'Change one URL', - body: 'Replace api.openai.com with your HoundShield endpoint. No agents, no code changes, no firewall rules.', - code: 'OPENAI_BASE_URL=https://proxy.houndshield.com', - }, - { - n: '02', - title: 'Every prompt scanned locally', - body: '16 detection engines check each request in under 10ms on your infrastructure. The AI never knows.', - }, - { - n: '03', - title: 'You get a compliance record', - body: 'Tamper-proof SHA-256 signed logs. Exportable PDF evidence for your C3PAO or auditor on demand.', - }, -] - -/* Lead product per HERMES doctrine: $499 one-time CMMC AI Risk Report. - * Subscription tiers ($299/$799/$1,499) deferred to Stage 2 (July 2026) — see /pricing. */ -const LEAD_PRODUCT = { - name: 'CMMC AI Risk Assessment Report', - price: 499, - priceSuffix: 'one-time', - desc: '14-day proxy deployment in your environment (Mode B Docker). SHA-256-signed PDF mapped to all 110 NIST 800-171 Rev 2 controls. Bypasses procurement.', - features: [ - 'Mode B (Docker) deployment in customer infrastructure', - 'Prompt content never leaves your network boundary', - '14-day live observation of all AI prompt traffic', - 'NIST 800-171 Rev 2 control mapping + SPRS delta estimate', - 'SHA-256 hash-chained audit log, verifiable offline', - 'C3PAO hand-off ready — no editing required', - ], - cta: 'Order the $499 Gap Report', - href: '/contact?intent=gap-report', -} - -const FAQ_ITEMS: FaqItem[] = [ - { - question: 'Does prompt content ever leave my network?', - answer: 'No. HoundShield runs entirely on your infrastructure. The scanning engine, detection patterns, and audit logs all stay local. Only a license key hash and prompt count (no content) go external for billing.', - }, - { - question: 'How long does setup take?', - answer: 'Under 10 minutes for most organizations. Change one environment variable to point AI tools at your HoundShield endpoint. Docker deployment is 3 commands. No agents, no firewall rules, no code changes.', - }, - { - question: 'Which AI tools does HoundShield support?', - answer: "Any tool using an OpenAI-compatible API: ChatGPT, Copilot, Claude, Gemini, Cursor, Codeium, and open-source models. It operates at the network layer, so it's model-agnostic.", - }, - { - question: 'Is HoundShield CMMC Level 2 compliant?', - answer: "HoundShield maps all 110 NIST 800-171 Rev 2 controls and generates C3PAO-ready PDF evidence. Because it's local-only, CUI never crosses your control boundary — satisfying NIST 3.13.1 and supporting CMMC Level 2 certification.", - }, - { - question: 'What happens when a violation is detected?', - answer: 'The prompt is blocked and the user receives a policy violation message. A tamper-proof, SHA-256 signed log entry is created with the timestamp, matched pattern, and affected framework. Webhook notifications are available on Pro and above.', - }, -] - -/* ─── Page ─────────────────────────────────────────────────────── */ +import { HomeV3 } from "@/components/landing/v3/HomeV3"; export default function HomePage() { - return ( -
- - - {/* ── 1. HERO ─────────────────────────────────────────────── */} -
-
-
- -
-
-
- - - - - CMMC Level 2 deadline: November 2026 -
-
- -
-
-

- Stop your team from leaking CUI to{' '} - ChatGPT. -

- -

- HoundShield intercepts every AI prompt before it leaves your network. 16 detection engines. Sub-10ms latency. CMMC Level 2, HIPAA, and SOC 2 — enforced simultaneously. -

- -
- - Start free — no card required - - - - See how it works - -
- -
- {['One URL change', 'Local-only', 'Free to start', 'C3PAO-ready'].map((t) => ( - - - {t} - - ))} -
-
- -
- -
-
-
-
- - {/* ── Deployment modes ─────────────────────────────────────── */} - - - {/* ── Live scanner demo ─────────────────────────────────────── */} - - - {/* ── 2. STATS STRIP ──────────────────────────────────────── */} -
-
-
- {STATS.map((s) => ( -
-
- {s.value} -
-
- {s.label} -
-
{s.sub}
-
- ))} -
-
-
- - {/* ── 3. ASYMMETRIC ADVANTAGE ─────────────────────────────── */} -
-
-
-
-

- THE ASYMMETRIC ADVANTAGE -

-

- Every other tool makes -
- the problem worse. -

-

- Nightfall, Strac, and Microsoft Purview all send your CUI to their cloud to scan it. - That's itself a DFARS 7012 spill. HoundShield scans locally. Nothing leaves Jordan's network. -

-
- -
-
- - {/* ── 4. FEATURES GRID ────────────────────────────────────── */} - - - {/* ── 5. HOW IT WORKS ─────────────────────────────────────── */} -
-
-
-

- Live in ten minutes. -
- Audited in ten seconds. -

-

- No agents. No code changes. One environment variable. -

-
- -
- {STEPS.map((step) => ( -
-
- {step.n} -
-

- {step.title} -

-

- {step.body} -

- {step.code && } -
- ))} -
-
-
- - {/* ── 6. JORDAN SECTION ───────────────────────────────────── */} -
-
-

- BUILT FOR JORDAN -

- -
- {/* Pull quote — 3/5 */} -
-
-

- “I needed the PDF I could hand my C3PAO assessor. Not another dashboard that tells me I have problems.” -

-
-
Jordan M.
-
IT Security Manager · 180-person DoD subcontractor
-
-
-
- - {/* Buyer profile card — 2/5 */} -
-
-
- Buyer Profile -
- {[ - { label: 'Role', value: 'IT Security Manager' }, - { label: 'Company', value: '180-person DoD subcontractor' }, - { label: 'Fear', value: 'CUI spill triggering DFARS audit' }, - { label: 'Goal', value: 'CMMC Level 2 certification by Nov 2026' }, - { label: 'Budget', value: '$500–$1,500/mo' }, - { label: 'Deadline', value: 'November 10, 2026' }, - ].map(({ label, value }) => ( -
- {label} - {value} -
- ))} -
-
-
-
-
- - {/* ── 7. PRICING — HERMES Stage 1 lead product ──────────── */} -
-
-
-

- Stage 1 — Lead Product -

-

- $499. One-time. C3PAO-ready PDF in two weeks. -

-

- A $499 PO bypasses procurement. Subscriptions don't. Lead with the report, - graduate to monitoring in Stage 2 (July 2026). -

-
- -
-
- - ${LEAD_PRODUCT.price} - - - {LEAD_PRODUCT.priceSuffix} - -
-

- {LEAD_PRODUCT.name} -

-

- {LEAD_PRODUCT.desc} -

-
    - {LEAD_PRODUCT.features.map((f) => ( -
  • - - {f} -
  • - ))} -
-
- - {LEAD_PRODUCT.cta} - - - See Stage 2 subscription tiers - -
-
- -

- 14-day deployment · Mode B (Docker, customer infrastructure) · 30-day money-back ·{' '} - - full data-path statement - -

-
-
- - {/* ── 8. FAQ ──────────────────────────────────────────────── */} -
-
-

- Frequently asked -

- -
-
- - {/* ── 9. FINAL CTA ────────────────────────────────────────── */} -
-
-
- -
-
- CMMC Level 2 deadline -
- -
- -
- -

- The audit doesn't care if you were busy. -

-

- One URL change. 10 minutes to full AI compliance coverage. Your assessor will ask what changed — the answer is everything. -

- -
- - Start free — deploy in 10 min - - - - Talk to a compliance expert - -
-
-
- - -
- ) + return ; } diff --git a/compliance-firewall-agent/app/partner/clients/[orgId]/page.tsx b/compliance-firewall-agent/app/partner/clients/[orgId]/page.tsx index e7ebb0a7..a43b9d42 100644 --- a/compliance-firewall-agent/app/partner/clients/[orgId]/page.tsx +++ b/compliance-firewall-agent/app/partner/clients/[orgId]/page.tsx @@ -109,7 +109,7 @@ export default function ClientDetailPage() { const riskColor: Record = { CRITICAL: "text-rose-400", - HIGH: "text-orange-400", + HIGH: "text-brand-400", MEDIUM: "text-brand-400", LOW: "text-blue-400", NONE: "text-slate-400", diff --git a/compliance-firewall-agent/components/CursorGlow.tsx b/compliance-firewall-agent/components/CursorGlow.tsx index 0cd03c26..a5044dbf 100644 --- a/compliance-firewall-agent/components/CursorGlow.tsx +++ b/compliance-firewall-agent/components/CursorGlow.tsx @@ -78,7 +78,7 @@ export function CursorGlow() { height: 700, opacity: glowOpacity, background: - "radial-gradient(circle, rgba(129,166,198,1) 0%, rgba(129,166,198,0.4) 30%, transparent 70%)", + "radial-gradient(circle, rgba(129, 166, 198,1) 0%, rgba(129, 166, 198,0.4) 30%, transparent 70%)", }} /> diff --git a/compliance-firewall-agent/components/GlobalChat.tsx b/compliance-firewall-agent/components/GlobalChat.tsx index c69c32d4..de3cb977 100644 --- a/compliance-firewall-agent/components/GlobalChat.tsx +++ b/compliance-firewall-agent/components/GlobalChat.tsx @@ -395,7 +395,7 @@ export function GlobalChat() { key={i} className={`max-w-[92%] px-3.5 py-2.5 rounded-2xl text-[13px] leading-relaxed ${ msg.role === "user" - ? "self-end bg-indigo-500 text-white rounded-br-sm whitespace-pre-wrap" + ? "self-end bg-brand-500 text-white rounded-br-sm whitespace-pre-wrap" : "self-start bg-white/[0.05] text-white/85 border border-white/[0.07] rounded-bl-sm prose-chat" }`} > @@ -452,7 +452,7 @@ export function GlobalChat() { key={action} onClick={() => sendMessage(action)} disabled={isTyping} - className="text-[11px] px-2.5 py-1.5 rounded-full cursor-pointer transition-all text-indigo-300 hover:bg-indigo-500/25 disabled:opacity-40 disabled:pointer-events-none" + className="text-[11px] px-2.5 py-1.5 rounded-full cursor-pointer transition-all text-brand-300 hover:bg-brand-500/25 disabled:opacity-40 disabled:pointer-events-none" style={{ background: "rgba(99,102,241,0.1)", border: "1px solid rgba(99,102,241,0.25)", @@ -485,12 +485,12 @@ export function GlobalChat() { value={input} onChange={(e) => setInput(e.target.value)} placeholder="Ask Brain AI anything..." - className="flex-1 bg-black/40 border border-white/[0.08] rounded-[10px] px-3 py-2.5 text-[13px] text-white placeholder:text-white/20 outline-none focus:border-indigo-500/40 transition-colors" + className="flex-1 bg-black/40 border border-white/[0.08] rounded-[10px] px-3 py-2.5 text-[13px] text-white placeholder:text-white/20 outline-none focus:border-brand-500/40 transition-colors" /> diff --git a/compliance-firewall-agent/components/Logo.tsx b/compliance-firewall-agent/components/Logo.tsx index 79e011fd..42ff92c5 100644 --- a/compliance-firewall-agent/components/Logo.tsx +++ b/compliance-firewall-agent/components/Logo.tsx @@ -1,19 +1,79 @@ -export function Logo({ className = "", size = 38, variant }: { className?: string; size?: number; variant?: "light" | "dark" }) { - const colorClass = variant === "dark" ? "text-white" : "text-slate-900 dark:text-white"; +import Image from "next/image"; + +interface LogoProps { + className?: string; + size?: number; + /** + * When true (default), wraps the mark in a soft cream card — + * matches the v3 reference embed style. + */ + card?: boolean; + /** + * Surface variant. `"light"` (default) renders the cream card unchanged. + * `"dark"` swaps the card to a translucent slate surface so the mark + * still reads on dark navbars/footers. Accepted for back-compat with + * callers that pass `variant` down from the parent Navbar. + */ + variant?: "light" | "dark"; +} + +/** + * Brand mark: doberman + shield image inside a soft rounded card. + * + * Reference: HoundShield Landing v3 (`Logo({ size, dark })`) which used + * `borderRadius: size * 0.12`, a light backing surface, and `objectFit: contain`. + * We translate the JSX-style inline embed into a Next.js `` so the + * file stays statically optimized. + */ +export function Logo({ + className = "", + size = 32, + card = true, + variant = "light", +}: LogoProps) { + const radius = Math.round(size * 0.18); + const inner = Math.round(size * 0.78); + const pad = Math.round((size - inner) / 2); + + if (!card) { + return ( + HoundShield + ); + } + + const cardSurface = + variant === "dark" + ? "bg-white/10 ring-1 ring-white/15 shadow-card" + : "bg-cream-100 ring-1 ring-cream-300/60 shadow-card"; + return ( - + ); } diff --git a/compliance-firewall-agent/components/TextLogo.tsx b/compliance-firewall-agent/components/TextLogo.tsx index 4a62af02..8b7c3cfd 100644 --- a/compliance-firewall-agent/components/TextLogo.tsx +++ b/compliance-firewall-agent/components/TextLogo.tsx @@ -1,10 +1,34 @@ import React from "react"; -export function TextLogo({ className = "", variant = "light" }: { className?: string; variant?: "light" | "dark" }) { +interface TextLogoProps { + className?: string; + /** + * `dark` = renders on a dark surface (white wordmark). + * `light` = renders on a light/cream surface (ink wordmark). + * Both refer to the *surface* the logo sits on (matches the v3 reference, + * which exposed a `dark` boolean for the surface). + */ + variant?: "light" | "dark"; +} + +/** + * Wordmark: single-word "HoundShield" set in Fraunces serif. + * + * Mirrors the v3 reference where the nav rendered: + * fontFamily: 'Fraunces, Georgia, serif' + * fontWeight: 400 + * fontSize: 17 + * letterSpacing: -0.015em + * We keep that look but route through Tailwind so theming/responsive sizing stays consistent. + */ +export function TextLogo({ className = "", variant = "light" }: TextLogoProps) { + const color = variant === "dark" ? "text-white" : "text-ink-primary"; return ( - - Hound - Shield + + HoundShield ); } diff --git a/compliance-firewall-agent/components/__tests__/Logo.test.tsx b/compliance-firewall-agent/components/__tests__/Logo.test.tsx index ea0db9b5..dfb0b8a3 100644 --- a/compliance-firewall-agent/components/__tests__/Logo.test.tsx +++ b/compliance-firewall-agent/components/__tests__/Logo.test.tsx @@ -8,13 +8,18 @@ describe("Logo", () => { expect(container.firstChild).toBeTruthy(); }); - it("matches snapshot", () => { + it("matches snapshot (card variant)", () => { const { container } = render(); expect(container.firstChild).toMatchSnapshot(); }); + it("renders bare image when card=false", () => { + const { container } = render(); + expect((container.firstChild as HTMLElement).tagName).toBe("IMG"); + }); + it("applies custom className", () => { const { container } = render(); - expect(container.firstChild).toHaveClass("custom-class"); + expect((container.firstChild as HTMLElement).className).toContain("custom-class"); }); }); diff --git a/compliance-firewall-agent/components/__tests__/TextLogo.test.tsx b/compliance-firewall-agent/components/__tests__/TextLogo.test.tsx index e6abe437..573cfc02 100644 --- a/compliance-firewall-agent/components/__tests__/TextLogo.test.tsx +++ b/compliance-firewall-agent/components/__tests__/TextLogo.test.tsx @@ -3,23 +3,23 @@ import { describe, it, expect } from "vitest"; import { TextLogo } from "../TextLogo"; describe("TextLogo", () => { - it("renders 'Hound' text", () => { + it("renders 'HoundShield' wordmark", () => { render(); - expect(screen.getByText(/Hound/i)).toBeTruthy(); + expect(screen.getByText("HoundShield")).toBeTruthy(); }); - it("renders 'Shield' text", () => { - render(); - expect(screen.getByText(/Shield/i)).toBeTruthy(); - }); - - it("applies dark variant correctly", () => { + it("applies dark variant (white text on dark surfaces)", () => { const { container } = render(); expect(container.firstChild).toMatchSnapshot(); }); - it("applies light variant correctly", () => { + it("applies light variant (ink text on light surfaces)", () => { const { container } = render(); expect(container.firstChild).toMatchSnapshot(); }); + + it("merges custom className", () => { + const { container } = render(); + expect((container.firstChild as HTMLElement).className).toContain("text-3xl"); + }); }); diff --git a/compliance-firewall-agent/components/__tests__/__snapshots__/Logo.test.tsx.snap b/compliance-firewall-agent/components/__tests__/__snapshots__/Logo.test.tsx.snap index 51e4ebe0..0b370a18 100644 --- a/compliance-firewall-agent/components/__tests__/__snapshots__/Logo.test.tsx.snap +++ b/compliance-firewall-agent/components/__tests__/__snapshots__/Logo.test.tsx.snap @@ -1,18 +1,21 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Logo > matches snapshot 1`] = ` - - - + `; diff --git a/compliance-firewall-agent/components/__tests__/__snapshots__/TextLogo.test.tsx.snap b/compliance-firewall-agent/components/__tests__/__snapshots__/TextLogo.test.tsx.snap index 2a089e09..79a20087 100644 --- a/compliance-firewall-agent/components/__tests__/__snapshots__/TextLogo.test.tsx.snap +++ b/compliance-firewall-agent/components/__tests__/__snapshots__/TextLogo.test.tsx.snap @@ -1,35 +1,19 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`TextLogo > applies dark variant correctly 1`] = ` +exports[`TextLogo > applies dark variant (white text on dark surfaces) 1`] = ` - - Hound - - - Shield - + HoundShield `; -exports[`TextLogo > applies light variant correctly 1`] = ` +exports[`TextLogo > applies light variant (ink text on light surfaces) 1`] = ` - - Hound - - - Shield - + HoundShield `; diff --git a/compliance-firewall-agent/components/dashboard/ControlCard.tsx b/compliance-firewall-agent/components/dashboard/ControlCard.tsx index 2eb45dca..73089c86 100644 --- a/compliance-firewall-agent/components/dashboard/ControlCard.tsx +++ b/compliance-firewall-agent/components/dashboard/ControlCard.tsx @@ -210,7 +210,7 @@ export default function ControlCard({ {control.riskPriority} priority diff --git a/compliance-firewall-agent/components/dashboard/FamilySidebar.tsx b/compliance-firewall-agent/components/dashboard/FamilySidebar.tsx index 78fd488e..6236ed3b 100644 --- a/compliance-firewall-agent/components/dashboard/FamilySidebar.tsx +++ b/compliance-firewall-agent/components/dashboard/FamilySidebar.tsx @@ -86,7 +86,7 @@ export default function FamilySidebar({ {/* Status dots */}
{stats.met > 0 && } - {stats.partial > 0 && } + {stats.partial > 0 && } {stats.unmet > 0 && }
diff --git a/compliance-firewall-agent/components/dashboard/SPRSGauge.tsx b/compliance-firewall-agent/components/dashboard/SPRSGauge.tsx index f91dd035..33676552 100644 --- a/compliance-firewall-agent/components/dashboard/SPRSGauge.tsx +++ b/compliance-firewall-agent/components/dashboard/SPRSGauge.tsx @@ -29,7 +29,7 @@ export default function SPRSGauge({ if (score >= 50) return { stroke: "#2563eb", text: "text-brand-500", bg: "bg-brand-500/10", ring: "ring-brand-500/20" }; if (score >= 0) - return { stroke: "#f59e0b", text: "text-amber-600", bg: "bg-amber-500/10", ring: "ring-amber-200" }; + return { stroke: "#f59e0b", text: "text-brand-600", bg: "bg-brand-500/10", ring: "ring-brand-200" }; return { stroke: "#ef4444", text: "text-rose-600", bg: "bg-rose-500/10", ring: "ring-rose-500/20" }; }; diff --git a/compliance-firewall-agent/components/dashboard/agent-builder.tsx b/compliance-firewall-agent/components/dashboard/agent-builder.tsx index 55b2b1c4..f3927e42 100644 --- a/compliance-firewall-agent/components/dashboard/agent-builder.tsx +++ b/compliance-firewall-agent/components/dashboard/agent-builder.tsx @@ -446,10 +446,10 @@ import { OPENCLAW_TEMPLATES } from "./openclaw-templates"; const ALL_TEMPLATES = [...MCKINSEY_TEMPLATES, ...TECHNICAL_TEMPLATES, ...OPENCLAW_TEMPLATES]; const COLOR_MAP: Record = { - brand: { icon: "text-indigo-400", bg: "bg-indigo-500/10", border: "border-indigo-500/20" }, + brand: { icon: "text-brand-400", bg: "bg-brand-500/10", border: "border-brand-500/20" }, info: { icon: "text-cyan-400", bg: "bg-cyan-500/10", border: "border-cyan-500/20" }, success: { icon: "text-emerald-400", bg: "bg-emerald-500/100/10", border: "border-emerald-500/20" }, - warning: { icon: "text-amber-400", bg: "bg-amber-500/100/10", border: "border-amber-500/20" }, + warning: { icon: "text-brand-400", bg: "bg-brand-500/100/10", border: "border-brand-500/20" }, danger: { icon: "text-red-400", bg: "bg-red-500/10", border: "border-red-500/20" }, }; @@ -673,7 +673,7 @@ export function AgentBuilder({ @@ -785,7 +785,7 @@ export function AgentBuilder({
@@ -825,7 +825,7 @@ export function AgentBuilder({ value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} placeholder="e.g. Market Analyzer" - className="w-full bg-zinc-900 border border-white/10 rounded-lg px-3 py-2 text-sm text-white placeholder-white/20 focus:border-indigo-500/50 focus:outline-none transition-all" + className="w-full bg-zinc-900 border border-white/10 rounded-lg px-3 py-2 text-sm text-white placeholder-white/20 focus:border-brand-500/50 focus:outline-none transition-all" />
@@ -835,7 +835,7 @@ export function AgentBuilder({ value={form.description} onChange={(e) => setForm({ ...form, description: e.target.value })} placeholder="What does this agent do?" - className="w-full bg-zinc-900 border border-white/10 rounded-lg px-3 py-2 text-sm text-white placeholder-white/20 focus:border-indigo-500/50 focus:outline-none transition-all" + className="w-full bg-zinc-900 border border-white/10 rounded-lg px-3 py-2 text-sm text-white placeholder-white/20 focus:border-brand-500/50 focus:outline-none transition-all" />
@@ -846,7 +846,7 @@ export function AgentBuilder({ onChange={(e) => setForm({ ...form, systemPrompt: e.target.value })} placeholder="Define the agent's behavior..." rows={6} - className="w-full bg-zinc-900 border border-white/10 rounded-lg px-3 py-2 text-sm text-white placeholder-white/20 focus:border-indigo-500/50 focus:outline-none transition-all resize-none font-mono" + className="w-full bg-zinc-900 border border-white/10 rounded-lg px-3 py-2 text-sm text-white placeholder-white/20 focus:border-brand-500/50 focus:outline-none transition-all resize-none font-mono" />

{form.systemPrompt.length} characters

@@ -860,7 +860,7 @@ export function AgentBuilder({ key={tool.name} onClick={() => toggleFormTool(tool.name)} className={`px-2.5 py-1 rounded-lg text-[11px] transition-colors ${form.tools.includes(tool.name) - ? 'bg-indigo-500/20 text-indigo-300 border border-indigo-500/30' + ? 'bg-brand-500/20 text-brand-300 border border-brand-500/30' : 'bg-white/5 text-zinc-500 border border-white/10 hover:bg-white/10' }`} > @@ -876,7 +876,7 @@ export function AgentBuilder({ @@ -317,7 +317,7 @@ export default function KnowledgeBase() { onChange={(e) => setNewDoc(prev => ({ ...prev, content: e.target.value }))} placeholder="Paste your document content here..." rows={10} - className="w-full px-3 py-2 rounded-lg bg-white/5 border border-white/10 text-sm text-white placeholder:text-zinc-500 focus:outline-none focus:border-indigo-500/50 resize-none font-mono" + className="w-full px-3 py-2 rounded-lg bg-white/5 border border-white/10 text-sm text-white placeholder:text-zinc-500 focus:outline-none focus:border-brand-500/50 resize-none font-mono" />
{newDoc.content.length.toLocaleString()} characters @@ -335,7 +335,7 @@ export default function KnowledgeBase() { diff --git a/compliance-firewall-agent/components/dashboard/memory-view.tsx b/compliance-firewall-agent/components/dashboard/memory-view.tsx index e612d7f4..458b231b 100644 --- a/compliance-firewall-agent/components/dashboard/memory-view.tsx +++ b/compliance-firewall-agent/components/dashboard/memory-view.tsx @@ -89,7 +89,7 @@ const CATEGORY_COLORS: Record = { }; const SEVERITY_CONFIG: Record = { - warning: { bg: 'bg-amber-500/100/10', text: 'text-amber-400', border: 'border-amber-500/20' }, + warning: { bg: 'bg-brand-500/100/10', text: 'text-brand-400', border: 'border-brand-500/20' }, critical: { bg: 'bg-red-500/10', text: 'text-red-400', border: 'border-red-500/20' }, absolute: { bg: 'bg-red-500/20', text: 'text-red-300', border: 'border-red-500/30' }, }; @@ -369,7 +369,7 @@ export default function MemoryView() { {sg.severity.toUpperCase()} {sg.active ? : } {sg.active ? 'Active' : 'Disabled'} @@ -472,7 +472,7 @@ export default function MemoryView() { value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} placeholder="Search memories, lessons, safeguards..." - className="w-full pl-10 pr-4 py-2.5 text-sm bg-[#0c0c10] border border-white/10 rounded-xl text-white placeholder-white/20 focus:outline-none focus:border-indigo-500/50" + className="w-full pl-10 pr-4 py-2.5 text-sm bg-[#0c0c10] border border-white/10 rounded-xl text-white placeholder-white/20 focus:outline-none focus:border-brand-500/50" /> {searchQuery && ( @@ -534,7 +534,7 @@ export default function MemoryView() { onChange={(e) => setMemForm({ ...memForm, content: e.target.value })} placeholder="What should I remember?" rows={2} - className="w-full text-sm bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white placeholder-white/20 focus:outline-none focus:border-indigo-500/50 resize-none" + className="w-full text-sm bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white placeholder-white/20 focus:outline-none focus:border-brand-500/50 resize-none" />
@@ -542,7 +542,7 @@ export default function MemoryView() { setMemForm({ ...memForm, importance: e.target.value as Memory['importance'] })} - className="w-full text-xs bg-[#0c0c10] border border-white/10 rounded-md px-2 py-1.5 text-white focus:outline-none focus:border-indigo-500/50" + className="w-full text-xs bg-[#0c0c10] border border-white/10 rounded-md px-2 py-1.5 text-white focus:outline-none focus:border-brand-500/50" > @@ -569,13 +569,13 @@ export default function MemoryView() { value={memForm.tags} onChange={(e) => setMemForm({ ...memForm, tags: e.target.value })} placeholder="tag1, tag2" - className="w-full text-xs bg-[#0c0c10] border border-white/10 rounded-md px-2 py-1.5 text-white placeholder-white/20 focus:outline-none focus:border-indigo-500/50" + className="w-full text-xs bg-[#0c0c10] border border-white/10 rounded-md px-2 py-1.5 text-white placeholder-white/20 focus:outline-none focus:border-brand-500/50" />
- +
)} @@ -602,7 +602,7 @@ export default function MemoryView() { @@ -616,13 +616,13 @@ export default function MemoryView() { onChange={(e) => setLessonForm({ ...lessonForm, lesson: e.target.value })} placeholder="What was learned?" rows={2} - className="w-full text-sm bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white placeholder-white/20 focus:outline-none focus:border-indigo-500/50 resize-none" + className="w-full text-sm bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white placeholder-white/20 focus:outline-none focus:border-brand-500/50 resize-none" /> setLessonForm({ ...lessonForm, context: e.target.value })} placeholder="In what context?" - className="w-full text-sm bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white placeholder-white/20 focus:outline-none focus:border-indigo-500/50" + className="w-full text-sm bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white placeholder-white/20 focus:outline-none focus:border-brand-500/50" />
@@ -630,7 +630,7 @@ export default function MemoryView() { setLessonForm({ ...lessonForm, importance: e.target.value as Lesson['importance'] })} - className="w-full text-xs bg-[#0c0c10] border border-white/10 rounded-md px-2 py-1.5 text-white focus:outline-none focus:border-indigo-500/50" + className="w-full text-xs bg-[#0c0c10] border border-white/10 rounded-md px-2 py-1.5 text-white focus:outline-none focus:border-brand-500/50" > @@ -654,7 +654,7 @@ export default function MemoryView() {
- +
)} @@ -681,7 +681,7 @@ export default function MemoryView() { @@ -694,20 +694,20 @@ export default function MemoryView() { value={sgForm.rule} onChange={(e) => setSgForm({ ...sgForm, rule: e.target.value })} placeholder="Rule (e.g., Never expose API keys)" - className="w-full text-sm bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white placeholder-white/20 focus:outline-none focus:border-indigo-500/50" + className="w-full text-sm bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white placeholder-white/20 focus:outline-none focus:border-brand-500/50" /> setSgForm({ ...sgForm, reason: e.target.value })} placeholder="Reason" - className="w-full text-sm bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white placeholder-white/20 focus:outline-none focus:border-indigo-500/50" + className="w-full text-sm bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white placeholder-white/20 focus:outline-none focus:border-brand-500/50" />
setPrefForm({ ...prefForm, value: e.target.value })} placeholder="Value (e.g., detailed)" - className="w-full text-sm bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white placeholder-white/20 focus:outline-none focus:border-indigo-500/50" + className="w-full text-sm bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white placeholder-white/20 focus:outline-none focus:border-brand-500/50" />
- +
)} @@ -818,14 +818,14 @@ export default function MemoryView() { onChange={(e) => setImportJson(e.target.value)} placeholder="Paste exported DNA JSON here..." rows={10} - className="w-full text-xs font-mono bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white placeholder-white/20 focus:outline-none focus:border-indigo-500/50 resize-none" + className="w-full text-xs font-mono bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white placeholder-white/20 focus:outline-none focus:border-brand-500/50 resize-none" />
diff --git a/compliance-firewall-agent/components/dashboard/pixel-office/pixel-office.tsx b/compliance-firewall-agent/components/dashboard/pixel-office/pixel-office.tsx index 22d09267..da0406cc 100644 --- a/compliance-firewall-agent/components/dashboard/pixel-office/pixel-office.tsx +++ b/compliance-firewall-agent/components/dashboard/pixel-office/pixel-office.tsx @@ -31,7 +31,7 @@ const STATUS_LABELS: Record = { NONE: "text-zinc-400", LOW: "text-emerald-400", - MEDIUM: "text-amber-400", - HIGH: "text-orange-500", + MEDIUM: "text-brand-400", + HIGH: "text-brand-500", CRITICAL: "text-red-500", }; const RISK_BG: Record = { NONE: "bg-zinc-500/10", LOW: "bg-emerald-500/100/10", - MEDIUM: "bg-amber-500/100/10", - HIGH: "bg-orange-500/10", + MEDIUM: "bg-brand-500/100/10", + HIGH: "bg-brand-500/10", CRITICAL: "bg-red-500/10", }; @@ -70,7 +70,7 @@ const ACTION_ICONS: Record = { const ACTION_COLORS: Record = { ALLOWED: "text-emerald-400", BLOCKED: "text-red-400", - QUARANTINED: "text-amber-400", + QUARANTINED: "text-brand-400", }; const MAX_EVENTS = 200; @@ -180,7 +180,7 @@ export function RealtimeFeed() { onClick={() => setPaused(!paused)} className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium transition-colors ${ paused - ? "bg-amber-500/100/10 text-amber-400 hover:bg-amber-500/100/20" + ? "bg-brand-500/100/10 text-brand-400 hover:bg-brand-500/100/20" : "bg-zinc-800 text-zinc-400 hover:bg-zinc-700" }`} > @@ -222,7 +222,7 @@ export function RealtimeFeed() { value={metrics.blocked_count.toLocaleString()} /> } + icon={} label="Quarantined" value={metrics.quarantined_count.toLocaleString()} /> @@ -366,7 +366,7 @@ function EventRow({ {event.user_id} {event.entities_found > 0 && ( - + · {event.entities_found} entit{event.entities_found === 1 ? "y" : "ies"} )} diff --git a/compliance-firewall-agent/components/dashboard/simulation-graph.tsx b/compliance-firewall-agent/components/dashboard/simulation-graph.tsx index ca3bf0ef..fefb4953 100644 --- a/compliance-firewall-agent/components/dashboard/simulation-graph.tsx +++ b/compliance-firewall-agent/components/dashboard/simulation-graph.tsx @@ -204,14 +204,14 @@ const STATUS_CONFIG: Record< glow: "rgba(245,158,11,0.25)", label: "Thinking", icon: Brain, - textClass: "text-amber-500", + textClass: "text-brand-500", }, interacting: { color: "#818cf8", glow: "rgba(129,140,248,0.3)", label: "Interacting", icon: Zap, - textClass: "text-indigo-400", + textClass: "text-brand-400", }, }; diff --git a/compliance-firewall-agent/components/dashboard/tasks-board.tsx b/compliance-firewall-agent/components/dashboard/tasks-board.tsx index f5fbd4fd..98ae8da6 100644 --- a/compliance-firewall-agent/components/dashboard/tasks-board.tsx +++ b/compliance-firewall-agent/components/dashboard/tasks-board.tsx @@ -50,8 +50,8 @@ const COLUMNS: { key: TaskStatus; label: string; icon: React.ElementType; color: const PRIORITY_DOTS: Record = { low: 'bg-white/40', - medium: 'bg-amber-400', - high: 'bg-orange-500', + medium: 'bg-brand-400', + high: 'bg-brand-500', urgent: 'bg-red-500', }; @@ -143,7 +143,7 @@ export default function TasksBoard() {
@@ -153,7 +153,7 @@ export default function TasksBoard() {
@@ -172,7 +172,7 @@ export default function TasksBoard() { setFilterAssignee(e.target.value)} - className="appearance-none text-xs bg-[#0c0c10] border border-white/10 rounded-md pl-2.5 pr-7 py-1.5 text-white focus:outline-none focus:border-indigo-500/50 cursor-pointer" + className="appearance-none text-xs bg-[#0c0c10] border border-white/10 rounded-md pl-2.5 pr-7 py-1.5 text-white focus:outline-none focus:border-brand-500/50 cursor-pointer" > {AGENTS.map((a) => ( @@ -202,7 +202,7 @@ export default function TasksBoard() { setNewTask({ ...newTask, assignee: e.target.value })} - className="w-full text-sm bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white focus:outline-none focus:border-indigo-500/50" + className="w-full text-sm bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white focus:outline-none focus:border-brand-500/50" > {AGENTS.map((a) => )} @@ -345,7 +345,7 @@ export default function TasksBoard() { setNewTask({ ...newTask, type: e.target.value as TaskType })} - className="w-full text-sm bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white focus:outline-none focus:border-indigo-500/50" + className="w-full text-sm bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white focus:outline-none focus:border-brand-500/50" > @@ -374,7 +374,7 @@ export default function TasksBoard() { type="date" value={newTask.dueDate} onChange={(e) => setNewTask({ ...newTask, dueDate: e.target.value })} - className="w-full text-sm bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white focus:outline-none focus:border-indigo-500/50" + className="w-full text-sm bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white focus:outline-none focus:border-brand-500/50" />
@@ -388,7 +388,7 @@ export default function TasksBoard() { diff --git a/compliance-firewall-agent/components/dashboard/team-view.tsx b/compliance-firewall-agent/components/dashboard/team-view.tsx index a07526aa..64cbca0e 100644 --- a/compliance-firewall-agent/components/dashboard/team-view.tsx +++ b/compliance-firewall-agent/components/dashboard/team-view.tsx @@ -283,7 +283,7 @@ export default function TeamView() {
@@ -338,7 +338,7 @@ export default function TeamView() { onChange={(e) => setAssignText(e.target.value)} placeholder="Describe the task..." rows={3} - className="w-full text-sm bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white placeholder-white/20 focus:outline-none focus:border-indigo-500/50 resize-none" + className="w-full text-sm bg-[#0c0c10] border border-white/10 rounded-lg px-3 py-2 text-white placeholder-white/20 focus:outline-none focus:border-brand-500/50 resize-none" />
@@ -350,7 +350,7 @@ export default function TeamView() { diff --git a/compliance-firewall-agent/components/landing/PlatformDashboard.tsx b/compliance-firewall-agent/components/landing/PlatformDashboard.tsx index 8f26be1b..1a868098 100644 --- a/compliance-firewall-agent/components/landing/PlatformDashboard.tsx +++ b/compliance-firewall-agent/components/landing/PlatformDashboard.tsx @@ -117,7 +117,7 @@ export function PlatformDashboard() { const [scanPct, setScanPct] = useState(0); const [severityData, setSeverityData] = useState([ { level: "HIGH", count: rand(3, 12), color: "text-red-400", bg: "bg-red-500/10", border: "border-red-500/20" }, - { level: "MED", count: rand(8, 24), color: "text-amber-400", bg: "bg-amber-500/10", border: "border-amber-500/20" }, + { level: "MED", count: rand(8, 24), color: "text-brand-400", bg: "bg-brand-500/10", border: "border-brand-500/20" }, { level: "LOW", count: rand(15, 40), color: "text-slate-400", bg: "bg-white/[0.04]", border: "border-white/[0.06]" }, ]); const [latencyMs, setLatencyMs] = useState(rand(6, 11)); @@ -198,7 +198,7 @@ export function PlatformDashboard() { const t = setInterval(() => { setSeverityData([ { level: "HIGH", count: rand(3, 12), color: "text-red-400", bg: "bg-red-500/10", border: "border-red-500/20" }, - { level: "MED", count: rand(8, 24), color: "text-amber-400", bg: "bg-amber-500/10", border: "border-amber-500/20" }, + { level: "MED", count: rand(8, 24), color: "text-brand-400", bg: "bg-brand-500/10", border: "border-brand-500/20" }, { level: "LOW", count: rand(15, 40), color: "text-slate-400", bg: "bg-white/[0.04]", border: "border-white/[0.06]" }, ]); }, 2500); diff --git a/compliance-firewall-agent/components/landing/WhyHoundshield.tsx b/compliance-firewall-agent/components/landing/WhyHoundshield.tsx index 6374633c..ba567bd3 100644 --- a/compliance-firewall-agent/components/landing/WhyHoundshield.tsx +++ b/compliance-firewall-agent/components/landing/WhyHoundshield.tsx @@ -64,12 +64,12 @@ const PILLARS = [ { icon: FileCheck, iconBg: "bg-brand-50 border-brand-200", - iconColor: "text-brand-600", + iconColor: "text-brand-700", eyebrow: "vs. manual audit prep", title: "Audit-ready from day one.", body: "Every blocked query is logged in an immutable SHA-256 hash chain. Export your complete audit trail in seconds — SOC 2, HIPAA, or C3PAO.", stat: { label: "Audit findings prevented (avg)", value: 94, suffix: "%" }, - accentColor: "text-brand-600", + accentColor: "text-brand-700", }, ]; @@ -78,7 +78,7 @@ export function WhyHoundshield() {
-

Why houndshield

+

Why houndshield

One tool does what three tools used to.

diff --git a/compliance-firewall-agent/components/landing/v3/HomeV3.tsx b/compliance-firewall-agent/components/landing/v3/HomeV3.tsx new file mode 100644 index 00000000..9c0486c6 --- /dev/null +++ b/compliance-firewall-agent/components/landing/v3/HomeV3.tsx @@ -0,0 +1,815 @@ +import Image from "next/image"; +import Link from "next/link"; + +/* ──────────────────────────────────────────────────────────── + HoundShield Landing Page v3 + Mirrors the Claude Design reference (HoundShield Landing + Page v3.html). Uses --hs-* CSS variables defined in globals.css. + Server Component — no client JS for the landing surface itself. + ──────────────────────────────────────────────────────────── */ + +const FEATURES = [ + { + title: "100% Local Scanning", + body: "Every prompt is inspected on your network, before it leaves. Zero data egress, zero telemetry, zero exposure.", + }, + { + title: "C3PAO-Ready PDF Reports", + body: "Tamper-evident audit logs export to a signed PDF mapped to NIST 800-171 controls — the artifact your assessor accepts.", + }, + { + title: "Zero-Friction Deployment", + body: "One URL change. Works with ChatGPT, Copilot, Claude, Gemini, and any OpenAI-compatible endpoint. Docker in 10 minutes.", + }, + { + title: "OODA Behavioral Engine", + body: "Self-correcting agent loop (Observe → Orient → Decide → Act) tunes detection thresholds against your own audit cadence.", + }, + { + title: "16 CUI Detection Patterns", + body: "CUI//SP-CTI, ITAR, CAGE codes, clearance levels, contract numbers, PHI, PII, secrets — all pre-loaded, all editable.", + }, + { + title: "Brain AI Compliance Advisor", + body: "Ask plain-English compliance questions. Mapped to NIST 800-171 Rev 2 controls with citation, with consent-gated CUI handling.", + }, +]; + +const STEPS = [ + { + n: "01", + title: "Change one URL", + body: "Point your team's AI tools at the HoundShield proxy. No agents, no extensions, no per-user installation.", + }, + { + n: "02", + title: "Every prompt scanned locally", + body: "<10ms classification. 16 detection patterns. Blocked prompts never leave your control boundary.", + }, + { + n: "03", + title: "Audit PDF on demand", + body: "Generate a signed, hash-chained NIST 800-171 evidence pack the moment your C3PAO asks for it.", + }, +]; + +const PRICING = [ + { + name: "Free", + price: "$0", + cadence: "forever", + note: "Up to 1,000 prompts / month", + cta: "Get Started Free", + href: "/signup", + primary: false, + }, + { + name: "Pro", + price: "$199", + cadence: "/ month", + note: "SOC 2 + HIPAA coverage · 50k prompts", + cta: "Start Free Trial", + href: "/signup?plan=pro", + primary: true, + }, + { + name: "Growth", + price: "$499", + cadence: "/ month", + note: "PDF reports · 250k prompts · 5 admins", + cta: "Start Free Trial", + href: "/signup?plan=growth", + primary: false, + }, + { + name: "Enterprise", + price: "$999", + cadence: "/ month", + note: "Unlimited prompts · CMMC L2 · SSO", + cta: "Talk to Sales", + href: "/contact", + primary: false, + }, + { + name: "Agency", + price: "$2,499", + cadence: "/ month", + note: "White-label · multi-tenant · MSP", + cta: "Talk to Sales", + href: "/contact", + primary: false, + }, +]; + +const FOOTER_COLUMNS = [ + { + heading: "Product", + links: [ + { label: "Features", href: "/features" }, + { label: "Pricing", href: "/pricing" }, + { label: "How It Works", href: "/#how-it-works" }, + { label: "Changelog", href: "/changelog" }, + { label: "Roadmap", href: "/docs" }, + ], + }, + { + heading: "Company", + links: [ + { label: "About", href: "/about" }, + { label: "Blog", href: "/blog" }, + { label: "Careers", href: "/contact" }, + { label: "Press", href: "/contact" }, + { label: "Security", href: "/security" }, + ], + }, + { + heading: "Resources", + links: [ + { label: "Docs", href: "/docs" }, + { label: "API Reference", href: "/docs" }, + { label: "CMMC Guide", href: "/blog" }, + { label: "DFARS 7012 FAQ", href: "/blog" }, + { label: "C3PAO Finder", href: "/partners" }, + ], + }, +]; + +export function HomeV3() { + return ( +
+ {/* ── NAV ───────────────────────────────────────────────── */} +
+
+ + HoundShield + + HoundShield + + + +
+ + Sign in + + + Start Free + +
+
+
+ + {/* ── HERO ──────────────────────────────────────────────── */} +
+
+
+ + CMMC Level 2 enforcement — Nov 10, 2026 +
+ +

+ Stop your team from leaking CUI to ChatGPT. +

+ +

+ Local-only AI compliance firewall for DoD contractors. Intercepts CUI before it leaves your network. + CMMC Level 2, DFARS 7012, SOC 2. <10ms latency. One URL to deploy. +

+ +
+ + Deploy in 10 minutes → + + + Book a demo + +
+ +
+ ● NIST 800-171 Rev 2 + ● DFARS 252.204-7012 + ● CMMC Level 2 + ● SHA-256 Hash-Chained +
+
+
+ + {/* ── FEATURES ──────────────────────────────────────────── */} +
+
+
+

+ Built for ISSO / ISSM +

+

+ Everything your audit demands. +

+
+ +
+ {FEATURES.map((f) => ( +
+
+ + + +
+

+ {f.title} +

+

+ {f.body} +

+
+ ))} +
+
+
+ + {/* ── HOW IT WORKS (DARK NAVY) ──────────────────────────── */} +
+
+
+

+ How it works +

+

+ From URL change to audit PDF in three steps. +

+
+ +
+ {STEPS.map((s) => ( +
+ + {s.n} + +

+ {s.title} +

+

+ {s.body} +

+
+ ))} +
+
+
+ + {/* ── PRICING ───────────────────────────────────────────── */} +
+
+
+

+ Pricing +

+

+ Simple, transparent pricing. +

+

+ Every framework included. No per-user fees. Cancel anytime. +

+
+ +
+ {PRICING.map((t) => ( +
+ {t.primary && ( + + Recommended + + )} +

+ {t.name} +

+
+ + {t.price} + + {t.cadence} +
+

+ {t.note} +

+ + {t.cta} + +
+ ))} +
+
+
+ + {/* ── CTA STRIP ─────────────────────────────────────────── */} +
+
+

+ Ship the audit, not the spreadsheet. +

+

+ Hand your C3PAO a hash-chained PDF. Hand your team a proxy. Keep your CUI inside your boundary. +

+ + Start the 14-day audit pack → + +
+
+ + {/* ── FOOTER ────────────────────────────────────────────── */} +
+
+
+
+
+ HoundShield + + HoundShield + +
+

+ Local-only AI compliance firewall for DoD contractors, healthcare, and regulated industries. +

+
+ {FOOTER_COLUMNS.map((col) => ( +
+

+ {col.heading} +

+
    + {col.links.map((l) => ( +
  • + + {l.label} + +
  • + ))} +
+
+ ))} +
+
+ + © {new Date().getFullYear()} HoundShield. All rights reserved. + +
+ {["Privacy Policy", "Security", "Terms", "Cookie Policy"].map((l) => ( + + {l} + + ))} +
+
+
+
+
+ ); +} diff --git a/compliance-firewall-agent/components/theme-provider.tsx b/compliance-firewall-agent/components/theme-provider.tsx index e09f75d0..7c577be0 100644 --- a/compliance-firewall-agent/components/theme-provider.tsx +++ b/compliance-firewall-agent/components/theme-provider.tsx @@ -27,7 +27,7 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) { useEffect(() => { setMounted(true); const stored = localStorage.getItem("houndshield-theme") as Theme | null; - const initial = stored || "dark"; + const initial = stored || "light"; document.documentElement.setAttribute("data-theme", initial); document.documentElement.classList.toggle("dark", initial === "dark"); setThemeState(initial); diff --git a/compliance-firewall-agent/components/ui/demo-banner.tsx b/compliance-firewall-agent/components/ui/demo-banner.tsx index 81be7469..0cb42657 100644 --- a/compliance-firewall-agent/components/ui/demo-banner.tsx +++ b/compliance-firewall-agent/components/ui/demo-banner.tsx @@ -40,14 +40,14 @@ export function DemoBanner({ show }: DemoBannerProps) {
{/* Icon */} -