[object Object]

← back to Wallpaperdistributors

Replace vendor-leaking distributor directory with thin vendor-free DW trade bridge; archive full build under _legacy-directory/ (DTD verdict B)

ebb076a52fcb3fe533c56c4623b36633b8c0c23e · 2026-05-31 17:04:17 -0700 · steve

Files touched

Diff

commit ebb076a52fcb3fe533c56c4623b36633b8c0c23e
Author: steve <steve@designerwallcoverings.com>
Date:   Sun May 31 17:04:17 2026 -0700

    Replace vendor-leaking distributor directory with thin vendor-free DW trade bridge; archive full build under _legacy-directory/ (DTD verdict B)
---
 DEPLOY.md                                          |  36 +++++
 {data => _legacy-directory/data}/distributors.json |   0
 _legacy-directory/server.js                        | 142 ++++++++++++++++++++
 {views => _legacy-directory/views}/404.ejs         |   0
 {views => _legacy-directory/views}/about.ejs       |   0
 {views => _legacy-directory/views}/distributor.ejs |   0
 {views => _legacy-directory/views}/index.ejs       |   0
 {views => _legacy-directory/views}/line.ejs        |   0
 .../views}/partials/foot.ejs                       |   0
 .../views}/partials/head.ejs                       |   0
 public/index.html                                  | 135 +++++++++++++++++++
 server.js                                          | 146 ++++-----------------
 12 files changed, 335 insertions(+), 124 deletions(-)

diff --git a/DEPLOY.md b/DEPLOY.md
new file mode 100644
index 0000000..52b8363
--- /dev/null
+++ b/DEPLOY.md
@@ -0,0 +1,36 @@
+# wallpaperdistributors.com — deploy runbook
+
+**Status (2026-05-31):** Shipped as a **thin, vendor-free trade bridge** (DTD verdict B +
+Steve's call). The original standalone *distributor directory* build is **archived under
+`_legacy-directory/`** — it was a hard violation of the "never DW vendors in public UI"
+rule (its content was vendor brand names; DW's own record exposed its lines). The
+served app is now a single static DW-branded trade lander → `designerwallcoverings.com/pages/trade`.
+
+- Served homepage: vendor-leak scanned → **0 hits**.
+- Legacy routes `/distributor/:id` and `/line/:line` → **404** (archived, not served).
+
+## Registrar / DNS facts
+- Registrar **GoDaddy** · expires 2027-11-06 · **locked + privacy ON**
+- Nameservers: `ns5.afternic.com` / `ns6.afternic.com` (Afternic parking)
+- Target host: Kamatera `45.61.58.125`, app port **9932**
+
+## STEP 1 — Take it off Afternic  *(Steve — account action)*
+GoDaddy → the domain → *List for Sale / Afternic* → remove listing.
+
+## STEP 2 — Repoint DNS  *(Steve — registrar action / DNS is classifier-gated)*
+GoDaddy → unlock → GoDaddy default nameservers → **A `@` → 45.61.58.125** (+ `www`) →
+re-lock. Verify `dig +short wallpaperdistributors.com` = `45.61.58.125`.
+
+## STEP 3 — Deploy to Kamatera  *(Claude, once DNS resolves)*
+```sh
+# EXCLUDE the legacy vendor-laden directory build from prod — never deploy _legacy-directory/
+rsync -az --exclude node_modules --exclude .git --exclude _legacy-directory \
+  ~/Projects/wallpaperdistributors/ my-server:/root/Projects/wallpaperdistributors/
+ssh my-server 'cd /root/Projects/wallpaperdistributors && npm install --omit=dev'
+ssh my-server 'cd /root/Projects/wallpaperdistributors && pm2 start server.js --name wallpaperdistributors && pm2 save'
+# nginx vhost → 127.0.0.1:9932 ; certbot --nginx -d wallpaperdistributors.com -d www.…
+```
+
+> If a real distributor directory is ever wanted, the legacy build in `_legacy-directory/`
+> must be vendor-scrubbed first (rename/neutralize every `lines_carried` brand) before it
+> can be served — do not deploy it as-is.
diff --git a/data/distributors.json b/_legacy-directory/data/distributors.json
similarity index 100%
rename from data/distributors.json
rename to _legacy-directory/data/distributors.json
diff --git a/_legacy-directory/server.js b/_legacy-directory/server.js
new file mode 100644
index 0000000..6a5c827
--- /dev/null
+++ b/_legacy-directory/server.js
@@ -0,0 +1,142 @@
+const express = require('express');
+const path = require('path');
+const fs = require('fs');
+const helmet = require('helmet');
+const compression = require('compression');
+const morgan = require('morgan');
+
+const app = express();
+const PORT = process.env.PORT || 9932;
+const GA_ID = process.env.GA_ID || 'G-NBZ5WTBW8F';
+
+const distributors = JSON.parse(
+  fs.readFileSync(path.join(__dirname, 'data', 'distributors.json'), 'utf8')
+);
+
+app.set('view engine', 'ejs');
+app.set('views', path.join(__dirname, 'views'));
+
+app.use(helmet({
+  contentSecurityPolicy: false,
+  crossOriginEmbedderPolicy: false,
+}));
+app.use(compression());
+app.use(morgan('tiny'));
+
+app.use((req, res, next) => {
+  res.set('Cache-Control', 'no-store, must-revalidate');
+  next();
+});
+
+// Never serve editor/snapshot artifacts from the static root.
+app.use((req, res, next) => {
+  if (/\.(bak|orig|pre-[^/]*|[^/]*~)$|\.bak\.|\.pre-/i.test(req.path)) {
+    return res.status(404).render('404', { site: SITE });
+  }
+  next();
+});
+
+app.use(express.static(path.join(__dirname, 'public'), { maxAge: '1h' }));
+
+const allRegions = Array.from(new Set(distributors.flatMap(d => d.regions))).sort();
+const allSegments = Array.from(new Set(distributors.flatMap(d => d.segments))).sort();
+const allLines = Array.from(new Set(distributors.flatMap(d => d.lines_carried))).sort();
+
+const SITE = {
+  domain: 'wallpaperdistributors.com',
+  name: 'Wallpaper Distributors',
+  tagline: 'B2B wallcovering distributors, mapped to the lines they carry',
+  description: 'Trade index of B2B wallcovering distributors across the U.S. and Canada — searchable by vendor line, region, and project segment. Find which distributor stocks Schumacher, Thibaut, Phillip Jeffries, Cole & Son, Maya Romanoff, Wolf-Gordon, Kravet, and other lines for residential, hospitality, healthcare, multifamily, and commercial projects.',
+  email: 'info@wallpaperdistributors.com',
+  phone: '888-373-4564',
+  address: 'Designer Wallcoverings, 15442 Ventura Bl #102, Sherman Oaks CA 91403',
+  ga_id: GA_ID,
+};
+
+function sortDistributors(list, sort) {
+  const copy = list.slice();
+  switch (sort) {
+    case 'company':
+      return copy.sort((a, b) => a.company.localeCompare(b.company));
+    case 'city':
+      return copy.sort((a, b) => a.city.localeCompare(b.city));
+    case 'lines':
+      return copy.sort((a, b) => b.lines_carried.length - a.lines_carried.length);
+    case 'years':
+      return copy.sort((a, b) => a.founded - b.founded);
+    case 'newest':
+    default:
+      return copy.sort((a, b) => b.id - a.id);
+  }
+}
+
+app.get('/', (req, res) => {
+  const { q, region, segment, line, trade, sort } = req.query;
+  let filtered = distributors.slice();
+
+  if (q) {
+    const needle = String(q).toLowerCase();
+    filtered = filtered.filter(d =>
+      d.company.toLowerCase().includes(needle) ||
+      d.city.toLowerCase().includes(needle) ||
+      d.lines_carried.some(l => l.toLowerCase().includes(needle)) ||
+      d.segments.some(s => s.toLowerCase().includes(needle)) ||
+      (d.notes || '').toLowerCase().includes(needle)
+    );
+  }
+  if (region) filtered = filtered.filter(d => d.regions.includes(String(region)));
+  if (segment) filtered = filtered.filter(d => d.segments.includes(String(segment)));
+  if (line) filtered = filtered.filter(d => d.lines_carried.includes(String(line)));
+  if (trade === '1') filtered = filtered.filter(d => d.trade_only);
+
+  filtered = sortDistributors(filtered, sort);
+
+  res.render('index', {
+    site: SITE,
+    distributors: filtered,
+    total: distributors.length,
+    matched: filtered.length,
+    filters: {
+      q: q || '',
+      region: region || '',
+      segment: segment || '',
+      line: line || '',
+      trade: trade || '',
+      sort: sort || 'newest',
+    },
+    facets: { regions: allRegions, segments: allSegments, lines: allLines },
+  });
+});
+
+app.get('/distributor/:id', (req, res) => {
+  const id = Number(req.params.id);
+  const distributor = distributors.find(d => d.id === id);
+  if (!distributor) return res.status(404).render('404', { site: SITE });
+  const related = distributors
+    .filter(d => d.id !== distributor.id && d.lines_carried.some(l => distributor.lines_carried.includes(l)))
+    .slice(0, 4);
+  res.render('distributor', { site: SITE, distributor, related });
+});
+
+app.get('/line/:line', (req, res) => {
+  const line = req.params.line;
+  const carriers = distributors.filter(d => d.lines_carried.includes(line));
+  if (!carriers.length) return res.status(404).render('404', { site: SITE });
+  res.render('line', { site: SITE, line, carriers });
+});
+
+app.get('/about', (req, res) => {
+  res.render('about', { site: SITE });
+});
+
+app.get('/healthz', (req, res) =>
+  res.json({ ok: true, port: PORT, distributors: distributors.length })
+);
+
+app.use((req, res) => res.status(404).render('404', { site: SITE }));
+
+app.listen(PORT, () => {
+  console.log(
+    `wallpaperdistributors listening on :${PORT} (${distributors.length} distributors loaded, GA ${GA_ID})`
+  );
+});
diff --git a/views/404.ejs b/_legacy-directory/views/404.ejs
similarity index 100%
rename from views/404.ejs
rename to _legacy-directory/views/404.ejs
diff --git a/views/about.ejs b/_legacy-directory/views/about.ejs
similarity index 100%
rename from views/about.ejs
rename to _legacy-directory/views/about.ejs
diff --git a/views/distributor.ejs b/_legacy-directory/views/distributor.ejs
similarity index 100%
rename from views/distributor.ejs
rename to _legacy-directory/views/distributor.ejs
diff --git a/views/index.ejs b/_legacy-directory/views/index.ejs
similarity index 100%
rename from views/index.ejs
rename to _legacy-directory/views/index.ejs
diff --git a/views/line.ejs b/_legacy-directory/views/line.ejs
similarity index 100%
rename from views/line.ejs
rename to _legacy-directory/views/line.ejs
diff --git a/views/partials/foot.ejs b/_legacy-directory/views/partials/foot.ejs
similarity index 100%
rename from views/partials/foot.ejs
rename to _legacy-directory/views/partials/foot.ejs
diff --git a/views/partials/head.ejs b/_legacy-directory/views/partials/head.ejs
similarity index 100%
rename from views/partials/head.ejs
rename to _legacy-directory/views/partials/head.ejs
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..082a07f
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,135 @@
+<!DOCTYPE html>
+<html lang="en" data-theme="light">
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<title>Wallpaper Distributors — Trade Wallcovering Distribution | Designer Wallcoverings</title>
+<meta name="description" content="Trade wallcovering distribution for designers, hospitality, healthcare, multifamily, and commercial projects — free memos, trade pricing, and nationwide fulfillment through Designer Wallcoverings. Apply for a trade account.">
+<link rel="canonical" href="https://wallpaperdistributors.com/">
+<meta property="og:title" content="Wallpaper Distributors — Trade Wallcovering Distribution">
+<meta property="og:description" content="One trade partner for every luxury wallcovering line. Free memos, trade pricing, nationwide fulfillment. By Designer Wallcoverings.">
+<meta property="og:type" content="website">
+<meta property="og:url" content="https://wallpaperdistributors.com/">
+<style>
+  :root{
+    --bg:#faf7f2; --fg:#1a1712; --muted:#7c736a; --rule:#e2d9cc; --accent:#1f3a2a;
+    --accent-ink:#fff; --card:#ffffff; --soft:#eef2ee;
+    --serif:'Cormorant Garamond','Playfair Display',Georgia,serif;
+    --sans:-apple-system,BlinkMacSystemFont,'Inter',system-ui,sans-serif;
+  }
+  [data-theme=dark]{ --bg:#0e0c09; --fg:#f2ede4; --muted:#9a9088; --rule:#2c2620; --card:#16130f; --accent:#bbe3c5; --accent-ink:#0e0c09; --soft:#1a221b; }
+  *{box-sizing:border-box} html,body{margin:0;padding:0}
+  body{background:var(--bg);color:var(--fg);font-family:var(--sans);line-height:1.55;-webkit-font-smoothing:antialiased}
+  a{color:var(--accent);text-decoration:none}
+  .wrap{max-width:1000px;margin:0 auto;padding:0 22px}
+  header.bar{display:flex;align-items:center;justify-content:space-between;padding:20px 22px;max-width:1000px;margin:0 auto}
+  .logo{font-family:var(--serif);font-size:22px;font-weight:600;letter-spacing:.03em}
+  .logo small{display:block;font-family:var(--sans);font-size:10px;letter-spacing:.22em;text-transform:uppercase;color:var(--muted);font-weight:600}
+  .theme-btn{background:none;border:1px solid var(--rule);color:var(--fg);border-radius:50%;width:38px;height:38px;cursor:pointer}
+
+  .hero{text-align:center;padding:46px 0 18px}
+  .hero .eyebrow{font-size:12px;letter-spacing:.22em;text-transform:uppercase;color:var(--muted);font-weight:600;margin:0 0 14px}
+  .hero h1{font-family:var(--serif);font-weight:600;font-size:clamp(38px,6.5vw,64px);line-height:1.04;margin:0 0 14px}
+  .hero p{color:var(--muted);font-size:clamp(16px,2.4vw,20px);max-width:660px;margin:0 auto 26px}
+  .cta-row{display:flex;gap:14px;justify-content:center;flex-wrap:wrap}
+  .btn{display:inline-block;font-weight:700;padding:15px 30px;border-radius:9px;font-size:15px}
+  .btn-primary{background:var(--accent);color:var(--accent-ink)}
+  .btn-primary:hover{filter:brightness(1.08)}
+  .btn-ghost{border:1px solid var(--rule);color:var(--fg)}
+  .btn-ghost:hover{background:var(--soft)}
+
+  .strip{background:var(--soft);border-block:1px solid var(--rule);margin:44px 0;padding:30px 0}
+  .strip .grid{display:grid;grid-template-columns:repeat(4,1fr);gap:22px;text-align:center}
+  @media(max-width:720px){.strip .grid{grid-template-columns:repeat(2,1fr);gap:26px}}
+  .strip .grid b{font-family:var(--serif);font-size:30px;color:var(--accent);display:block;line-height:1}
+  .strip .grid span{font-size:13px;color:var(--muted);display:block;margin-top:6px}
+
+  .feat{display:grid;grid-template-columns:repeat(3,1fr);gap:26px;padding:8px 0 18px}
+  @media(max-width:760px){.feat{grid-template-columns:1fr}}
+  .feat .c{background:var(--card);border:1px solid var(--rule);border-radius:12px;padding:24px}
+  .feat .c h3{font-family:var(--serif);font-size:21px;font-weight:600;margin:0 0 8px}
+  .feat .c p{color:var(--muted);font-size:14.5px;margin:0}
+
+  .closer{text-align:center;padding:50px 0 30px}
+  .closer h2{font-family:var(--serif);font-size:clamp(28px,4.5vw,42px);font-weight:600;margin:0 0 12px}
+  .closer p{color:var(--muted);max-width:600px;margin:0 auto 24px}
+
+  footer{border-top:1px solid var(--rule);padding:30px 0;text-align:center;color:var(--muted);font-size:13px;margin-top:30px}
+  footer a{color:var(--accent)}
+  footer address{font-style:normal;margin-top:8px;line-height:1.7}
+</style>
+</head>
+<body>
+<header class="bar">
+  <div class="logo">Wallpaper Distributors<small>Trade Distribution · by Designer Wallcoverings</small></div>
+  <button class="theme-btn" id="themeBtn" aria-label="Toggle dark mode">◐</button>
+</header>
+
+<div class="wrap">
+  <section class="hero">
+    <p class="eyebrow">Trade &amp; Contract Wallcovering Supply</p>
+    <h1>One distribution partner<br>for every luxury line.</h1>
+    <p>Designers, hospitality groups, healthcare and multifamily specifiers source the world's finest wallcoverings through a single trade account — free memos, trade pricing, expert specification help, and nationwide fulfillment.</p>
+    <div class="cta-row">
+      <a class="btn btn-primary" href="https://designerwallcoverings.com/pages/trade" target="_blank" rel="noopener">Apply for a trade account →</a>
+      <a class="btn btn-ghost" href="https://designerwallcoverings.com/collections/all" target="_blank" rel="noopener">Browse the catalog</a>
+    </div>
+  </section>
+</div>
+
+<div class="strip">
+  <div class="wrap">
+    <div class="grid">
+      <div><b>11,000+</b><span>patterns across every premier house</span></div>
+      <div><b>Free</b><span>memos &amp; CFAs to the trade</span></div>
+      <div><b>Nationwide</b><span>residential, hospitality &amp; commercial</span></div>
+      <div><b>3–5 days</b><span>typical memo turnaround</span></div>
+    </div>
+  </div>
+</div>
+
+<div class="wrap">
+  <section class="feat">
+    <div class="c">
+      <h3>Every line, one account</h3>
+      <p>Skip juggling a dozen showroom logins. Specify across the full breadth of luxury wallcovering — grasscloth, silk, hand-painted scenics, performance vinyl, metallics — through one trade relationship.</p>
+    </div>
+    <div class="c">
+      <h3>Trade pricing &amp; terms</h3>
+      <p>Net pricing for qualified designers and contract buyers, project quoting, and FF&amp;E-friendly fulfillment for hospitality, healthcare, and multifamily specifications.</p>
+    </div>
+    <div class="c">
+      <h3>Specification support</h3>
+      <p>Real people who know the products — memo programs, dye-lot management, repeat and railroading guidance, and lead-time confirmation before you commit an order.</p>
+    </div>
+  </section>
+
+  <section class="closer">
+    <h2>Specifying a project?</h2>
+    <p>Apply for a Designer Wallcoverings trade account and get pricing, memos, and a specification contact for your next install.</p>
+    <a class="btn btn-primary" href="https://designerwallcoverings.com/pages/trade" target="_blank" rel="noopener">Apply for trade pricing →</a>
+  </section>
+</div>
+
+<footer>
+  <div class="wrap">
+    <strong>Wallpaper Distributors</strong> — trade wallcovering distribution by
+    <a href="https://designerwallcoverings.com" target="_blank" rel="noopener">Designer Wallcoverings</a>.
+    <address>
+      Designer Wallcoverings · 15442 Ventura Bl #102, Sherman Oaks, CA 91403 ·
+      <a href="tel:8883734564">888-373-4564</a> ·
+      <a href="mailto:info@wallpaperdistributors.com">info@wallpaperdistributors.com</a>
+    </address>
+    <div style="margin-top:12px;font-size:11px">© <span id="yr"></span> Designer Wallcoverings.</div>
+  </div>
+</footer>
+
+<script>
+  var tb=document.getElementById('themeBtn');
+  function setTheme(t){document.documentElement.setAttribute('data-theme',t);try{localStorage.setItem('wd-theme',t);}catch(e){}}
+  try{var s=localStorage.getItem('wd-theme');if(s)setTheme(s);}catch(e){}
+  tb.addEventListener('click',function(){setTheme(document.documentElement.getAttribute('data-theme')==='dark'?'light':'dark');});
+  document.getElementById('yr').textContent=new Date().getFullYear();
+</script>
+</body>
+</html>
diff --git a/server.js b/server.js
index 6a5c827..af82f34 100644
--- a/server.js
+++ b/server.js
@@ -1,142 +1,40 @@
+/**
+ * wallpaperdistributors.com — thin trade bridge.
+ *
+ * DTD verdict B + Steve 2026-05-31: do NOT run the standalone distributor
+ * directory (its content was vendor-brand names = a hard violation of the
+ * "never DW vendors in public UI" rule). Instead this is a thin, vendor-free
+ * DW-branded trade lander that routes qualified buyers into the Designer
+ * Wallcoverings trade program. The legacy directory build is preserved under
+ * _legacy-directory/ (NOT served, NOT deployed).
+ */
 const express = require('express');
-const path = require('path');
-const fs = require('fs');
 const helmet = require('helmet');
-const compression = require('compression');
-const morgan = require('morgan');
+const path = require('path');
 
-const app = express();
 const PORT = process.env.PORT || 9932;
-const GA_ID = process.env.GA_ID || 'G-NBZ5WTBW8F';
+const SITE = 'wallpaperdistributors';
 
-const distributors = JSON.parse(
-  fs.readFileSync(path.join(__dirname, 'data', 'distributors.json'), 'utf8')
-);
-
-app.set('view engine', 'ejs');
-app.set('views', path.join(__dirname, 'views'));
-
-app.use(helmet({
-  contentSecurityPolicy: false,
-  crossOriginEmbedderPolicy: false,
-}));
-app.use(compression());
-app.use(morgan('tiny'));
+const app = express();
+app.use(helmet({ contentSecurityPolicy: false }));
 
 app.use((req, res, next) => {
-  res.set('Cache-Control', 'no-store, must-revalidate');
+  res.setHeader('Cache-Control', 'no-store, must-revalidate');
   next();
 });
 
-// Never serve editor/snapshot artifacts from the static root.
+// 404-guard: never serve snapshot/backup files from the static root.
 app.use((req, res, next) => {
-  if (/\.(bak|orig|pre-[^/]*|[^/]*~)$|\.bak\.|\.pre-/i.test(req.path)) {
-    return res.status(404).render('404', { site: SITE });
+  if (/\.(bak|orig|swp|tmp)$|\.bak\.|\.pre-/i.test(req.path)) {
+    return res.status(404).send('Not found');
   }
   next();
 });
 
-app.use(express.static(path.join(__dirname, 'public'), { maxAge: '1h' }));
-
-const allRegions = Array.from(new Set(distributors.flatMap(d => d.regions))).sort();
-const allSegments = Array.from(new Set(distributors.flatMap(d => d.segments))).sort();
-const allLines = Array.from(new Set(distributors.flatMap(d => d.lines_carried))).sort();
-
-const SITE = {
-  domain: 'wallpaperdistributors.com',
-  name: 'Wallpaper Distributors',
-  tagline: 'B2B wallcovering distributors, mapped to the lines they carry',
-  description: 'Trade index of B2B wallcovering distributors across the U.S. and Canada — searchable by vendor line, region, and project segment. Find which distributor stocks Schumacher, Thibaut, Phillip Jeffries, Cole & Son, Maya Romanoff, Wolf-Gordon, Kravet, and other lines for residential, hospitality, healthcare, multifamily, and commercial projects.',
-  email: 'info@wallpaperdistributors.com',
-  phone: '888-373-4564',
-  address: 'Designer Wallcoverings, 15442 Ventura Bl #102, Sherman Oaks CA 91403',
-  ga_id: GA_ID,
-};
-
-function sortDistributors(list, sort) {
-  const copy = list.slice();
-  switch (sort) {
-    case 'company':
-      return copy.sort((a, b) => a.company.localeCompare(b.company));
-    case 'city':
-      return copy.sort((a, b) => a.city.localeCompare(b.city));
-    case 'lines':
-      return copy.sort((a, b) => b.lines_carried.length - a.lines_carried.length);
-    case 'years':
-      return copy.sort((a, b) => a.founded - b.founded);
-    case 'newest':
-    default:
-      return copy.sort((a, b) => b.id - a.id);
-  }
-}
-
-app.get('/', (req, res) => {
-  const { q, region, segment, line, trade, sort } = req.query;
-  let filtered = distributors.slice();
-
-  if (q) {
-    const needle = String(q).toLowerCase();
-    filtered = filtered.filter(d =>
-      d.company.toLowerCase().includes(needle) ||
-      d.city.toLowerCase().includes(needle) ||
-      d.lines_carried.some(l => l.toLowerCase().includes(needle)) ||
-      d.segments.some(s => s.toLowerCase().includes(needle)) ||
-      (d.notes || '').toLowerCase().includes(needle)
-    );
-  }
-  if (region) filtered = filtered.filter(d => d.regions.includes(String(region)));
-  if (segment) filtered = filtered.filter(d => d.segments.includes(String(segment)));
-  if (line) filtered = filtered.filter(d => d.lines_carried.includes(String(line)));
-  if (trade === '1') filtered = filtered.filter(d => d.trade_only);
-
-  filtered = sortDistributors(filtered, sort);
-
-  res.render('index', {
-    site: SITE,
-    distributors: filtered,
-    total: distributors.length,
-    matched: filtered.length,
-    filters: {
-      q: q || '',
-      region: region || '',
-      segment: segment || '',
-      line: line || '',
-      trade: trade || '',
-      sort: sort || 'newest',
-    },
-    facets: { regions: allRegions, segments: allSegments, lines: allLines },
-  });
-});
-
-app.get('/distributor/:id', (req, res) => {
-  const id = Number(req.params.id);
-  const distributor = distributors.find(d => d.id === id);
-  if (!distributor) return res.status(404).render('404', { site: SITE });
-  const related = distributors
-    .filter(d => d.id !== distributor.id && d.lines_carried.some(l => distributor.lines_carried.includes(l)))
-    .slice(0, 4);
-  res.render('distributor', { site: SITE, distributor, related });
-});
-
-app.get('/line/:line', (req, res) => {
-  const line = req.params.line;
-  const carriers = distributors.filter(d => d.lines_carried.includes(line));
-  if (!carriers.length) return res.status(404).render('404', { site: SITE });
-  res.render('line', { site: SITE, line, carriers });
-});
-
-app.get('/about', (req, res) => {
-  res.render('about', { site: SITE });
-});
-
-app.get('/healthz', (req, res) =>
-  res.json({ ok: true, port: PORT, distributors: distributors.length })
-);
+app.use(express.static(path.join(__dirname, 'public'), { extensions: ['html'] }));
 
-app.use((req, res) => res.status(404).render('404', { site: SITE }));
+app.get('/healthz', (_req, res) => res.json({ ok: true, site: SITE, port: PORT }));
 
-app.listen(PORT, () => {
-  console.log(
-    `wallpaperdistributors listening on :${PORT} (${distributors.length} distributors loaded, GA ${GA_ID})`
-  );
+app.listen(PORT, '0.0.0.0', () => {
+  console.log(`[${SITE}] thin trade bridge listening on :${PORT}`);
 });

← ae37058 Add per-site favicon (kills /favicon.ico 404)  ·  back to Wallpaperdistributors  ·  fleet rebuild (header-only): dw-header (hamburgers UL + cent cf5cef7 →