← back to Designersalesreps
public/index.html
354 lines
<!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 noreferrer">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 noreferrer" 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>