# Project export: Adrei

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: Cal Hacks 12.0
- Tagline: “Find your next home with confidence, not confusion
- Devpost: https://devpost.com/software/adrei-03nbps
- GitHub: https://github.com/AmolBhalerao8/CalHacks12.git
- Team: 0 GitHub contributor(s) — 

## Devpost submission (written by the team)

### Inspiration

Finding reliable housing as a student or renter is frustrating and time-consuming. Scattered data, outdated listings, and fake reviews make it difficult to trust what’s online — especially for students moving to new cities or countries. We wanted to build something that brings transparency, trust, and intelligence into the housing search process. That’s how Room Hunt was born — an agentic AI platform that combines Bright Data web scraping, Gemini LLM reasoning, and Mapbox geospatial intelligence to help users discover verified, well-rated apartments with evidence-backed insights.

### What it does

Room Hunt allows users to search for apartments using natural language queries, such as: “Find apartments under $1,800 within 1 mile of CSU Chico with good management.” The system automatically: Understands the query using Gemini LLM. Understands the query using Gemini LLM. Scrapes listings from trusted housing sources via Bright Data. Scrapes listings from trusted housing sources via Bright Data. Enriches data with reviews, amenities, and neighborhood information. Enriches data with reviews, amenities, and neighborhood information. Calculates distances using Mapbox and Google Maps APIs. Calculates distances using Mapbox and Google Maps APIs. Analyzes sentiment to evaluate management, maintenance, and safety. Analyzes sentiment to evaluate management, maintenance, and safety. Scores each property (0–100) across five key categories: Scores each property (0–100) across five key categories: Management – reliability & responsiveness Locality – safety & accessibility Value for Money – rent vs amenities Maintenance – repair quality Commute – proximity to university Each result is displayed as an Apartment Intelligence Card — showing verified data, category scores, and transparent explanations so users can make confident decisions. “Find apartments under $1,800 within 1 mile of CSU Chico with good management.” The system automatically: Understands the query using Gemini LLM. Understands the query using Gemini LLM. Scrapes listings from trusted housing sources via Bright Data. Scrapes listings from trusted housing sources via Bright Data. Enriches data with reviews, amenities, and neighborhood information. Enriches data with reviews, amenities, and neighborhood information.

### How we built it

Bright Data → multi-source web scraping (Zillow, Apartments.com, Google Maps, Reddit). Gemini LLM → query interpretation, reasoning, and summarization. Mapbox + Google Maps API → geocoding, distance, and nearby points-of-interest analysis. Python (Pandas, NLP) → data cleaning, enrichment, sentiment scoring. React + Node.js → prototype frontend and API integration. To quantify apartment performance, we built a weighted scoring model:

### Challenges we ran into

Handling dynamic and changing website structures during scraping. Dealing with incomplete / inconsistent data across platforms. Managing API rate limits and scraping costs efficiently. Maintaining LLM explainability — every score required traceable reasoning. Ensuring smooth multi-API integration without latency bottlenecks.

### Accomplishments we're proud of

Built a fully functional end-to-end AI pipeline — Query → Scrape → Enrich → Score → Summarize. Seamlessly integrated Bright Data, Gemini, Mapbox, and Google Maps APIs. Delivered transparent, evidence-backed scoring to promote user trust. Designed an intuitive UI for apartment filtering, comparison, and visualization. Demonstrated how agentic AI orchestration can make complex, real-world searches simple and reliable.

### What we learned

How to orchestrate LLMs as autonomous agents for reasoning and enrichment. The importance of cross-source validation in ensuring data reliability. How geospatial APIs enhance contextual insights for decision-making. Why explainability and user trust are as vital as technical performance.

### What's next

AI-based roommate matchmaking — connecting renters with compatible housemates through behavior, lifestyle, and habit analysis. Personalized scoring that adapts to user preferences (e.g., safety vs price). Fraud detection and lease document analysis for safer renting. User profiles and saved searches for ongoing discovery. Expansion to multiple universities and cities with continuous data freshness tracking. Our long-term vision is to make Room Hunt the most transparent, personalized, and trustworthy AI housing assistant — helping people find homes and roommates they’ll truly love.

## README (from the GitHub repository)

# ADREI - AI-Driven Real Estate Intelligence System

**ADREI** (AI-Driven Real Estate Intelligence) is an advanced property analysis platform that uses a sophisticated **multi-agent AI framework** with **Bright Data APIs** for web scraping, **Google Gemini AI** for intelligent data extraction and analysis, and **Mapbox** for location-based filtering.

## 🌟 Features

- **Intelligent Query Caching**:
  - AI detects similar queries (e.g., "apartments near UC Berkeley" = "housing near Berkeley campus")
  - Automatically reuses previously scraped data
  - Skips Stage 1 & 2 entirely for duplicate searches
  - Saves API costs and time!
- **Four-Stage Processing**:
  - **Stage 0**: Check query cache with AI similarity detection
  - **Stage 1**: Quick scan for address, price, and URL (if not cached)
  - **Stage 2**: **Parallel deep scrape** - Sends all requests to Bright Data simultaneously (6-8x faster!) + **AI Detail Extraction** (bedrooms, bathrooms, amenities, utilities, contact info) stored in metadata
  - **Stage 3**: Distance-based filtering with AI-powered location understanding + Mapbox geocoding
  - **Stage 4**: AI-powered property management quality analysis & scoring (0-100)
- **Intelligent Search**: Uses Bright Data's SERP API to search Google for apartment listings
- **Multi-Site Scraping**: Automatically visits and scrapes multiple apartment listing websites
- **AI-Powered Parsing**: Leverages Google Gemini to extract structured data from diverse HTML layouts
- **Location Intelligence**: 
  - AI corrects spelling mistakes and typos automatically
  - Expands abbreviations (e.g., "MIT" → full name with location)
  - Calculate walking distances from any location (universities, workplaces, etc.)
  - Fallback to straight-line distance if route calculation fails
- **Smart Caching**: 
  - Avoids re-scraping already visited apartments
  - Caches geocoded coordinates for all apartments
  - Caches distance calculations between apartment + target location pairs
  - Caches management analysis results permanently
  - Up to 90% API cost savings on repeat queries!
- **Unique IDs**: Each apartment gets a deterministic ID for linking data from multiple sources
- **Price Filtering**: Filter by price (e.g., "under $2000")
- **Rich Data Storage**: Saves complete HTML and comprehensive metadata for future reference
  - Basic info: address, price, URL
  - **Apartment details**: bedrooms, bathrooms, square feet, deposit, lease length
  - **Amenities**: pool, gym, parking, etc.
  - **Utilities**: water, electricity, internet (included or tenant pays)
  - **Contact info**: phone, email, office hours
  - **Pet policy** and **parking** details
- **Management Analysis**: 
  - AI-powered reputation research
  - Quality scoring (0-100 scale)
  - Strengths, concerns, and recommendations
  - Permanently cached to avoid re-analysis
- **🆕 Detailed Apartment Viewer**: 
  - Query "look into apartment 3" to see ALL details for a specific apartment
  - Shows bedrooms, bathrooms, amenities, utilities, contact info
  - Displays comprehensive scores from all research agents
  - Shows management analysis with key strengths and concerns
- **🆕 4 Specialized Research Agents** (Micro-Location Analysis):
  - **Security Agent**: Street lighting, crime, foot traffic, security features (5 sub-metrics)
  - **Accessibility Agent**: Sidewalks, healthcare, transit, wheelchair access (5 sub-metrics)
  - **Pet Friendliness Agent**: Parks, vets, pet stores, walking quality (5 sub-metrics)
  - **Lifestyle Agent**: Dining, nightlife, entertainment, shopping, fitness (5 sub-metrics)
  - Each agent analyzes **specific street + 2-3 block radius** (not city-wide!)
  - Provides overall score + multiple sub-metrics + key findings + detailed reasoning
  - Answers ANY question: "is it safe?", "good for pets?", "wheelchair accessible?", "good nightlife?"
  - Cached permanently (instant results on re-query)
- **High Performance**: 
  - Parallel processing in Stage 2 (all apartments scraped simultaneously)
  - 6-8x faster than sequential scraping
  - Bright Data cloud handles concurrent requests efficiently

## 🏗️ Architecture

```
                        STAGE 0: INTELLIGENT QUERY CACHE
User Query → Gemini AI (Normalize Query) → Check Query Cache → 
Compare with Previous Queries → [MATCH?] → Load Cached Apartments
                                    ↓ [NO MATCH]
                                    
                        STAGE 1: DISCOVERY
SERP API (Google Search) → Filter Relevant Sites → 
Web Unlocker (Scrape) → Gemini AI (Extract Address/Price/URL) → 
Price Filter

                        STAGE 2: PARALLEL COLLECTION
Batch All Apartments → Parallel Requests to Bright Data Cloud → 
Wait for All Responses → Save Raw Markdown + Metadata → Generate Unique IDs →
Save Query to Cache

                        STAGE 3: LOCATION FILTERING (Optional)
User Distance Query → Gemini AI (Interpret & Correct Location) → 
Mapbox Geocoding (Target Location) → Mapbox Geocoding (Each Apartment) → 
Calculate Walking Distances → Filter by Radius → Save Distance Data

                        STAGE 4: MANAGEMENT ANALYSIS (Optional)
User Details Query → Load Apartment Markdown → Gemini AI (Extract Management Name) →
Gemini AI (Analyze Reputation & Quality) → Score 0-100 → 
Save Analysis to Metadata → Display Results
```

## 📋 Prerequisites

### Required Accounts & API Keys

1. **Bright Data Account** (https://brightdata.com)
   - API Key
   - SERP Zone configured
   - Web Unlocker Zone configured

2. **Google AI Studio Account** (https://ai.google.dev/)
   - Google API Key for Gemini

3. **Mapbox Account** (https://www.mapbox.com/)
   - Mapbox Access Token (for geocoding and distance calculations)

4. **Node.js** (v16 or higher)

## 🚀 Setup Instructions

### 1. Navigate to Project

```bash
cd "C:\Studies\Project\CAL HACKS 12.0\Room Hunt"
```

### 2. Install Dependencies

```bash
npm install
```

This will install:
- `dotenv` - Environment variable management
- `node-fetch` - HTTP client for API calls
- `@google/generative-ai` - Google Gemini AI SDK
- `@mapbox/mapbox-sdk` - Mapbox Geocoding & Directions API
- `readline` - CLI interface

### 3. Configure Environment Variables

Create a `.env` file in the project root with the following:

```env
# Bright Data API Configuration
BRIGHTDATA_API_KEY="your_brightdata_api_key_here"
BRIGHTDATA_SERP_ZONE="your_serp_zone_id_here"
BRIGHTDATA_UNLOCKER_ZONE_ID="your_unlocker_zone_id_here"

# Google AI Configuration
GOOGLE_API_KEY="your_google_api_key_here"

# Mapbox Configuration (Optional - hardcoded token included, but you can override)
MAPBOX_ACCESS_TOKEN="your_mapbox_access_token_here"
```

#### How to Get Your API Keys:

**Bright Data:**
1. Log in to your Bright Data dashboard
2. Go to **Zones** → Find your **SERP Zone** → Copy the Zone ID
3. Go to **Zones** → Find your **Web Unlocker Zone** → Copy the Zone ID
4. Go to **Settings** → **API** → Copy your API Key

**Google Gemini:**
1. Visit https://ai.google.dev/
2. Click **Get API Key**
3. Create a new project or select existing
4. Copy your API key

**Mapbox:**
1. Visit https://www.mapbox.com/
2. Sign up for a free account
3. Go to **Account** → **Access tokens**
4. Copy your default public token (or create a new one)
5. Note: Free tier includes 100,000 requests/month

### 4. Test the Setup (Optional)

Test Bright Data connectivity:
```bash
npm test
```

This runs the demo script to verify your API credentials work.

## 💻 Usage

### Option 1: Web Interface - ADREI Web UI (NEW! 🎉)

Start the ADREI web server:
```bash
npm run web
```

Then open your browser to: **http://localhost:3000**

*Server file: `web-server.js`*

Features:
- 🎨 Professional white-themed UI with intelligent chat assistant
- 🏠 Dynamic apartment cards with comprehensive property data
- 💬 Interactive ADREI Assistant (same intelligence as CLI)
- 📊 Real-time updates via Server-Sent Events
- 🔍 Detailed property viewer with full analysis
- 🔗 Direct links to origina

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 9 recognized source files, 212 KB.
- CSS (language) — detected in the code
- Express (technology) — detected in the code
- Google Gemini (technology) — detected in the code
- JavaScript (language) — detected in the code
- Node.js (technology) — claimed on Devpost, not found in the code
- Python (language) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (120 of 224)

```
.cursorignore
.gitignore
apartment_data/apt_0037a961837f__dist_13d8946a.json
apartment_data/apt_0037a961837f.json
apartment_data/apt_00de7cf05d59__dist_f1e236c8.json
apartment_data/apt_00de7cf05d59.json
apartment_data/apt_01e01f9f8438__dist_13d8946a.json
apartment_data/apt_01e01f9f8438.json
apartment_data/apt_0675c24d6c34__dist_13d8946a.json
apartment_data/apt_0675c24d6c34.json
apartment_data/apt_0951b2ae2559__dist_f1e236c8.json
apartment_data/apt_0951b2ae2559.json
apartment_data/apt_098bba247878__dist_13d8946a.json
apartment_data/apt_098bba247878.json
apartment_data/apt_09f6bd36f239.json
apartment_data/apt_0a49ac40eaa1__dist_13d8946a.json
apartment_data/apt_0a49ac40eaa1.json
apartment_data/apt_0d4455901df4__dist_f1e236c8.json
apartment_data/apt_0d4455901df4.json
apartment_data/apt_10bfae361f71__dist_13d8946a.json
apartment_data/apt_10bfae361f71.json
apartment_data/apt_11a1c3b610a2__dist_13d8946a.json
apartment_data/apt_11a1c3b610a2.json
apartment_data/apt_14ac844fe46e__dist_13d8946a.json
apartment_data/apt_14ac844fe46e.json
apartment_data/apt_1570017cff4f__dist_f1e236c8.json
apartment_data/apt_1570017cff4f.json
apartment_data/apt_16690b1cf12a__dist_13d8946a.json
apartment_data/apt_16690b1cf12a.json
apartment_data/apt_168035c57f9e__dist_f1e236c8.json
apartment_data/apt_168035c57f9e.json
apartment_data/apt_18cc5ddacf68__dist_f1e236c8.json
apartment_data/apt_18cc5ddacf68.json
apartment_data/apt_20c263037510__dist_13d8946a.json
apartment_data/apt_20c263037510.json
apartment_data/apt_21b5d75903ca__dist_f1e236c8.json
apartment_data/apt_21b5d75903ca.json
apartment_data/apt_21eac76f6bca__dist_13d8946a.json
apartment_data/apt_21eac76f6bca.json
apartment_data/apt_253530441a99__dist_13d8946a.json
apartment_data/apt_253530441a99.json
apartment_data/apt_2678501058a7__dist_13d8946a.json
apartment_data/apt_2678501058a7.json
apartment_data/apt_2ca79c82c858__dist_f1e236c8.json
apartment_data/apt_2ca79c82c858.json
apartment_data/apt_2caf1e18a7fa__dist_f1e236c8.json
apartment_data/apt_2caf1e18a7fa.json
apartment_data/apt_32b33d49803f__dist_f1e236c8.json
apartment_data/apt_32b33d49803f.json
apartment_data/apt_3387b3b4343f__dist_13d8946a.json
apartment_data/apt_3387b3b4343f.json
apartment_data/apt_33b86a9936a8__dist_f1e236c8.json
apartment_data/apt_33b86a9936a8.json
apartment_data/apt_3684362be06b.json
apartment_data/apt_378c94bed402__dist_f1e236c8.json
apartment_data/apt_378c94bed402.json
apartment_data/apt_3dcf3ddedaa3__dist_f1e236c8.json
apartment_data/apt_3dcf3ddedaa3.json
apartment_data/apt_3de404090d30__dist_13d8946a.json
apartment_data/apt_3de404090d30.json
apartment_data/apt_402304ccb205__dist_13d8946a.json
apartment_data/apt_402304ccb205.json
apartment_data/apt_40e5fc3bb718__dist_f1e236c8.json
apartment_data/apt_40e5fc3bb718.json
apartment_data/apt_41653e8497cb__dist_f1e236c8.json
apartment_data/apt_41653e8497cb.json
apartment_data/apt_431d984431ad__dist_f1e236c8.json
apartment_data/apt_431d984431ad.json
apartment_data/apt_4495cf54e32b__dist_13d8946a.json
apartment_data/apt_4495cf54e32b.json
apartment_data/apt_4571b050d5ad__dist_13d8946a.json
apartment_data/apt_4571b050d5ad.json
apartment_data/apt_4c478f7a477b__dist_13d8946a.json
apartment_data/apt_4c478f7a477b.json
apartment_data/apt_4cbf65bdea9d__dist_13d8946a.json
apartment_data/apt_4cbf65bdea9d.json
apartment_data/apt_4daa22360e7a__dist_13d8946a.json
apartment_data/apt_4daa22360e7a.json
apartment_data/apt_509466fff80f__dist_13d8946a.json
apartment_data/apt_509466fff80f.json
apartment_data/apt_51cda29224d7__dist_13d8946a.json
apartment_data/apt_51cda29224d7.json
apartment_data/apt_66d8f02fc34c__dist_13d8946a.json
apartment_data/apt_66d8f02fc34c.json
apartment_data/apt_69fc2297f11d__dist_13d8946a.json
apartment_data/apt_69fc2297f11d.json
apartment_data/apt_6d5ab335974b__dist_13d8946a.json
apartment_data/apt_6d5ab335974b.json
apartment_data/apt_6ebfc11db11f__dist_13d8946a.json
apartment_data/apt_6ebfc11db11f.json
apartment_data/apt_713924134c88__dist_13d8946a.json
apartment_data/apt_713924134c88.json
apartment_data/apt_722d0efeb3b5__dist_f1e236c8.json
apartment_data/apt_722d0efeb3b5.json
apartment_data/apt_7365f3114070__dist_13d8946a.json
apartment_data/apt_7365f3114070.json
apartment_data/apt_73f06b8aeae0__dist_f1e236c8.json
apartment_data/apt_73f06b8aeae0.json
apartment_data/apt_74fd3fbbc93d__dist_13d8946a.json
apartment_data/apt_74fd3fbbc93d.json
apartment_data/apt_7a1e962be5b4__dist_13d8946a.json
apartment_data/apt_7a1e962be5b4.json
apartment_data/apt_7b8a20bc261c__dist_f1e236c8.json
apartment_data/apt_7b8a20bc261c.json
apartment_data/apt_7fa6cf02ae27__dist_f1e236c8.json
apartment_data/apt_7fa6cf02ae27.json
apartment_data/apt_860d49895084__dist_f1e236c8.json
apartment_data/apt_860d49895084.json
apartment_data/apt_86ea0ddfaff2__dist_f1e236c8.json
apartment_data/apt_86ea0ddfaff2.json
apartment_data/apt_8787b77660d9__dist_13d8946a.json
apartment_data/apt_8787b77660d9.json
apartment_data/apt_87e73cd8707d__dist_f1e236c8.json
apartment_data/apt_87e73cd8707d.json
apartment_data/apt_8b37756d4362__dist_13d8946a.json
apartment_data/apt_8b37756d4362.json
apartment_data/apt_90ab61fe0b8d__dist_f1e236c8.json
apartment_data/apt_90ab61fe0b8d.json
apartment_data/apt_933d1d0192a3__dist_f1e236c8.json
apartment_data/apt_933d1d0192a3.json
[104 more files omitted for size]
```

### Dependencies

- package.json: @google/generative-ai@^0.2.1, @mapbox/mapbox-sdk@^0.16.0, dotenv@^16.3.1, express@^4.21.2, node-fetch@^3.3.2, readline@^1.3.0

### Recent commits (newest first)

- Initial commit: ADREI - AI-Driven Real Estate Intelligence System for Cal Hacks 12.0

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

### package.json

```
{
  "name": "adrei",
  "version": "1.0.0",
  "description": "ADREI - AI-Driven Real Estate Intelligence System. Advanced property analysis using multi-agent AI framework, Bright Data, and Google Gemini",
  "main": "apartment-finder.js",
  "type": "module",
  "scripts": {
    "start": "node apartment-finder.js",
    "web": "node web-server.js",
    "test": "node brightdata_api_demo.js"
  },
  "keywords": [
    "adrei",
    "ai",
    "real-estate",
    "apartment",
    "rental",
    "intelligence",
    "multi-agent",
    "bright-data",
    "gemini",
    "property-analysis"
  ],
  "author": "",
  "license": "MIT",
  "dependencies": {
    "@google/generative-ai": "^0.2.1",
    "@mapbox/mapbox-sdk": "^0.16.0",
    "dotenv": "^16.3.1",
    "express": "^4.21.2",
    "node-fetch": "^3.3.2",
    "readline": "^1.3.0"
  }
}

```

### public/app.js

```javascript
// Frontend JavaScript for Apartment Finder

// Generate unique client ID
const clientId = 'client_' + Math.random().toString(36).substr(2, 9);

// DOM Elements
const chatMessages = document.getElementById('chat-messages');
const chatInput = document.getElementById('chat-input');
const sendBtn = document.getElementById('send-btn');
const apartmentsContainer = document.getElementById('apartments-container');
const statusIndicator = document.getElementById('status-indicator');
const statusText = document.getElementById('status-text');

// State
let isProcessing = false;
let eventSource = null;

// Initialize SSE connection
function initializeSSE() {
    eventSource = new EventSource(`/api/stream/${clientId}`);
    
    eventSource.onopen = () => {
        updateStatus('connected', 'Connected');
        addSystemMessage('✓ Connected to server');
    };
    
    eventSource.onerror = () => {
        updateStatus('disconnected', 'Disconnected');
        addSystemMessage('✗ Connection lost. Retrying...');
        
        // Retry connection
        setTimeout(() => {
            initializeSSE();
        }, 3000);
    };
    
    eventSource.onmessage = (event) => {
        const message = JSON.parse(event.data);
        handleServerMessage(message);
    };
}

// Update status indicator
function updateStatus(status, text) {
    statusIndicator.className = `status-dot ${status}`;
    statusText.textContent = text;
}

// Handle messages from server
function handleServerMessage(message) {
    const { type, data } = message;
    
    switch (type) {
        case 'log':
            addLogMessage(data);
            break;
        case 'error':
            addErrorMessage(data);
            isProcessing = false;
            updateSendButton();
            break;
        case 'apartments':
            displayApartments(data);
            isProcessing = false;
            updateSendButton();
            break;
        case 'apartment_detail':
            displayApartmentDetail(data);
            isProcessing = false;
            updateSendButton();
            break;
        default:
            console.log('Unknown message type:', type, data);
    }
}

// Add messages to chat
function addUserMessage(text) {
    const messageDiv = document.createElement('div');
    messageDiv.className = 'message user-message';
    messageDiv.innerHTML = `<strong>You:</strong> ${escapeHtml(text)}`;
    chatMessages.appendChild(messageDiv);
    scrollToBottom();
}

function addSystemMessage(text) {
    const messageDiv = document.createElement('div');
    messageDiv.className = 'message system-message';
    messageDiv.innerHTML = `<strong>System:</strong> ${escapeHtml(text)}`;
    chatMessages.appendChild(messageDiv);
    scrollToBottom();
}

function addLogMessage(text) {
    const messageDiv = document.createElement('div');
    messageDiv.className = 'message log-message';
    messageDiv.textContent = text;
    chatMessages.appendChild(messageDiv);
    scrollToBottom();
}

function addErrorMessage(text) {
    const messageDiv = document.createElement('div');
    messageDiv.className = 'message error-message';
    messageDiv.innerHTML = `<strong>Error:</strong> ${escapeHtml(text)}`;
    chatMessages.appendChild(messageDiv);
    scrollToBottom();
}

// Scroll chat to bottom
function scrollToBottom() {
    chatMessages.scrollTop = chatMessages.scrollHeight;
}

// Escape HTML to prevent XSS
function escapeHtml(text) {
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
}

// Send query to server
async function sendQuery() {
    const query = chatInput.value.trim();
    
    if (!query || isProcessing) return;
    
    // Add user message to chat
    addUserMessage(query);
    chatInput.value = '';
    
    // Update UI
    isProcessing = true;
    updateSendButton();
    addSystemMessage('Processing your query...');
    
    try {
        const response = await fetch('/api/query', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({ query, clientId })
        });
        
        if (!response.ok) {
            throw new Error('Server error');
        }
        
        // Results will come through SSE
    } catch (error) {
        addErrorMessage('Failed to send query: ' + error.message);
        isProcessing = false;
        updateSendButton();
    }
}

// Update send button state
function updateSendButton() {
    sendBtn.disabled = isProcessing;
    sendBtn.innerHTML = isProcessing 
        ? '<span class="loading"></span>' 
        : `<span>Send</span>
           <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
               <line x1="22" y1="2" x2="11" y2="13"></line>
               <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
           </svg>`;
}

// Display apartments as cards
function displayApartments(apartments) {
    if (!apartments || apartments.length === 0) {
        apartmentsContainer.innerHTML = `
            <div class="welcome-message">
                <h2>No apartments found</h2>
                <p>Try a different query or adjust your filters.</p>
            </div>
        `;
        addSystemMessage('No apartments found');
        return;
    }
    
    apartmentsContainer.innerHTML = '';
    
    apartments.forEach((apt, index) => {
        const card = createApartmentCard(apt, index + 1);
        apartmentsContainer.appendChild(card);
    });
    
    addSystemMessage(`✓ Displaying ${apartments.length} apartment(s)`);
}

// Create apartment card element
function createApartmentCard(apt, number) {
    const card = document.createElement('div');
    card.className = 'apartment-card';
    
    // Load full metadata if available
    const price = apt.price || 'N/A';
    const address = apt.address || 'N/A';
    const listingUrl = apt.listing_url || null;
    
    // Check for additional data
 
[truncated — 14343 more characters]
```

### brightdata_api_demo.js

```javascript
/**
 * Bright Data API Demo
 * Direct HTTP requests - maximum control and flexibility
 */

import 'dotenv/config';
import fetch from 'node-fetch';
import fs from 'fs';
import path from 'path';

const CONFIG = {
  apiKey: process.env.BRIGHTDATA_API_KEY,
  serpZone: process.env.SERP_ZONE,
  unlockerZone: process.env.UNLOCKER_ZONE,
  endpoint: 'https://api.brightdata.com/request'
};

console.log('METHOD 1: Direct API Requests\n');

// Example 1: Search
async function demoSearch(maxResults = 10) {
  console.log('Example 1: Searching Google...');
  
  const searchUrl = 'https://www.google.com/search?q=web+scraping+2025&brd_json=1';
  
  const response = await fetch(CONFIG.endpoint, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${CONFIG.apiKey}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      zone: CONFIG.serpZone,
      url: searchUrl,
      format: 'raw'
    })
  });

  const data = await response.json();
  const results = data.organic || [];
  
  console.log(`Found ${results.length} results:`);
  results.forEach((r, i) => console.log(`  ${i + 1}. ${r.title}`));
  console.log();

  // Collect up to maxResults URLs
  const urls = [];
  for (const r of results) {
    const u = r?.link || r?.url || r?.href;
    if (u) urls.push(u);
    if (urls.length >= maxResults) break;
  }
  return urls;
}

// Example 2: Fetch multiple pages concurrently and save as HTML
async function demoFetchAll(urls) {
  console.log('Example 2: Fetching page content (concurrent)...');

  if (!urls || urls.length === 0) {
    console.log('  No URLs to fetch; skipping.');
    return [];
  }

  const outDir = path.join(process.cwd(), 'out');
  if (!fs.existsSync(outDir)) {
    fs.mkdirSync(outDir, { recursive: true });
  }

  function sanitizeFilename(str) {
    return str.replace(/[^a-z0-9._-]/gi, '-').slice(0, 120);
  }

  const jobs = urls.map((url, idx) => (async () => {
    try {
      const res = await fetch(CONFIG.endpoint, {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${CONFIG.apiKey}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          zone: CONFIG.unlockerZone,
          url,
          format: 'raw',
          data_format: 'html'
        })
      });

      const html = await res.text();
      const u = new URL(url);
      const baseName = `${String(idx + 1).padStart(2, '0')}-${sanitizeFilename(u.hostname + u.pathname) || 'page'}.html`;
      const filePath = path.join(outDir, baseName);
      fs.writeFileSync(filePath, html, 'utf8');
      console.log(`  Saved: ${filePath} (${html.length} chars)`);
      return { url, filePath, length: html.length };
    } catch (err) {
      console.log(`  Failed: ${url} (${err.message})`);
      return { url, error: err.message };
    }
  })());

  const results = await Promise.all(jobs);
  console.log();
  return results;
}

// Run demos
async function main() {
  try {
    const urls = await demoSearch(10);
    await demoFetchAll(urls);
    
    console.log('API demos complete');
    console.log('\nKey Points:');
    console.log('  - Simple HTTP POST requests');
    console.log('  - Works with any HTTP client');
    console.log('  - Bright Data handles blocking/CAPTCHAs');
    
  } catch (error) {
    console.error('Error:', error.message);
    process.exit(1);
  }
}

main();

```

### cache-manager.js

```javascript
/**
 * Cache Manager Module
 * Handles data storage, caching, and file management
 */

import fs from 'fs';
import path from 'path';
import crypto from 'crypto';
import { CONFIG } from './api-services.js';
import { normalizeQuery, areQueriesSimilar } from './api-services.js';

// Create output directory if it doesn't exist
if (!fs.existsSync(CONFIG.outputDir)) {
  fs.mkdirSync(CONFIG.outputDir, { recursive: true });
}

// ============================================================================
// UNIQUE ID GENERATION
// ============================================================================

/**
 * Generate a unique, deterministic ID for an apartment based on its URL
 * This allows consistent identification across different data sources
 */
export function generateApartmentId(url) {
  const hash = crypto.createHash('sha256').update(url).digest('hex');
  return `apt_${hash.substring(0, 12)}`;
}

// ============================================================================
// URL CACHE MANAGEMENT
// ============================================================================

/**
 * Load cache of previously scraped URLs from metadata files
 */
export function loadScrapedURLsCache() {
  const cache = new Map();
  
  if (!fs.existsSync(CONFIG.outputDir)) {
    return cache;
  }

  const files = fs.readdirSync(CONFIG.outputDir);
  const metadataFiles = files.filter(f => f.endsWith('.json') && !f.includes('index') && !f.includes('cache') && !f.includes('distance'));

  metadataFiles.forEach(file => {
    try {
      const metadata = JSON.parse(fs.readFileSync(path.join(CONFIG.outputDir, file), 'utf8'));
      if (metadata.listing_url) {
        cache.set(metadata.listing_url, {
          apartmentId: metadata.apartment_id || file.replace('.json', ''),
          htmlFile: file.replace('.json', '.html'),
          metadataFile: file,
          scrapedAt: metadata.scraped_at
        });
      }
    } catch (error) {
      // Skip invalid files
    }
  });

  return cache;
}

/**
 * Check if a URL has already been scraped
 */
export function isAlreadyScraped(url, cache) {
  return cache.has(url);
}

// ============================================================================
// FILE STORAGE
// ============================================================================

/**
 * Save raw HTML and metadata for an apartment
 */
export function saveRawHTML(html, listing, index) {
  const apartmentId = generateApartmentId(listing.listing_url);
  
  const htmlFilename = `${apartmentId}.html`;
  const metadataFilename = `${apartmentId}.json`;
  
  const htmlPath = path.join(CONFIG.outputDir, htmlFilename);
  const metadataPath = path.join(CONFIG.outputDir, metadataFilename);
  
  // Save raw HTML
  fs.writeFileSync(htmlPath, html, 'utf8');
  
  // Save metadata
  const metadata = {
    apartment_id: apartmentId,
    address: listing.address || 'N/A',
    price: listing.price || 'N/A',
    listing_url: listing.listing_url || 'N/A',
    scraped_at: new Date().toISOString(),
    html_file: htmlFilename,
    html_size_bytes: html.length
  };
  fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
  
  console.log(`[Storage] ✅ Saved HTML: ${htmlFilename}`);
  console.log(`[Storage] ✅ Saved metadata: ${metadataFilename}`);
  console.log(`[Storage] 🆔 Apartment ID: ${apartmentId}`);
  
  return { htmlPath, metadataPath, apartmentId };
}

/**
 * Load HTML content for an apartment
 */
export function loadApartmentHTML(apartmentId) {
  const htmlPath = path.join(CONFIG.outputDir, `${apartmentId}.html`);
  
  if (fs.existsSync(htmlPath)) {
    try {
      return fs.readFileSync(htmlPath, 'utf8');
    } catch (error) {
      return null;
    }
  }
  return null;
}

/**
 * Updates the master index file with all apartments
 */
export function updateMasterIndex(apartments) {
  const indexPath = path.join(CONFIG.outputDir, 'apartments_index.json');
  
  let existingIndex = [];
  
  if (fs.existsSync(indexPath)) {
    try {
      existingIndex = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
    } catch (error) {
      console.log('[Index] Creating new index file');
    }
  }
  
  const indexMap = new Map(existingIndex.map(apt => [apt.apartment_id, apt]));
  
  apartments.forEach(apt => {
    indexMap.set(apt.apartment_id, {
      apartment_id: apt.apartment_id,
      address: apt.address,
      price: apt.price,
      listing_url: apt.listing_url,
      last_updated: new Date().toISOString()
    });
  });
  
  const updatedIndex = Array.from(indexMap.values())
    .sort((a, b) => new Date(b.last_updated) - new Date(a.last_updated));
  
  fs.writeFileSync(indexPath, JSON.stringify(updatedIndex, null, 2));
  console.log(`\n[Index] ✅ Updated master index: ${updatedIndex.length} total apartments`);
  console.log(`[Index] 📄 File: apartments_index.json`);
}

// ============================================================================
// INTELLIGENT QUERY CACHING
// ============================================================================

/**
 * Load query cache from disk
 */
export function loadQueryCache() {
  const cachePath = path.join(CONFIG.outputDir, 'query_cache.json');
  if (fs.existsSync(cachePath)) {
    try {
      const cache = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
      return cache.queries || [];
    } catch (error) {
      console.log('[Query Cache] Creating new cache file');
      return [];
    }
  }
  return [];
}

/**
 * Save query to cache
 */
export function saveQueryToCache(queryData) {
  const cachePath = path.join(CONFIG.outputDir, 'query_cache.json');
  const cache = loadQueryCache();
  
  cache.push(queryData);
  
  fs.writeFileSync(cachePath, JSON.stringify({ queries: cache }, null, 2));
  console.log(`[Query Cache] Saved query cache`);
}

/**
 * Check if we have cached results for a similar query
 */
export async function checkQueryCache(userQuery) {
  console.log('\n[Query Cache] Checking for similar previous searches...');
  
  const cache = loadQueryC
[truncated — 8885 more characters]
```

### apartment-finder-new.js

```javascript
/**
 * Apartment Finder - Main Orchestration & CLI
 * Coordinates the apartment search process and provides user interface
 */

import readline from 'readline';
import fs from 'fs';
import path from 'path';
import fetch from 'node-fetch';

// Import API services
import { 
  CONFIG,
  searchGoogle,
  scrapeUrl,
  quickExtractListings,
  normalizeQuery,
  interpretLocationWithAI,
  geocodeAddress,
  calculateDistance
} from './api-services.js';

// Import cache management
import {
  generateApartmentId,
  loadScrapedURLsCache,
  isAlreadyScraped,
  saveRawHTML,
  updateMasterIndex,
  checkQueryCache,
  loadApartmentsByIds,
  saveQueryToCache,
  saveDistanceData
} from './cache-manager.js';

// ============================================================================
// FILTERING FUNCTIONS
// ============================================================================

function filterApartmentSites(serpResults) {
  const excludeDomains = [
    'zillow.com',
    'youtube.com',
    'facebook.com',
    'twitter.com',
    'linkedin.com',
    'wikipedia.org',
    'pinterest.com'
  ];

  return serpResults.filter(result => {
    const link = result.link || '';
    const title = (result.title || '').toLowerCase();
    
    const isExcluded = excludeDomains.some(domain => link.includes(domain));
    if (isExcluded) return false;
    
    const apartmentKeywords = ['apartment', 'rental', 'housing', 'rent', 'property', 'lease'];
    const hasKeyword = apartmentKeywords.some(keyword => title.includes(keyword) || link.includes(keyword));
    
    return hasKeyword;
  });
}

// ============================================================================
// PRICE EXTRACTION AND FILTERING
// ============================================================================

function extractMaxPrice(query) {
  const patterns = [
    /under\s+\$?([\d,]+)/i,
    /below\s+\$?([\d,]+)/i,
    /less than\s+\$?([\d,]+)/i,
    /max\s+\$?([\d,]+)/i,
    /maximum\s+\$?([\d,]+)/i,
    /\$?([\d,]+)\s+or less/i,
  ];

  for (const pattern of patterns) {
    const match = query.match(pattern);
    if (match) {
      return parseInt(match[1].replace(/,/g, ''));
    }
  }
  return null;
}

function filterByPrice(listings, maxPrice) {
  if (!maxPrice) return listings;

  return listings.filter(listing => {
    if (!listing.price || listing.price === 'N/A') return true;
    
    const priceMatch = listing.price.match(/\$?([\d,]+)/);
    if (priceMatch) {
      const price = parseInt(priceMatch[1].replace(/,/g, ''));
      return price <= maxPrice;
    }
    return true;
  });
}

// ============================================================================
// DISTANCE QUERY PARSING
// ============================================================================

function parseDistanceQuery(query) {
  const patterns = [
    /within\s+(\d+(?:\.\d+)?)\s+(mile|miles|mi|meter|meters|m|km|kilometer|kilometers)\s+(?:of|from)\s+(.+)/i,
    /less\s+than\s+(\d+(?:\.\d+)?)\s+(mile|miles|mi|meter|meters|m|km|kilometer|kilometers)\s+(?:of|from)\s+(.+)/i,
    /under\s+(\d+(?:\.\d+)?)\s+(mile|miles|mi|meter|meters|m|km|kilometer|kilometers)\s+(?:of|from)\s+(.+)/i,
  ];

  for (const pattern of patterns) {
    const match = query.match(pattern);
    if (match) {
      const distance = parseFloat(match[1]);
      const unit = match[2].toLowerCase();
      const location = match[3].trim();
      
      let distanceInMiles = distance;
      if (unit.startsWith('m') && !unit.startsWith('mi')) {
        distanceInMiles = distance * 0.000621371;
      } else if (unit.startsWith('km')) {
        distanceInMiles = distance * 0.621371;
      }
      
      return { distance: distanceInMiles, unit, originalDistance: distance, location };
    }
  }
  return null;
}

// ============================================================================
// MAIN APARTMENT FINDER LOGIC
// ============================================================================

async function findApartments(userQuery) {
  console.log('\n' + '='.repeat(80));
  console.log('APARTMENT FINDER - INTELLIGENT QUERY CACHING');
  console.log('='.repeat(80));
  console.log(`Query: ${userQuery}\n`);

  // Check query cache first!
  const cachedQueryResult = await checkQueryCache(userQuery);
  
  if (cachedQueryResult && cachedQueryResult.apartment_ids.length > 0) {
    console.log(`\n${'='.repeat(80)}`);
    console.log('USING CACHED RESULTS - SKIPPING STAGE 1 & 2');
    console.log('='.repeat(80));
    console.log(`[Cache] Loading ${cachedQueryResult.apartment_ids.length} apartments from previous search...`);
    
    const apartments = loadApartmentsByIds(cachedQueryResult.apartment_ids);
    
    console.log(`[Cache] ✅ Loaded ${apartments.length} apartments from cache`);
    console.log(`[Cache] 💰 Saved API costs by reusing existing data!`);
    
    return apartments;
  }

  console.log('\n[New Search] Proceeding with Stage 1 & 2...');

  const maxPrice = extractMaxPrice(userQuery);
  if (maxPrice) {
    console.log(`[Filter] Max price detected: $${maxPrice}`);
  }

  // ===== STAGE 1: Find Listing Pages =====
  console.log('\n' + '='.repeat(60));
  console.log('STAGE 1: SCANNING FOR LISTINGS');
  console.log('='.repeat(60));

  const enhancedQuery = `${userQuery} apartments rent`;
  const serpResults = await searchGoogle(enhancedQuery, 10);

  if (serpResults.length === 0) {
    console.log('\n❌ No search results found');
    return [];
  }

  const filteredResults = filterApartmentSites(serpResults);
  const topResults = filteredResults.slice(0, 5);

  console.log(`\n[Stage 1] Found ${topResults.length} relevant sites (excluding Zillow):`);
  topResults.forEach((result, i) => {
    console.log(`  ${i + 1}. ${result.title?.substring(0, 60) || 'N/A'}...`);
    console.log(`     ${result.link || 'N/A'}`);
  });

  if (topResults.length === 0) {
    console.log('\n❌ No relevant sites found');
    return [];
  }

  const allBasicListings = [];
  for (const result of topResults) {
    c
[truncated — 13898 more characters]
```

### web-server.js

```javascript
/**
 * Web Server for Apartment Finder
 * Provides web interface while maintaining CLI backend flow
 */

import express from 'express';
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
import fetch from 'node-fetch';

// Import backend functions
import { 
  CONFIG,
  searchGoogle,
  scrapeUrl,
  quickExtractListings,
  extractApartmentDetails,
  extractManagementName,
  analyzeManagement,
  detectResearchAgent,
  runSecurityResearch,
  runAccessibilityResearch,
  runPetFriendlinessResearch,
  runLifestyleResearch,
  interpretLocationWithAI,
  geocodeAddress,
  calculateDistance
} from './api-services.js';

import {
  generateApartmentId,
  loadScrapedURLsCache,
  isAlreadyScraped,
  saveRawHTML,
  checkQueryCache,
  loadApartmentsByIds,
  saveQueryToCache,
  saveManagementAnalysis,
  loadManagementAnalysis,
  loadApartmentHTML,
  saveAgentResearch,
  loadAgentResearch,
  saveDistanceData,
  loadDistanceData,
  generateLocationKey,
  saveApartmentCoordinates,
  loadApartmentCoordinates
} from './cache-manager.js';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const app = express();
const PORT = 3000;

// Middleware
app.use(express.json());
app.use(express.static('public'));

// Store active SSE connections
const clients = new Map();

// Helper function to send messages to frontend
function sendToClient(clientId, type, data) {
  const client = clients.get(clientId);
  if (client) {
    try {
      client.write(`data: ${JSON.stringify({ type, data })}\n\n`);
    } catch (error) {
      console.error('Error sending to client:', error.message);
      clients.delete(clientId);
    }
  }
}

// Helper functions for query detection
function parseLookIntoQuery(query) {
  const patterns = [
    /look (?:into|at|closely at) (?:apartment |apt )?(\d+)/i,
    /show (?:me )?(?:apartment |apt )?(\d+)/i,
    /details? (?:for|of|on) (?:apartment |apt )?(\d+)/i,
    /apartment (\d+) details?/i
  ];
  for (const pattern of patterns) {
    const match = query.match(pattern);
    if (match) return parseInt(match[1]);
  }
  return null;
}

function parseKeepTopKQuery(query) {
  const patterns = [
    /keep (?:top |first )?(\d+)/i,
    /show (?:only |me |top |first )?(\d+)/i,
    /limit (?:to )?(\d+)/i,
    /first (\d+)/i,
    /top (\d+)/i
  ];
  for (const pattern of patterns) {
    const match = query.match(pattern);
    if (match) return parseInt(match[1]);
  }
  return null;
}

function isManagementAnalysisQuery(query) {
  const keywords = ['detail', 'report', 'summary', 'information', 'analysis', 'management', 'review', 'quality', 'score'];
  return keywords.some(keyword => query.toLowerCase().includes(keyword));
}

function filterApartmentSites(serpResults) {
  const excludeDomains = ['zillow.com', 'youtube.com', 'facebook.com', 'twitter.com'];
  return serpResults.filter(result => {
    const link = result.link || '';
    const isExcluded = excludeDomains.some(domain => link.includes(domain));
    if (isExcluded) return false;
    const apartmentKeywords = ['apartment', 'rental', 'housing', 'rent', 'property'];
    const title = (result.title || '').toLowerCase();
    return apartmentKeywords.some(keyword => link.includes(keyword) || title.includes(keyword));
  });
}

// Main apartment search function
async function findApartments(userQuery, clientId) {
  sendToClient(clientId, 'log', 'APARTMENT FINDER - INTELLIGENT QUERY CACHING');
  
  // Check cache
  const cachedQueryResult = await checkQueryCache(userQuery);
  if (cachedQueryResult && cachedQueryResult.apartment_ids.length > 0) {
    sendToClient(clientId, 'log', 'USING CACHED RESULTS - SKIPPING STAGE 1 & 2');
    const apartments = loadApartmentsByIds(cachedQueryResult.apartment_ids);
    
    // IMPORTANT: REPLACE the index (not merge) so subsequent filters work on THESE apartments only
    const indexPath = path.join(CONFIG.outputDir, 'apartments_index.json');
    fs.writeFileSync(indexPath, JSON.stringify(apartments, null, 2));
    sendToClient(clientId, 'log', `✓ Working with ${apartments.length} apartments`);
    
    const enriched = enrichApartmentsWithMetadata(apartments);
    sendToClient(clientId, 'apartments', enriched);
    return apartments;
  }
  
  sendToClient(clientId, 'log', 'New Search - Proceeding with Stage 1 & 2...');
  
  // Stage 1: Search
  sendToClient(clientId, 'log', 'STAGE 1: SCANNING FOR LISTINGS');
  const enhancedQuery = `${userQuery} apartments rent`;
  const serpResults = await searchGoogle(enhancedQuery, 10);
  
  if (serpResults.length === 0) {
    sendToClient(clientId, 'error', 'No search results found');
    return [];
  }
  
  const filteredResults = filterApartmentSites(serpResults);
  const topResults = filteredResults.slice(0, 5);
  
  sendToClient(clientId, 'log', `Found ${topResults.length} relevant sites`);
  
  const allBasicListings = [];
  for (const result of topResults) {
    const url = result.link;
    if (!url) continue;
    
    sendToClient(clientId, 'log', `Scraping: ${url.substring(0, 70)}...`);
    const html = await scrapeUrl(url);
    
    if (html) {
      const listings = await quickExtractListings(html, url);
      allBasicListings.push(...listings);
      sendToClient(clientId, 'log', `✅ Found ${listings.length} listings`);
    }
    
    await new Promise(resolve => setTimeout(resolve, 2000));
  }
  
  sendToClient(clientId, 'log', `Total listings found: ${allBasicListings.length}`);
  
  if (allBasicListings.length === 0) {
    sendToClient(clientId, 'error', 'No apartments found');
    return [];
  }
  
  // Stage 2: Deep scrape
  sendToClient(clientId, 'log', 'STAGE 2: DEEP SCRAPE - PARALLEL MODE');
  
  const urlCache = loadScrapedURLsCache();
  const cachedApartments = [];
  const apartmentsToScrape = [];
  
  allBasicListings.forEach((listing) => {
    if (!listing.listing_url || !listing.listing_url.startsWith('http')) return;
    
    if (isAlreadyScraped(listing.listing_url, urlCache)) {
    
[truncated — 22104 more characters]
```

### public/styles.css

```css
/* Modern Monochrome Design for ADREI */

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

:root {
    --primary-color: #000000;
    --primary-dark: #1a1a1a;
    --secondary-color: #333333;
    --accent-color: #666666;
    --success-color: #2d2d2d;
    --success-dark: #1a1a1a;
    --bg-primary: #ffffff;
    --bg-secondary: #f8f8f8;
    --bg-tertiary: #f0f0f0;
    --text-primary: #000000;
    --text-secondary: #666666;
    --text-tertiary: #999999;
    --border-color: #e0e0e0;
    --border-hover: #cccccc;
    --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
    --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
    --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
    --radius-sm: 4px;
    --radius-md: 8px;
    --radius-lg: 12px;
}

body {
    font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', Roboto, sans-serif;
    background: var(--bg-secondary);
    color: var(--text-primary);
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
}

.container {
    display: flex;
    height: 100vh;
    overflow: hidden;
}

/* Main Content Area */
.main-content {
    flex: 1;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: var(--bg-secondary);
}

/* Header with Logo */
.header {
    background: var(--bg-primary);
    border-bottom: 2px solid var(--primary-color);
    padding: 1.5rem 2rem;
}

.logo {
    display: flex;
    align-items: center;
    gap: 1rem;
}

.logo-icon {
    width: 48px;
    height: 48px;
    background: var(--primary-color);
    border-radius: var(--radius-md);
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
    font-size: 1.5rem;
}

.logo-text h1 {
    font-family: 'Cormorant', serif;
    font-size: 2.75rem;
    font-weight: 700;
    font-style: italic;
    letter-spacing: 0.03em;
    color: var(--text-primary);
}

.tagline {
    font-size: 0.875rem;
    color: var(--text-secondary);
    font-weight: 500;
    margin-top: 0.125rem;
}

/* Apartments Grid Container */
.apartments-grid {
    flex: 1;
    padding: 2rem;
    overflow-y: auto;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));
    grid-auto-rows: max-content;
    gap: 2rem;
    align-content: start;
}

/* Welcome Message */
.welcome-message {
    grid-column: 1 / -1;
    background: var(--bg-primary);
    border: 2px solid var(--primary-color);
    border-radius: var(--radius-lg);
    padding: 3rem;
    text-align: center;
    max-width: 700px;
    margin: 2rem auto;
}

.welcome-icon {
    width: 64px;
    height: 64px;
    background: var(--primary-color);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
    font-size: 2rem;
    margin: 0 auto 1.5rem;
}

.welcome-message h2 {
    font-size: 1.75rem;
    font-weight: 700;
    color: var(--text-primary);
    margin-bottom: 0.75rem;
}

.welcome-desc {
    color: var(--text-secondary);
    font-size: 1rem;
    line-height: 1.6;
    margin-bottom: 2rem;
}

.example-queries {
    margin-top: 2rem;
    text-align: left;
}

.example-queries h3 {
    font-size: 0.875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-tertiary);
    margin-bottom: 1rem;
}

.example-queries ul {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.example-queries li {
    padding: 0.875rem 1rem;
    background: var(--bg-primary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    color: var(--text-secondary);
    font-size: 0.875rem;
    transition: all 0.2s ease;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.example-queries li i {
    color: var(--primary-color);
    width: 16px;
    text-align: center;
}

.example-queries li:hover {
    background: var(--bg-tertiary);
    border-color: var(--primary-color);
    color: var(--text-primary);
    transform: translateX(4px);
}

/* ===== APARTMENT CARDS - SIMPLE & ROBUST ===== */

/* Card Container - Simple Flexbox */
.apartment-card {
    background: var(--bg-primary);
    border: 2px solid var(--border-color);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    transition: all 0.3s ease;
    display: block;
    width: 100%;
    min-height: 500px;
}

.apartment-card:hover {
    border-color: var(--primary-color);
    box-shadow: var(--shadow-lg);
    transform: translateY(-2px);
}

/* Card Header */
.card-header {
    background: var(--primary-color);
    color: white;
    padding: 2rem;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}

.card-number {
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    opacity: 0.85;
    margin: 0 0 0.75rem 0;
    display: block;
}

.card-price {
    font-size: 1.875rem;
    font-weight: 700;
    margin: 0 0 0.75rem 0;
    display: block;
    line-height: 1.2;
}

.card-address {
    font-size: 0.9375rem;
    opacity: 0.95;
    line-height: 1.5;
    margin: 0;
    display: block;
}

/* Card Body */
.card-body {
    padding: 2rem;
    background: var(--bg-primary);
}

/* Distance Badge */
.card-distance {
    background: var(--primary-color);
    color: white;
    padding: 1rem 1.25rem;
    border-radius: var(--radius-md);
    margin: 0 0 1.5rem 0;
    display: inline-flex;
    align-items: center;
    gap: 0.625rem;
    font-size: 0.875rem;
    font-weight: 600;
}

.distance-icon {
    font-size: 0.875rem;
}

/* Info Section */
.card-info {
    margin: 0 0 1.5rem 0;
}

.info-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 1rem 0;
    border-bottom: 1px solid var(--border-color);
}

.info-row:first-child {
    padding-top: 0;
}

.info-row:last-child {
    border-bottom: none;
    padding-bottom: 0;
}

.info-label {
    color: var(--text-secondary);
    font-size: 0.875rem;
    font-weight: 500;
    display:
[truncated — 7782 more characters]
```

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