← back to Designersalesreps
initial scaffold
53e8a4f8b068268e2f1880dd95c903872a069056 · 2026-05-10 21:40:34 -0700 · Steve Abrams
Files touched
A .gitignoreA data/reps.jsonA package.jsonA public/favicon.svgA public/index.htmlA server.js
Diff
commit 53e8a4f8b068268e2f1880dd95c903872a069056
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sun May 10 21:40:34 2026 -0700
initial scaffold
---
.gitignore | 8 ++
data/reps.json | 42 +++++++
package.json | 8 ++
public/favicon.svg | 6 +
public/index.html | 353 +++++++++++++++++++++++++++++++++++++++++++++++++++++
server.js | 38 ++++++
6 files changed, 455 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..455a514
--- /dev/null
+++ b/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "designersalesreps",
+ "version": "1.0.0",
+ "description": "Designer Sales Reps directory — B2B trade rep stub 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..b41ddf5
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,353 @@
+<!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>Designer Sales Reps — Find Trade Sales Representatives | DesignerSalesReps.com</title>
+<meta name="description" content="Find top designer sales reps and trade representatives across the US. Searchable directory of 40+ showroom reps covering wallcoverings, textiles, and contract fabrics. Free samples via Designer Wallcoverings.">
+<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: #d4a04a;
+ --accent-light: #e8c07a;
+ --accent-dark: #a07030;
+ --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: #f8f4ec;
+ --card-bg: rgba(10,10,10,0.04);
+ --header-bg: rgba(248,244,236,0.92);
+ --ink: #1a1614;
+ --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%,#e8c07a 0%,#d4a04a 60%,#a07030 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:5rem 2rem 3rem;text-align:center}
+.hero h1{font-family:'Cormorant Garamond',Georgia,serif;font-size:clamp(2rem,5vw,3.5rem);font-weight:400;line-height:1.15;margin-bottom:1rem;color:var(--ink)}
+.hero h1 em{font-style:italic;color:var(--accent)}
+.hero p{color:var(--ink-soft);max-width:560px;margin:0 auto 2.5rem;font-size:1.05rem;line-height:1.6}
+.search-glass{background:rgba(245,241,232,0.06);backdrop-filter:blur(20px) saturate(180%);border:1px solid var(--line);border-radius:14px;padding:1.5rem;max-width:680px;margin:0 auto;display:flex;flex-direction:column;gap:1rem}
+[data-theme="light"] .search-glass{background:rgba(10,10,10,0.06)}
+.search-row{display:flex;gap:.75rem;flex-wrap:wrap}
+.search-input{flex:1;min-width:200px;background:var(--card-bg);border:1px solid var(--line);color:var(--ink);padding:.75rem 1rem;border-radius:8px;font-size:1rem;font-family:inherit;outline:none}
+.search-input::placeholder{color:var(--ink-faint)}
+.search-input:focus{border-color:var(--accent)}
+select.filter{background:var(--card-bg);border:1px solid var(--line);color:var(--ink);padding:.75rem 1rem;border-radius:8px;font-size:.9rem;font-family:inherit;cursor:pointer;outline:none}
+select.filter:focus{border-color:var(--accent)}
+.controls{max-width:1200px;margin:0 auto 1rem;padding:0 2rem;display:flex;align-items:center;gap:1rem;flex-wrap:wrap}
+.count{color:var(--ink-soft);font-size:.9rem}
+.sort-select{background:var(--card-bg);border:1px solid var(--line);color:var(--ink);padding:.5rem .75rem;border-radius:6px;font-size:.85rem;cursor:pointer;font-family:inherit}
+.density-wrap{display:flex;align-items:center;gap:.5rem;color:var(--ink-soft);font-size:.85rem}
+.density-wrap input[type=range]{accent-color:var(--accent);width:80px}
+.grid{max-width:1200px;margin:0 auto 4rem;padding:0 2rem;display:grid;grid-template-columns:repeat(auto-fill,minmax(var(--card-min,300px),1fr));gap:1.25rem}
+.card{background:var(--card-bg);backdrop-filter:blur(12px);border:1px solid var(--line);border-radius:12px;padding:1.5rem;cursor:pointer;transition:transform .2s,box-shadow .2s,border-color .2s;position:relative;overflow:hidden}
+.card:hover{transform:translateY(-2px);box-shadow:0 8px 32px rgba(212,160,74,0.12);border-color:rgba(212,160,74,0.3)}
+.card-accent{width:4px;height:100%;position:absolute;left:0;top:0;background:linear-gradient(to bottom,var(--accent),var(--accent-dark));border-radius:4px 0 0 4px}
+.card-name{font-family:'Cormorant Garamond',Georgia,serif;font-size:1.3rem;font-weight:600;margin-bottom:.25rem;padding-left:.5rem}
+.card-agency{color:var(--accent);font-size:.85rem;font-weight:600;letter-spacing:.05em;text-transform:uppercase;padding-left:.5rem;margin-bottom:.75rem}
+.card-meta{padding-left:.5rem;display:flex;flex-direction:column;gap:.4rem}
+.meta-row{display:flex;align-items:flex-start;gap:.5rem;font-size:.85rem;color:var(--ink-soft)}
+.meta-label{color:var(--ink-faint);min-width:70px;flex-shrink:0}
+.tags{display:flex;flex-wrap:wrap;gap:.35rem;margin-top:.5rem;padding-left:.5rem}
+.tag{background:rgba(212,160,74,0.12);border:1px solid rgba(212,160,74,0.25);color:var(--accent);padding:.2rem .55rem;border-radius:20px;font-size:.75rem;font-weight:500}
+.tag.territory{background:rgba(245,241,232,0.06);border-color:var(--line);color:var(--ink-soft)}
+.claim-btn{display:inline-block;margin-top:1rem;padding:.45rem 1rem;background:var(--accent);color:#0a0a0a;border:none;border-radius:6px;font-size:.8rem;font-weight:600;cursor:pointer;text-transform:uppercase;letter-spacing:.05em;transition:background .2s}
+.claim-btn:hover{background:var(--accent-light)}
+.claim-section{max-width:600px;margin:2rem auto 5rem;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:#0a0a0a;border:none;padding:.8rem 2rem;border-radius:8px;font-size:1rem;font-weight:600;cursor:pointer;letter-spacing:.03em;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}
+.modal-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.7);z-index:200;align-items:center;justify-content:center}
+.modal-overlay.open{display:flex}
+.modal{background:var(--bg);border:1px solid var(--line);border-radius:14px;padding:2rem;max-width:480px;width:90%;position:relative}
+.modal h3{font-family:'Cormorant Garamond',Georgia,serif;font-size:1.5rem;margin-bottom:1rem}
+.modal-close{position:absolute;top:1rem;right:1rem;background:none;border:none;color:var(--ink-soft);font-size:1.5rem;cursor:pointer;line-height:1}
+</style>
+</head>
+<body>
+<header class="header">
+ <a href="/" class="brand">
+ <div class="brand-dot"></div>
+ <span class="brand-name">DesignerSalesReps</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>Find the Right <em>Design Sales Rep</em><br>for Your Showroom</h1>
+ <p>The definitive directory of trade sales representatives covering wallcoverings, textiles, and contract fabrics across the United States.</p>
+ <div class="search-glass">
+ <div class="search-row">
+ <input class="search-input" id="q" type="text" placeholder="Search by rep name, agency, or product line…" autocomplete="off">
+ </div>
+ <div class="search-row">
+ <select class="filter" id="territory">
+ <option value="">All Territories</option>
+ <option>CA</option><option>NY</option><option>TX</option><option>FL</option><option>IL</option>
+ <option>GA</option><option>WA</option><option>OR</option><option>AZ</option><option>CO</option>
+ <option>NV</option><option>NJ</option><option>PA</option><option>MA</option><option>NC</option>
+ </select>
+ <select class="filter" id="vertical">
+ <option value="">All Verticals</option>
+ <option>Residential</option><option>Contract</option><option>Hospitality</option>
+ <option>Healthcare</option><option>Commercial</option><option>Education</option>
+ <option>Office</option><option>Retail</option><option>Luxury</option><option>Resort</option>
+ </select>
+ </div>
+ </div>
+</section>
+
+<div class="controls">
+ <span class="count" id="count">40 reps</span>
+ <select class="sort-select" id="sort">
+ <option value="name">Sort: Name A–Z</option>
+ <option value="agency">Sort: Agency A–Z</option>
+ <option value="city">Sort: City A–Z</option>
+ <option value="territory">Sort: Territory</option>
+ </select>
+ <div class="density-wrap">
+ Grid:
+ <input type="range" id="density" min="220" max="420" step="20" value="300">
+ </div>
+</div>
+
+<div class="grid" id="grid"></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" placeholder="Anything else you'd like us to know" rows="3"></textarea>
+ <button type="submit" class="claim-submit">Submit My Listing</button>
+ </form>
+</div>
+
+<footer class="footer">
+ <p>info@designersalesreps.com | © 2026 DesignerSalesReps.com | 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":"Designer Sales Reps Directory","description":"Trade sales representatives for design showrooms across the US","url":"https://designersalesreps.com"}</script>
+</footer>
+
+<div class="modal-overlay" id="modal">
+ <div class="modal">
+ <button class="modal-close" id="modal-close">×</button>
+ <h3 id="modal-name"></h3>
+ <div id="modal-body"></div>
+ </div>
+</div>
+
+<script>
+const grid = document.getElementById('grid');
+const countEl = document.getElementById('count');
+const qEl = document.getElementById('q');
+const terrEl = document.getElementById('territory');
+const vertEl = document.getElementById('vertical');
+const sortEl = document.getElementById('sort');
+const densityEl = document.getElementById('density');
+
+let debounceT;
+function debounce(fn, ms=300){ clearTimeout(debounceT); debounceT=setTimeout(fn,ms); }
+
+function load(){
+ const params = new URLSearchParams({q:qEl.value,territory:terrEl.value,vertical:vertEl.value,sort:sortEl.value});
+ fetch('/api/search?'+params).then(r=>r.json()).then(data=>{
+ countEl.textContent = data.total+' rep'+(data.total!==1?'s':'');
+ grid.innerHTML = data.results.map(r=>`
+ <div class="card" onclick="showModal(${r.id})" data-id="${r.id}">
+ <div class="card-accent"></div>
+ <div class="card-name">${r.name}</div>
+ <div class="card-agency">${r.agency}</div>
+ <div class="card-meta">
+ <div class="meta-row"><span class="meta-label">City</span>${r.city}</div>
+ <div class="meta-row"><span class="meta-label">Lines</span>${r.lines.slice(0,2).join(', ')}${r.lines.length>2?' +'+( r.lines.length-2)+' more':''}</div>
+ </div>
+ <div class="tags">
+ ${r.territories.map(t=>`<span class="tag territory">${t}</span>`).join('')}
+ ${r.vertical.map(v=>`<span class="tag">${v}</span>`).join('')}
+ </div>
+ <button class="claim-btn" onclick="event.stopPropagation();document.getElementById('claim').scrollIntoView({behavior:'smooth'})">Claim / Update Listing</button>
+ </div>
+ `).join('');
+ });
+}
+
+qEl.addEventListener('input',()=>debounce(load));
+terrEl.addEventListener('change',load);
+vertEl.addEventListener('change',load);
+sortEl.addEventListener('change',()=>{localStorage.setItem('dsr-sort',sortEl.value);load();});
+densityEl.addEventListener('input',()=>{document.querySelector('.grid').style.setProperty('--card-min',densityEl.value+'px');localStorage.setItem('dsr-density',densityEl.value);});
+
+// restore prefs
+const sv=localStorage.getItem('dsr-sort'); if(sv) sortEl.value=sv;
+const dv=localStorage.getItem('dsr-density'); if(dv){densityEl.value=dv;grid.style.setProperty('--card-min',dv+'px');}
+
+let allReps=[];
+fetch('/api/search?').then(r=>r.json()).then(d=>{allReps=d.results;});
+
+function showModal(id){
+ const r = allReps.find(x=>x.id===id); if(!r) return;
+ document.getElementById('modal-name').textContent=r.name;
+ document.getElementById('modal-body').innerHTML=`
+ <p style="color:var(--accent);font-weight:600;margin-bottom:1rem">${r.agency}</p>
+ <p><strong>City:</strong> ${r.city}</p>
+ <p><strong>Territories:</strong> ${r.territories.join(', ')}</p>
+ <p><strong>Verticals:</strong> ${r.vertical.join(', ')}</p>
+ <p><strong>Lines:</strong> ${r.lines.join(', ')}</p>
+ <p style="margin-top:1rem"><a href="${r.contact_url}" target="_blank" rel="noopener" style="color:var(--accent)">Contact ${r.agency} →</a></p>
+ `;
+ document.getElementById('modal').classList.add('open');
+}
+document.getElementById('modal-close').onclick=()=>document.getElementById('modal').classList.remove('open');
+document.getElementById('modal').onclick=e=>{if(e.target===document.getElementById('modal'))document.getElementById('modal').classList.remove('open');};
+
+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 your listing within 2 business days.');});
+};
+
+// theme toggle
+const btn=document.getElementById('theme-toggle');
+function syncTheme(){var t=document.documentElement.getAttribute('data-theme');btn.setAttribute('aria-pressed',t==='dark'?'true':'false');}
+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();});
+
+load();
+</script>
+<!-- DW-HAMBURGER-NAV v1 (canonical 2026-05-10) -->
+<style id="dw-hamburger-styles">
+ /* Canonical hamburger nav — always visible top-right, all viewports.
+ Standing rule (Steve, 2026-05-08): hamburger is the primary nav. */
+ .dw-burger-btn {
+ position: fixed; top: 18px; right: 18px; z-index: 9999;
+ width: 44px; height: 44px;
+ background: rgba(20,20,20,0.78); color: #f5f1e8;
+ backdrop-filter: blur(8px) saturate(140%);
+ -webkit-backdrop-filter: blur(8px) saturate(140%);
+ border: 1px solid rgba(245,241,232,0.18); border-radius: 50%;
+ cursor: pointer; padding: 0;
+ display: flex; flex-direction: column; align-items: center; justify-content: center;
+ gap: 4px;
+ box-shadow: 0 6px 20px rgba(0,0,0,0.35);
+ transition: background 200ms, transform 200ms;
+ }
+ .dw-burger-btn:hover { background: rgba(20,20,20,0.92); }
+ .dw-burger-btn span {
+ display: block; width: 20px; height: 1.5px; background: currentColor;
+ transition: transform 220ms, opacity 220ms;
+ }
+ .dw-burger-btn[aria-expanded="true"] span:nth-child(1) {
+ transform: translateY(5.5px) rotate(45deg);
+ }
+ .dw-burger-btn[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
+ .dw-burger-btn[aria-expanded="true"] span:nth-child(3) {
+ transform: translateY(-5.5px) rotate(-45deg);
+ }
+ .dw-burger-panel {
+ position: fixed; top: 72px; right: 18px; z-index: 9998;
+ min-width: 240px; max-width: 88vw;
+ background: rgba(15,15,15,0.96);
+ backdrop-filter: blur(14px) saturate(140%);
+ -webkit-backdrop-filter: blur(14px) saturate(140%);
+ border: 1px solid rgba(245,241,232,0.12);
+ border-radius: 14px;
+ box-shadow: 0 18px 50px rgba(0,0,0,0.55);
+ padding: 10px 0;
+ display: none;
+ font-family: -apple-system, "SF Pro Text", system-ui, sans-serif;
+ }
+ .dw-burger-panel.open { display: block; }
+ .dw-burger-panel a,
+ .dw-burger-panel button {
+ display: block; width: 100%;
+ padding: 13px 22px;
+ color: #f5f1e8 !important;
+ background: transparent; border: 0; cursor: pointer;
+ text-decoration: none !important;
+ font-size: 15px; font-weight: 400;
+ text-align: left;
+ letter-spacing: 0;
+ border-bottom: 1px solid rgba(245,241,232,0.06);
+ transition: background 160ms, color 160ms;
+ box-sizing: border-box;
+ }
+ .dw-burger-panel a:last-child,
+ .dw-burger-panel button:last-child { border-bottom: 0; }
+ .dw-burger-panel a:hover,
+ .dw-burger-panel button:hover {
+ background: rgba(245,241,232,0.06);
+ color: #ffffff !important;
+ }
+ @media (max-width: 480px) {
+ .dw-burger-btn { top: 12px; right: 12px; width: 40px; height: 40px; }
+ .dw-burger-panel { top: 62px; right: 12px; }
+ }
+ /* Avoid double-print on sites already adopting a top-of-page header
+ that overlaps z-index. Reserve safe space near top-right. */
+</style>
+<button type="button" class="dw-burger-btn" id="dwBurgerBtn"
+ aria-label="Toggle menu" aria-controls="dwBurgerPanel"
+ aria-expanded="false">
+ <span></span><span></span><span></span>
+</button>
+<nav class="dw-burger-panel" id="dwBurgerPanel" aria-label="Primary">
+ <a href="/">DesignerSalesReps</a>
+ <a href="mailto:info@designersalesreps.com">Contact</a>
+</nav>
+<script>
+(function(){
+ var btn = document.getElementById('dwBurgerBtn');
+ var panel = document.getElementById('dwBurgerPanel');
+ if (!btn || !panel) return;
+ function close(){ panel.classList.remove('open'); btn.setAttribute('aria-expanded','false'); }
+ function toggle(){
+ var open = panel.classList.toggle('open');
+ btn.setAttribute('aria-expanded', open ? 'true' : 'false');
+ }
+ btn.addEventListener('click', function(e){ e.stopPropagation(); toggle(); });
+ document.addEventListener('click', function(e){
+ if (panel.classList.contains('open') && !panel.contains(e.target) && e.target !== btn) close();
+ });
+ document.addEventListener('keydown', function(e){ if (e.key === 'Escape') close(); });
+})();
+</script>
+<!-- /DW-HAMBURGER-NAV v1 -->
+
+</body>
+</html>
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..e61711d
--- /dev/null
+++ b/server.js
@@ -0,0 +1,38 @@
+'use strict';
+const express = require('express');
+const fs = require('fs');
+const path = require('path');
+const app = express();
+const PORT = process.env.PORT || 9901;
+const DOMAIN = 'designersalesreps.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 if (sort === 'territory') results.sort((a, b) => a.territories[0].localeCompare(b.territories[0]));
+ 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 Designersalesreps
·
gitignore — exclude .bak/.pre snapshots + runtime claims.jso 6839ed3 →