# Project export: Ignis - Emergency AI Responder

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: CruzHacks 2026
- Tagline: “IGNIS turns emergency phone calls into real‑time AI intelligence — instantly visualizing fire spread and the safest escape routes to help save lives.”
- Devpost: https://devpost.com/software/ignis-emergency-ai-responder
- GitHub: https://github.com/Aniridh/CruzHacks-Jan16-18
- Video: https://www.youtube.com/embed/fsch4jlKQ-w?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — jajajadagoat (10 commits)

## Devpost submission (written by the team)

No Devpost description available.

## README (from the GitHub repository)

# IGNIS (Emergency Insight System)

IGNIS is an AI-powered decision-support system that helps first responders gain clarity during fire emergencies by transforming emergency calls into structured spatial insight.

When a victim calls for help, their spoken description is often chaotic, emotional, and unstructured. IGNIS listens to the call, extracts critical details, and visualizes the situation in a clear layout so responders can act faster, safer, and with better situational awareness.

## Project Overview

This is a training and decision-support prototype designed to reduce confusion during high-stress emergencies. It is **not** an automated response system or a real 911 system replacement.

### 🚀 Key Innovation: Works Offline!

IGNIS is designed **demo-first** with **zero dependencies**:
- ✅ **No database** - All processing in-memory
- ✅ **No API keys required** - Built-in rule-based analysis fallback
- ✅ **Works offline** - Demo mode runs completely locally
- ✅ **Progressive enhancement** - OpenAI when available, deterministic fallback always works

Perfect for hackathon demos, presentations, and rapid prototyping!

## Tech Stack

- **Voice**: Vapi AI (live voice input and transcription)
- **AI**: OpenAI GPT-4 (situation analysis)
- **Frontend**: Next.js 16, React, TypeScript, Tailwind CSS
- **Visualization**: SVG rendering
- **Layouts**: Local JSON templates
- **Demo**: Preloaded scenarios with one-click run
- **Deploy**: Vercel

## Features

- **Live Voice Input**: Record emergency calls in real-time using Vapi AI
  - Live voice transcription with Deepgram
  - Interactive AI assistant for clarifying questions
  - Real-time transcript display
  - Seamless integration with analysis pipeline

- **AI-Powered Analysis** (with Rule-Based Fallback): Extracts structured insights from emergency call transcripts
  - **OpenAI GPT-4**: Primary analysis engine (when API key available)
  - **Rule-Based Fallback**: Deterministic keyword/heuristic analysis (always works)
  - Environment type detection (apartment, office, school, forest)
  - Fire origin identification with confidence scores
  - Hazard assessment
  - Urgency classification
  - Fire spread probability estimation
  - **Guaranteed results**: System NEVER fails due to missing API keys

- **Spatial Visualization**: Clear 2D layout visualization showing:
  - Fire origin location (with simple pulsing animation)
  - Risk zones (heat map: red = high, orange = medium, yellow = low)
  - Safe path recommendations (green dashed lines)
  - Strike nodes (high-priority intervention points)

- **Decision Reasoning**: Transparent explanations for all AI-driven decisions
  - Risk zone reasoning
  - Path recommendations
  - Strike node priorities
  - Uncertainty markers with confidence scores

- **Demo Mode**: Pre-loaded emergency scenarios for quick hackathon demos
  - One-click scenario loading
  - Realistic emergency call transcripts
  - All environment types covered

## Getting Started

### Prerequisites

**Required:**
- Node.js 18+ and npm

**Optional** (for enhanced features):
- OpenAI API key (for GPT-4 analysis, falls back to rule-based if missing)
- Vapi AI Public Key (for live voice input, demo mode available without it)

### Installation

1. Clone the repository:
```bash
git clone <repository-url>
cd CruzHacks-Jan16-18
```

2. Install dependencies:
```bash
npm install
```

3. **(Optional)** Create `.env.local` file with your API keys:
```env
# OpenAI (Optional - falls back to rule-based analysis)
OPENAI_API_KEY=your_openai_api_key_here

# Vapi AI (Optional - demo mode works without it)
NEXT_PUBLIC_VAPI_PUBLIC_KEY=your_vapi_public_key_here

# Vapi Webhook Secret (Optional - for production webhook validation)
VAPI_WEBHOOK_SECRET=your_webhook_secret_here
```

**How to get API keys** (optional):
- **OpenAI**: https://platform.openai.com/api-keys
- **Vapi AI**: https://dashboard.vapi.ai/ → Settings → Public Key

**Note**: The system works perfectly without any API keys! Demo mode uses local analysis.

4. Run the development server:
```bash
npm run dev
```

5. Open [http://localhost:3000](http://localhost:3000) in your browser

## Usage

### Landing Page

Visit `/landing` for the tactical-themed landing page featuring:
- Military-style HUD interface
- Animated wireframe buildings
- Tactical grid and targeting reticle
- Quick access to dashboard and GitHub

### Dashboard

The dashboard offers two input modes:

#### Live Voice Input Mode (🎤)
1. **Start Voice Call**: Click "Start Voice Call" to begin recording
2. **Speak Clearly**: Describe the emergency situation naturally
3. **AI Assistant**: The system will ask clarifying questions about location, fire origin, and hazards
4. **End Call**: Click "End Call & Analyze" when finished
5. **View Analysis**: The system automatically analyzes your transcript using GPT-4

#### Demo Scenarios Mode (📋)
1. **Select a Demo Scenario**: Click on any scenario card to load a pre-recorded emergency call
2. **View Analysis**: The system automatically analyzes the transcript using GPT-4

#### Analysis Results (Both Modes)
3. **Explore Visualization**: See the spatial visualization with fire zones, safe paths, and strike nodes
4. **Review Reasoning**: Expand sections in the reasoning log to understand AI decisions
5. **Check Confidence**: View confidence scores for each AI prediction

## API Endpoints

The system provides three API endpoints:

### 1. `/api/analyze` (Primary Analysis)
- **Input**: Emergency call transcript
- **Output**: Structured `SituationAnalysis` with confidence scores
- **Fallback**: OpenAI GPT-4 → Rule-based analysis
- **Always works**: Never fails due to missing API keys

### 2. `/api/vapi/webhook` (Vapi Integration)
- **Purpose**: Receives transcripts from Vapi AI voice calls
- **Security**: Validates webhook secret (optional)
- **Processing**: Runs full analysis pipeline
- **Output**: Structured analysis + callId

### 3. `/api/ingest` (Manual Testing)
- **Purpose**: Test analysis without Vapi (debugging/demos)
- **Input**: Raw transcript text
- **Output**: Complete analysis results
- **Usage**: `curl -X POST /api/ingest -d '{"transcript": "..."}'`

## Project Structure

```
/
├── app/
│   ├── api/
│   │   ├── analyze/route.ts          # Primary analysis endpoint
│   │   ├── vapi/webhook/route.ts     # Vapi webhook handler
│   │   └── ingest/route.ts           # Manual testing endpoint
│   ├── page.tsx                      # Main dashboard
│   ├── landing/page.tsx              # Tactical landing page
│   └── layout.tsx                    # Root layout
├── components/
│   ├── VoiceRecorder.tsx             # Live voice input with Vapi AI
│   ├── DemoMode.tsx                  # Demo scenario selector
│   ├── SituationReport.tsx           # Main situation report component
│   ├── SituationVisualizer.tsx       # SVG visualization
│   ├── FireSpreadOverlay.tsx         # Animated fire spread
│   ├── ReasoningLog.tsx              # Decision reasoning display
│   └── landing/                      # Landing page components
├── data/
│   ├── layouts/                      # JSON layout templates
│   └── demoScenarios.ts              # Pre-loaded scenarios
├── types/
│   └── index.ts                      # TypeScript type definitions
├── utils/
│   ├── runAnalysis.ts                # Analysis pipeline (OpenAI + fallback)
│   ├── layoutSelector.ts             # Layout template loader
│   ├── visualizationLogic.ts         # Risk zones, paths, strike nodes
│   └── reasoningGenerator.ts         # Decision reasoning generator
└── [Back-End]/                       # Backend files mirror
    ├── api/                          # API routes
    └── utils/                        # Utilities
```

## Git Workflow & SSH

The repository is configured to use SSH for git operations. Helper scripts are provided for easy commits:

### Quick Commit Commands

```bash
# Commit changes with a message
./commit.sh "Your commit message here"

# Push committed changes to remote
./push.sh

# 

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 75 recognized source files, 376 KB.
- CSS (language) — detected in the code
- MongoDB (technology) — detected in the code
- Next.js (technology) — detected in the code
- OpenAI (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 (87 of 87)

```
.env.example
.gitignore
[Back-End]/api/analyze-route.ts
[Back-End]/api/analyze/route.ts
[Back-End]/api/ingest-route.ts
[Back-End]/api/vapi-webhook-route.ts
[Back-End]/README.md
[Back-End]/utils/runAnalysis.ts
[Front-End]/app/landing-page.tsx
[Front-End]/components/DemoMode.tsx
[Front-End]/components/landing/TacticalButton.tsx
[Front-End]/components/landing/TacticalGrid.tsx
[Front-End]/components/landing/TargetingReticle.tsx
[Front-End]/components/landing/WireframeBuilding.tsx
[Front-End]/components/ReasoningLog.tsx
[Front-End]/components/SituationReport.tsx
[Front-End]/components/SituationVisualizer.tsx
[Front-End]/components/VoiceRecorder.tsx
[Front-End]/globals.css
[Front-End]/layout.tsx
[Front-End]/page.tsx
[Front-End]/README.md
app/api/analyze/route.ts
app/api/incidents/[id]/route.ts
app/api/incidents/route.ts
app/api/ingest/route.ts
app/api/vapi/webhook/route.ts
app/globals.css
app/landing/page.tsx
app/layout.tsx
app/page.tsx
commit-and-push.sh
commit.sh
COMPLETION_SUMMARY.md
components/DemoMode.tsx
components/FireSpreadControls.tsx
components/FireSpreadOverlay.tsx
components/landing/TacticalButton.tsx
components/landing/TacticalGrid.tsx
components/landing/TargetingReticle.tsx
components/landing/WireframeBuilding.tsx
components/ReasoningLog.tsx
components/SituationReport.tsx
components/SituationVisualizer.tsx
components/VoiceRecorder.tsx
data/demoScenarios.ts
data/layouts/apartment.json
data/layouts/forest.json
data/layouts/office.json
data/layouts/school.json
deploy.sh
DEPLOYMENT_READY.md
ENV_VARS_PRODUCTION.txt
eslint.config.mjs
FINAL_IMPLEMENTATION_SUMMARY.md
FIRE_SPREAD_IMPLEMENTATION_SUMMARY.md
FIRE_SPREAD_VISUALIZATION.md
GIT_WORKFLOW.md
IMPLEMENTATION_SUMMARY.md
ISSUES_FOUND.md
ISSUES_REPORT.md
LANDING_PAGE_INTEGRATION.md
LANDING_PAGE.md
lib/mongo.ts
LICENSE
models/Incident.ts
next.config.ts
package.json
postcss.config.mjs
PRODUCTION_ENV_SETUP.md
push.sh
QUICK_REFERENCE.md
README.md
SETUP_INSTRUCTIONS.md
SYSTEM_ARCHITECTURE.md
TESTING_GUIDE.md
tsconfig.json
types/index.ts
utils/envValidation.ts
utils/layoutSelector.ts
utils/reasoningGenerator.ts
utils/runAnalysis.ts
utils/visualizationLogic.ts
VAPI_SETUP_DETAILED.md
VAPI_SETUP.md
VAPI_TROUBLESHOOTING.md
VERCEL_DEPLOYMENT_AUDIT.md
```

### Dependencies

- package.json: @tailwindcss/postcss@^4, @types/node@^20, @types/react@^19, @types/react-dom@^19, @vapi-ai/web@^2.5.2, eslint@^9, eslint-config-next@16.1.3, framer-motion@^12.26.2, lucide-react@^0.562.0, mongoose@^9.1.4, next@^16.1.3, openai@^6.16.0, react@^19.2.3, react-dom@^19.2.3, tailwindcss@^4, typescript@^5, zod@^4.3.5

### Recent commits (newest first)

- Fix legend text visibility
- Production security fixes
- vercel finals
- Vercel Implementation
- DONE
- Authroized/Installed vapi ai function - calling!!
- Update dependencies and refactor analyze API logic
- - Improved the logic for hazard detection and updated related UI elements for better clarity.
- Add framer-motion and lucide-react dependencies; update README and project structure
- Full Implementation Summary
- - Removed logging fetch calls for risk color determination.
- Enhance hazard detection display in SituationReport component
- Organize project structure into Front-End and Back-End folders
- Basic To-do list finished
- Initial commit

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

### GIT_WORKFLOW.md

```markdown
# Git Workflow Guide - IGNIS

## ✅ SSH Configuration Complete

Your repository is now configured to use SSH for all git operations:
- **Remote URL**: `git@github.com:Aniridh/CruzHacks-Jan16-18.git`
- **SSH Authentication**: Already configured (tested successfully)

## 📝 Quick Commands

### Commit Changes
```bash
./commit.sh "Your descriptive commit message"
```

### Push to Remote
```bash
./push.sh
```

### Commit and Push in One Step
```bash
./commit-and-push.sh "Your descriptive commit message"
```

## 📋 Examples

```bash
# Example 1: Commit new feature
./commit.sh "Add confidence summary panel to SituationReport"

# Example 2: Commit bug fix
./commit.sh "Fix OpenAI API initialization for build"

# Example 3: Commit and push immediately
./commit-and-push.sh "Organize files into Front-End and Back-End folders"

# Example 4: Just push existing commits
./push.sh
```

## 🔧 How It Works

1. **`commit.sh`** - Stages all changes (`git add -A`) and commits with your message
2. **`push.sh`** - Pushes commits to `origin main` via SSH
3. **`commit-and-push.sh`** - Does both in one command

All scripts provide clear feedback with ✅ success and ❌ error messages.

## 🚨 Important Notes

- **Always provide a commit message** - The scripts will fail without one
- **SSH keys required** - Make sure your SSH keys are added to GitHub
- **Pushes go to `main` branch** - The scripts use `git push origin main`

## 📂 Current Status

To see what files need to be committed:
```bash
git status
```

To see what's staged:
```bash
git status --short
```

## 🔄 Alternative: Manual Git Commands

If you prefer using git directly:

```bash
# Stage all changes
git add -A

# Commit with message
git commit -m "Your message here"

# Push via SSH (automatic with current setup)
git push origin main
```

The helper scripts just make this faster and more convenient!

```

### PRODUCTION_ENV_SETUP.md

```markdown
# 🔐 Production Environment Variables - Quick Setup

## ⚡ REQUIRED FOR VERCEL (Copy-Paste Ready)

### Vercel Dashboard → Settings → Environment Variables

**Add these ONE BY ONE:**

```
Name: OPENAI_API_KEY
Value: sk-your-actual-openai-key-here
Environment: Production
```

```
Name: NEXT_PUBLIC_VAPI_PUBLIC_KEY
Value: 57ac3c37-a8aa-429b-8d94-afbfff2cab86
Environment: Production
```

**OPTIONAL** (add if you have them):

```
Name: NEXT_PUBLIC_VAPI_ASSISTANT_ID
Value: your-assistant-id
Environment: Production
```

```
Name: VAPI_WEBHOOK_SECRET
Value: your-webhook-secret
Environment: Production
```

---

## ✅ Environment Variable Reference

| Variable | Type | Required? | Default Behavior |
|----------|------|-----------|------------------|
| `OPENAI_API_KEY` | Server | NO | Uses rule-based fallback |
| `NEXT_PUBLIC_VAPI_PUBLIC_KEY` | Client | NO | Demo mode only |
| `NEXT_PUBLIC_VAPI_ASSISTANT_ID` | Client | NO | Uses inline config |
| `VAPI_WEBHOOK_SECRET` | Server | NO | Webhook validation disabled |
| `MONGODB_URI` | Server | NO | No incident logging |

---

## 🚨 SECURITY RULES

### ✅ DO:
- Use `NEXT_PUBLIC_` prefix for browser-safe values only
- Keep API keys without `NEXT_PUBLIC_` prefix server-side
- Add all env vars in Vercel Dashboard, not in code
- Redeploy after adding new env vars

### ❌ DON'T:
- Never commit `.env.local` to git
- Never log API key values
- Never use `NEXT_PUBLIC_` for secrets
- Never hardcode API keys in code

---

## 🎯 Production Deployment Checklist

### Before Deploy:
- [ ] Add `OPENAI_API_KEY` to Vercel (recommended)
- [ ] Add `NEXT_PUBLIC_VAPI_PUBLIC_KEY` to Vercel (optional)
- [ ] Verify `.env.local` is in `.gitignore`
- [ ] Remove all `console.log` with secrets

### Deploy:
```bash
vercel --prod
```

### After Deploy:
- [ ] Test `/landing` page
- [ ] Test demo mode (works without keys)
- [ ] Test voice mode (if Vapi key added)
- [ ] Check browser console (no secret logs)

---

## 🔍 How to Check Security

### Browser Console (F12):
```
❌ BAD: "API Key: sk-abc123..."
✅ GOOD: "[Vapi] Initializing..."
```

### Vercel Logs:
```
❌ BAD: console.log(process.env.OPENAI_API_KEY)
✅ GOOD: console.log('[API] Processing request...')
```

---

## 🚀 Quick Deploy (2 Minutes)

```bash
# 1. Add ONE env var to Vercel
OPENAI_API_KEY=sk-your-key

# 2. Deploy
vercel --prod

# 3. Test
# Visit: https://your-app.vercel.app/landing
# Click: Demo Scenarios → Select any → Should work!
```

**Done! System works with just OpenAI key.**

---

## 📝 Current Status

- ✅ **No secrets logged** in production
- ✅ **Graceful fallbacks** if keys missing
- ✅ **Validation utility** prevents misconfig
- ✅ **Production-safe** error messages
- ✅ **Works offline** with demo mode

**Security Score**: 10/10 ✅

```

### package.json

```
{
  "name": "ignis-app",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint"
  },
  "dependencies": {
    "@vapi-ai/web": "^2.5.2",
    "framer-motion": "^12.26.2",
    "lucide-react": "^0.562.0",
    "mongoose": "^9.1.4",
    "next": "^16.1.3",
    "openai": "^6.16.0",
    "react": "^19.2.3",
    "react-dom": "^19.2.3",
    "zod": "^4.3.5"
  },
  "devDependencies": {
    "@tailwindcss/postcss": "^4",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "16.1.3",
    "tailwindcss": "^4",
    "typescript": "^5"
  }
}

```

### app/layout.tsx

```typescript
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "IGNIS - Emergency Insight System",
  description: "AI-powered decision-support system that transforms emergency calls into structured spatial insights for first responders",
};

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

```

### types/index.ts

```typescript
// Core data models for IGNIS

export type EnvironmentType = 'apartment' | 'office' | 'school' | 'forest' | 'warehouse';

export type UrgencyLevel = 'critical' | 'high' | 'medium' | 'low';

export type Severity = 'high' | 'medium' | 'low';

export interface Transcript {
  text: string;
  timestamp?: Date;
}

export interface FireOrigin {
  floor: number;
  area: string;
  coordinates?: { x: number; y: number };
  confidence: number; // 0-100
}

export interface Landmark {
  name: string;
  type: string;
  location: string;
}

export interface Hazard {
  type: string;
  location: string;
  severity: Severity;
  confidence: number; // 0-100
}

export interface SituationAnalysis {
  environmentType: EnvironmentType;
  environmentConfidence: number; // 0-100
  fireOrigin: FireOrigin;
  landmarks: Landmark[];
  hazards: Hazard[];
  urgency: UrgencyLevel;
  spreadProbability?: number; // 0-100 (likelihood of rapid fire spread)
  inferred: boolean; // indicates if data was inferred vs. explicitly stated
}

export interface Room {
  id: string;
  name: string;
  type: string;
  coordinates: { x: number; y: number; width: number; height: number };
}

export interface Exit {
  id: string;
  name: string;
  coordinates: { x: number; y: number };
  type: 'door' | 'stairwell' | 'elevator' | 'window';
}

export interface LayoutTemplate {
  id: string;
  environmentType: EnvironmentType;
  name: string;
  floors: number;
  rooms: Room[];
  exits: Exit[];
  coordinateSystem: { width: number; height: number }; // normalized coordinates
}

export interface RiskZone {
  id: string;
  coordinates: { x: number; y: number; width: number; height: number };
  severity: Severity;
  confidence: number;
}

export interface SafePath {
  id: string;
  points: { x: number; y: number }[];
  priority: number; // lower number = higher priority
  description: string;
}

export interface StrikeNode {
  id: string;
  coordinates: { x: number; y: number };
  type: string;
  priority: number;
  description: string;
}

export interface VisualizationData {
  layout: LayoutTemplate;
  fireOrigin: FireOrigin;
  riskZones: RiskZone[];
  safePaths: SafePath[];
  strikeNodes: StrikeNode[];
}

export interface DecisionReasoning {
  riskZoneReasoning: { zoneId: string; explanation: string; confidence: number }[];
  pathReasoning: { pathId: string; explanation: string; recommended: boolean }[];
  strikeNodeReasoning: { nodeId: string; explanation: string; priority: number }[];
  uncertaintyMarkers: { field: string; explanation: string; confidence: number }[];
}

```

### app/page.tsx

```typescript
'use client';

import { useState } from 'react';
import DemoMode from '@/components/DemoMode';
import VoiceRecorder from '@/components/VoiceRecorder';
import SituationReport from '@/components/SituationReport';
import { DemoScenario } from '@/data/demoScenarios';
import { SituationAnalysis } from '@/types';

export default function Home() {
  const [transcript, setTranscript] = useState<string>('');
  const [analysis, setAnalysis] = useState<SituationAnalysis | null>(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [inputMode, setInputMode] = useState<'demo' | 'voice'>('voice'); // Default to voice mode

  const analyzeTranscript = async (transcriptText: string) => {
    setTranscript(transcriptText);
    setAnalysis(null);
    setError(null);
    setIsLoading(true);

    try {
      // Call the analyze API
      const response = await fetch('/api/analyze', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          transcript: transcriptText,
        }),
      });

      if (!response.ok) {
        const errorData = await response.json();
        throw new Error(errorData.error || 'Failed to analyze transcript');
      }

      const analysisData: SituationAnalysis = await response.json();
      setAnalysis(analysisData);
    } catch (err) {
      console.error('Error analyzing transcript:', err);
      setError(err instanceof Error ? err.message : 'An unknown error occurred');
    } finally {
      setIsLoading(false);
    }
  };

  const handleScenarioSelect = async (scenario: DemoScenario) => {
    await analyzeTranscript(scenario.transcript.text);
  };

  const handleVoiceTranscript = async (transcriptText: string) => {
    await analyzeTranscript(transcriptText);
  };

  return (
    <div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900">
      {/* Header */}
      <header className="bg-gradient-to-r from-red-600 via-orange-600 to-red-700 text-white shadow-2xl border-b-2 border-orange-500/50">
        <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
          <div className="flex items-center justify-between">
            <div>
              <div className="flex items-center gap-3 mb-2">
                <div className="w-2 h-2 bg-orange-400 rounded-full animate-pulse" />
                <h1 className="text-4xl font-bold tracking-tight">IGNIS</h1>
              </div>
              <p className="text-orange-100 text-lg font-light">
                Emergency Insight System — Transforming emergency calls into clear spatial insight
              </p>
            </div>
            <a
              href="/landing"
              className="hidden md:flex items-center gap-2 px-6 py-3 bg-slate-900/50 hover:bg-slate-900 backdrop-blur-sm rounded border border-orange-500/50 hover:border-orange-400 text-sm font-medium transition-all duration-300 hover:scale-105"
            >
              <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 19l-7-7m0 0l7-7m-7 7h18" />
              </svg>
              Landing Page
            </a>
          </div>
        </div>
      </header>

      {/* Main Content */}
      <main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10">
        {/* Mode Toggle */}
        <div className="mb-8 flex justify-center gap-4">
          <button
            onClick={() => setInputMode('voice')}
            className={`px-8 py-4 rounded-lg font-semibold transition-all duration-300 ${
              inputMode === 'voice'
                ? 'bg-gradient-to-r from-orange-500 to-red-600 text-white shadow-lg shadow-orange-500/50'
                : 'bg-slate-800/50 text-gray-400 hover:text-gray-200 border border-slate-700'
            }`}
          >
            🎤 Live Voice Input
          </button>
          <button
            onClick={() => setInputMode('demo')}
            className={`px-8 py-4 rounded-lg font-semibold transition-all duration-300 ${
              inputMode === 'demo'
                ? 'bg-gradient-to-r from-orange-500 to-red-600 text-white shadow-lg shadow-orange-500/50'
                : 'bg-slate-800/50 text-gray-400 hover:text-gray-200 border border-slate-700'
            }`}
          >
            📋 Demo Scenarios
          </button>
        </div>

        {/* Voice Recorder */}
        {inputMode === 'voice' && (
          <div className="mb-8 relative">
            <VoiceRecorder onTranscriptComplete={handleVoiceTranscript} />
          </div>
        )}

        {/* Demo Mode */}
        {inputMode === 'demo' && (
          <DemoMode onScenarioSelect={handleScenarioSelect} isLoading={isLoading} />
        )}

        {/* Error Display */}
        {error && (
          <div className="relative mb-6 p-6 bg-gradient-to-br from-red-900/50 to-red-950/50 border-2 border-red-500/50 rounded-xl shadow-2xl backdrop-blur-sm">
            <div className="absolute top-0 left-0 w-3 h-3 border-t-2 border-l-2 border-red-500" />
            <div className="absolute top-0 right-0 w-3 h-3 border-t-2 border-r-2 border-red-500" />
            <div className="absolute bottom-0 left-0 w-3 h-3 border-b-2 border-l-2 border-red-500" />
            <div className="absolute bottom-0 right-0 w-3 h-3 border-b-2 border-r-2 border-red-500" />
            
            <div className="flex items-center gap-2 mb-3">
              <div className="w-2 h-2 bg-red-500 rounded-full animate-pulse" />
              <h3 className="font-bold text-red-400 font-mono uppercase tracking-wider text-sm">System Error</h3>
            </div>
            <p className="text-red-200">{error}</p>
          </div>
        )}

        {/* Situation Report */}
        {transcript && (
          <SituationReport transcript={transcript} analysis={analysis} isLoading={isLoading} 
[truncated — 3034 more characters]
```

### app/landing/page.tsx

```typescript
'use client';

import React from 'react';
import { motion } from 'framer-motion';
import { TacticalGrid } from '@/components/landing/TacticalGrid';
import { WireframeBuilding } from '@/components/landing/WireframeBuilding';
import { TargetingReticle } from '@/components/landing/TargetingReticle';
import { TacticalButton } from '@/components/landing/TacticalButton';
import { Shield, Radio, Crosshair } from 'lucide-react';
import { useRouter } from 'next/navigation';

export default function LandingPage() {
  const router = useRouter();

  return (
    <main className="relative w-full h-screen bg-[#020617] text-slate-200 overflow-hidden font-sans selection:bg-orange-500/30 selection:text-orange-200">
      {/* Background Layers */}
      <TacticalGrid />
      <WireframeBuilding />
      <TargetingReticle />

      {/* Content Container */}
      <div className="relative z-20 w-full h-full flex flex-col justify-center items-center px-4 sm:px-6 lg:px-8">
        {/* Header Section */}
        <div className="max-w-4xl w-full text-center space-y-8">
          {/* Tactical Label */}
          <motion.div
            initial={{ opacity: 0, y: -20 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.6, delay: 0.2 }}
            className="flex items-center justify-center gap-2 text-orange-500 font-mono text-xs tracking-[0.3em] uppercase mb-4"
          >
            <span className="w-2 h-2 bg-orange-500 animate-pulse" />
            System Online
            <span className="w-2 h-2 bg-orange-500 animate-pulse" />
          </motion.div>

          {/* Main Title */}
          <motion.h1
            initial={{ opacity: 0, scale: 0.9 }}
            animate={{ opacity: 1, scale: 1 }}
            transition={{ duration: 0.8, delay: 0.4 }}
            className="text-5xl md:text-7xl font-bold tracking-tight text-white uppercase"
          >
            <span className="inline-block bg-clip-text text-transparent bg-gradient-to-b from-white to-slate-400">
              IGNIS
            </span>
            <span className="block text-transparent bg-clip-text bg-gradient-to-b from-orange-400 to-orange-600 mt-2">
              Emergency Insight System
            </span>
          </motion.h1>

          {/* Description */}
          <motion.p
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            transition={{ duration: 0.8, delay: 0.6 }}
            className="max-w-2xl mx-auto text-lg text-slate-400 font-light leading-relaxed border-l-2 border-orange-500/30 pl-6 text-left md:text-center md:border-l-0 md:pl-0"
          >
            AI-powered decision-support system transforming emergency calls into
            structured spatial insights for first responders. Real-time analysis,
            visualization, and strategic coordination when every second matters.
          </motion.p>

          {/* Feature Grid */}
          <motion.div
            initial={{ opacity: 0, y: 20 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.8, delay: 0.8 }}
            className="grid grid-cols-1 md:grid-cols-3 gap-4 max-w-3xl mx-auto py-8"
          >
            <div className="bg-slate-900/40 border border-slate-800 p-4 backdrop-blur-sm flex flex-col items-center gap-2 group hover:border-orange-500/50 transition-colors">
              <Shield className="w-6 h-6 text-orange-500 mb-1" />
              <h3 className="font-mono text-sm text-slate-300 uppercase">
                AI Analysis
              </h3>
              <p className="text-xs text-slate-500 text-center">
                GPT-4 powered situational awareness extraction
              </p>
            </div>
            <div className="bg-slate-900/40 border border-slate-800 p-4 backdrop-blur-sm flex flex-col items-center gap-2 group hover:border-orange-500/50 transition-colors">
              <Radio className="w-6 h-6 text-orange-500 mb-1" />
              <h3 className="font-mono text-sm text-slate-300 uppercase">
                Spatial Mapping
              </h3>
              <p className="text-xs text-slate-500 text-center">
                Real-time fire zone and safe path visualization
              </p>
            </div>
            <div className="bg-slate-900/40 border border-slate-800 p-4 backdrop-blur-sm flex flex-col items-center gap-2 group hover:border-orange-500/50 transition-colors">
              <Crosshair className="w-6 h-6 text-orange-500 mb-1" />
              <h3 className="font-mono text-sm text-slate-300 uppercase">
                Decision Support
              </h3>
              <p className="text-xs text-slate-500 text-center">
                Transparent reasoning with confidence scores
              </p>
            </div>
          </motion.div>

          {/* Actions */}
          <motion.div
            initial={{ opacity: 0, y: 20 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.8, delay: 1 }}
            className="flex flex-col sm:flex-row items-center justify-center gap-6"
          >
            <TacticalButton
              variant="primary"
              onClick={() => router.push('/')}
            >
              Launch Dashboard
            </TacticalButton>
            <TacticalButton
              variant="secondary"
              onClick={() => window.open('https://github.com/Aniridh/CruzHacks-Jan16-18', '_blank')}
            >
              View on GitHub
            </TacticalButton>
          </motion.div>
        </div>
      </div>

      {/* Footer Status Bar */}
      <div className="absolute bottom-0 left-0 right-0 h-10 bg-slate-950/80 border-t border-slate-800 flex items-center justify-between px-6 z-30 backdrop-blur-md">
        <div className="flex items-center gap-4 font-mono text-[10px] text-slate-500">
          <span className="text-orange-500">● LIVE SYSTEM</span>
          <span>CRUZHACKS 2026 - JUSTICE TRACK</span>
        </div>
        <div className="flex
[truncated — 208 more characters]
```

### [Front-End]/app/landing-page.tsx

```typescript
'use client';

import React, { useEffect, useState } from 'react';
import { motion } from 'framer-motion';
import { TacticalGrid } from '@/components/landing/TacticalGrid';
import { WireframeBuilding } from '@/components/landing/WireframeBuilding';
import { TargetingReticle } from '@/components/landing/TargetingReticle';
import { TacticalButton } from '@/components/landing/TacticalButton';
import { Shield, Radio, Crosshair } from 'lucide-react';
import { useRouter } from 'next/navigation';

export default function LandingPage() {
  const [mounted, setMounted] = useState(false);
  const router = useRouter();

  useEffect(() => {
    setMounted(true);
  }, []);

  return (
    <main className="relative w-full h-screen bg-[#020617] text-slate-200 overflow-hidden font-sans selection:bg-orange-500/30 selection:text-orange-200">
      {/* Background Layers */}
      <TacticalGrid />
      <WireframeBuilding />
      <TargetingReticle />

      {/* Content Container */}
      <div className="relative z-20 w-full h-full flex flex-col justify-center items-center px-4 sm:px-6 lg:px-8">
        {/* Header Section */}
        <div className="max-w-4xl w-full text-center space-y-8">
          {/* Tactical Label */}
          <motion.div
            initial={{ opacity: 0, y: -20 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.6, delay: 0.2 }}
            className="flex items-center justify-center gap-2 text-orange-500 font-mono text-xs tracking-[0.3em] uppercase mb-4"
          >
            <span className="w-2 h-2 bg-orange-500 animate-pulse" />
            System Online
            <span className="w-2 h-2 bg-orange-500 animate-pulse" />
          </motion.div>

          {/* Main Title */}
          <motion.h1
            initial={{ opacity: 0, scale: 0.9 }}
            animate={{ opacity: 1, scale: 1 }}
            transition={{ duration: 0.8, delay: 0.4 }}
            className="text-5xl md:text-7xl font-bold tracking-tight text-white uppercase"
          >
            <span className="inline-block bg-clip-text text-transparent bg-gradient-to-b from-white to-slate-400">
              IGNIS
            </span>
            <span className="block text-transparent bg-clip-text bg-gradient-to-b from-orange-400 to-orange-600 mt-2">
              Emergency Insight System
            </span>
          </motion.h1>

          {/* Description */}
          <motion.p
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            transition={{ duration: 0.8, delay: 0.6 }}
            className="max-w-2xl mx-auto text-lg text-slate-400 font-light leading-relaxed border-l-2 border-orange-500/30 pl-6 text-left md:text-center md:border-l-0 md:pl-0"
          >
            AI-powered decision-support system transforming emergency calls into
            structured spatial insights for first responders. Real-time analysis,
            visualization, and strategic coordination when every second matters.
          </motion.p>

          {/* Feature Grid */}
          <motion.div
            initial={{ opacity: 0, y: 20 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.8, delay: 0.8 }}
            className="grid grid-cols-1 md:grid-cols-3 gap-4 max-w-3xl mx-auto py-8"
          >
            <div className="bg-slate-900/40 border border-slate-800 p-4 backdrop-blur-sm flex flex-col items-center gap-2 group hover:border-orange-500/50 transition-colors">
              <Shield className="w-6 h-6 text-orange-500 mb-1" />
              <h3 className="font-mono text-sm text-slate-300 uppercase">
                AI Analysis
              </h3>
              <p className="text-xs text-slate-500 text-center">
                GPT-4 powered situational awareness extraction
              </p>
            </div>
            <div className="bg-slate-900/40 border border-slate-800 p-4 backdrop-blur-sm flex flex-col items-center gap-2 group hover:border-orange-500/50 transition-colors">
              <Radio className="w-6 h-6 text-orange-500 mb-1" />
              <h3 className="font-mono text-sm text-slate-300 uppercase">
                Spatial Mapping
              </h3>
              <p className="text-xs text-slate-500 text-center">
                Real-time fire zone and safe path visualization
              </p>
            </div>
            <div className="bg-slate-900/40 border border-slate-800 p-4 backdrop-blur-sm flex flex-col items-center gap-2 group hover:border-orange-500/50 transition-colors">
              <Crosshair className="w-6 h-6 text-orange-500 mb-1" />
              <h3 className="font-mono text-sm text-slate-300 uppercase">
                Decision Support
              </h3>
              <p className="text-xs text-slate-500 text-center">
                Transparent reasoning with confidence scores
              </p>
            </div>
          </motion.div>

          {/* Actions */}
          <motion.div
            initial={{ opacity: 0, y: 20 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.8, delay: 1 }}
            className="flex flex-col sm:flex-row items-center justify-center gap-6"
          >
            <TacticalButton
              variant="primary"
              onClick={() => router.push('/')}
            >
              Launch Dashboard
            </TacticalButton>
            <TacticalButton
              variant="secondary"
              onClick={() => window.open('https://github.com/Aniridh/CruzHacks-Jan16-18', '_blank')}
            >
              View on GitHub
            </TacticalButton>
          </motion.div>
        </div>
      </div>

      {/* Footer Status Bar */}
      <div className="absolute bottom-0 left-0 right-0 h-10 bg-slate-950/80 border-t border-slate-800 flex items-center justify-between px-6 z-30 backdrop-blur-md">
        <div className="flex items-center gap-4 font-mono text-[10px] text-slate-500">
          <span className="text-ora
[truncated — 335 more characters]
```

### app/api/analyze/route.ts

```typescript
import { NextRequest, NextResponse } from 'next/server';
import { runAnalysis } from '@/utils/runAnalysis';

export async function POST(request: NextRequest) {
  try {
    const { transcript } = await request.json();

    if (!transcript || typeof transcript !== 'string') {
      return NextResponse.json(
        { error: 'Invalid transcript' },
        { status: 400 }
      );
    }

    const analysis = await runAnalysis(transcript);
    return NextResponse.json(analysis);
  } catch (error) {
    console.error('Error in analyze API:', error);
    return NextResponse.json(
      { error: 'Internal server error', details: error instanceof Error ? error.message : 'Unknown error' },
      { status: 500 }
    );
  }
}

```

### app/api/ingest/route.ts

```typescript
// Manual transcript ingestion endpoint
// For testing without Vapi
// NO DATABASE - returns analysis directly

import { NextRequest, NextResponse } from 'next/server';
import { runAnalysis } from '@/utils/runAnalysis';

export async function POST(request: NextRequest) {
  try {
    const { transcript } = await request.json();

    if (!transcript || typeof transcript !== 'string') {
      return NextResponse.json(
        { error: 'Invalid transcript', details: 'Transcript must be a non-empty string' },
        { status: 400 }
      );
    }

    console.log('Manual ingest - analyzing transcript:', transcript.substring(0, 100) + '...');

    // Run analysis pipeline
    const analysis = await runAnalysis(transcript);

    console.log('Analysis complete:', {
      environmentType: analysis.environmentType,
      urgency: analysis.urgency,
      spreadProbability: analysis.spreadProbability,
    });

    return NextResponse.json({
      success: true,
      analysis,
      source: 'manual_ingest',
      timestamp: new Date().toISOString(),
    });
  } catch (error) {
    console.error('Error in ingest API:', error);
    return NextResponse.json(
      {
        success: false,
        error: 'Internal server error',
        details: error instanceof Error ? error.message : 'Unknown error',
      },
      { status: 500 }
    );
  }
}

// GET for testing
export async function GET() {
  return NextResponse.json({
    service: 'IGNIS Manual Ingest',
    status: 'ready',
    usage: 'POST { "transcript": "your transcript here" }',
    timestamp: new Date().toISOString(),
  });
}

```

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