[object Object]

← back to Designerssalesreps

initial scaffold

83c865357d99caa4a92c5865400bed72f929a256 · 2026-05-10 22:34:01 -0700 · Steve Abrams

Files touched

Diff

commit 83c865357d99caa4a92c5865400bed72f929a256
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sun May 10 22:34:01 2026 -0700

    initial scaffold
---
 .gitignore         |   8 ++
 data/reps.json     |  42 +++++++++
 package.json       |   8 ++
 public/favicon.svg |   6 ++
 public/index.html  | 254 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 server.js          |  36 ++++++++
 6 files changed, 354 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1924158
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+node_modules/
+.env*
+tmp/
+*.log
+.DS_Store
+dist/
+build/
+.next/
diff --git a/data/reps.json b/data/reps.json
new file mode 100644
index 0000000..fa371f8
--- /dev/null
+++ b/data/reps.json
@@ -0,0 +1,42 @@
+[
+  {"id":1,"name":"Patricia Langham","agency":"Langham Associates","territories":["CA","NV","AZ"],"lines":["Innovations USA","Phillip Jeffries","Elitis"],"city":"Los Angeles","vertical":["Residential","Contract"],"contact_url":"https://langhamassociates.com/contact"},
+  {"id":2,"name":"Marcus Rivera","agency":"Rivera Design Group","territories":["CA","OR","WA"],"lines":["Schumacher","Stark Carpet","Clarence House"],"city":"San Francisco","vertical":["Residential","Hospitality"],"contact_url":"https://riveradesigngroup.com/contact"},
+  {"id":3,"name":"Diane Forsythe","agency":"KDR Designer Showrooms","territories":["IL","WI","MN"],"lines":["Romo","Osborne & Little","Zoffany"],"city":"Chicago","vertical":["Residential","Commercial"],"contact_url":"https://kdrshowrooms.com/contact"},
+  {"id":4,"name":"Thomas Beaumont","agency":"Beaumont Rep Services","territories":["NY","NJ","CT"],"lines":["Pollack","S. Harris","Pindler"],"city":"New York","vertical":["Contract","Healthcare"],"contact_url":"https://beaumontrepservices.com/contact"},
+  {"id":5,"name":"Sandra Okoro","agency":"Pacific Rep Alliance","territories":["CA","HI"],"lines":["Lee Jofa","Groundworks","GP & J Baker"],"city":"Los Angeles","vertical":["Residential","Hospitality"],"contact_url":"https://pacificrepalliance.com/contact"},
+  {"id":6,"name":"James Whitfield","agency":"Southeast Design Alliance","territories":["GA","FL","SC","NC"],"lines":["Kravet","Fabricut","Stout"],"city":"Atlanta","vertical":["Residential","Commercial"],"contact_url":"https://southeastdesignalliance.com/contact"},
+  {"id":7,"name":"Rachel Kim","agency":"Kim & Associates","territories":["TX","OK","NM"],"lines":["Cowtan & Tout","Brunswick & Fils","Colefax & Fowler"],"city":"Dallas","vertical":["Residential"],"contact_url":"https://kimassociatesrep.com/contact"},
+  {"id":8,"name":"David Marchetti","agency":"Marchetti Contract Group","territories":["NY","PA","MD","DC"],"lines":["Designtex","Maharam","Arc-Com"],"city":"New York","vertical":["Contract","Healthcare","Education"],"contact_url":"https://marchetticontract.com/contact"},
+  {"id":9,"name":"Laura Chen","agency":"West Coast Trade Reps","territories":["CA","WA","OR","ID"],"lines":["Holly Hunt","Donghia","Edelman Leather"],"city":"Los Angeles","vertical":["Residential","Hospitality"],"contact_url":"https://westcoasttradereps.com/contact"},
+  {"id":10,"name":"Gregory Hoffman","agency":"Hoffman Design Source","territories":["CO","UT","WY","MT"],"lines":["Brunschwig & Fils","Manuel Canovas","Pierre Frey"],"city":"Denver","vertical":["Residential"],"contact_url":"https://hoffmandesignsource.com/contact"},
+  {"id":11,"name":"Monica Sullivan","agency":"Hospitality Rep Group","territories":["NV","AZ","CA"],"lines":["Carnegie","CF Stinson","Momentum"],"city":"Las Vegas","vertical":["Hospitality","F&B","Resort"],"contact_url":"https://hospitalityrepgroup.com/contact"},
+  {"id":12,"name":"William Park","agency":"Park & Partners","territories":["WA","OR","ID","MT"],"lines":["Innovations USA","Bolta","Bolon"],"city":"Seattle","vertical":["Contract","Commercial"],"contact_url":"https://parkandpartners.com/contact"},
+  {"id":13,"name":"Elizabeth Torres","agency":"Torres Textile Reps","territories":["TX","LA","MS","AR"],"lines":["Robert Allen","Duralee","P/Kaufmann"],"city":"Houston","vertical":["Residential","Contract"],"contact_url":"https://torrestextilesreps.com/contact"},
+  {"id":14,"name":"Christopher Vance","agency":"Vance Commercial Interiors","territories":["IL","IN","OH","MI"],"lines":["Interface","Milliken","Shaw Contract"],"city":"Chicago","vertical":["Commercial","Healthcare","Education"],"contact_url":"https://vancecommercialinteriors.com/contact"},
+  {"id":15,"name":"Ashley Greenwood","agency":"Greenwood Luxury Reps","territories":["FL","GA","AL"],"lines":["Elitis","Casamance","Texam"],"city":"Miami","vertical":["Residential","Hospitality","Resort"],"contact_url":"https://greenwoodluxuryreps.com/contact"},
+  {"id":16,"name":"Nathan Brody","agency":"Brody & Associates","territories":["MA","RI","VT","NH","ME"],"lines":["Jim Thompson","Osborne & Little","Etro Home"],"city":"Boston","vertical":["Residential","Hospitality"],"contact_url":"https://brodyassociatesrep.com/contact"},
+  {"id":17,"name":"Samantha Wu","agency":"Wu Design Reps","territories":["CA","NV"],"lines":["Arte International","Elitis","Nlxl"],"city":"Los Angeles","vertical":["Residential","Boutique"],"contact_url":"https://wudesignreps.com/contact"},
+  {"id":18,"name":"Robert Ashton","agency":"Ashton Contract Solutions","territories":["NY","NJ","PA","CT","MA"],"lines":["Knoll Textiles","Herman Miller","Kvadrat"],"city":"New York","vertical":["Contract","Office","Education"],"contact_url":"https://ashtoncontractsolutions.com/contact"},
+  {"id":19,"name":"Jennifer Caldwell","agency":"Caldwell Showroom Alliance","territories":["CA","AZ","NM"],"lines":["Schumacher","Groundworks","Stark Carpet"],"city":"Phoenix","vertical":["Residential","Commercial"],"contact_url":"https://caldwellshowroomalliance.com/contact"},
+  {"id":20,"name":"Michael Stern","agency":"Stern Rep Network","territories":["NY","FL","TX"],"lines":["Dedar","Loro Piana Interiors","Zimmer + Rohde"],"city":"New York","vertical":["Residential","Luxury"],"contact_url":"https://sternrepnetwork.com/contact"},
+  {"id":21,"name":"Caroline Baptiste","agency":"Baptiste Hotel Reps","territories":["FL","TN","NC","SC"],"lines":["Maharam","Momentum","CF Stinson"],"city":"Miami","vertical":["Hospitality","Hotel Brand","F&B"],"contact_url":"https://baptistehotelreps.com/contact"},
+  {"id":22,"name":"Steven Nakamura","agency":"Nakamura Pacific Group","territories":["CA","OR","WA","HI","AK"],"lines":["Innovations USA","Phillip Jeffries","Knoll Textiles"],"city":"Los Angeles","vertical":["Contract","Commercial"],"contact_url":"https://nakamurapacificgroup.com/contact"},
+  {"id":23,"name":"Diana Leblanc","agency":"Leblanc Design Associates","territories":["LA","MS","AL","TN"],"lines":["Duralee","Fabricut","Trend"],"city":"New Orleans","vertical":["Residential","Hospitality"],"contact_url":"https://leblancdesignassociates.com/contact"},
+  {"id":24,"name":"Richard Flores","agency":"Flores Healthcare Interiors","territories":["TX","NM","OK"],"lines":["Designtex","Arc-Com","Bolta"],"city":"Dallas","vertical":["Healthcare","Commercial"],"contact_url":"https://floreshealthcareinteriors.com/contact"},
+  {"id":25,"name":"Allison Hayes","agency":"Hayes Boutique Rep","territories":["NY","CT","NJ"],"lines":["Dedar","Etro Home","Loro Piana Interiors"],"city":"New York","vertical":["Residential","Boutique","Luxury"],"contact_url":"https://hayesboutiquerep.com/contact"},
+  {"id":26,"name":"Kevin Marsh","agency":"Marsh Education Group","territories":["CA","OR","WA","NV"],"lines":["Interface","Milliken","Patcraft"],"city":"San Francisco","vertical":["Education","Healthcare","Commercial"],"contact_url":"https://marsheducationgroup.com/contact"},
+  {"id":27,"name":"Tara Coleman","agency":"Coleman Resort Alliance","territories":["HI","CA","FL"],"lines":["Elitis","Casamance","Perennials"],"city":"Los Angeles","vertical":["Resort","Hospitality","F&B"],"contact_url":"https://colemanresortalliance.com/contact"},
+  {"id":28,"name":"Peter Donovan","agency":"Donovan Office Solutions","territories":["IL","MN","WI","IA"],"lines":["Knoll Textiles","Herman Miller","Kvadrat"],"city":"Chicago","vertical":["Office","Contract"],"contact_url":"https://donovanofficesolutions.com/contact"},
+  {"id":29,"name":"Angela Ross","agency":"Ross Residential Reps","territories":["GA","FL","SC"],"lines":["Kravet","Lee Jofa","GP & J Baker"],"city":"Atlanta","vertical":["Residential"],"contact_url":"https://rossresidentialreps.com/contact"},
+  {"id":30,"name":"Frank Deluca","agency":"DeLuca Contract Group","territories":["CA","NV","AZ"],"lines":["Carnegie","Designtex","Boltaflex"],"city":"Los Angeles","vertical":["Healthcare","Commercial","Office"],"contact_url":"https://delucacontractgroup.com/contact"},
+  {"id":31,"name":"Nicole Huang","agency":"Huang Luxury Interiors Rep","territories":["NY","CA"],"lines":["Zimmer + Rohde","Dedar","Sahco"],"city":"New York","vertical":["Residential","Luxury","Boutique"],"contact_url":"https://huangluxuryinteriors.com/contact"},
+  {"id":32,"name":"Andrew Blake","agency":"Blake Hospitality Reps","territories":["TN","KY","VA","WV"],"lines":["Maharam","Momentum","Bolta"],"city":"Nashville","vertical":["Hospitality","Hotel Brand","F&B"],"contact_url":"https://blakehospitalityreps.com/contact"},
+  {"id":33,"name":"Stephanie Osei","agency":"Osei Commercial Alliance","territories":["GA","AL","TN","SC"],"lines":["Interface","Shaw Contract","Patcraft"],"city":"Atlanta","vertical":["Commercial","Office","Retail"],"contact_url":"https://oseicommercialalliance.com/contact"},
+  {"id":34,"name":"Timothy Grant","agency":"Grant & Grant Design Reps","territories":["PA","DE","MD","VA"],"lines":["Schumacher","Brunschwig & Fils","Clarence House"],"city":"Philadelphia","vertical":["Residential","Contract"],"contact_url":"https://grantgrantdesignreps.com/contact"},
+  {"id":35,"name":"Lisa Johansson","agency":"Nordic West Reps","territories":["WA","OR","ID","MT","WY"],"lines":["Bolon","Baux","Kvadrat"],"city":"Seattle","vertical":["Commercial","Office","Education"],"contact_url":"https://nordicwestreps.com/contact"},
+  {"id":36,"name":"Omar Farouk","agency":"Farouk Luxury Reps","territories":["CA","TX","NY"],"lines":["Loro Piana Interiors","Dedar","Sahco"],"city":"Los Angeles","vertical":["Luxury","Residential","Boutique"],"contact_url":"https://faroukluxuryreps.com/contact"},
+  {"id":37,"name":"Catherine Moore","agency":"Moore Wallcovering Reps","territories":["IL","MO","KS","NE"],"lines":["Innovations USA","Arte International","Nlxl"],"city":"Chicago","vertical":["Residential","Contract"],"contact_url":"https://moorewallcoveringreps.com/contact"},
+  {"id":38,"name":"Henry Vasquez","agency":"Vasquez Southwest Reps","territories":["AZ","NM","TX","CO"],"lines":["Pollack","Pindler","Duralee"],"city":"Phoenix","vertical":["Residential","Commercial"],"contact_url":"https://vasquezSouthwestreps.com/contact"},
+  {"id":39,"name":"Pamela Nkosi","agency":"Nkosi Retail Design Reps","territories":["NY","NJ","CT","MA"],"lines":["Maharam","Momentum","Interface"],"city":"New York","vertical":["Retail","Commercial","Office"],"contact_url":"https://nkosiretaildesignreps.com/contact"},
+  {"id":40,"name":"Jonathan Reyes","agency":"Reyes Pacific Hospitality","territories":["CA","HI","NV"],"lines":["Perennials","Elitis","Casamance"],"city":"Los Angeles","vertical":["Hospitality","Resort","Boutique"],"contact_url":"https://reyespacifichospitality.com/contact"}
+]
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..a6306f9
--- /dev/null
+++ b/package.json
@@ -0,0 +1,8 @@
+{
+  "name": "designerssalesreps",
+  "version": "1.0.0",
+  "description": "Designers Sales Reps — map-first B2B trade rep directory",
+  "main": "server.js",
+  "scripts": { "start": "node server.js" },
+  "dependencies": { "express": "^4.18.2" }
+}
diff --git a/public/favicon.svg b/public/favicon.svg
new file mode 100644
index 0000000..5d453a4
--- /dev/null
+++ b/public/favicon.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
+  <rect width="64" height="64" rx="10" fill="#0a0a0a"/>
+  <text x="50%" y="50%" text-anchor="middle" dominant-baseline="central"
+        font-family="Georgia, 'Times New Roman', serif" font-weight="700"
+        font-size="32" fill="#ffffff">DE</text>
+</svg>
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..0b9c014
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,254 @@
+<!DOCTYPE html>
+<html lang="en" data-theme="dark">
+<head>
+    <link rel="icon" type="image/svg+xml" href="/favicon.svg">
+
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<script>(function(){try{var t=localStorage.getItem('theme');if(!t){t=window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light';}document.documentElement.setAttribute('data-theme',t);}catch(e){}})();</script>
+<title>Designers Sales Reps — Map-Based Trade Rep Finder | DesignersSalesReps.com</title>
+<meta name="description" content="Find design trade sales reps by geography. Map-first directory of 40+ showroom reps covering wallcoverings, textiles, and contract fabrics. Claim your free listing.">
+<link rel="preconnect" href="https://fonts.googleapis.com">
+<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,600;1,400&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
+<style>
+:root{--accent:#3a7a78;--accent-light:#5a9a98;--accent-dark:#2a5a58;--bg:#0a0a0a;--card-bg:rgba(245,241,232,0.04);--header-bg:rgba(10,10,10,0.92);--ink:#f5f1e8;--ink-soft:rgba(245,241,232,0.7);--ink-faint:rgba(245,241,232,0.35);--line:rgba(245,241,232,0.10)}
+[data-theme="light"]{--bg:#f5f9f9;--card-bg:rgba(10,10,10,0.04);--header-bg:rgba(245,249,249,0.92);--ink:#0d1f1f;--ink-soft:rgba(10,10,10,0.7);--ink-faint:rgba(10,10,10,0.35);--line:rgba(10,10,10,0.10)}
+*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
+body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--ink);min-height:100vh;transition:background .2s,color .2s}
+.header{position:sticky;top:0;z-index:100;background:var(--header-bg);backdrop-filter:blur(8px);border-bottom:1px solid var(--line);padding:0 2rem;display:flex;align-items:center;justify-content:space-between;height:64px}
+.brand{display:flex;align-items:center;gap:12px;text-decoration:none;color:var(--ink)}
+.brand-dot{width:28px;height:28px;border-radius:50%;background:radial-gradient(circle at 35% 35%,#5a9a98 0%,#3a7a78 60%,#2a5a58 100%);flex-shrink:0}
+.brand-name{font-family:'Cormorant Garamond',Georgia,serif;font-size:1.25rem;font-weight:600;letter-spacing:.02em}
+.nav-actions{display:flex;align-items:center;gap:1rem}
+.theme-toggle{background:none;border:1px solid var(--line);color:var(--ink);padding:6px 10px;border-radius:6px;cursor:pointer;display:flex;align-items:center;gap:6px}
+[data-theme="dark"] .icon-sun{display:block}[data-theme="dark"] .icon-moon{display:none}
+[data-theme="light"] .icon-sun{display:none}[data-theme="light"] .icon-moon{display:block}
+.hero{padding:3rem 2rem 2rem;text-align:center}
+.hero h1{font-family:'Cormorant Garamond',Georgia,serif;font-size:clamp(1.8rem,4vw,3rem);font-weight:400;line-height:1.2;margin-bottom:.75rem;color:var(--ink)}
+.hero h1 em{font-style:italic;color:var(--accent)}
+.hero p{color:var(--ink-soft);max-width:540px;margin:0 auto 1.5rem;font-size:1rem;line-height:1.6}
+.stub-banner{background:rgba(58,122,120,0.12);border:1px solid rgba(58,122,120,0.3);color:var(--accent-light);padding:.5rem 1.5rem;text-align:center;font-size:.82rem;letter-spacing:.04em}
+.search-bar{max-width:560px;margin:0 auto 2rem;display:flex;gap:.75rem;padding:0 1rem}
+.search-input{flex:1;background:var(--card-bg);backdrop-filter:blur(12px);border:1px solid var(--line);color:var(--ink);padding:.75rem 1rem;border-radius:8px;font-size:1rem;font-family:inherit;outline:none}
+.search-input:focus{border-color:var(--accent)}
+.search-input::placeholder{color:var(--ink-faint)}
+.layout{display:flex;height:calc(100vh - 200px);min-height:500px;max-height:680px;gap:0}
+.map-panel{flex:1;position:relative;background:rgba(10,30,30,0.8);border-right:1px solid var(--line);overflow:hidden}
+.map-placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--ink-soft)}
+.map-placeholder svg{opacity:.4;margin-bottom:1rem}
+.map-grid{display:grid;grid-template-columns:repeat(5,1fr);grid-template-rows:repeat(4,1fr);gap:2px;padding:1rem;height:100%;width:100%}
+.map-cell{border-radius:6px;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .2s;font-size:.7rem;font-weight:600;letter-spacing:.05em;text-transform:uppercase;padding:.25rem;text-align:center}
+.map-cell.has-reps{background:rgba(58,122,120,0.2);border:1px solid rgba(58,122,120,0.3);color:var(--accent-light)}
+.map-cell.has-reps:hover{background:rgba(58,122,120,0.4);border-color:var(--accent)}
+.map-cell.empty{background:rgba(245,241,232,0.02);border:1px solid rgba(245,241,232,0.04);color:var(--ink-faint)}
+.map-cell.selected{background:var(--accent);color:#fff;border-color:var(--accent-light)}
+.list-panel{width:380px;display:flex;flex-direction:column;overflow:hidden}
+.list-header{padding:1rem 1.25rem .75rem;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;flex-shrink:0}
+.list-header h3{font-family:'Cormorant Garamond',Georgia,serif;font-size:1.1rem}
+.list-count{color:var(--ink-faint);font-size:.8rem}
+.list-scroll{flex:1;overflow-y:auto;padding:.75rem}
+.list-scroll::-webkit-scrollbar{width:4px}
+.list-scroll::-webkit-scrollbar-thumb{background:var(--accent);border-radius:2px}
+.rep-row{padding:.85rem 1rem;border:1px solid var(--line);border-radius:8px;margin-bottom:.5rem;cursor:pointer;transition:border-color .2s,background .2s;background:var(--card-bg);backdrop-filter:blur(12px)}
+.rep-row:hover{border-color:var(--accent);background:rgba(58,122,120,0.06)}
+.rep-row.active{border-color:var(--accent);background:rgba(58,122,120,0.1)}
+.rep-row-name{font-family:'Cormorant Garamond',Georgia,serif;font-size:1.05rem;font-weight:600;margin-bottom:.2rem}
+.rep-row-agency{color:var(--accent);font-size:.78rem;text-transform:uppercase;letter-spacing:.05em;margin-bottom:.3rem}
+.rep-row-meta{color:var(--ink-faint);font-size:.78rem}
+.rep-tags{display:flex;flex-wrap:wrap;gap:.25rem;margin-top:.4rem}
+.rep-tag{background:rgba(58,122,120,0.12);border:1px solid rgba(58,122,120,0.25);color:var(--accent-light);padding:.15rem .45rem;border-radius:12px;font-size:.68rem}
+.detail-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.7);z-index:200;align-items:center;justify-content:center}
+.detail-overlay.open{display:flex}
+.detail-box{background:var(--bg);border:1px solid var(--line);border-radius:14px;padding:2rem;max-width:480px;width:90%;position:relative}
+.detail-box h3{font-family:'Cormorant Garamond',Georgia,serif;font-size:1.6rem;margin-bottom:.5rem}
+.detail-close{position:absolute;top:1rem;right:1rem;background:none;border:none;color:var(--ink-soft);font-size:1.5rem;cursor:pointer}
+.detail-agency{color:var(--accent);font-size:.85rem;text-transform:uppercase;letter-spacing:.06em;font-weight:600;margin-bottom:1.2rem}
+.detail-grid{display:grid;grid-template-columns:auto 1fr;gap:.4rem .75rem;font-size:.88rem}
+.detail-lbl{color:var(--ink-faint)}
+.detail-val{color:var(--ink-soft)}
+.detail-link{display:inline-block;margin-top:1.25rem;padding:.6rem 1.5rem;background:var(--accent);color:#fff;border-radius:8px;text-decoration:none;font-size:.85rem;font-weight:600;transition:background .2s}
+.detail-link:hover{background:var(--accent-light)}
+.claim-section{max-width:600px;margin:2rem auto 4rem;padding:2rem;background:var(--card-bg);backdrop-filter:blur(12px);border:1px solid var(--line);border-radius:14px;text-align:center}
+.claim-section h2{font-family:'Cormorant Garamond',Georgia,serif;font-size:1.8rem;margin-bottom:.5rem}
+.claim-section p{color:var(--ink-soft);margin-bottom:1.5rem;font-size:.95rem}
+.claim-form{display:flex;flex-direction:column;gap:.75rem;text-align:left}
+.claim-form input,.claim-form textarea{background:var(--card-bg);border:1px solid var(--line);color:var(--ink);padding:.75rem;border-radius:8px;font-size:.9rem;font-family:inherit;outline:none;resize:vertical}
+.claim-form input:focus,.claim-form textarea:focus{border-color:var(--accent)}
+.claim-submit{background:var(--accent);color:#fff;border:none;padding:.8rem 2rem;border-radius:8px;font-size:1rem;font-weight:600;cursor:pointer;transition:background .2s}
+.claim-submit:hover{background:var(--accent-light)}
+.footer{border-top:1px solid var(--line);padding:2rem;text-align:center;color:var(--ink-faint);font-size:.85rem}
+.footer a{color:var(--accent);text-decoration:none}
+</style>
+</head>
+<body>
+<div class="stub-banner">Stub directory — claim your listing to be officially featured</div>
+<header class="header">
+  <a href="/" class="brand">
+    <div class="brand-dot"></div>
+    <span class="brand-name">DesignersSalesReps</span>
+  </a>
+  <div class="nav-actions">
+    <button class="theme-toggle" id="theme-toggle" aria-label="Toggle theme">
+      <svg class="icon-sun" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></svg>
+      <svg class="icon-moon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M21 12.79A9 9 0 1 1 11.21 3a7 7 0 0 0 9.79 9.79z"/></svg>
+    </button>
+  </div>
+</header>
+
+<section class="hero">
+  <h1>Trade Reps by <em>Territory</em></h1>
+  <p>Select a region on the map to browse sales representatives covering that market.</p>
+  <div class="search-bar">
+    <input class="search-input" id="q" type="text" placeholder="Search rep, agency, or product line…" autocomplete="off">
+  </div>
+</section>
+
+<div class="layout">
+  <div class="map-panel">
+    <div class="map-grid" id="map-grid"></div>
+  </div>
+  <div class="list-panel">
+    <div class="list-header">
+      <h3 id="region-label">All Regions</h3>
+      <span class="list-count" id="list-count">40 reps</span>
+    </div>
+    <div class="list-scroll" id="list-scroll"></div>
+  </div>
+</div>
+
+<div class="claim-section" id="claim">
+  <h2>Claim Your Listing</h2>
+  <p>Are you a design trade rep not yet in the directory? Get listed free.</p>
+  <form class="claim-form" id="claim-form">
+    <input type="text" name="name" placeholder="Your full name" required>
+    <input type="text" name="agency" placeholder="Agency or firm name" required>
+    <input type="email" name="email" placeholder="Business email" required>
+    <input type="text" name="territories" placeholder="Territories covered (e.g., CA, OR, WA)">
+    <input type="text" name="lines" placeholder="Product lines you represent">
+    <textarea name="note" rows="3" placeholder="Anything else"></textarea>
+    <button type="submit" class="claim-submit">Submit My Listing</button>
+  </form>
+</div>
+
+<footer class="footer">
+  <p>info@designerssalesreps.com &nbsp;|&nbsp; &copy; 2026 DesignersSalesReps.com &nbsp;|&nbsp; A partner of <a href="https://designerwallcoverings.com" target="_blank" rel="noopener">Designer Wallcoverings</a></p>
+  <script type="application/ld+json">{"@context":"https://schema.org","@type":"ItemList","name":"Designers Sales Reps Directory","description":"Trade sales reps by territory for design showrooms","url":"https://designerssalesreps.com"}</script>
+</footer>
+
+<div class="detail-overlay" id="detail">
+  <div class="detail-box">
+    <button class="detail-close" id="detail-close">&times;</button>
+    <h3 id="d-name"></h3>
+    <div class="detail-agency" id="d-agency"></div>
+    <div class="detail-grid" id="d-grid"></div>
+    <a href="#" id="d-link" class="detail-link" target="_blank" rel="noopener">Contact Agency &rarr;</a>
+  </div>
+</div>
+
+<script>
+const regions = [
+  {label:'New England',states:['MA','RI','VT','NH','ME','CT']},
+  {label:'Tri-State',states:['NY','NJ','CT']},
+  {label:'Mid-Atlantic',states:['PA','DE','MD','DC','VA']},
+  {label:'Southeast',states:['GA','FL','SC','NC']},
+  {label:'Gulf Coast',states:['AL','MS','LA','TN']},
+  {label:'Texas',states:['TX','OK','NM','AR']},
+  {label:'Great Lakes',states:['IL','WI','MN','IA']},
+  {label:'Ohio Valley',states:['OH','MI','IN','KY','WV']},
+  {label:'Mountain',states:['CO','UT','WY','MT','ID']},
+  {label:'Southwest',states:['AZ','NV','NM']},
+  {label:'Carolinas',states:['NC','SC']},
+  {label:'Midwest',states:['KS','NE','MO','ND','SD']},
+  {label:'Pacific NW',states:['WA','OR','ID']},
+  {label:'California',states:['CA']},
+  {label:'Hawaii/AK',states:['HI','AK']},
+  {label:'National',states:[]}
+];
+
+let allReps = [], selectedRegion = null, searchQ = '';
+
+fetch('/api/search?').then(r=>r.json()).then(d=>{
+  allReps = d.results;
+  buildMap();
+  renderList(allReps, 'All Regions');
+});
+
+function buildMap(){
+  const grid = document.getElementById('map-grid');
+  grid.innerHTML = regions.map((reg, i) => {
+    const count = reg.states.length ? allReps.filter(r => r.territories.some(t => reg.states.includes(t))).length : allReps.length;
+    const cls = count > 0 ? 'has-reps' : 'empty';
+    return `<div class="map-cell ${cls}" data-idx="${i}" onclick="selectRegion(${i})">${reg.label}<br><small style="font-size:.65rem;opacity:.7">${count} rep${count!==1?'s':''}</small></div>`;
+  }).join('');
+}
+
+function selectRegion(idx){
+  selectedRegion = idx;
+  document.querySelectorAll('.map-cell').forEach((el,i) => {
+    el.classList.toggle('selected', i===idx);
+  });
+  const reg = regions[idx];
+  const filtered = reg.states.length ? allReps.filter(r => r.territories.some(t => reg.states.includes(t))) : allReps;
+  const label = reg.label;
+  renderList(applySearch(filtered), label);
+}
+
+function applySearch(list){
+  if(!searchQ) return list;
+  const q = searchQ.toLowerCase();
+  return list.filter(r => r.name.toLowerCase().includes(q) || r.agency.toLowerCase().includes(q) || r.lines.some(l=>l.toLowerCase().includes(q)));
+}
+
+function renderList(reps, regionLabel){
+  document.getElementById('region-label').textContent = regionLabel;
+  document.getElementById('list-count').textContent = reps.length + ' rep' + (reps.length!==1?'s':'');
+  document.getElementById('list-scroll').innerHTML = reps.map(r => `
+    <div class="rep-row" onclick="showDetail(${r.id})">
+      <div class="rep-row-name">${r.name}</div>
+      <div class="rep-row-agency">${r.agency}</div>
+      <div class="rep-row-meta">${r.city} &middot; ${r.territories.slice(0,3).join(', ')}</div>
+      <div class="rep-tags">${r.vertical.slice(0,3).map(v=>`<span class="rep-tag">${v}</span>`).join('')}</div>
+    </div>
+  `).join('') || '<p style="color:var(--ink-faint);padding:1rem;text-align:center">No reps found in this region.</p>';
+}
+
+function showDetail(id){
+  const r = allReps.find(x=>x.id===id); if(!r) return;
+  document.getElementById('d-name').textContent = r.name;
+  document.getElementById('d-agency').textContent = r.agency;
+  document.getElementById('d-grid').innerHTML = `
+    <span class="detail-lbl">City</span><span class="detail-val">${r.city}</span>
+    <span class="detail-lbl">Territories</span><span class="detail-val">${r.territories.join(', ')}</span>
+    <span class="detail-lbl">Verticals</span><span class="detail-val">${r.vertical.join(', ')}</span>
+    <span class="detail-lbl">Lines</span><span class="detail-val">${r.lines.join(', ')}</span>
+  `;
+  document.getElementById('d-link').href = r.contact_url;
+  document.getElementById('detail').classList.add('open');
+}
+document.getElementById('detail-close').onclick=()=>document.getElementById('detail').classList.remove('open');
+document.getElementById('detail').onclick=e=>{if(e.target===document.getElementById('detail'))document.getElementById('detail').classList.remove('open');};
+
+let debT;
+document.getElementById('q').addEventListener('input', e=>{
+  searchQ = e.target.value;
+  clearTimeout(debT);
+  debT = setTimeout(()=>{
+    const base = selectedRegion !== null ? (regions[selectedRegion].states.length ? allReps.filter(r=>r.territories.some(t=>regions[selectedRegion].states.includes(t))) : allReps) : allReps;
+    renderList(applySearch(base), selectedRegion !== null ? regions[selectedRegion].label : 'All Regions');
+  }, 250);
+});
+
+document.getElementById('claim-form').onsubmit=e=>{
+  e.preventDefault();
+  const data=Object.fromEntries(new FormData(e.target));
+  fetch('/api/claim',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(data)})
+    .then(()=>{e.target.reset();alert('Thank you! We will review and follow up.');});
+};
+
+const btn=document.getElementById('theme-toggle');
+function syncTheme(){var t=document.documentElement.getAttribute('data-theme');btn.setAttribute('aria-pressed',t==='dark');}
+syncTheme();
+btn.addEventListener('click',function(){var cur=document.documentElement.getAttribute('data-theme')||'dark';var nxt=cur==='dark'?'light':'dark';document.documentElement.setAttribute('data-theme',nxt);try{localStorage.setItem('theme',nxt);}catch(e){}syncTheme();});
+</script>
+</body>
+</html>
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..2347570
--- /dev/null
+++ b/server.js
@@ -0,0 +1,36 @@
+'use strict';
+const express = require('express');
+const fs = require('fs');
+const path = require('path');
+const app = express();
+const PORT = process.env.PORT || 9916;
+const DOMAIN = 'designerssalesreps.com';
+
+app.use(express.json());
+app.use(express.static(path.join(__dirname, 'public')));
+
+const reps = JSON.parse(fs.readFileSync(path.join(__dirname, 'data/reps.json'), 'utf8'));
+
+app.get('/api/search', (req, res) => {
+  const { q = '', territory = '', vertical = '', sort = 'name' } = req.query;
+  let results = reps.filter(r => {
+    const qLow = q.toLowerCase();
+    const matchQ = !q || r.name.toLowerCase().includes(qLow) || r.agency.toLowerCase().includes(qLow) || r.lines.some(l => l.toLowerCase().includes(qLow)) || r.city.toLowerCase().includes(qLow);
+    const matchT = !territory || r.territories.includes(territory);
+    const matchV = !vertical || r.vertical.includes(vertical);
+    return matchQ && matchT && matchV;
+  });
+  if (sort === 'agency') results.sort((a, b) => a.agency.localeCompare(b.agency));
+  else if (sort === 'city') results.sort((a, b) => a.city.localeCompare(b.city));
+  else results.sort((a, b) => a.name.localeCompare(b.name));
+  res.json({ total: results.length, results });
+});
+
+app.post('/api/claim', (req, res) => {
+  const entry = { ...req.body, ts: new Date().toISOString(), domain: DOMAIN };
+  fs.appendFileSync(path.join(__dirname, 'claims.jsonl'), JSON.stringify(entry) + '\n');
+  res.json({ ok: true });
+});
+
+app.get('/health', (req, res) => res.json({ ok: true, domain: DOMAIN }));
+app.listen(PORT, '127.0.0.1', () => console.log(`${DOMAIN} listening on ${PORT}`));

(oldest)  ·  back to Designerssalesreps  ·  hamburger nav: apply canonical pattern from sister-site flee 57192f9 →