# Project export: Resonant CWPP: AI Wildfire Safety Advisor

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2026
- Tagline: AI-powered wildfire safety advisor that transforms complex CWPP policy documents into personalized, actionable, and culturally resonant guidance for residents.
- Devpost: https://devpost.com/software/resonant-cwpp-ai-wildfire-safety-advisor
- GitHub: https://github.com/DeveshKaushal-9/Treehacks-2026
- Video: https://www.youtube.com/embed/svFAcb_MEhk?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 0 GitHub contributor(s) — 

## Devpost submission (written by the team)

No Devpost description available.

## README (from the GitHub repository)

# Resonant CWPP 🛡️

**Built for TreeHacks 2026**

California is burning. Every year, we watch as our communities are threatened by increasingly severe wildfires. But the problem isn't just the fire—it's the disconnect between the data we have and the people who need it.

We built **Resonant CWPP** because we believe that a Community Wildfire Protection Plan shouldn't just be a PDF sitting on a government server. It should be a living, breathing platform that connects residents, first responders, and vulnerable populations in real-time.

## The Problem
Most wildfire apps are either too generic (providing just a weather forecast) or too complex (aimed only at forestry experts). They fail to account for the human element:
- **Language barriers** prevent critical info from reaching everyone.
- **Generic alerts** lead to "alarm fatigue."
- **Vulnerable populations** (AFN) are often left as an afterthought in evacuation planning.

## Our Solution
We created an **Intelligence Layer** that adapts the entire application based on who you are.

### 🚒 For First Responders ("The Commander")
We give you the data you actually need to make decisions.
- **Tactical Dark Mode**: Designed for low-light environments.
- **Live Unit Tracking**: Know exactly where your assets are.
- **Vulnerable Population Mapping**: See exactly where AFN residents are located to prioritize evacuations.

### 🏡 For Residents ("The Protector")
We turn anxiety into action.
- **Defensible Space Score**: A gamified way to track how safe your home is.
- **Specific Action Plans**: Instead of "be prepared," we say "Clear the dry leaves from your north gutter."
- **Hyper-Local Alerts**: We use your exact location to filter out noise.

### 🎓 For Youth ("The Future")
We engage the next generation.
- **Gamified Learning**: Earn ranks like "Junior Ranger" by learning about fire safety.
- **Missions**: Simple tasks that help the whole family stay safe.

## How We Built It
We used **Next.js** for a snappy, responsive frontend and **Tailwind CSS** to build a custom design system that feels premium and trustworthy. The "Command Center" uses **Framer Motion** for fluid transitions because in a crisis, clunky UI is a safety hazard.

For the intelligence layer, we mocked a **Fire Weather Index (FWI)** engine that takes real-time inputs (Wind, Temp, Humidity) and determines the exact risk level for your specific micro-climate.

## Try It Out
1. Clone the repo.
2. `npm install`
3. `npm run dev`
4. Sign up as a **Resident**, **First Responder**, or **Student** to see how the UI changes for you.

---

*Made with ❤️ (and a lot of caffeine) at Stanford.*


## Detected evidence (automated analysis)

Indexed codebase: 27 recognized source files, 180 KB.
- CSS (language) — detected in the code
- Next.js (technology) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (33 of 33)

```
.gitignore
DEMO_GUIDE.md
eslint.config.mjs
LICENSE
next.config.ts
package.json
postcss.config.mjs
README.md
src/app/api/chat/route.ts
src/app/globals.css
src/app/layout.tsx
src/app/login/page.tsx
src/app/page.tsx
src/app/signup/page.tsx
src/components/action-plan.tsx
src/components/dashboards/afn-dashboard.tsx
src/components/dashboards/resident-dashboard.tsx
src/components/dashboards/responder-dashboard.tsx
src/components/dashboards/youth-dashboard.tsx
src/components/feedback-loop.tsx
src/components/hero-section.tsx
src/components/interactive-map.tsx
src/components/live-dashboard.tsx
src/components/personalization-wizard.tsx
src/components/screens/alerts-screen.tsx
src/components/screens/chat-screen.tsx
src/components/screens/community-screen.tsx
src/components/screens/dashboard-screen.tsx
src/components/screens/profile-screen.tsx
src/components/screens/todo-screen.tsx
src/components/sidebar-layout.tsx
src/lib/auth-context.tsx
tsconfig.json
```

### Dependencies

- package.json: @tailwindcss/postcss@^4, @types/leaflet@^1.9.21, @types/node@^20, @types/react@^19, @types/react-dom@^19, axios@^1.13.5, clsx@^2.1.1, date-fns@^4.1.0, eslint@^9, eslint-config-next@16.1.6, framer-motion@^12.34.0, leaflet@^1.9.4, lucide-react@^0.564.0, next@16.1.6, react@19.2.3, react-dom@19.2.3, react-leaflet@^5.0.0, react-markdown@^10.1.0, recharts@^3.7.0, remark-gfm@^4.0.1, tailwind-merge@^3.4.0, tailwindcss@^4, typescript@^5

### Recent commits (newest first)

- commit
- updated readme file
- Merge remote-tracking branch 'origin/main'
- feat: Implement Role-Based Intelligence Layer and Auth
- Initial commit
- Initial commit from Create Next App

## Key source files (fetched from GitHub, selected and truncated for size)

### DEMO_GUIDE.md

```markdown
# Quick Demo Guide - Resonant CWPP

## 🚀 How to Demo Your New Dashboard

### **What Changed:**
✅ **Collapsible Sidebar** - Clean navigation with 5 main screens  
✅ **Mobile Responsive** - Works perfectly on all devices  
✅ **5 Dedicated Screens** - Dashboard, To-Do, Community, Alerts, Profile  

---

## 📱 Demo Flow (3 minutes)

### **1. Login/Signup (30 seconds)**
1. Go to http://localhost:3000
2. Click "Create Account"
3. Fill in:
   - Name: "Sarah Chen"
   - Email: "sarah@example.com"
   - Phone: "(650) 555-1234"
   - City: "Palo Alto"
   - Role: **Resident**
   - Password: "demo123"
4. Click "Sign Up" → "Sign In"

### **2. Dashboard Screen (45 seconds)**
**Show:**
- ✅ Real-time fire risk gauge (pulls live weather data!)
- ✅ Temperature, wind, humidity metrics
- ✅ Defensible Space Score (72/100)
- ✅ Property protection stats

**Say:** *"This is personalized to Sarah's location and profile type. The fire risk updates in real-time based on weather conditions."*

### **3. To-Do List Screen (30 seconds)**
**Click:** "To-Do List" in sidebar

**Show:**
- ✅ Progress bar (shows completion percentage)
- ✅ Task categories (structure, landscape, community, emergency)
- ✅ Difficulty levels
- ✅ Click a task to mark complete

**Say:** *"Instead of generic advice, residents get specific, actionable tasks they can complete."*

### **4. Community Screen (30 seconds)**
**Click:** "Community" in sidebar

**Show:**
- ✅ Red Flag Warning banner
- ✅ Updates from Fire Dept, Police, Local Gov
- ✅ Priority filtering (critical, high, medium, low)
- ✅ Timestamps ("45 minutes ago")

**Say:** *"This is the two-way communication channel - residents get official updates from authorities, and they can provide feedback."*

### **5. Sidebar Features (15 seconds)**
**Show:**
- ✅ Click collapse button (top right of sidebar)
- ✅ Sidebar collapses to icons only
- ✅ On mobile, click hamburger menu

**Say:** *"The interface adapts - collapsible on desktop, slide-out on mobile."*

### **6. Profile Screen (15 seconds)**
**Click:** "Profile" in sidebar

**Show:**
- ✅ User info with avatar
- ✅ Impact stats (47 days active, 12 tasks done)
- ✅ Edit mode

**Say:** *"Users can manage their account and see their progress over time."*

---

## 🎯 Key Talking Points for Judges

### **Challenge Alignment:**

1. **Accessible** ✅
   - Clean navigation, not overwhelming
   - Gamified scoring (72/100) instead of technical jargon
   - Specific actions ("Clear dry leaves") not generic advice

2. **Interactive** ✅
   - Real-time weather integration
   - Task completion tracking
   - Community updates with filtering

3. **Culturally Resonant** ✅
   - Different dashboards for different personas
   - Personalized recommendations
   - Values-based customization

---

## 🔥 Demo Tips

**DO:**
- ✅ Show the sidebar collapse feature
- ✅ Mark a task as complete to show interactivity
- ✅ Filter community updates by priority
- ✅ Mention the real-time weather API

**DON'T:**
- ❌ Spend too long on any one screen
-
[truncated — 1164 more characters]
```

### package.json

```
{
  "name": "treehacks",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint"
  },
  "dependencies": {
    "@types/leaflet": "^1.9.21",
    "axios": "^1.13.5",
    "clsx": "^2.1.1",
    "date-fns": "^4.1.0",
    "framer-motion": "^12.34.0",
    "leaflet": "^1.9.4",
    "lucide-react": "^0.564.0",
    "next": "16.1.6",
    "react": "19.2.3",
    "react-dom": "19.2.3",
    "react-leaflet": "^5.0.0",
    "react-markdown": "^10.1.0",
    "recharts": "^3.7.0",
    "remark-gfm": "^4.0.1",
    "tailwind-merge": "^3.4.0"
  },
  "devDependencies": {
    "@tailwindcss/postcss": "^4",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "16.1.6",
    "tailwindcss": "^4",
    "typescript": "^5"
  }
}

```

### src/app/layout.tsx

```typescript
import type { Metadata } from "next";
import { Inter, Outfit } from "next/font/google";
import "./globals.css";
import { AuthProvider } from "@/lib/auth-context";

const inter = Inter({
  variable: "--font-inter",
  subsets: ["latin"],
});

const outfit = Outfit({
  variable: "--font-outfit",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "Resonant CWPP | TreeHacks 2026",
  description: "A culturally resonant, interactive Community Wildfire Prevention Plan for the modern age.",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body
        className={`${inter.variable} ${outfit.variable} antialiased`}
      >
        <AuthProvider>
          {children}
        </AuthProvider>
      </body>
    </html>
  );
}

```

### src/app/page.tsx

```typescript
"use client";

import { useAuth } from "@/lib/auth-context";
import { HeroSection } from "@/components/hero-section";
import { PersonalizationWizard } from "@/components/personalization-wizard";
import { ActionPlan } from "@/components/action-plan";
import { FeedbackLoop } from "@/components/feedback-loop";
import { LiveDashboard } from "@/components/live-dashboard";
import Link from "next/link"; // For the mocked login button in Hero

export default function Home() {
  const { user, isLoading } = useAuth();

  if (isLoading) return null; // Or a loading spinner

  if (user) {
    return <LiveDashboard />;
  }

  return (
    <main className="min-h-screen">
      <HeroSection />

      {/* Temporary Nav for creating account */}
      <div className="fixed top-6 right-6 z-50">
        <Link
          href="/signup"
          className="px-6 py-3 bg-white text-forest-900 font-bold rounded-full shadow-lg hover:bg-gray-100 transition-colors"
        >
          Create Account
        </Link>
      </div>

      <PersonalizationWizard />

      <ActionPlan />

      <FeedbackLoop />

      {/* Footer / Community */}
      <section className="py-20 px-6 bg-forest-900 text-white border-t border-forest-800">
        <div className="max-w-4xl mx-auto text-center">
          <h2 className="text-3xl font-display font-bold mb-6">Why It Matters</h2>
          <p className="text-white/80 text-lg leading-relaxed max-w-2xl mx-auto">
            California faces an urgent task. We need to bridge the gap between technical data
            and human connection. Your participation shapes the future of our safety.
          </p>
          <div className="mt-12 text-sm text-forest-400">
            © 2026 Resonant CWPP Prototype. Built for TreeHacks.
          </div>
        </div>
      </section>
    </main>
  );
}

```

### src/app/login/page.tsx

```typescript
"use client";

import { useState } from "react";
import { motion } from "framer-motion";
import { useRouter, useSearchParams } from "next/navigation";
import { Shield, Loader2, AlertCircle } from "lucide-react";
import Link from "next/link";
import { useAuth } from "@/lib/auth-context";

export default function LoginPage() {
    const router = useRouter();
    const searchParams = useSearchParams();
    const { login } = useAuth(); // This line is used later in the handleLogin function. The instruction's comment seems to be a typo. I will keep it.

    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState("");
    const [formData, setFormData] = useState({
        email: "",
        password: ""
    });

    const registered = searchParams.get("registered");

    const handleLogin = async (e: React.FormEvent) => {
        e.preventDefault();
        setIsLoading(true);
        setError("");

        // Simulate API delay
        await new Promise(resolve => setTimeout(resolve, 1000));

        // Check "database"
        const users = JSON.parse(localStorage.getItem("cwpp_users_db") || "[]");
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const user = users.find((u: any) => u.email === formData.email && u.password === formData.password);

        if (user) {
            // Remove password before session storage
            const safeUser = { ...user };
            delete safeUser.password;

            login(safeUser);
            router.push("/");
        } else {
            setError("Invalid email or password");
            setIsLoading(false);
        }
    };

    return (
        <div className="min-h-screen bg-sand-50 flex items-center justify-center p-6">
            <div className="w-full max-w-md">
                <div className="text-center mb-8">
                    <Link href="/" className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-forest-900 text-white mb-6">
                        <Shield className="w-5 h-5 fill-fire-500 text-fire-500" />
                        <span className="font-bold tracking-wide">Resonant CWPP</span>
                    </Link>
                    <h1 className="text-3xl md:text-4xl font-display font-bold text-forest-900 mb-2">
                        Welcome Back
                    </h1>
                    <p className="text-forest-800/60">
                        Join your community&apos;s wildfire defense network.
                    </p>
                </div>

                <motion.div
                    initial={{ opacity: 0, y: 20 }}
                    animate={{ opacity: 1, y: 0 }}
                    className="bg-white rounded-3xl shadow-glass border border-forest-900/5 p-8"
                >
                    {registered && (
                        <div className="bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-xl mb-6 flex items-center gap-2">
                            <Shield className="w-4 h-4" />
                            <span className="font-bold text-sm">Account created! Please sign in.</span>
                        </div>
                    )}

                    <form onSubmit={handleLogin} className="space-y-4">
                        <div className="space-y-1">
                            <label className="text-xs font-bold text-forest-900 uppercase ml-1">Email</label>
                            <input
                                required
                                type="email"
                                className="w-full px-4 py-3 rounded-xl bg-gray-50 border-2 border-gray-100 focus:border-fire-500 outline-none font-medium"
                                placeholder="john@example.com"
                                value={formData.email}
                                onChange={e => setFormData({ ...formData, email: e.target.value })}
                            />
                        </div>

                        <div className="space-y-1">
                            <label className="text-xs font-bold text-forest-900 uppercase ml-1">Password</label>
                            <input
                                required
                                type="password"
                                className="w-full px-4 py-3 rounded-xl bg-gray-50 border-2 border-gray-100 focus:border-fire-500 outline-none font-medium"
                                value={formData.password}
                                onChange={e => setFormData({ ...formData, password: e.target.value })}
                            />
                        </div>

                        {error && (
                            <div className="flex items-center gap-2 text-red-500 text-sm font-bold justify-center">
                                <AlertCircle className="w-4 h-4" /> {error}
                            </div>
                        )}

                        <button
                            type="submit"
                            disabled={isLoading}
                            className="w-full bg-forest-900 text-white rounded-xl font-bold py-4 text-lg flex items-center justify-center gap-2 hover:bg-forest-800 transition-all shadow-lg hover:shadow-xl disabled:opacity-70 mt-2"
                        >
                            {isLoading ? <Loader2 className="animate-spin" /> : "Sign In"}
                        </button>

                        <p className="text-center text-gray-500 mt-6">
                            Don&apos;t have an account? <Link href="/signup" className="font-bold text-forest-900 hover:underline">Create Account</Link>
                        </p>
                    </form>
                </motion.div>
            </div>
        </div>
    );
}

```

### src/app/signup/page.tsx

```typescript
"use client";

import { useState } from "react";
import { motion } from "framer-motion";
import { useRouter } from "next/navigation";
import { Shield, Loader2 } from "lucide-react";
import Link from "next/link";
import { UserProfileType, User } from "@/lib/auth-context";

export default function SignupPage() {
    const router = useRouter();
    // const { login } = useAuth(); // We'll manually handle login redirect after signup
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState("");

    const [formData, setFormData] = useState({
        name: "",
        email: "",
        phone: "",
        city: "Palo Alto", // Default
        profileType: "resident_general" as UserProfileType,
        password: "",
        confirmPassword: ""
    });

    const handleSignup = async (e: React.FormEvent) => {
        e.preventDefault();
        setIsLoading(true);
        setError("");

        if (formData.password !== formData.confirmPassword) {
            setError("Passwords do not match");
            setIsLoading(false);
            return;
        }

        // Simulate API call
        await new Promise(resolve => setTimeout(resolve, 1500));

        // Create User Object (Simulating backend creation)
        const newUser: User = {
            id: Math.random().toString(36).substr(2, 9),
            name: formData.name,
            email: formData.email,
            phone: formData.phone,
            city: formData.city,
            profileType: formData.profileType,
            location: { lat: 37.4, lng: -122.1 } // Default lat/long for now
        };

        // Store in localStorage 'db' to simulate persistence for the Login page to find
        const users = JSON.parse(localStorage.getItem("cwpp_users_db") || "[]");
        users.push({ ...newUser, password: formData.password });
        localStorage.setItem("cwpp_users_db", JSON.stringify(users));

        // Redirect to Login Page as requested
        router.push("/login?registered=true");
    };

    return (
        <div className="min-h-screen bg-sand-50 flex items-center justify-center p-6">
            <div className="w-full max-w-lg">
                <div className="text-center mb-8">
                    <Link href="/" className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-forest-900 text-white mb-6">
                        <Shield className="w-5 h-5 fill-fire-500 text-fire-500" />
                        <span className="font-bold tracking-wide">Resonant CWPP</span>
                    </Link>
                    <h1 className="text-3xl md:text-4xl font-display font-bold text-forest-900 mb-2">
                        Create Account
                    </h1>
                    <p className="text-forest-800/60">
                        Join your community&apos;s wildfire defense network.
                    </p>
                </div>

                <motion.div
                    initial={{ opacity: 0, y: 20 }}
                    animate={{ opacity: 1, y: 0 }}
                    className="bg-white rounded-3xl shadow-glass border border-forest-900/5 p-8 md:p-10"
                >
                    <form onSubmit={handleSignup} className="space-y-4">
                        <div className="space-y-1">
                            <label className="text-xs font-bold text-forest-900 uppercase ml-1">Full Name</label>
                            <input
                                required
                                type="text"
                                className="w-full px-4 py-3 rounded-xl bg-gray-50 border-2 border-gray-100 focus:border-fire-500 outline-none font-medium"
                                placeholder="John Doe"
                                value={formData.name}
                                onChange={e => setFormData({ ...formData, name: e.target.value })}
                            />
                        </div>

                        <div className="space-y-1">
                            <label className="text-xs font-bold text-forest-900 uppercase ml-1">Email</label>
                            <input
                                required
                                type="email"
                                className="w-full px-4 py-3 rounded-xl bg-gray-50 border-2 border-gray-100 focus:border-fire-500 outline-none font-medium"
                                placeholder="john@example.com"
                                value={formData.email}
                                onChange={e => setFormData({ ...formData, email: e.target.value })}
                            />
                        </div>

                        <div className="grid grid-cols-2 gap-4">
                            <div className="space-y-1">
                                <label className="text-xs font-bold text-forest-900 uppercase ml-1">Phone</label>
                                <input
                                    required
                                    type="tel"
                                    className="w-full px-4 py-3 rounded-xl bg-gray-50 border-2 border-gray-100 focus:border-fire-500 outline-none font-medium"
                                    placeholder="(555) 123-4567"
                                    value={formData.phone}
                                    onChange={e => setFormData({ ...formData, phone: e.target.value })}
                                />
                            </div>
                            <div className="space-y-1">
                                <label className="text-xs font-bold text-forest-900 uppercase ml-1">City (CA)</label>
                                <select
                                    className="w-full px-4 py-3 rounded-xl bg-gray-50 border-2 border-gray-100 focus:border-fire-500 outline-none font-medium"
                                    value={formData.city}
                                    onChange={e => setFormData({ ...formData, city: e.
[truncated — 4510 more characters]
```

### src/app/api/chat/route.ts

```typescript
import { NextResponse } from 'next/server';

export async function POST(req: Request) {
    try {
        const { messages, userProfile } = await req.json();

        const apiKey = process.env.PERPLEXITY_API_KEY;

        if (!apiKey) {
            // Fallback for demo if no key is present
            console.warn("No Perplexity API key found. Using mock response.");
            return NextResponse.json({
                choices: [
                    {
                        message: {
                            role: "assistant",
                            content: "I'm currently running in demo mode because the Perplexity API key hasn't been configured. To get real-time answers, please add your API key to .env.local with the name PERPLEXITY_API_KEY.\n\nIn the meantime: Make sure to clear dry leaves from your gutters, keep a go-bag ready, and know your evacuation zone."
                        }
                    }
                ]
            });
        }

        // specific system prompt based on user profile
        const systemPrompt = `You are the Resonant Safety Advisor, an expert AI assistant for wildfire safety and preparation.
    
    User Context:
    - Name: ${userProfile?.name || 'Resident'}
    - Role: ${userProfile?.profileType || 'resident'}
    - Location: ${userProfile?.location || 'California'}
    
    Your goal is to provide accurate, helpful, and calm advice about wildfire preparedness, evacuation features, and risk assessment.
    Always prioritize official sources and remind users to follow local emergency orders.
    Keep answers concise and actionable.`;

        const response = await fetch('https://api.perplexity.ai/chat/completions', {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${apiKey}`,
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                model: 'sonar-pro',
                messages: [
                    { role: 'system', content: systemPrompt },
                    ...messages
                ],
                temperature: 0.2,
                top_p: 0.9,
                max_tokens: 1000,
                stream: false
            }),
        });

        if (!response.ok) {
            const errorData = await response.text();
            console.error('Perplexity API Error details:', errorData);
            throw new Error(`Perplexity API Error: ${response.status} ${response.statusText}`);
        }

        const data = await response.json();
        return NextResponse.json(data);

    } catch (error) {
        console.error('Chat Route Error:', error);
        return NextResponse.json(
            { error: "Failed to process chat request" },
            { status: 500 }
        );
    }
}

```

### next.config.ts

```typescript
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'images.unsplash.com',
      },
    ],
  },
};

export default nextConfig;

```

### src/components/live-dashboard.tsx

```typescript
"use client";

import { useState } from "react";
import { SidebarLayout } from "./sidebar-layout";
import { DashboardScreen } from "./screens/dashboard-screen";
import { TodoScreen } from "./screens/todo-screen";
import { CommunityScreen } from "./screens/community-screen";
import { AlertsScreen } from "./screens/alerts-screen";
import { ProfileScreen } from "./screens/profile-screen";
import { ChatScreen } from "./screens/chat-screen";

export function LiveDashboard() {
    const [activeScreen, setActiveScreen] = useState<"dashboard" | "todo" | "community" | "alerts" | "profile" | "chat">("dashboard");

    const renderScreen = () => {
        switch (activeScreen) {
            case "dashboard":
                return <DashboardScreen />;
            case "todo":
                return <TodoScreen />;
            case "community":
                return <CommunityScreen />;
            case "alerts":
                return <AlertsScreen />;
            case "profile":
                return <ProfileScreen />;
            case "chat":
                return <ChatScreen />;
            default:
                return <DashboardScreen />;
        }
    };

    return (
        <SidebarLayout activeScreen={activeScreen} onScreenChange={setActiveScreen}>
            {renderScreen()}
        </SidebarLayout>
    );
}

```

### src/components/interactive-map.tsx

```typescript
"use client";

import { useEffect } from "react";
import { MapContainer, TileLayer, Marker, Popup, useMap } from "react-leaflet";
import "leaflet/dist/leaflet.css";
import L from "leaflet";

// Fix for default Leaflet marker icons in Next.js
const iconUrl = "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png";
const iconRetinaUrl = "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png";
const shadowUrl = "https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png";

const customIcon = new L.Icon({
    iconUrl,
    iconRetinaUrl,
    shadowUrl,
    iconSize: [25, 41],
    iconAnchor: [12, 41],
    popupAnchor: [1, -34],
    shadowSize: [41, 41]
});

// Component to handle map resizing
function MapResizer() {
    const map = useMap();
    useEffect(() => {
        const resizeObserver = new ResizeObserver(() => {
            map.invalidateSize();
        });
        const container = map.getContainer();
        resizeObserver.observe(container);
        return () => resizeObserver.disconnect();
    }, [map]);
    return null;
}

interface InteractiveMapProps {
    center: [number, number];
    zoom?: number;
    markers?: Array<{ pos: [number, number]; label: string }>;
}

export default function InteractiveMap({ center, zoom = 13, markers = [] }: InteractiveMapProps) {
    return (
        <MapContainer
            center={center}
            zoom={zoom}
            style={{ height: "100%", width: "100%", zIndex: 0 }}
            scrollWheelZoom={true} // Enable zoom
        >
            <TileLayer
                attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
                url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
                // Using a dark theme provider for tactical look
                className="map-tiles"
            />
            {/* Dark mode overlay effect handled by parent CSS filter if needed */}

            <MapResizer />

            <Marker position={center} icon={customIcon}>
                <Popup>
                    Command Center <br /> (Current HQ)
                </Popup>
            </Marker>

            {markers.map((m, i: number) => (
                <Marker key={i} position={m.pos} icon={customIcon}>
                    <Popup>{m.label}</Popup>
                </Marker>
            ))}
        </MapContainer>
    );
}

```

[17 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]