# 34 — Enhanced IDX Search Interface

**Status:** Phase 2.5 — Tier 1 (High priority, consumer-facing, requires instruction 15 Next.js + instruction 18 MLS)

**Inspiration:** Luxury Presence proprietary split-screen IDX with 3-minute MLS refresh, subdivision filtering, 270+ MLS feed support, mobile-first design, neighborhood intelligence.

**Purpose:** Build a best-in-class property search interface for consumer-facing pages (sogbreak.io listings, agent websites, white-label portals). Deliver split-screen map + list, advanced filtering, real-time MLS sync, SEO-optimized property pages, and neighborhood intelligence.

**Dependencies:** Instruction 18 (MLS/RETS integration), instruction 15 (Next.js SSR), instruction 28 (Google One Tap for lead capture), instruction 30 (Collaborative boards for saved searches), instruction 25 (Home valuation for seller CTA).

**Scope:** 6 new database tables, 15 API endpoints, SSR property detail pages (Next.js), real-time MLS sync, neighborhood landing pages, map clustering, image optimization, SEO structured data.

---

## OVERVIEW

### The Problem

Fogbreak currently serves listings via properties.php API + vanilla JS. Search is basic (price range, beds/baths). No map interface. Lead capture is manual form submission. No neighborhood intelligence. No SEO optimization for property pages.

Result: Poor consumer experience vs. competitors. Missed SEO traffic (neighborhood + property-specific searches). Low mobile conversion.

### The Solution

**Enterprise-class search UI + consumer site:**
1. **Split-screen search:** Left panel = property cards (with photos, price, key details). Right panel = interactive map with pins, clustering, overlays.
2. **Advanced filters:** Price, beds, baths, sqft, property type, lot size, year built, garage, pool, view, stories, HOA, keywords. Saved searches + alerts.
3. **Real-time MLS sync:** Properties updated every 3 minutes. Status changes (new, price change, pending, sold) trigger alerts to matching users.
4. **SSR property pages:** Next.js pages with full details, photo gallery, price history, tax history, neighborhood stats, schools, walk score, similar properties, mortgage calculator, agent contact form.
5. **Neighborhood intelligence:** Boundary maps, median price, DOM trends, school data, walk score, crime rates (optional), local stats. Neighborhood-specific landing pages for SEO.
6. **Lead capture:** Google One Tap integration on search + property detail pages. Pre-filled contact form for showings. Opt-in to property alerts.
7. **Mobile-first:** Responsive design. Touch gestures (swipe gallery, pinch zoom map). Fast mobile performance.
8. **SEO:** Structured data (Schema.org RealEstateListing). Dynamic meta tags per property. Sitemap generation. Neighborhood landing pages.
9. **White-label:** CSS variables for tenant branding. Custom map styles per brokerage.

**Result:** 10–15% increase in consumer lead volume (vs. existing properties portal). High mobile conversion (50%+ of traffic). Improved local SEO rankings.

### Key Features

#### 1. Split-Screen Search Interface
- **Layout:** 50/50 desktop (left list, right map). Stack on mobile (<768px).
- **Left panel (list):**
  - Property cards: photo thumbnail, price, beds/baths/sqft, address, DOM (days on market), status tag (new, price change, pending)
  - Virtual scrolling for performance (render only visible cards)
  - Click card → map pans to property, pin highlighted
  - "Like" button to favorite. "Share" button.
- **Right panel (map):**
  - Leaflet.js map with OpenStreetMap (free) or Mapbox (premium)
  - Property pins colored by status: green=new, orange=price change, blue=active, gray=pending/sold
  - Cluster pins when zoomed out (e.g., "15 homes" at zoom level 12)
  - Click pin → highlight card on left, scroll into view
  - Double-click map → add/remove property bounds filter (draw polygon)
- **Responsive:** On mobile, swap to tab view (List | Map) or stacked card + small map footer

#### 2. Advanced Search Filters (Persistent & Saveable)
- **Price range:** Slider, min/max input, or preset quick filters ($500K, $1M, $2M+)
- **Beds/baths:** Min selector (0+ to 5+)
- **Sqft:** Min/max slider
- **Property type:** Checkboxes (SFR, Condo, Townhouse, Land, Multi-family, Vacant)
- **Lot size:** Min/max (acres or sqft)
- **Year built:** Min slider
- **Garage:** Min spaces (0–4)
- **Pool:** Yes/No
- **View type:** Checkboxes (Ocean, Mountain, Valley, City, Garden)
- **Stories:** 1-story, 2-story, 3+ story
- **HOA max:** Max annual fee
- **Keywords:** Text search (address, neighborhood, agent name)
- **Status:** Checkboxes (Active, Pending, Sold, New listing)
- **DOM range:** 0–30 days, 30–90 days, 90+ days (for pricing analysis)
- **Save search:** Name + set alerts (daily, instant) → linked to logged-in user

#### 3. Subdivision & Neighborhood Filtering
- **Neighborhood boundaries:** GeoJSON files per jurisdiction (loaded via API)
- **Neighborhood dropdown:** Pre-populated list (e.g., "Carmel-by-the-Sea", "Pebble Beach", "Pacific Grove")
- **Click neighborhood → map zooms to boundary, list filters to properties inside**
- **Multi-select neighborhoods:** Filter across multiple communities
- **Neighborhood cards on map:** Hover neighborhood → tooltip with median price, inventory count
- **Neighborhood landing pages (Next.js):**
  - SSR page: `/neighborhoods/carmel-by-the-sea`
  - Stats: median price, avg DOM, months of inventory, price trend (6-month chart)
  - School data: schools in area, ratings (via SchoolDigger API or data partner)
  - Walk score, crime rate (optional via API)
  - Recent sales (last 10 sold in neighborhood)
  - Featured listings (3–5 active)
  - CTA: "Search homes in {neighborhood}"
  - SEO: custom meta tags, Schema.org LocalBusiness + RealEstateMarket

#### 4. MLS Data Pipeline & Real-Time Sync
- **Data source:** RETS or RESO Web API (instruction 18) synced to `properties` table
- **Refresh cadence:** Every 3 minutes (via cron job in instruction 18)
- **Status detection:**
  - New listing: property created in last 24 hours
  - Price change: list_price changed since last sync
  - Pending: status changed to "Pending Contract"
  - Sold: status changed to "Sold"
  - Back on market: was pending/sold, now active again
- **Change triggers:**
  - User with matching saved search gets email alert (via properties.php alert engine)
  - If user is online (WebSocket connected) → real-time notification + list refresh
  - Notification tone: "New listing matching your search: {address} | {price}"
- **Historical data:** Retain all status changes in `idx_property_history` table (for price history, status timeline display)

#### 5. SSR Property Detail Pages (Next.js, Instruction 15)
- **Route:** `/properties/{property_id}/{slug}` or `/listings/{mls_number}`
- **Render:** Server-side (Next.js getServerSideProps) for SEO
- **Content:**
  - **Hero:** Full-width photo carousel (swipe on mobile, thumbnails below)
  - **Key details:** Price (large), beds, baths, sqft, lot size, year built, garage, pool, HOA, tax ID, MLS #
  - **Status badge:** New (7 days), price change, pending, sold, price per sqft
  - **Description:** Marketing remarks (from MLS)
  - **Property details table:** Detailed specs (basement, foundation, roof, heating, cooling, etc.)
  - **Photos:** Full gallery with lightbox + metadata (e.g., "Master Bedroom", "Kitchen")
  - **3D/video:** Virtual tour embed (if available from MLS or Matterport)
  - **Map:** Embedded Leaflet map showing property location + nearby schools/transit
  - **Price history:** Line chart (list price, sale price if sold, price per sqft over time)
  - **Tax history:** Table of past 3 years (estimated tax, assessment)
  - **Neighborhood stats:** Card showing median price, walk score, crime rate, school ratings (linked to neighborhood landing page)
  - **School data:** Schools serving address (elementary, middle, high) with ratings/links
  - **Walk score:** Embed Walk Score widget
  - **Similar properties:** "Recently sold nearby" (last 3 months, within 0.25 miles)
  - **Mortgage calculator:** "See what you can afford" form (loan amount, rate, down payment) → monthly payment
  - **Agent contact form:** "Schedule showing" or "Get more info" (Google One Tap pre-fill if logged in)
  - **Share buttons:** Facebook, Twitter, email, copy link

#### 6. Map Features & Overlays
- **Base layers:** Street view, satellite, street+satellite
- **Overlays (togglable):**
  - School district boundaries (GeoJSON)
  - Flood zone (FEMA flood maps via API)
  - Commute time heatmap (10 min, 20 min, 30 min to specific address via Google Maps API)
  - Neighborhood boundaries
  - MLS data points (new listings, price changes)
- **Cluster behavior:**
  - Zoom 1–10: cluster pins, show "42 homes"
  - Zoom 11–13: expand clusters
  - Zoom 14+: show all pins individually
- **Pin interactions:**
  - Hover pin → tooltip (address, price, beds, photo)
  - Click pin → highlight card, scroll into view
  - Right-click pin → context menu (view details, add to board, share)
- **Performance:** Lazy load pins based on zoom + viewport. Unload off-screen pins.

#### 7. Saved Searches & Alerts
- **Saved search:** User (authenticated or anonymous) saves search criteria
- **Alert types:** Daily digest (1 AM), instant (real-time as matches), weekly summary
- **Frequency:** Configurable per saved search
- **Unsubscribe:** One-click link in email
- **Integration:** Synced with drips.php (can auto-enroll user in buyer nurture sequence on first save, instruction 33)

#### 8. Recently Viewed & Favorites
- **Recently viewed:** Tracked per session (localStorage on frontend, server-side for logged-in users)
- **Max 10 recently viewed** shown in sidebar or dedicated page
- **Favorites (liked properties):**
  - Authenticated users only
  - Stored in `idx_favorites` table
  - Displayed in "My Favorites" page
  - Shareable board (instruction 30) — "Share my favorites" creates shareable link
  - Email digest option: "Weekly favorites summary"

#### 9. Performance & UX
- **Virtual scrolling:** List renders only 5–10 visible cards, unloads off-screen
- **Image lazy loading:** Thumbnails load on scroll into viewport, full images load on detail page
- **Debounced search:** Filter changes debounced 500ms before API call
- **Map clustering:** Off-load to Leaflet library (handles 10,000+ pins smoothly)
- **Caching:**
  - Property list results cached 5 minutes (via API response header)
  - Property detail pages cached 1 hour at CDN (Vercel/Netlify edge)
  - Neighborhood stats cached 1 day
- **Mobile optimization:** <3s first paint on 4G. Responsive images. Touch-friendly buttons (48px min).
- **Pagination:** Load next 50 results on scroll (infinite scroll) or "Load more" button option

#### 10. SEO Optimization
- **Structured data (Schema.org):**
  - `RealEstateListing` on property detail pages: schema.org/RealEstateListing with price, address, bedrooms, bathrooms, photo URLs, agent contact, description
  - `LocalBusiness` + `RealEstateMarket` on neighborhood pages: median price, inventory, trend
  - `AggregateOffer` for "homes from $X to $Y"
- **Meta tags:**
  - Property detail: `<title>` "{address} - {price} | {broker_name}"
  - `description`: "{beds}bd/{baths}ba in {neighborhood}. {price}. See photos and details."
  - `og:image`: property photo URL
  - `og:url`: property detail URL
- **Dynamic sitemap:**
  - `/sitemap.xml` → index of sitemaps
  - `/sitemap-properties.xml` → all active listings (updated daily)
  - `/sitemap-neighborhoods.xml` → all neighborhood pages
  - `/sitemap-cities.xml` → all city pages
- **Canonical URLs:** Prevent duplicate content (if same property reachable via multiple routes)
- **City/zip landing pages (Next.js):**
  - `/cities/carmel` or `/zip/93921`
  - Aggregate data: # homes for sale, price range, avg price, recently sold
  - Featured listings (3–5 active)
  - Link to neighborhood pages
  - SEO keyword targeting

#### 11. White-Label & Branding
- **CSS variables:** `--primary`, `--accent`, `--text`, `--bg` (tied to tenant config)
- **Custom map styles:** Per tenant (colors, fonts, etc.)
- **Agent attribution:** "Listed by {agent name} | {brokerage logo}"
- **Broker branding:** Logo, colors, contact info on all pages

#### 12. Integration Points
- **properties.php:** Existing API for listing data. Extend with filter/search endpoints.
- **MLS feed (instruction 18):** Real-time data import, status change detection.
- **Google One Tap (instruction 28):** Pre-fill contact form on property page. Lead capture on search.
- **Collaborative boards (instruction 30):** Save property to board. Share board with others.
- **Home valuation (instruction 25):** CTA on property page: "Seller? Estimate your home value."
- **Analytics:** Track search behavior, property views, lead sources. Feed back to lead scoring.

---

## DATABASE SCHEMA

### 1. `idx_saved_searches`
Extends existing `saved_searches` table with new fields for advanced filtering.

```sql
CREATE TABLE IF NOT EXISTS idx_saved_searches (
    id INT AUTO_INCREMENT PRIMARY KEY,
    tenant_id INT DEFAULT NULL,
    user_id INT NOT NULL, -- admin_id if authenticated, NULL if anonymous
    name VARCHAR(255) NOT NULL,
    search_criteria JSON NOT NULL, -- {price_min, price_max, beds_min, baths_min, sqft_min, sqft_max, property_types: [], neighborhoods: [...], keywords, status: []}
    alert_enabled BOOLEAN DEFAULT FALSE,
    alert_frequency VARCHAR(50) DEFAULT 'daily', -- 'instant', 'daily', 'weekly'
    alert_email VARCHAR(255) DEFAULT NULL, -- override user email
    results_count INT DEFAULT 0, -- cached count
    last_alert_sent_at TIMESTAMP DEFAULT NULL,
    last_results_at TIMESTAMP DEFAULT NULL, -- when last results returned
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
    FOREIGN KEY (user_id) REFERENCES admins(id) ON DELETE CASCADE,
    INDEX (tenant_id, user_id),
    INDEX (alert_enabled, last_alert_sent_at)
);
```

### 2. `idx_search_history`
Track all searches (anonymous + authenticated) for analytics.

```sql
CREATE TABLE IF NOT EXISTS idx_search_history (
    id INT AUTO_INCREMENT PRIMARY KEY,
    tenant_id INT DEFAULT NULL,
    user_id INT DEFAULT NULL, -- NULL if anonymous
    session_id VARCHAR(255) DEFAULT NULL, -- session cookie value
    search_criteria JSON NOT NULL,
    results_count INT DEFAULT NULL,
    ip_address VARCHAR(45) DEFAULT NULL,
    user_agent VARCHAR(500) DEFAULT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
    INDEX (tenant_id, created_at),
    INDEX (user_id)
);
```

### 3. `idx_favorites`
Properties saved by authenticated users.

```sql
CREATE TABLE IF NOT EXISTS idx_favorites (
    id INT AUTO_INCREMENT PRIMARY KEY,
    tenant_id INT DEFAULT NULL,
    user_id INT NOT NULL,
    property_id INT NOT NULL,
    notes VARCHAR(500) DEFAULT NULL, -- user notes
    shared_board_id INT DEFAULT NULL, -- if added to a board (instruction 30)
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
    FOREIGN KEY (user_id) REFERENCES admins(id) ON DELETE CASCADE,
    FOREIGN KEY (property_id) REFERENCES properties(id) ON DELETE CASCADE,
    UNIQUE KEY (tenant_id, user_id, property_id),
    INDEX (user_id, created_at)
);
```

### 4. `idx_neighborhoods`
Neighborhood boundary data + stats (manually configured or synced from external source).

```sql
CREATE TABLE IF NOT EXISTS idx_neighborhoods (
    id INT AUTO_INCREMENT PRIMARY KEY,
    tenant_id INT DEFAULT NULL,
    name VARCHAR(255) NOT NULL,
    jurisdiction VARCHAR(100) DEFAULT NULL, -- city/county
    boundary_geojson LONGTEXT DEFAULT NULL, -- GeoJSON FeatureCollection
    median_price INT DEFAULT NULL,
    avg_dom INT DEFAULT NULL, -- days on market
    inventory_count INT DEFAULT 0,
    months_inventory DECIMAL(5, 2) DEFAULT NULL,
    price_trend_6m DECIMAL(5, 2) DEFAULT NULL, -- % change
    walk_score INT DEFAULT NULL, -- 0-100
    crime_rate DECIMAL(5, 2) DEFAULT NULL, -- per 1000 residents (optional)
    avg_school_rating DECIMAL(3, 1) DEFAULT NULL, -- 1-10 average
    featured_listings_count INT DEFAULT 5,
    description TEXT DEFAULT NULL,
    seo_slug VARCHAR(255) DEFAULT NULL, -- 'carmel-by-the-sea'
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
    INDEX (tenant_id, seo_slug),
    INDEX (median_price)
);
```

### 5. `idx_property_history`
Track property status changes, price changes over time.

```sql
CREATE TABLE IF NOT EXISTS idx_property_history (
    id INT AUTO_INCREMENT PRIMARY KEY,
    tenant_id INT DEFAULT NULL,
    property_id INT NOT NULL,
    field_name VARCHAR(100) NOT NULL, -- 'list_price', 'status', 'photo_count'
    old_value VARCHAR(500) DEFAULT NULL,
    new_value VARCHAR(500) DEFAULT NULL,
    change_type VARCHAR(50) DEFAULT NULL, -- 'new_listing', 'price_change', 'status_change', 'photo_add'
    synced_from_mls BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
    FOREIGN KEY (property_id) REFERENCES properties(id) ON DELETE CASCADE,
    INDEX (tenant_id, property_id, created_at),
    INDEX (change_type, created_at)
);
```

### 6. `idx_page_views`
Analytics on property detail page views.

```sql
CREATE TABLE IF NOT EXISTS idx_page_views (
    id INT AUTO_INCREMENT PRIMARY KEY,
    tenant_id INT DEFAULT NULL,
    property_id INT NOT NULL,
    user_id INT DEFAULT NULL, -- NULL if anonymous
    session_id VARCHAR(255) DEFAULT NULL,
    referrer VARCHAR(500) DEFAULT NULL,
    ip_address VARCHAR(45) DEFAULT NULL,
    time_on_page_seconds INT DEFAULT NULL,
    clicked_contact_form BOOLEAN DEFAULT FALSE,
    clicked_schedule_showing BOOLEAN DEFAULT FALSE,
    scrolled_to_percent INT DEFAULT NULL, -- 0-100
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
    FOREIGN KEY (property_id) REFERENCES properties(id) ON DELETE CASCADE,
    INDEX (tenant_id, property_id, created_at),
    INDEX (user_id)
);
```

**Note on `idx_map_configs`:** Store per-tenant map styling + overlays in existing `tenants` table as JSON field (avoid extra table). Example: `tenants.idx_config = {map_style: 'mapbox_style_url', overlays: ['schools', 'flood'], ...}`

---

## API ENDPOINTS

### Search & Filtering

#### 1. POST `/api/idx.php?action=searchProperties`
Search properties by criteria with pagination.
- **Auth:** None (public)
- **Request:** `{price_min, price_max, beds_min, baths_min, sqft_min, sqft_max, property_types: [], neighborhoods: [], keywords, status: [], limit: 50, offset: 0}`
- **Response:** `{success, data: {properties: [{id, address, price, beds, baths, sqft, photo_url, status, dom, ...}], total: int, applied_filters: {...}}}`
- **Caching:** Response header `Cache-Control: public, max-age=300` (5 min)

#### 2. POST `/api/idx.php?action=getPropertyDetail`
Get full property details (for detail page).
- **Auth:** None
- **Request:** `{property_id: int}`
- **Response:** `{success, data: {property: {...}, price_history: [{date, price, type: 'list|sold'}, ...], tax_history: [...], similar_properties: [...], neighborhood_stats: {...}}}`
- **Note:** Returns full property record + related historical data

#### 3. POST `/api/idx.php?action=getPropertyPageSEO`
Get SEO metadata for property detail page.
- **Auth:** None
- **Request:** `{property_id: int}`
- **Response:** `{success, data: {title, description, og_image, canonical_url, schema_json: {...}}}`
- **Purpose:** Called by Next.js getServerSideProps to set meta tags

#### 4. POST `/api/idx.php?action=saveSearch`
Save a search with optional alerts.
- **Auth:** Session token (users must be logged in to save)
- **Request:** `{name: string, search_criteria: {...}, alert_enabled: bool, alert_frequency: 'instant|daily|weekly'}`
- **Response:** `{success, data: {search_id: int, alert_enabled: bool}}`

#### 5. POST `/api/idx.php?action=getSavedSearches`
Get authenticated user's saved searches.
- **Auth:** Session token
- **Request:** `{}`
- **Response:** `{success, data: {searches: [{id, name, criteria, alert_enabled, results_count, ...}]}}`

#### 6. POST `/api/idx.php?action=deleteSavedSearch`
Delete a saved search.
- **Auth:** Session token
- **Request:** `{search_id: int}`
- **Response:** `{success, message: "Search deleted"}`

### Neighborhoods

#### 7. POST `/api/idx.php?action=getNeighborhoods`
Get list of neighborhoods for filter dropdown.
- **Auth:** None
- **Request:** `{jurisdiction: string (optional)}`
- **Response:** `{success, data: {neighborhoods: [{id, name, jurisdiction, boundary_geojson, ...}]}}`

#### 8. POST `/api/idx.php?action=getNeighborhoodStats`
Get detailed stats for a neighborhood (for landing page).
- **Auth:** None
- **Request:** `{neighborhood_id: int}`
- **Response:** `{success, data: {name, stats: {median_price, avg_dom, inventory, walk_score, ...}, recent_sales: [...], featured_listings: [...]}}`

### Favorites & History

#### 9. POST `/api/idx.php?action=addFavorite`
Add property to favorites.
- **Auth:** Session token
- **Request:** `{property_id: int, notes: string (optional)}`
- **Response:** `{success, data: {favorite_id: int}}`

#### 10. POST `/api/idx.php?action=removeFavorite`
Remove from favorites.
- **Auth:** Session token
- **Request:** `{property_id: int}`
- **Response:** `{success, message: "Removed"}`

#### 11. POST `/api/idx.php?action=getFavorites`
Get user's favorites list.
- **Auth:** Session token
- **Request:** `{limit: 50, offset: 0}`
- **Response:** `{success, data: {favorites: [...], total: int}}`

#### 12. POST `/api/idx.php?action=getRecentlyViewed`
Get recently viewed properties (session-based for anonymous, user-based for logged-in).
- **Auth:** None (uses session/auth cookie)
- **Request:** `{limit: 10}`
- **Response:** `{success, data: {properties: [...]}}`

### Map & Analytics

#### 13. POST `/api/idx.php?action=getMapConfig`
Get map styling + overlay config for tenant.
- **Auth:** None
- **Request:** `{}`
- **Response:** `{success, data: {map_style: string, overlays_available: [...], primary_color, accent_color}}`

#### 14. POST `/api/idx.php?action=getSimilarProperties`
Get properties similar to a given property (same neighborhood, similar price/beds).
- **Auth:** None
- **Request:** `{property_id: int, limit: 5}`
- **Response:** `{success, data: {properties: [...]}}`

#### 15. POST `/api/idx.php?action=generateSitemap`
Generate sitemaps (XML) for search engines.
- **Auth:** Session token (admin only)
- **Request:** `{type: 'properties|neighborhoods|cities'}`
- **Response:** Raw XML (content-type: application/xml)
- **Note:** Called by background job, stored as static files in `/sitemaps/`

---

## IMPLEMENTATION STEPS

### Phase 1: Database & API Setup (Days 1–2)

1. **Create 6 new tables** using DDL above. Auto-migration in idx.php.
2. **Create `/api/idx.php`** with 15 endpoints. Follow existing patterns:
   - PDO prepared statements
   - Session auth for write operations
   - JSON responses
   - Tenant filtering on all queries
3. **Test locally:**
   ```bash
   curl -X POST http://fogbreak.local/api/idx.php?action=searchProperties \
     -d '{"price_max": 5000000, "beds_min": 3}'
   ```

### Phase 2: MLS Integration & Data Pipeline (Days 3–4)

4. **Extend instruction 18 (MLS) sync:**
   - On each RETS pull, check for new/changed properties
   - Detect: new listing (created_at < 24h), price change (list_price changed), status change (status != prior)
   - Create `idx_property_history` entry for each change
   - Trigger alert job (every 5 min): find users with matching saved searches, send email/notification
5. **Property photos:**
   - Store photo URLs from MLS in `properties.photos` (JSON array of {url, caption, sort_order})
   - Download + optimize images for web (via Autoenhance.ai or local ImageMagick):
     - Thumbnail: 300x200px (search results)
     - Medium: 800x600px (property cards)
     - Full: 1600x1200px (detail gallery)
   - Host optimized images on CDN (Cloudflare, Vercel) or Bluehost
6. **Neighborhood boundary data:**
   - Manually create or import GeoJSON for all neighborhoods (Carmel, Pebble Beach, Pacific Grove, etc.)
   - Store in `idx_neighborhoods.boundary_geojson`
   - Test: filter by neighborhood, verify results within boundary

### Phase 3: Next.js SSR & Property Pages (Days 5–7)

7. **Scaffold Next.js app** (instruction 15 handles this):
   - Pages: `/pages/properties/[property_id]/[slug].tsx`, `/pages/neighborhoods/[slug].tsx`, `/pages/cities/[slug].tsx`
   - Layouts: full-width header (branding + search bar), footer with links
8. **Property detail page (`/properties/[id]/[slug].tsx`):**
   - `getServerSideProps` calls `/api/idx.php?action=getPropertyDetail` + `action=getPropertyPageSEO`
   - Render: hero carousel, key details, description, specs table, price history chart, tax history, neighborhood stats, schools, walk score, similar properties, mortgage calculator, contact form
   - Image gallery: Lightbox with swipe (react-image-lightbox library)
   - Charts: recharts for price history + tax trends
   - Structured data: inject Schema.org JSON-LD in `<head>`
9. **Neighborhood landing page (`/neighborhoods/[slug].tsx`):**
   - `getStaticProps` (cache 1 day): fetch neighborhood + featured listings
   - Render: hero with neighborhood name/photo, stats cards (median price, DOM, inventory), 6-month price trend chart, featured listings, recent sales, schools
   - CTA: "Search homes in {neighborhood}"
10. **City landing page (`/cities/[slug].tsx`):**
    - `getStaticProps`: aggregate data across all neighborhoods in city
    - Render: city name, # homes for sale, price range, featured listings, top neighborhoods
11. **SEO setup:**
    - Dynamic meta tags: `<title>`, `description`, `og:image`, `canonical`
    - Schema.org JSON-LD injection for RealEstateListing + LocalBusiness
    - Sitemap generation (instruction 15 handles via `next-sitemap`)

### Phase 4: Frontend Search Interface (Days 8–10)

12. **Build split-screen search UI (React):**
    - Create components:
      - `SearchFilters.tsx` — price slider, beds/baths dropdowns, property type checkboxes, status filter, save search button
      - `PropertyList.tsx` — virtual-scrolled list of property cards, lazy images
      - `PropertyCard.tsx` — photo thumbnail, price, beds/baths, DOM, status badge, like/share buttons
      - `MapView.tsx` — Leaflet map wrapper with clustering
      - `SearchResults.tsx` — combines list + map, responsive layout
    - State management: React Context or Zustand (simple search state)
    - Props: search criteria, results, loading state, error handling
13. **Leaflet map integration:**
    - `MapView.tsx` uses `react-leaflet` library
    - Props: properties array, selected property, onPropertyClick callback
    - Features: zoom controls, layer toggle, clustering via Leaflet.markercluster
    - Pin colors by status (CSS classes)
    - Hover tooltip on pin
14. **Responsive layout:**
    - Desktop (>768px): 50/50 split (list left, map right)
    - Tablet (768px): stack with list on top, map below (draggable divider)
    - Mobile (<768px): tabs (List | Map)
15. **Performance optimizations:**
    - Image lazy loading: `<img loading="lazy" />`
    - Debounced search: 500ms delay before API call
    - Virtual scrolling: react-window for property list (render only 5–10 visible cards)

### Phase 5: Lead Capture & Alerts (Days 11–12)

16. **Google One Tap integration** (instruction 28):
    - Embed One Tap widget on search page + property detail page
    - On user sign-in: pre-populate contact form, auto-create saved search, enroll in buyer drip (instruction 33)
17. **Contact form (Schedule Showing / Get Info):**
    - Form fields: name, email, phone, message
    - Pre-fill name + email if logged in or One Tap signed in
    - On submit: create interaction in CRM, trigger AI nurture (instruction 33), send confirmation email
    - CTA: "Schedule showing" vs. "Get more info" (based on property status/agent settings)
18. **Property alerts:**
    - "Save search and get alerts" button
    - After save: show frequency options (instant, daily, weekly)
    - Backend (cron, every 5 min): check for new listings matching saved searches
    - Send email with subject "New listing matching your search" + link to property
    - One-click unsubscribe in email footer

### Phase 6: Analytics & Reporting (Days 13–14)

19. **Track search behavior:**
    - Every search logged to `idx_search_history` (criteria, result count, user, session)
    - Every property page view logged to `idx_page_views` (duration, scroll depth, form clicks)
    - Segment by user (if logged in), anonymous session, traffic source
20. **Analytics dashboard (admin UI):**
    - New "IDX Analytics" panel in fogbreak.html
    - Displays: total searches (day/week/month), top searched neighborhoods/price ranges, conversion (search → showing), top viewed properties, lead source breakdown
    - Export reports (CSV)

### Phase 7: White-Label & Performance (Days 15)

21. **Branding:**
    - CSS variables in `idx.css`: `--primary`, `--accent`, `--bg`, `--text`
    - Map custom style per tenant (Leaflet Mapbox style URL)
    - Agent logo + contact on property detail
22. **Performance audit:**
    - Lighthouse CI on property detail pages (target: >90 on mobile)
    - Database query optimization (add indexes on `created_at`, `status`, `property_types`)
    - CDN caching headers (property images cached 1 month, list results 5 min)

---

## TESTING CRITERIA

### Unit Tests
- [ ] Search filters: price min/max, beds, neighborhood — results filter correctly
- [ ] Neighborhood boundary: properties within boundary included, outside excluded
- [ ] Image lazy loading: verify `loading="lazy"` attribute, images load on scroll
- [ ] Virtual scrolling: list with 1,000 properties, render only visible cards (verify DOM count ~10)
- [ ] Sitemap generation: properties, neighborhoods, cities sitemaps created, valid XML

### Integration Tests
- [ ] MLS sync: new property → appears in search results within 5 min
- [ ] Price change detected: list price changes in MLS → `idx_property_history` entry created → alert sent to matching users
- [ ] Saved search alerts: user saves search, new property matches criteria, email alert sent <10 min
- [ ] Google One Tap → contact form pre-fill: user signs in via One Tap, name/email auto-filled
- [ ] Neighborhood page renders: `/neighborhoods/carmel-by-the-sea` loads, displays median price, featured listings

### End-to-End Tests
- [ ] **Scenario: Consumer searches for home**
  1. Load search page (fogbreak.io/search)
  2. Set filters: price $1.5M–$3M, beds 3+, neighborhood "Carmel", new listing
  3. Results show 12 properties matching criteria
  4. Click property → detail page loads with full info, price history chart, similar properties
  5. Scroll detail page → Google Analytics event fires (page view tracked)
  6. Click "Schedule showing" → contact form opens, if logged in name/email pre-filled
  7. Submit form → creates CRM interaction, sends confirmation email
- [ ] **Scenario: User enables alerts**
  1. Complete search above
  2. Click "Save search" → modal prompts for name + alert frequency
  3. Select "Instant alerts"
  4. Next day, new listing matches criteria
  5. Email alert sent <5 min with property address + link + one-click unsubscribe
  6. Click link → property detail page loads (attributed to search alert in analytics)
- [ ] **Scenario: Mobile search experience**
  1. Load search on iPhone (mobile viewport)
  2. Tap "List" tab → property cards display (scrollable)
  3. Tap "Map" tab → map displays full screen
  4. Tap property pin → scrolls card into view, card highlighted
  5. Tap card → navigates to detail page (mobile optimized)
  6. Detail page images swipeable left/right (touch gesture)

### Performance Tests
- [ ] Search result page: load <2 sec on 4G (property list + map rendered)
- [ ] Property detail page: load <3 sec, Lighthouse >85 mobile
- [ ] Image gallery: 20 photos, swipe responsive (<100ms between swipes)
- [ ] Map clustering: 5,000 properties, zoom/pan smooth, no jank
- [ ] Sitemap generation: 10,000 properties, sitemap generated in <30 sec

### SEO Tests
- [ ] Property detail page: inspect HTML, verify Schema.org RealEstateListing in `<head>`
- [ ] Meta tags: `<title>`, `og:image`, `canonical` present + accurate
- [ ] Neighborhood page: index in Google (submit to Search Console), verify appears for "[neighborhood] homes for sale"
- [ ] Sitemap: `/sitemaps/sitemap-properties.xml` valid, crawlable by search engines
- [ ] Duplicate content: same property accessible via multiple routes, canonical tag points to primary

### White-Label Tests
- [ ] Tenant branding: search page uses tenant colors, logo displays
- [ ] Tenant switching: switch tenant via config, colors change, logo changes
- [ ] Map style: colors and theme applied from tenant config (if configured)

---

## SUCCESS METRICS (First 30 Days)

- **Organic traffic:** 20% increase from local SEO (neighborhood + property searches)
- **Lead volume:** 10–15% increase from search → contact form submissions
- **Mobile conversion:** 50%+ of traffic from mobile, 5%+ conversion to contact form (vs. 2% on mobile now)
- **Saved searches:** 30% of users save a search (vs. 5% current)
- **Alert engagement:** 40%+ open rate on new listing emails (vs. 25% current)
- **Average session duration:** 3+ min on search page (vs. 1 min current)
- **Bounce rate:** <40% on property detail pages
- **Favorite usage:** 20% of users favorite at least one property
- **SEO metrics:** 200+ neighborhoods + city pages ranking for local keywords within 60 days

---

## INTEGRATION SUMMARY

```
idx.php (core API)
├── properties.php (extend for listing data)
├── instruction 18 (MLS feed)
│   └── idx_property_history (track changes)
├── instruction 15 (Next.js)
│   ├── /pages/properties/[id]/[slug].tsx
│   ├── /pages/neighborhoods/[slug].tsx
│   └── /pages/cities/[slug].tsx
├── instruction 28 (Google One Tap)
│   └── Contact form pre-fill
├── instruction 33 (AI Lead Nurture)
│   └── Saved search enrollment → drip campaign
├── instruction 30 (Collaborative Boards)
│   └── Save property to board
└── instruction 25 (Home Valuation)
    └── Seller CTA on property page
```

---

**Owner:** Kean (Architecture + SEO strategy)
**Dependencies:** Instruction 15 (Next.js), Instruction 18 (MLS), Instruction 28 (Google One Tap), Instruction 30 (Boards), Instruction 25 (Valuation)
**Tier:** Phase 2.5 High Priority (consumer-facing revenue driver)
**Effort Estimate:** 3 weeks (120 hours)
