[object Object]

← back to Commercialrealestate

closed-sales: full LA County coverage — 228 resolved regions, price-banding past the 350 cap, resume ledger, honest ~5yr depth note (6,391 -> 17,023 rows)

cc6c1a95f424f46e028c691069d062d8dde9fa59 · 2026-06-28 10:36:32 -0700 · Steve Abrams

Files touched

Diff

commit cc6c1a95f424f46e028c691069d062d8dde9fa59
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sun Jun 28 10:36:32 2026 -0700

    closed-sales: full LA County coverage — 228 resolved regions, price-banding past the 350 cap, resume ledger, honest ~5yr depth note (6,391 -> 17,023 rows)
---
 data/redfin-neighborhoods.json          | 619 ++++++++++++++++++++++++++++-
 data/redfin-regions.json                | 670 +++++++++++++++++++++++++++++++-
 public/sales.html                       |   4 +-
 scripts/fetch-closed-sales.js           | 119 ++++--
 scripts/resolve-redfin-neighborhoods.js |  35 +-
 scripts/resolve-redfin-regions.js       |  40 +-
 scripts/serve.js                        |   4 +-
 7 files changed, 1442 insertions(+), 49 deletions(-)

diff --git a/data/redfin-neighborhoods.json b/data/redfin-neighborhoods.json
index c5a5657..eba7099 100644
--- a/data/redfin-neighborhoods.json
+++ b/data/redfin-neighborhoods.json
@@ -1,5 +1,6 @@
 {
-  "resolved_at": "2026-06-28T14:07:18.399Z",
+  "resolved_at": "2026-06-28T17:25:09.011Z",
+  "count": 93,
   "regions": [
     {
       "query": "Van Nuys",
@@ -128,6 +129,622 @@
       "region_id": "14609",
       "region_type": 1,
       "url": "/neighborhood/14609/CA/Rancho-Palos-Verdes/San-Pedro-Hill"
+    },
+    {
+      "query": "Panorama City",
+      "name": "Panorama City",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "10731",
+      "region_type": 1,
+      "url": "/neighborhood/10731/CA/Los-Angeles/Panorama-City"
+    },
+    {
+      "query": "Sun Valley",
+      "name": "Sun Valley",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "10243",
+      "region_type": 1,
+      "url": "/neighborhood/10243/CA/Los-Angeles/Sun-Valley"
+    },
+    {
+      "query": "Granada Hills",
+      "name": "Granada Hills",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "1126",
+      "region_type": 1,
+      "url": "/neighborhood/1126/CA/Los-Angeles/Granada-Hills"
+    },
+    {
+      "query": "Tarzana",
+      "name": "Tarzana",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "2692",
+      "region_type": 1,
+      "url": "/neighborhood/2692/CA/Los-Angeles/Tarzana"
+    },
+    {
+      "query": "Winnetka",
+      "name": "Winnetka",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "3099",
+      "region_type": 1,
+      "url": "/neighborhood/3099/CA/Los-Angeles/Winnetka"
+    },
+    {
+      "query": "West Hills",
+      "name": "West Hills",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "3006",
+      "region_type": 1,
+      "url": "/neighborhood/3006/CA/Los-Angeles/West-Hills"
+    },
+    {
+      "query": "Lake Balboa",
+      "name": "Lake Balboa",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "112192",
+      "region_type": 1,
+      "url": "/neighborhood/112192/CA/Los-Angeles/Lake-Balboa"
+    },
+    {
+      "query": "Valley Village",
+      "name": "Valley Village",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "2852",
+      "region_type": 1,
+      "url": "/neighborhood/2852/CA/Los-Angeles/Valley-Village"
+    },
+    {
+      "query": "Valley Glen",
+      "name": "Valley Glen",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "2850",
+      "region_type": 1,
+      "url": "/neighborhood/2850/CA/Los-Angeles/Valley-Glen"
+    },
+    {
+      "query": "Toluca Lake",
+      "name": "Greater Toluca Lake",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "2749",
+      "region_type": 1,
+      "url": "/neighborhood/2749/CA/Los-Angeles/Greater-Toluca-Lake"
+    },
+    {
+      "query": "Arleta",
+      "name": "Arleta",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "72",
+      "region_type": 1,
+      "url": "/neighborhood/72/CA/Los-Angeles/Arleta"
+    },
+    {
+      "query": "Pacoima",
+      "name": "Pacoima",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "2079",
+      "region_type": 1,
+      "url": "/neighborhood/2079/CA/Los-Angeles/Pacoima"
+    },
+    {
+      "query": "Mission Hills",
+      "name": "Mission Hills",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "1734",
+      "region_type": 1,
+      "url": "/neighborhood/1734/CA/Los-Angeles/Mission-Hills"
+    },
+    {
+      "query": "Sylmar",
+      "name": "Sylmar",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "2675",
+      "region_type": 1,
+      "url": "/neighborhood/2675/CA/Los-Angeles/Sylmar"
+    },
+    {
+      "query": "Tujunga",
+      "name": "Sunland-Tujunga",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "2642",
+      "region_type": 1,
+      "url": "/neighborhood/2642/CA/Los-Angeles/Sunland-Tujunga"
+    },
+    {
+      "query": "Sunland",
+      "name": "Sunland",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "9719",
+      "region_type": 1,
+      "url": "/neighborhood/9719/CA/Los-Angeles/Sunland"
+    },
+    {
+      "query": "Porter Ranch",
+      "name": "Porter Ranch",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "2179",
+      "region_type": 1,
+      "url": "/neighborhood/2179/CA/Los-Angeles/Porter-Ranch"
+    },
+    {
+      "query": "Chatsworth",
+      "name": "Chatsworth",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "460",
+      "region_type": 1,
+      "url": "/neighborhood/460/CA/Los-Angeles/Chatsworth"
+    },
+    {
+      "query": "North Hills",
+      "name": "North Hills",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "351587",
+      "region_type": 1,
+      "url": "/neighborhood/351587/CA/Los-Angeles/North-Hills"
+    },
+    {
+      "query": "Sepulveda",
+      "name": "Sepulveda",
+      "sub": "Glendale, CA, USA",
+      "region_id": "14610",
+      "region_type": 1,
+      "url": "/neighborhood/14610/CA/Glendale/Sepulveda"
+    },
+    {
+      "query": "Shadow Hills",
+      "name": "Shadow Hills",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "11702",
+      "region_type": 1,
+      "url": "/neighborhood/11702/CA/Los-Angeles/Shadow-Hills"
+    },
+    {
+      "query": "Lake View Terrace",
+      "name": "Lake View Terrace",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "10632",
+      "region_type": 1,
+      "url": "/neighborhood/10632/CA/Los-Angeles/Lake-View-Terrace"
+    },
+    {
+      "query": "Cahuenga Pass",
+      "name": "Cahuenga Pass",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "59009",
+      "region_type": 1,
+      "url": "/neighborhood/59009/CA/Los-Angeles/Cahuenga-Pass"
+    },
+    {
+      "query": "Brentwood",
+      "name": "Brentwood",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "258",
+      "region_type": 1,
+      "url": "/neighborhood/258/CA/Los-Angeles/Brentwood"
+    },
+    {
+      "query": "Westwood",
+      "name": "Westwood",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "3055",
+      "region_type": 1,
+      "url": "/neighborhood/3055/CA/Los-Angeles/Westwood"
+    },
+    {
+      "query": "West Los Angeles",
+      "name": "Los Nietos Junction",
+      "sub": "West Whittier-Los Nietos, CA, USA",
+      "region_id": "14587",
+      "region_type": 1,
+      "url": "/neighborhood/14587/CA/West-Whittier-Los-Nietos/Los-Nietos-Junction"
+    },
+    {
+      "query": "Sawtelle",
+      "name": "Sawtelle",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "9168",
+      "region_type": 1,
+      "url": "/neighborhood/9168/CA/Los-Angeles/Sawtelle"
+    },
+    {
+      "query": "Playa Del Rey",
+      "name": "Playa del Rey",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "3844",
+      "region_type": 1,
+      "url": "/neighborhood/3844/CA/Los-Angeles/Playa-del-Rey"
+    },
+    {
+      "query": "Playa Vista",
+      "name": "Playa Vista",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "496706",
+      "region_type": 1,
+      "url": "/neighborhood/496706/CA/Los-Angeles/Playa-Vista"
+    },
+    {
+      "query": "Palms",
+      "name": "Palms",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "2083",
+      "region_type": 1,
+      "url": "/neighborhood/2083/CA/Los-Angeles/Palms"
+    },
+    {
+      "query": "Cheviot Hills",
+      "name": "Cheviot Hills",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "10493",
+      "region_type": 1,
+      "url": "/neighborhood/10493/CA/Los-Angeles/Cheviot-Hills"
+    },
+    {
+      "query": "Rancho Park",
+      "name": "Rancho Park",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "9744",
+      "region_type": 1,
+      "url": "/neighborhood/9744/CA/Los-Angeles/Rancho-Park"
+    },
+    {
+      "query": "Century City",
+      "name": "Century City",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "446",
+      "region_type": 1,
+      "url": "/neighborhood/446/CA/Los-Angeles/Century-City"
+    },
+    {
+      "query": "Beverly Crest",
+      "name": "Beverly Crest",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "10825",
+      "region_type": 1,
+      "url": "/neighborhood/10825/CA/Los-Angeles/Beverly-Crest"
+    },
+    {
+      "query": "Bel Air",
+      "name": "Bel Air",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "760389",
+      "region_type": 1,
+      "url": "/neighborhood/760389/CA/Los-Angeles/Bel-Air"
+    },
+    {
+      "query": "Bel-Air",
+      "name": "Bel Air-Beverly Crest",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "169",
+      "region_type": 1,
+      "url": "/neighborhood/169/CA/Los-Angeles/Bel-Air-Beverly-Crest"
+    },
+    {
+      "query": "Hollywood",
+      "name": "Hollywood Hills",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "547223",
+      "region_type": 1,
+      "url": "/neighborhood/547223/CA/Los-Angeles/Hollywood-Hills"
+    },
+    {
+      "query": "East Hollywood",
+      "name": "East Hollywood",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "811",
+      "region_type": 1,
+      "url": "/neighborhood/811/CA/Los-Angeles/East-Hollywood"
+    },
+    {
+      "query": "West Hollywood",
+      "name": "North Hollywood West",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "547227",
+      "region_type": 1,
+      "url": "/neighborhood/547227/CA/Los-Angeles/North-Hollywood-West"
+    },
+    {
+      "query": "Hancock Park",
+      "name": "Hancock Park",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "110322",
+      "region_type": 1,
+      "url": "/neighborhood/110322/CA/Los-Angeles/Hancock-Park"
+    },
+    {
+      "query": "Larchmont",
+      "name": "Larchmont",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "113723",
+      "region_type": 1,
+      "url": "/neighborhood/113723/CA/Los-Angeles/Larchmont"
+    },
+    {
+      "query": "Mid-City",
+      "name": "Mid-City",
+      "sub": "Santa Monica, CA, USA",
+      "region_id": "1692",
+      "region_type": 1,
+      "url": "/neighborhood/1692/CA/Santa-Monica/Mid-City"
+    },
+    {
+      "query": "Mid City",
+      "name": "Mid City",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "9917",
+      "region_type": 1,
+      "url": "/neighborhood/9917/CA/Los-Angeles/Mid-City"
+    },
+    {
+      "query": "Mid-Wilshire",
+      "name": "Mid-Wilshire",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "547225",
+      "region_type": 1,
+      "url": "/neighborhood/547225/CA/Los-Angeles/Mid-Wilshire"
+    },
+    {
+      "query": "Pico-Union",
+      "name": "Pico Union",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "2140",
+      "region_type": 1,
+      "url": "/neighborhood/2140/CA/Los-Angeles/Pico-Union"
+    },
+    {
+      "query": "Westlake",
+      "name": "Westlake",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "10094",
+      "region_type": 1,
+      "url": "/neighborhood/10094/CA/Los-Angeles/Westlake"
+    },
+    {
+      "query": "Silver Lake",
+      "name": "Silver Lake",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "7511",
+      "region_type": 1,
+      "url": "/neighborhood/7511/CA/Los-Angeles/Silver-Lake"
+    },
+    {
+      "query": "Silverlake",
+      "name": "Ivanhoe / Silverlake",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "1369",
+      "region_type": 1,
+      "url": "/neighborhood/1369/CA/Los-Angeles/Ivanhoe-Silverlake"
+    },
+    {
+      "query": "Los Feliz",
+      "name": "Los Feliz",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "9961",
+      "region_type": 1,
+      "url": "/neighborhood/9961/CA/Los-Angeles/Los-Feliz"
+    },
+    {
+      "query": "Atwater Village",
+      "name": "Atwater Village",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "104",
+      "region_type": 1,
+      "url": "/neighborhood/104/CA/Los-Angeles/Atwater-Village"
+    },
+    {
+      "query": "Eagle Rock",
+      "name": "Eagle Rock",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "786",
+      "region_type": 1,
+      "url": "/neighborhood/786/CA/Los-Angeles/Eagle-Rock"
+    },
+    {
+      "query": "Mount Washington",
+      "name": "Mount Washington",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "1790",
+      "region_type": 1,
+      "url": "/neighborhood/1790/CA/Los-Angeles/Mount-Washington"
+    },
+    {
+      "query": "Glassell Park",
+      "name": "Glassell Park",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "1085",
+      "region_type": 1,
+      "url": "/neighborhood/1085/CA/Los-Angeles/Glassell-Park"
+    },
+    {
+      "query": "Cypress Park",
+      "name": "Cypress Park",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "661",
+      "region_type": 1,
+      "url": "/neighborhood/661/CA/Los-Angeles/Cypress-Park"
+    },
+    {
+      "query": "Lincoln Heights",
+      "name": "Lincoln Heights",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "1531",
+      "region_type": 1,
+      "url": "/neighborhood/1531/CA/Los-Angeles/Lincoln-Heights"
+    },
+    {
+      "query": "El Sereno",
+      "name": "El Sereno",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "888",
+      "region_type": 1,
+      "url": "/neighborhood/888/CA/Los-Angeles/El-Sereno"
+    },
+    {
+      "query": "Boyle Heights",
+      "name": "Boyle Heights",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "247",
+      "region_type": 1,
+      "url": "/neighborhood/247/CA/Los-Angeles/Boyle-Heights"
+    },
+    {
+      "query": "Downtown Los Angeles",
+      "name": "Downtown Los Angeles",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "290722",
+      "region_type": 1,
+      "url": "/neighborhood/290722/CA/Los-Angeles/Downtown-Los-Angeles"
+    },
+    {
+      "query": "Arts District",
+      "name": "Arts District",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "91",
+      "region_type": 1,
+      "url": "/neighborhood/91/CA/Los-Angeles/Arts-District"
+    },
+    {
+      "query": "Chinatown",
+      "name": "Chinatown",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "483",
+      "region_type": 1,
+      "url": "/neighborhood/483/CA/Los-Angeles/Chinatown"
+    },
+    {
+      "query": "Historic Filipinotown",
+      "name": "Historic Filipinotown",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "62693",
+      "region_type": 1,
+      "url": "/neighborhood/62693/CA/Los-Angeles/Historic-Filipinotown"
+    },
+    {
+      "query": "South Los Angeles",
+      "name": "Los Altos South",
+      "sub": "Long Beach, CA, USA",
+      "region_id": "1565",
+      "region_type": 1,
+      "url": "/neighborhood/1565/CA/Long-Beach/Los-Altos-South"
+    },
+    {
+      "query": "South La",
+      "name": "South LA",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "92255",
+      "region_type": 1,
+      "url": "/neighborhood/92255/CA/Los-Angeles/South-LA"
+    },
+    {
+      "query": "Jefferson Park",
+      "name": "Jefferson Park",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "3958",
+      "region_type": 1,
+      "url": "/neighborhood/3958/CA/Los-Angeles/Jefferson-Park"
+    },
+    {
+      "query": "West Adams",
+      "name": "West Adams",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "2967",
+      "region_type": 1,
+      "url": "/neighborhood/2967/CA/Los-Angeles/West-Adams"
+    },
+    {
+      "query": "Leimert Park",
+      "name": "Leimert Park",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "10210",
+      "region_type": 1,
+      "url": "/neighborhood/10210/CA/Los-Angeles/Leimert-Park"
+    },
+    {
+      "query": "Baldwin Hills",
+      "name": "Baldwin Hills",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "10309",
+      "region_type": 1,
+      "url": "/neighborhood/10309/CA/Los-Angeles/Baldwin-Hills"
+    },
+    {
+      "query": "Crenshaw",
+      "name": "Crenshaw",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "633",
+      "region_type": 1,
+      "url": "/neighborhood/633/CA/Los-Angeles/Crenshaw"
+    },
+    {
+      "query": "View Park",
+      "name": "View Park",
+      "sub": "View Park-Windsor Hills, CA, USA",
+      "region_id": "9726",
+      "region_type": 1,
+      "url": "/neighborhood/9726/CA/View-Park-Windsor-Hills/View-Park"
+    },
+    {
+      "query": "Windsor Hills",
+      "name": "Windsor Hills",
+      "sub": "View Park-Windsor Hills, CA, USA",
+      "region_id": "9865",
+      "region_type": 1,
+      "url": "/neighborhood/9865/CA/View-Park-Windsor-Hills/Windsor-Hills"
+    },
+    {
+      "query": "Watts",
+      "name": "Watts",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "2946",
+      "region_type": 1,
+      "url": "/neighborhood/2946/CA/Los-Angeles/Watts"
+    },
+    {
+      "query": "Wilmington",
+      "name": "Wilmington",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "113062",
+      "region_type": 1,
+      "url": "/neighborhood/113062/CA/Los-Angeles/Wilmington"
+    },
+    {
+      "query": "Harbor City",
+      "name": "Harbor City",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "1192",
+      "region_type": 1,
+      "url": "/neighborhood/1192/CA/Los-Angeles/Harbor-City"
+    },
+    {
+      "query": "Harbor Gateway",
+      "name": "Harbor Gateway North",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "119076",
+      "region_type": 1,
+      "url": "/neighborhood/119076/CA/Los-Angeles/Harbor-Gateway-North"
+    },
+    {
+      "query": "South Park",
+      "name": "Park South",
+      "sub": "Calabasas, CA, USA",
+      "region_id": "118902",
+      "region_type": 1,
+      "url": "/neighborhood/118902/CA/Calabasas/Park-South"
+    },
+    {
+      "query": "University Park",
+      "name": "University Park",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "116045",
+      "region_type": 1,
+      "url": "/neighborhood/116045/CA/Los-Angeles/University-Park"
+    },
+    {
+      "query": "Exposition Park",
+      "name": "Exposition Park",
+      "sub": "Los Angeles, CA, USA",
+      "region_id": "61153",
+      "region_type": 1,
+      "url": "/neighborhood/61153/CA/Los-Angeles/Exposition-Park"
     }
   ]
 }
\ No newline at end of file
diff --git a/data/redfin-regions.json b/data/redfin-regions.json
index c0a9e9f..e2ce7d3 100644
--- a/data/redfin-regions.json
+++ b/data/redfin-regions.json
@@ -1,6 +1,6 @@
 {
-  "resolved_at": "2026-06-28T14:06:30.766Z",
-  "count": 25,
+  "resolved_at": "2026-06-28T17:24:12.760Z",
+  "count": 136,
   "regions": [
     {
       "query": "Van Nuys",
@@ -215,6 +215,672 @@
       "name": "Santa Clarita",
       "region_id": "17676",
       "url": "/city/17676/CA/Santa-Clarita"
+    },
+    {
+      "query": "Agoura Hills",
+      "name": "Agoura Hills",
+      "region_id": "77",
+      "url": "/city/77/CA/Agoura-Hills"
+    },
+    {
+      "query": "Alhambra",
+      "name": "Alhambra",
+      "region_id": "208",
+      "url": "/city/208/CA/Alhambra"
+    },
+    {
+      "query": "Arcadia",
+      "name": "Arcadia",
+      "region_id": "645",
+      "url": "/city/645/CA/Arcadia"
+    },
+    {
+      "query": "Artesia",
+      "name": "Artesia",
+      "region_id": "761",
+      "url": "/city/761/CA/Artesia"
+    },
+    {
+      "query": "Avalon",
+      "name": "Avalon",
+      "region_id": "878",
+      "url": "/city/878/CA/Avalon"
+    },
+    {
+      "query": "Azusa",
+      "name": "Azusa",
+      "region_id": "921",
+      "url": "/city/921/CA/Azusa"
+    },
+    {
+      "query": "Baldwin Park",
+      "name": "Baldwin Park",
+      "region_id": "998",
+      "url": "/city/998/CA/Baldwin-Park"
+    },
+    {
+      "query": "Bell",
+      "name": "Bell",
+      "region_id": "1285",
+      "url": "/city/1285/CA/Bell"
+    },
+    {
+      "query": "Bell Gardens",
+      "name": "Bell Gardens",
+      "region_id": "1331",
+      "url": "/city/1331/CA/Bell-Gardens"
+    },
+    {
+      "query": "Bellflower",
+      "name": "Bellflower",
+      "region_id": "1328",
+      "url": "/city/1328/CA/Bellflower"
+    },
+    {
+      "query": "Bradbury",
+      "name": "Bradbury",
+      "region_id": "2048",
+      "url": "/city/2048/CA/Bradbury"
+    },
+    {
+      "query": "Calabasas",
+      "name": "Calabasas",
+      "region_id": "2471",
+      "url": "/city/2471/CA/Calabasas"
+    },
+    {
+      "query": "Carson",
+      "name": "Carson",
+      "region_id": "2990",
+      "url": "/city/2990/CA/Carson"
+    },
+    {
+      "query": "Cerritos",
+      "name": "Cerritos",
+      "region_id": "3268",
+      "url": "/city/3268/CA/Cerritos"
+    },
+    {
+      "query": "Claremont",
+      "name": "Claremont",
+      "region_id": "3578",
+      "url": "/city/3578/CA/Claremont"
+    },
+    {
+      "query": "Commerce",
+      "name": "Commerce",
+      "region_id": "3870",
+      "url": "/city/3870/CA/Commerce"
+    },
+    {
+      "query": "Compton",
+      "name": "Compton",
+      "region_id": "3890",
+      "url": "/city/3890/CA/Compton"
+    },
+    {
+      "query": "Covina",
+      "name": "Covina",
+      "region_id": "4367",
+      "url": "/city/4367/CA/Covina"
+    },
+    {
+      "query": "Cudahy",
+      "name": "Cudahy",
+      "region_id": "4539",
+      "url": "/city/4539/CA/Cudahy"
+    },
+    {
+      "query": "Diamond Bar",
+      "name": "Diamond Bar",
+      "region_id": "4957",
+      "url": "/city/4957/CA/Diamond-Bar"
+    },
+    {
+      "query": "Duarte",
+      "name": "Duarte",
+      "region_id": "5152",
+      "url": "/city/5152/CA/Duarte"
+    },
+    {
+      "query": "El Segundo",
+      "name": "El Segundo",
+      "region_id": "5771",
+      "url": "/city/5771/CA/El-Segundo"
+    },
+    {
+      "query": "Glendora",
+      "name": "Glendora",
+      "region_id": "7650",
+      "url": "/city/7650/CA/Glendora"
+    },
+    {
+      "query": "Hawaiian Gardens",
+      "name": "Hawaiian Gardens",
+      "region_id": "8292",
+      "url": "/city/8292/CA/Hawaiian-Gardens"
+    },
+    {
+      "query": "Hermosa Beach",
+      "name": "Hermosa Beach",
+      "region_id": "8536",
+      "url": "/city/8536/CA/Hermosa-Beach"
+    },
+    {
+      "query": "Hidden Hills",
+      "name": "Hidden Hills",
+      "region_id": "8583",
+      "url": "/city/8583/CA/Hidden-Hills"
+    },
+    {
+      "query": "Huntington Park",
+      "name": "Huntington Park",
+      "region_id": "9186",
+      "url": "/city/9186/CA/Huntington-Park"
+    },
+    {
+      "query": "Industry",
+      "name": "Industry",
+      "region_id": "9288",
+      "url": "/city/9288/CA/Industry"
+    },
+    {
+      "query": "Irwindale",
+      "name": "Irwindale",
+      "region_id": "9373",
+      "url": "/city/9373/CA/Irwindale"
+    },
+    {
+      "query": "La Canada Flintridge",
+      "name": "La Cañada Flintridge",
+      "region_id": "9895",
+      "url": "/city/9895/CA/La-Caada-Flintridge"
+    },
+    {
+      "query": "La Habra Heights",
+      "name": "La Habra Heights",
+      "region_id": "9982",
+      "url": "/city/9982/CA/La-Habra-Heights"
+    },
+    {
+      "query": "La Mirada",
+      "name": "La Mirada",
+      "region_id": "10208",
+      "url": "/city/10208/CA/La-Mirada"
+    },
+    {
+      "query": "La Puente",
+      "name": "La Puente",
+      "region_id": "10290",
+      "url": "/city/10290/CA/La-Puente"
+    },
+    {
+      "query": "La Verne",
+      "name": "La Verne",
+      "region_id": "10417",
+      "url": "/city/10417/CA/La-Verne"
+    },
+    {
+      "query": "Lakewood",
+      "name": "Lakewood",
+      "region_id": "10167",
+      "url": "/city/10167/CA/Lakewood"
+    },
+    {
+      "query": "Lawndale",
+      "name": "Lawndale",
+      "region_id": "10433",
+      "url": "/city/10433/CA/Lawndale"
+    },
+    {
+      "query": "Lomita",
+      "name": "Lomita",
+      "region_id": "10810",
+      "url": "/city/10810/CA/Lomita"
+    },
+    {
+      "query": "Lynwood",
+      "name": "Lynwood",
+      "region_id": "11368",
+      "url": "/city/11368/CA/Lynwood"
+    },
+    {
+      "query": "Manhattan Beach",
+      "name": "Manhattan Beach",
+      "region_id": "11576",
+      "url": "/city/11576/CA/Manhattan-Beach"
+    },
+    {
+      "query": "Maywood",
+      "name": "Maywood",
+      "region_id": "11864",
+      "url": "/city/11864/CA/Maywood"
+    },
+    {
+      "query": "Monrovia",
+      "name": "Monrovia",
+      "region_id": "12449",
+      "url": "/city/12449/CA/Monrovia"
+    },
+    {
+      "query": "Montebello",
+      "name": "Montebello",
+      "region_id": "12502",
+      "url": "/city/12502/CA/Montebello"
+    },
+    {
+      "query": "Monterey Park",
+      "name": "Monterey Park",
+      "region_id": "12524",
+      "url": "/city/12524/CA/Monterey-Park"
+    },
+    {
+      "query": "Norwalk",
+      "name": "Norwalk",
+      "region_id": "13533",
+      "url": "/city/13533/CA/Norwalk"
+    },
+    {
+      "query": "Palos Verdes Estates",
+      "name": "Palos Verdes Estates",
+      "region_id": "14343",
+      "url": "/city/14343/CA/Palos-Verdes-Estates"
+    },
+    {
+      "query": "Paramount",
+      "name": "Paramount",
+      "region_id": "14399",
+      "url": "/city/14399/CA/Paramount"
+    },
+    {
+      "query": "Pico Rivera",
+      "name": "Pico Rivera",
+      "region_id": "14737",
+      "url": "/city/14737/CA/Pico-Rivera"
+    },
+    {
+      "query": "Rancho Palos Verdes",
+      "name": "Rancho Palos Verdes",
+      "region_id": "15404",
+      "url": "/city/15404/CA/Rancho-Palos-Verdes"
+    },
+    {
+      "query": "Redondo Beach",
+      "name": "Redondo Beach",
+      "region_id": "15507",
+      "url": "/city/15507/CA/Redondo-Beach"
+    },
+    {
+      "query": "Rolling Hills",
+      "name": "Rolling Hills",
+      "region_id": "16076",
+      "url": "/city/16076/CA/Rolling-Hills"
+    },
+    {
+      "query": "Rolling Hills Estates",
+      "name": "Rolling Hills Estates",
+      "region_id": "16085",
+      "url": "/city/16085/CA/Rolling-Hills-Estates"
+    },
+    {
+      "query": "Rosemead",
+      "name": "Rosemead",
+      "region_id": "16137",
+      "url": "/city/16137/CA/Rosemead"
+    },
+    {
+      "query": "San Dimas",
+      "name": "San Dimas",
+      "region_id": "16923",
+      "url": "/city/16923/CA/San-Dimas"
+    },
+    {
+      "query": "San Fernando",
+      "name": "San Fernando",
+      "region_id": "16938",
+      "url": "/city/16938/CA/San-Fernando"
+    },
+    {
+      "query": "San Gabriel",
+      "name": "San Gabriel",
+      "region_id": "17164",
+      "url": "/city/17164/CA/San-Gabriel"
+    },
+    {
+      "query": "San Marino",
+      "name": "San Marino",
+      "region_id": "17481",
+      "url": "/city/17481/CA/San-Marino"
+    },
+    {
+      "query": "Santa Fe Springs",
+      "name": "Santa Fe Springs",
+      "region_id": "17691",
+      "url": "/city/17691/CA/Santa-Fe-Springs"
+    },
+    {
+      "query": "Sierra Madre",
+      "name": "Sierra Madre",
+      "region_id": "18335",
+      "url": "/city/18335/CA/Sierra-Madre"
+    },
+    {
+      "query": "Signal Hill",
+      "name": "Signal Hill",
+      "region_id": "18347",
+      "url": "/city/18347/CA/Signal-Hill"
+    },
+    {
+      "query": "South El Monte",
+      "name": "South El Monte",
+      "region_id": "18603",
+      "url": "/city/18603/CA/South-El-Monte"
+    },
+    {
+      "query": "South Gate",
+      "name": "South Gate",
+      "region_id": "18624",
+      "url": "/city/18624/CA/South-Gate"
+    },
+    {
+      "query": "South Pasadena",
+      "name": "South Pasadena",
+      "region_id": "18661",
+      "url": "/city/18661/CA/South-Pasadena"
+    },
+    {
+      "query": "Temple City",
+      "name": "Temple City",
+      "region_id": "19707",
+      "url": "/city/19707/CA/Temple-City"
+    },
+    {
+      "query": "Vernon",
+      "name": "Vernon",
+      "region_id": "20486",
+      "url": "/city/20486/CA/Vernon"
+    },
+    {
+      "query": "Walnut",
+      "name": "Walnut",
+      "region_id": "20628",
+      "url": "/city/20628/CA/Walnut"
+    },
+    {
+      "query": "West Covina",
+      "name": "West Covina",
+      "region_id": "20753",
+      "url": "/city/20753/CA/West-Covina"
+    },
+    {
+      "query": "Westlake Village",
+      "name": "Westlake Village",
+      "region_id": "20777",
+      "url": "/city/20777/CA/Westlake-Village"
+    },
+    {
+      "query": "Marina Del Rey",
+      "name": "Marina del Rey",
+      "region_id": "24172",
+      "url": "/city/24172/CA/Marina-del-Rey"
+    },
+    {
+      "query": "Topanga",
+      "name": "Topanga",
+      "region_id": "35311",
+      "url": "/city/35311/CA/Topanga"
+    },
+    {
+      "query": "East Los Angeles",
+      "name": "East Los Angeles",
+      "region_id": "22411",
+      "url": "/city/22411/CA/East-Los-Angeles"
+    },
+    {
+      "query": "East La",
+      "name": "East La Mirada",
+      "region_id": "22409",
+      "url": "/city/22409/CA/East-La-Mirada"
+    },
+    {
+      "query": "Ladera Heights",
+      "name": "Ladera Heights",
+      "region_id": "23650",
+      "url": "/city/23650/CA/Ladera-Heights"
+    },
+    {
+      "query": "Lennox",
+      "name": "Lennox",
+      "region_id": "23841",
+      "url": "/city/23841/CA/Lennox"
+    },
+    {
+      "query": "Del Aire",
+      "name": "Del Aire",
+      "region_id": "22254",
+      "url": "/city/22254/CA/Del-Aire"
+    },
+    {
+      "query": "Westmont",
+      "name": "Westmont",
+      "region_id": "26746",
+      "url": "/city/26746/CA/Westmont"
+    },
+    {
+      "query": "Florence-Graham",
+      "name": "Florence-Graham",
+      "region_id": "22672",
+      "url": "/city/22672/CA/Florence-Graham"
+    },
+    {
+      "query": "Willowbrook",
+      "name": "Willowbrook",
+      "region_id": "26788",
+      "url": "/city/26788/CA/Willowbrook"
+    },
+    {
+      "query": "Walnut Park",
+      "name": "Walnut Park",
+      "region_id": "26696",
+      "url": "/city/26696/CA/Walnut-Park"
+    },
+    {
+      "query": "West Whittier",
+      "name": "West Whittier-Los Nietos",
+      "region_id": "26760",
+      "url": "/city/26760/CA/West-Whittier-Los-Nietos"
+    },
+    {
+      "query": "South Whittier",
+      "name": "South Whittier",
+      "region_id": "26107",
+      "url": "/city/26107/CA/South-Whittier"
+    },
+    {
+      "query": "Hacienda Heights",
+      "name": "Hacienda Heights",
+      "region_id": "23174",
+      "url": "/city/23174/CA/Hacienda-Heights"
+    },
+    {
+      "query": "Rowland Heights",
+      "name": "Rowland Heights",
+      "region_id": "25415",
+      "url": "/city/25415/CA/Rowland-Heights"
+    },
+    {
+      "query": "Avocado Heights",
+      "name": "Avocado Heights",
+      "region_id": "21303",
+      "url": "/city/21303/CA/Avocado-Heights"
+    },
+    {
+      "query": "Valinda",
+      "name": "Valinda",
+      "region_id": "26623",
+      "url": "/city/26623/CA/Valinda"
+    },
+    {
+      "query": "Charter Oak",
+      "name": "Charter Oak",
+      "region_id": "21898",
+      "url": "/city/21898/CA/Charter-Oak"
+    },
+    {
+      "query": "Citrus",
+      "name": "Citrus Heights",
+      "region_id": "3539",
+      "url": "/city/3539/CA/Citrus-Heights"
+    },
+    {
+      "query": "Vincent",
+      "name": "Vincent",
+      "region_id": "31340",
+      "url": "/city/31340/CA/Vincent"
+    },
+    {
+      "query": "South San Gabriel",
+      "name": "South San Gabriel",
+      "region_id": "26088",
+      "url": "/city/26088/CA/South-San-Gabriel"
+    },
+    {
+      "query": "East Pasadena",
+      "name": "East Pasadena",
+      "region_id": "22426",
+      "url": "/city/22426/CA/East-Pasadena"
+    },
+    {
+      "query": "East San Gabriel",
+      "name": "East San Gabriel",
+      "region_id": "22439",
+      "url": "/city/22439/CA/East-San-Gabriel"
+    },
+    {
+      "query": "Kinneloa Mesa",
+      "name": "Kinneloa Mesa",
+      "region_id": "28298",
+      "url": "/city/28298/CA/Kinneloa-Mesa"
+    },
+    {
+      "query": "Mayflower Village",
+      "name": "Mayflower Village",
+      "region_id": "24216",
+      "url": "/city/24216/CA/Mayflower-Village"
+    },
+    {
+      "query": "North El Monte",
+      "name": "North El Monte",
+      "region_id": "24615",
+      "url": "/city/24615/CA/North-El-Monte"
+    },
+    {
+      "query": "South Monrovia Island",
+      "name": "South Monrovia Island",
+      "region_id": "35036",
+      "url": "/city/35036/CA/South-Monrovia-Island"
+    },
+    {
+      "query": "Stevenson Ranch",
+      "name": "Stevenson Ranch",
+      "region_id": "35080",
+      "url": "/city/35080/CA/Stevenson-Ranch"
+    },
+    {
+      "query": "Castaic",
+      "name": "Castaic",
+      "region_id": "35836",
+      "url": "/city/35836/CA/Castaic"
+    },
+    {
+      "query": "Val Verde",
+      "name": "Val Verde",
+      "region_id": "26642",
+      "url": "/city/26642/CA/Val-Verde"
+    },
+    {
+      "query": "Agua Dulce",
+      "name": "Agua Dulce",
+      "region_id": "28258",
+      "url": "/city/28258/CA/Agua-Dulce"
+    },
+    {
+      "query": "Acton",
+      "name": "Acton",
+      "region_id": "21123",
+      "url": "/city/21123/CA/Acton"
+    },
+    {
+      "query": "Littlerock",
+      "name": "Littlerock",
+      "region_id": "23908",
+      "url": "/city/23908/CA/Littlerock"
+    },
+    {
+      "query": "Quartz Hill",
+      "name": "Quartz Hill",
+      "region_id": "25118",
+      "url": "/city/25118/CA/Quartz-Hill"
+    },
+    {
+      "query": "Lake Los Angeles",
+      "name": "Lake Los Angeles",
+      "region_id": "23695",
+      "url": "/city/23695/CA/Lake-Los-Angeles"
+    },
+    {
+      "query": "Sun Village",
+      "name": "Sun Village",
+      "region_id": "35235",
+      "url": "/city/35235/CA/Sun-Village"
+    },
+    {
+      "query": "Desert View Highlands",
+      "name": "Desert View Highlands",
+      "region_id": "22297",
+      "url": "/city/22297/CA/Desert-View-Highlands"
+    },
+    {
+      "query": "La Crescenta",
+      "name": "La Crescenta-Montrose",
+      "region_id": "23645",
+      "url": "/city/23645/CA/La-Crescenta-Montrose"
+    },
+    {
+      "query": "West Carson",
+      "name": "West Carson",
+      "region_id": "26728",
+      "url": "/city/26728/CA/West-Carson"
+    },
+    {
+      "query": "Rancho Dominguez",
+      "name": "West Rancho Dominguez",
+      "region_id": "35544",
+      "url": "/city/35544/CA/West-Rancho-Dominguez"
+    },
+    {
+      "query": "East Rancho Dominguez",
+      "name": "East Rancho Dominguez",
+      "region_id": "32416",
+      "url": "/city/32416/CA/East-Rancho-Dominguez"
+    },
+    {
+      "query": "Athens",
+      "name": "West Athens",
+      "region_id": "26725",
+      "url": "/city/26725/CA/West-Athens"
+    },
+    {
+      "query": "Ladera",
+      "name": "Ladera Ranch",
+      "region_id": "33325",
+      "url": "/city/33325/CA/Ladera-Ranch"
+    },
+    {
+      "query": "View Park-Windsor Hills",
+      "name": "View Park-Windsor Hills",
+      "region_id": "26660",
+      "url": "/city/26660/CA/View-Park-Windsor-Hills"
     }
   ]
 }
\ No newline at end of file
diff --git a/public/sales.html b/public/sales.html
index 3a25a4f..d238b9f 100644
--- a/public/sales.html
+++ b/public/sales.html
@@ -31,6 +31,7 @@
 </header>
 <div class="wrap">
   <div class="stat" id="stat">loading…</div>
+  <div id="depthNote" style="font-size:12px;color:var(--muted);margin:4px 0 10px;padding:6px 10px;border-left:2px solid #C8A24B;background:rgba(200,162,75,.06)"></div>
   <div class="ctrls">
     <input type="text" id="city" placeholder="filter city… e.g. Long Beach">
     <input type="number" id="minP" placeholder="min $" step="50000">
@@ -57,7 +58,8 @@ async function load(){
   if($('#maxP').value) p.set('maxPrice',$('#maxP').value);
   const d=await (await fetch('/api/closed-sales?'+p)).json();
   const t=d.total||{};
-  $('#stat').innerHTML=`<b>${(t.n||0).toLocaleString()}</b> sold condos · avg <b>${money(t.avg_price)}</b> · ${(t.oldest||'').toString().slice(0,10)} → ${(t.newest||'').toString().slice(0,10)} · <span style="color:var(--muted)">${d.source}</span>`;
+  $('#stat').innerHTML=`<b>${(t.n||0).toLocaleString()}</b> sold condos · avg <b>${money(t.avg_price)}</b> · ${(t.oldest||'').toString().slice(0,10)} → ${(t.newest||'').toString().slice(0,10)} · <span style="color:var(--muted)">${d.source||''}</span>`;
+  if(d.depthNote) $('#depthNote').textContent='ℹ '+d.depthNote;
   $('#tbl tbody').innerHTML=(d.rows||[]).slice(0,300).map(r=>`<tr><td>${r.url?`<a class="row" href="${r.url}" target="_blank" rel="noopener noreferrer">${r.address}</a>`:r.address}</td><td>${r.city}</td><td class="n price">${money(r.sold_price)}</td><td class="n" style="color:var(--muted)">${(r.sold_date||'').toString().slice(0,10)}</td></tr>`).join('');
   // charts
   const bc=d.byCity||[];
diff --git a/scripts/fetch-closed-sales.js b/scripts/fetch-closed-sales.js
index 212c020..8a1d42f 100644
--- a/scripts/fetch-closed-sales.js
+++ b/scripts/fetch-closed-sales.js
@@ -19,6 +19,13 @@ const ROOT = path.join(__dirname, '..');
 const pool = new Pool({ host: '/tmp', port: 5432, database: 'cre', user: process.env.USER || 'stevestudio2' });
 const MAXR = process.env.CC_MAXREGIONS != null ? +process.env.CC_MAXREGIONS : 12;
 const PROBE = process.env.CC_PROBE === '1';
+const NUM_HOMES = 350;                 // Redfin gis-csv per-query cap
+const CAP_THRESHOLD = NUM_HOMES - 5;   // >= this => region is truncated, re-query in price bands
+// Price bands (USD) to union when a region is capped. Open-ended top band catches luxury tail.
+const PRICE_BANDS = [
+  [0, 500000], [500000, 750000], [750000, 1000000],
+  [1000000, 1500000], [1500000, 3000000], [3000000, 0]
+];
 
 function parseCsv(text) {
   const lines = text.trim().split('\n'); if (lines.length < 2) return [];
@@ -34,21 +41,57 @@ function parseCsv(text) {
     id serial PRIMARY KEY, address text, city text, zip text, sold_price bigint, sold_date date,
     beds numeric, baths numeric, sqft int, year_built int, mls text, url text UNIQUE,
     source text DEFAULT 'redfin gis-csv (sold)', created_at timestamptz DEFAULT now())`);
+  // Resume ledger: which region+type pairs have been fully swept, so a re-run after a session timeout
+  // skips finished regions and picks up the tail. Idempotent regardless (url is the unique key).
+  await pool.query(`CREATE TABLE IF NOT EXISTS swept_region (
+    region_id text, region_type int, name text, swept_at timestamptz DEFAULT now(),
+    PRIMARY KEY (region_id, region_type))`);
+  const FRESH = process.env.CC_FRESH === '1';
+  if (FRESH) await pool.query('TRUNCATE swept_region');
+  const done = new Set((await pool.query('SELECT region_id, region_type FROM swept_region')).rows
+    .map(r => r.region_id + ':' + r.region_type));
 
   // Region list = resolved city regions (type 6) + neighborhood regions (type 1), from both resolver files.
   const load = (f, type) => { try { return (JSON.parse(fs.readFileSync(path.join(ROOT, 'data', f), 'utf8')).regions || []).filter(r => r.region_id).map(r => ({ ...r, region_type: r.region_type || type })); } catch (_) { return []; } };
   const seen = new Set();
   const regions = [...load('redfin-regions.json', 6), ...load('redfin-neighborhoods.json', 1)]
-    .filter(r => { const k = r.region_id + ':' + r.region_type; if (seen.has(k)) return false; seen.add(k); return true; });
+    .filter(r => { const k = r.region_id + ':' + r.region_type; if (seen.has(k)) return false; seen.add(k); return true; })
+    .filter(r => PROBE || !done.has(r.region_id + ':' + r.region_type));  // resume: skip already-swept
   const work = (MAXR ? regions.slice(0, MAXR) : regions);
-  console.log(`closed-sales: ${work.length} regions${PROBE ? ' (PROBE)' : ''}`);
+  console.log(`closed-sales: ${work.length} regions to sweep${PROBE ? ' (PROBE)' : ''} (${done.size} already done)`);
+
+  // Per-region incremental DB flush so a mid-session Browserbase timeout still persists everything
+  // fetched so far (idempotent on url). Returns # newly inserted.
+  const num = v => { const n = +String(v == null ? '' : v).replace(/[^0-9.]/g, ''); return Number.isFinite(n) && n > 0 ? Math.round(n) : null; };
+  let added = 0, captured = 0;
+  const flush = async (recs, region) => {
+    captured += recs.length;
+    for (const r of recs) {
+      const addr = r['ADDRESS'] || r['Address'], price = num(r['PRICE'] || r['Price']);
+      const url = r['URL'] || r['URL (SEE https://www.redfin.com/buy-a-home/comparative-market-analysis FOR INFO ON PRICING)'];
+      const sold = r['SOLD DATE'] || r['Sold Date'] || r['SOLD_DATE'];
+      if (!addr || !url) continue;
+      try {
+        const ins = await pool.query(`INSERT INTO closed_sale(address,city,zip,sold_price,sold_date,beds,baths,sqft,year_built,mls,url)
+          VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11) ON CONFLICT(url) DO NOTHING`,
+          [addr, r['CITY'] || r['City'], r['ZIP OR POSTAL CODE'] || r['ZIP'], price,
+           sold && /\d/.test(sold) ? new Date(sold) : null, num(r['BEDS']), num(r['BATHS']),
+           num(r['SQUARE FEET']), num(r['YEAR BUILT']), r['MLS#'] || r['MLS'], url.startsWith('http') ? url : 'https://www.redfin.com' + url]);
+        added += ins.rowCount || 0;
+      } catch (_) {}
+    }
+    if (region) await pool.query('INSERT INTO swept_region(region_id,region_type,name) VALUES($1,$2,$3) ON CONFLICT DO NOTHING',
+      [region.region_id, region.region_type, region.name || region.query]);
+  };
 
   // Candidate sold-filter params to try (Redfin's gis-csv sold mode). First that returns rows wins.
+  // Redfin's free sold feed empirically returns ~5yr depth even with sold_within_days=3650 (10yr) —
+  // the depth is verified honestly AFTER the sweep (see min(sold_date) report); we do NOT label 10yr.
   const SOLD_VARIANTS = [
-    's=9&num_homes=350&sold_within_days=3650',           // sold last 10yr (3650d)
-    'status=9&sold_within_days=3650&num_homes=350',
-    'sold=1&sold_within_days=3650&num_homes=350',
-    'include=sold-3yr&num_homes=350'
+    `s=9&num_homes=${NUM_HOMES}&sold_within_days=3650`,
+    `status=9&sold_within_days=3650&num_homes=${NUM_HOMES}`,
+    `sold=1&sold_within_days=3650&num_homes=${NUM_HOMES}`,
+    `include=sold-3yr&num_homes=${NUM_HOMES}`
   ];
 
   let browser, rows = [], soldParam = null;
@@ -61,9 +104,20 @@ function parseCsv(text) {
     page.setDefaultTimeout(45000);
     await page.goto('https://www.redfin.com/', { waitUntil: 'domcontentloaded' }); await page.waitForTimeout(3000);
 
+    // in-page fetch of one gis-csv url -> parsed rows (or [] on err). 'price' = optional [min,max] band.
+    const fetchRegion = async (r, soldP, band) => {
+      let extra = '';
+      if (band) { if (band[0] > 0) extra += `&min_price=${band[0]}`; if (band[1] > 0) extra += `&max_price=${band[1]}`; }
+      const url = `https://www.redfin.com/stingray/api/gis-csv?al=1&region_id=${r.region_id}&region_type=${r.region_type || 6}&uipt=3&${soldP}${extra}`;
+      const txt = await page.evaluate(async u => { try { const x = await fetch(u); return x.ok ? await x.text() : ('ERR' + x.status); } catch (e) { return 'EXC'; } }, url);
+      if (typeof txt === 'string' && (txt.startsWith('ERR') || txt === 'EXC')) return [];
+      return parseCsv(txt);
+    };
+
     // PROBE: find the sold param that returns rows, on the first region.
     const r0 = work[0];
-    for (const v of SOLD_VARIANTS) {
+    if (!r0) { console.log('nothing to sweep — all resolved regions already in swept_region (CC_FRESH=1 to re-sweep).'); }
+    else for (const v of SOLD_VARIANTS) {
       const url = `https://www.redfin.com/stingray/api/gis-csv?al=1&region_id=${r0.region_id}&region_type=${r0.region_type}&uipt=3&${v}`;
       const txt = await page.evaluate(async u => { try { const r = await fetch(u); return r.ok ? await r.text() : ('ERR' + r.status); } catch (e) { return 'EXC'; } }, url);
       const n = (typeof txt === 'string' && txt.startsWith('ERR')) ? 0 : parseCsv(txt).length;
@@ -72,34 +126,39 @@ function parseCsv(text) {
     }
     if (!soldParam) { console.log('NO sold variant returned rows — Redfin sold feed param needs revisiting.'); }
     else if (!PROBE) {
+      let bandedRegions = 0, sweptCount = 0;
       for (const r of work) {
-        const url = `https://www.redfin.com/stingray/api/gis-csv?al=1&region_id=${r.region_id}&region_type=${r.region_type || 6}&uipt=3&${soldParam}`;
-        const txt = await page.evaluate(async u => { try { const x = await fetch(u); return x.ok ? await x.text() : 'ERR'; } catch (e) { return 'EXC'; } }, url);
-        if (typeof txt === 'string' && (txt === 'ERR' || txt === 'EXC')) continue;
-        rows.push(...parseCsv(txt).map(x => ({ ...x, _region: r.name })));
-        await page.waitForTimeout(800);
+        let base;
+        try { base = await fetchRegion(r, soldParam); }
+        catch (e) { console.error('  session closed mid-sweep — flushed regions persist, re-run to resume:', e.message); break; }
+        if (base.length < CAP_THRESHOLD) {
+          await flush(base, r); sweptCount++;
+          await page.waitForTimeout(400);
+          continue;
+        }
+        // Region is capped/truncated — re-query in price bands and union (dedupe on url before flush).
+        bandedRegions++;
+        const seenUrl = new Set(); const unioned = [];
+        const keep = arr => { for (const x of arr) { const u = x['URL'] || x['URL (SEE https://www.redfin.com/buy-a-home/comparative-market-analysis FOR INFO ON PRICING)']; if (u && !seenUrl.has(u)) { seenUrl.add(u); unioned.push(x); } } };
+        keep(base);
+        let broke = false;
+        for (const band of PRICE_BANDS) {
+          try { keep(await fetchRegion(r, soldParam, band)); }
+          catch (e) { broke = true; break; }
+          await page.waitForTimeout(350);
+        }
+        await flush(unioned, broke ? null : r); sweptCount++;  // only mark done if all bands completed
+        process.stderr.write(`  [banded] ${r.name}: base ${base.length} -> ${seenUrl.size} unioned across ${PRICE_BANDS.length} bands${broke ? ' (PARTIAL — not marked done)' : ''}\n`);
+        if (broke) { console.error('  session closed mid-band — re-run to resume.'); break; }
       }
+      console.log(`swept ${sweptCount}/${work.length} regions this session (${bandedRegions} banded)`);
     }
   } catch (e) { console.error('FATAL', e.message); } finally { if (browser) await browser.close().catch(() => {}); }
 
-  // ingest
-  const num = v => { const n = +String(v == null ? '' : v).replace(/[^0-9.]/g, ''); return Number.isFinite(n) && n > 0 ? Math.round(n) : null; };
-  let added = 0;
-  for (const r of rows) {
-    const addr = r['ADDRESS'] || r['Address'], price = num(r['PRICE'] || r['Price']), url = r['URL'] || r['URL (SEE https://www.redfin.com/buy-a-home/comparative-market-analysis FOR INFO ON PRICING)'];
-    const sold = r['SOLD DATE'] || r['Sold Date'] || r['SOLD_DATE'];
-    if (!addr || !url) continue;
-    try {
-      await pool.query(`INSERT INTO closed_sale(address,city,zip,sold_price,sold_date,beds,baths,sqft,year_built,mls,url)
-        VALUES($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11) ON CONFLICT(url) DO NOTHING`,
-        [addr, r['CITY'] || r['City'], r['ZIP OR POSTAL CODE'] || r['ZIP'], price,
-         sold && /\d/.test(sold) ? new Date(sold) : null, num(r['BEDS']), num(r['BATHS']),
-         num(r['SQUARE FEET']), num(r['YEAR BUILT']), r['MLS#'] || r['MLS'], url.startsWith('http') ? url : 'https://www.redfin.com' + url]);
-      added++;
-    } catch (_) {}
-  }
-  const total = (await pool.query('SELECT count(*)::int n FROM closed_sale')).rows[0].n;
-  console.log(`\nsold param = ${soldParam || 'NONE'} | captured ${rows.length} rows, +${added} new | closed_sale total ${total}`);
+  const depth = (await pool.query(
+    'SELECT count(*)::int n, min(sold_date) oldest, max(sold_date) newest, count(distinct city)::int cities FROM closed_sale')).rows[0];
+  console.log(`\nsold param = ${soldParam || 'NONE'} | captured ${captured} rows, +${added} new | closed_sale total ${depth.n}`);
+  console.log(`HONEST DEPTH: ${depth.oldest ? depth.oldest.toISOString().slice(0, 10) : '?'} -> ${depth.newest ? depth.newest.toISOString().slice(0, 10) : '?'} across ${depth.cities} cities (Redfin free sold feed ~5yr; NOT 10yr).`);
   console.log('cost: ~$0.04-0.10 (1 Browserbase session).');
   await pool.end();
 })();
diff --git a/scripts/resolve-redfin-neighborhoods.js b/scripts/resolve-redfin-neighborhoods.js
index 79dc8f3..a21dcbc 100644
--- a/scripts/resolve-redfin-neighborhoods.js
+++ b/scripts/resolve-redfin-neighborhoods.js
@@ -5,12 +5,27 @@ const fs = require('fs');
 const path = require('path');
 const { chromium } = require('playwright-core');
 const Browserbase = require('@browserbasehq/sdk').default;
+const { LA_NEIGHBORHOODS } = require('./sources/la-county');
 const env = fs.readFileSync(process.env.HOME + '/.claude/skills/browserbase/.env', 'utf8');
 const get = (t, k) => (t.match(new RegExp('^' + k + '=(.*)$', 'm')) || [])[1]?.replace(/['"]/g, '').trim();
 const KEY = get(env, 'BROWSERBASE_API_KEY'), PROJECT = get(env, 'BROWSERBASE_PROJECT_ID');
-const HOODS = (process.env.CC_HOODS || 'Van Nuys,Reseda,North Hollywood,Sherman Oaks,Encino,Woodland Hills,Northridge,Canoga Park,Studio City,Echo Park,Highland Park,Koreatown,Venice,Mar Vista,Pacific Palisades,San Pedro').split(',').map(s => s.trim());
+
+// CC_HOODS=<csv> overrides; CC_ALL=1 = full LA_NEIGHBORHOODS set (title-cased, deduped on alias spellings).
+const titleCase = s => s.replace(/\b\w/g, c => c.toUpperCase());
+const FULL_HOODS = [...new Set(LA_NEIGHBORHOODS.map(titleCase))];
+const HOODS = (process.env.CC_HOODS ? process.env.CC_HOODS.split(',')
+  : (process.env.CC_ALL === '1' ? FULL_HOODS
+    : 'Van Nuys,Reseda,North Hollywood,Sherman Oaks,Encino,Woodland Hills,Northridge,Canoga Park,Studio City,Echo Park,Highland Park,Koreatown,Venice,Mar Vista,Pacific Palisades,San Pedro'.split(','))).map(s => s.trim()).filter(Boolean);
+const HOODS_FILE = path.join(__dirname, '..', 'data', 'redfin-neighborhoods.json');
 
 (async () => {
+  let prior = { regions: [] };
+  try { prior = JSON.parse(fs.readFileSync(HOODS_FILE, 'utf8')); } catch (_) {}
+  const haveIds = new Set((prior.regions || []).filter(r => r.region_id).map(r => String(r.region_id)));
+  const haveQueries = new Set((prior.regions || []).filter(r => r.region_id).map(r => (r.query || '').toLowerCase()));
+  const todo = HOODS.filter(h => !haveQueries.has(h.toLowerCase()));
+  process.stderr.write(`resolve-hoods: ${HOODS.length} requested, ${todo.length} new (skipping ${HOODS.length - todo.length} already resolved)\n`);
+
   const regions = [];
   let browser, session;
   try {
@@ -23,7 +38,7 @@ const HOODS = (process.env.CC_HOODS || 'Van Nuys,Reseda,North Hollywood,Sherman
     await page.goto('https://www.redfin.com/', { waitUntil: 'domcontentloaded' });
     await page.waitForTimeout(3000);
 
-    for (const hood of HOODS) {
+    for (const hood of todo) {
       const r = await page.evaluate(async (q) => {
         try {
           const resp = await fetch('https://www.redfin.com/stingray/do/location-autocomplete?location=' +
@@ -51,8 +66,16 @@ const HOODS = (process.env.CC_HOODS || 'Van Nuys,Reseda,North Hollywood,Sherman
   } catch (e) { process.stderr.write('ERR ' + e.message + '\n'); }
   finally { if (browser) try { await browser.close(); } catch (_) {} }
 
-  fs.writeFileSync(path.join(__dirname, '..', 'data', 'redfin-neighborhoods.json'),
-    JSON.stringify({ resolved_at: new Date().toISOString(), regions }, null, 2));
-  console.log(JSON.stringify({ requested: HOODS.length, resolved: regions.filter(r => r.region_id).length,
-    missing: regions.filter(r => !r.region_id).map(r => r.query) }));
+  const merged = [...(prior.regions || [])];
+  let appended = 0, miss = [];
+  for (const r of regions) {
+    if (!r.region_id) { miss.push(r.query); continue; }
+    if (haveIds.has(String(r.region_id))) continue;
+    haveIds.add(String(r.region_id)); merged.push(r); appended++;
+  }
+  const resolvedTotal = merged.filter(r => r.region_id).length;
+  fs.writeFileSync(HOODS_FILE,
+    JSON.stringify({ resolved_at: new Date().toISOString(), count: resolvedTotal, regions: merged }, null, 2));
+  console.log(JSON.stringify({ requested: HOODS.length, newly_queried: todo.length,
+    appended, resolved_total: resolvedTotal, missing: miss }));
 })();
diff --git a/scripts/resolve-redfin-regions.js b/scripts/resolve-redfin-regions.js
index e10a3eb..02d02d3 100644
--- a/scripts/resolve-redfin-regions.js
+++ b/scripts/resolve-redfin-regions.js
@@ -7,13 +7,29 @@ const fs = require('fs');
 const path = require('path');
 const { chromium } = require('playwright-core');
 const Browserbase = require('@browserbasehq/sdk').default;
-const { NAV_HUBS } = require('./sources/la-county');
+const { NAV_HUBS, INCORPORATED, UNINCORPORATED } = require('./sources/la-county');
 const env = fs.readFileSync(process.env.HOME + '/.claude/skills/browserbase/.env', 'utf8');
 const get = (t, k) => (t.match(new RegExp('^' + k + '=(.*)$', 'm')) || [])[1]?.replace(/['"]/g, '').trim();
 const KEY = get(env, 'BROWSERBASE_API_KEY'), PROJECT = get(env, 'BROWSERBASE_PROJECT_ID');
-const CITIES = (process.env.CC_CITIES ? process.env.CC_CITIES.split(',') : NAV_HUBS).map(s => s.trim()).filter(Boolean);
+
+// CC_CITIES=<csv> overrides; CC_ALL=1 = full county place set (88 incorporated + unincorporated
+// communities), title-cased for the autocomplete query; default = the NAV_HUBS investment set.
+const titleCase = s => s.replace(/\b\w/g, c => c.toUpperCase());
+const FULL_PLACES = [...new Set([...INCORPORATED, ...UNINCORPORATED].map(titleCase))];
+const CITIES = (process.env.CC_CITIES ? process.env.CC_CITIES.split(',')
+  : (process.env.CC_ALL === '1' ? FULL_PLACES : NAV_HUBS)).map(s => s.trim()).filter(Boolean);
+const REGIONS_FILE = path.join(__dirname, '..', 'data', 'redfin-regions.json');
 
 (async () => {
+  // Load existing so we APPEND + DEDUPE by region_id rather than overwrite (cities that don't resolve
+  // are logged + skipped, never faked).
+  let prior = { regions: [] };
+  try { prior = JSON.parse(fs.readFileSync(REGIONS_FILE, 'utf8')); } catch (_) {}
+  const haveIds = new Set((prior.regions || []).filter(r => r.region_id).map(r => String(r.region_id)));
+  const haveQueries = new Set((prior.regions || []).filter(r => r.region_id).map(r => (r.query || '').toLowerCase()));
+  const todo = CITIES.filter(c => !haveQueries.has(c.toLowerCase()));
+  process.stderr.write(`resolve-regions: ${CITIES.length} requested, ${todo.length} new (skipping ${CITIES.length - todo.length} already resolved)\n`);
+
   const regions = [];
   let browser, session;
   try {
@@ -26,7 +42,7 @@ const CITIES = (process.env.CC_CITIES ? process.env.CC_CITIES.split(',') : NAV_H
     await page.goto('https://www.redfin.com/', { waitUntil: 'domcontentloaded' });
     await page.waitForTimeout(3000);
 
-    for (const city of CITIES) {
+    for (const city of todo) {
       const r = await page.evaluate(async (q) => {
         try {
           const resp = await fetch('https://www.redfin.com/stingray/do/location-autocomplete?location=' +
@@ -51,9 +67,17 @@ const CITIES = (process.env.CC_CITIES ? process.env.CC_CITIES.split(',') : NAV_H
   } catch (e) { process.stderr.write('ERR ' + e.message + '\n'); }
   finally { if (browser) try { await browser.close(); } catch (_) {} }
 
-  const resolved = regions.filter(r => r.region_id);
-  fs.writeFileSync(path.join(__dirname, '..', 'data', 'redfin-regions.json'),
-    JSON.stringify({ resolved_at: new Date().toISOString(), count: resolved.length, regions }, null, 2));
-  console.log(JSON.stringify({ requested: CITIES.length, resolved: resolved.length,
-    missing: regions.filter(r => !r.region_id).map(r => r.query) }));
+  // MERGE: keep all prior entries, append newly-resolved ones whose region_id we don't already have.
+  const merged = [...(prior.regions || [])];
+  let appended = 0, miss = [];
+  for (const r of regions) {
+    if (!r.region_id) { miss.push(r.query); continue; }
+    if (haveIds.has(String(r.region_id))) continue;
+    haveIds.add(String(r.region_id)); merged.push(r); appended++;
+  }
+  const resolvedTotal = merged.filter(r => r.region_id).length;
+  fs.writeFileSync(REGIONS_FILE,
+    JSON.stringify({ resolved_at: new Date().toISOString(), count: resolvedTotal, regions: merged }, null, 2));
+  console.log(JSON.stringify({ requested: CITIES.length, newly_queried: todo.length,
+    appended, resolved_total: resolvedTotal, missing: miss }));
 })();
diff --git a/scripts/serve.js b/scripts/serve.js
index a07080a..bb64c90 100644
--- a/scripts/serve.js
+++ b/scripts/serve.js
@@ -368,7 +368,9 @@ app.get('/api/closed-sales', async (req, res) => {
     const byCity = await q(`SELECT city, count(*)::int n, round(avg(sold_price))::bigint avg_price FROM closed_sale ${w} GROUP BY city ORDER BY n DESC LIMIT 16`, args);
     const byYear = await q(`SELECT extract(year FROM sold_date)::int yr, count(*)::int n, round(avg(sold_price))::bigint avg_price FROM closed_sale ${w} AND sold_date IS NOT NULL GROUP BY 1 ORDER BY 1`, args);
     const tot = (await q(`SELECT count(*)::int n, round(avg(sold_price))::bigint avg_price, min(sold_date) oldest, max(sold_date) newest FROM closed_sale ${w}`, args))[0];
-    res.json({ rows, byCity, byYear, total: tot, source: 'Redfin sold feed (gis-csv, ~5yr) — area market comps' });
+    res.json({ rows, byCity, byYear, total: tot,
+      source: 'Redfin public sold feed (gis-csv)',
+      depthNote: 'Redfin public sold feed (~5yr depth, 2021+); full 10-yr history requires a paid records source (ATTOM / county recorder).' });
   } catch (e) { res.status(502).json({ error: String(e.message).split('\n')[0], rows: [] }); }
 });
 

← 5287876 CRCP: sortable columns on broker + firm tables (click any he  ·  back to Commercialrealestate  ·  CRE: sortable columns on /sales.html comps table (address/ci 4d1c2a9 →