← back to Wallpaper Tour
initial scaffold
916a5f91141a8763355433309fa8fda566ce9dbd · 2026-05-13 05:27:38 -0700 · Steve
Files touched
A .gitignoreA build-index.mjsA clips/01-1800swallpaper.webmA clips/02-1890swallpaper.webmA clips/03-1900swallpaper.webmA clips/04-1920swallpaper.webmA clips/05-1930swallpaper.webmA clips/06-1940swallpaper.webmA clips/07-1950swallpaper.webmA clips/08-1960swallpaper.webmA clips/09-1970swallpaper.webmA clips/10-1980swallpaper.webmA clips/11-agedwallpaper.webmA clips/12-apartmentwallpaper.webmA clips/13-architecturalwallcoverings.webmA clips/14-bestwallpaperplace.webmA clips/15-blockprintedwallpaper.webmA clips/16-carmelwallpapers.webmA clips/17-chinoiseriewallpaper.webmA clips/18-contractwallpaper.webmA clips/19-corkwallcovering.webmA clips/20-designerwallcoverings.webmA clips/21-embroideredwallpaper.webmA clips/22-fabricwallpaper.webmA clips/23-flockedwallpaper.webmA clips/24-glassbeadedwallpaper.webmA clips/25-glitterwalls.webmA clips/26-goldleafwallpaper.webmA clips/27-grassclothwallpaper.webmA clips/28-greenwallcoverings.webmA clips/29-handcraftedwallpaper.webmA clips/30-healthcarewallpaper.webmA clips/31-hospitalitywallcoverings.webmA clips/32-hospitalitywallpaper.webmA clips/33-hotelwallcoverings.webmA clips/34-jutewallpaper.webmA clips/35-linenwallpaper.webmA clips/36-madagascarwallpaper.webmA clips/37-metallicwallpaper.webmA clips/38-micawallpaper.webmA clips/39-museumwallpaper.webmA clips/40-mylarwallpaper.webmA clips/41-naturaltextilewallpaper.webmA clips/42-naturalwallcoverings.webmA clips/43-pastelwallpaper.webmA clips/44-raffiawallcoverings.webmA clips/45-raffiawalls.webmA clips/46-recycledwallpaper.webmA clips/47-restaurantwallpaper.webmA clips/48-restorationwallpaper.webmA clips/49-retrowalls.webmA clips/50-saloonwallpaper.webmA clips/51-screenprintedwallpaper.webmA clips/52-selfadhesivewallpaper.webmA clips/53-silkwallcoverings.webmA clips/54-silkwallpaper.webmA clips/55-silverleafwallpaper.webmA clips/56-stringwallpaper.webmA clips/57-suedewallpaper.webmA clips/58-textilewallpaper.webmA clips/59-vinylwallpaper.webmA clips/60-wallpapercanada.webmA clips/61-wallpapercontractor.webmA clips/62-wallpaperdefinitions.webmA clips/63-wallpaperdictionary.webmA clips/64-wallpaperdistributors.webmA clips/65-wallpaperestimator.webmA clips/66-wallpaperglossary.webmA clips/67-wallpaperhistory.webmA clips/68-wallpapersback.webmA clips/69-wallpaperwednesday.webmA clips/70-wallsandfabrics.webmA index.htmlA inventory.jsonA live-urls.txtA preflight-results.jsonA preflight.mjsA projects.txtA smoketest.mjsA stitch.mjsA thumbs/1800swallpaper.jpgA thumbs/1890swallpaper.jpgA thumbs/1900swallpaper.jpgA thumbs/1920swallpaper.jpgA thumbs/1930swallpaper.jpgA thumbs/1940swallpaper.jpgA thumbs/1950swallpaper.jpgA thumbs/1960swallpaper.jpgA thumbs/1970swallpaper.jpgA thumbs/1980swallpaper.jpgA thumbs/agedwallpaper.jpgA thumbs/apartmentwallpaper.jpgA thumbs/architecturalwallcoverings.jpgA thumbs/bestwallpaperplace.jpgA thumbs/blockprintedwallpaper.jpgA thumbs/carmelwallpapers.jpgA thumbs/chinoiseriewallpaper.jpgA thumbs/contractwallpaper.jpgA thumbs/corkwallcovering.jpgA thumbs/designerwallcoverings.jpgA thumbs/embroideredwallpaper.jpgA thumbs/fabricwallpaper.jpgA thumbs/flockedwallpaper.jpgA thumbs/glassbeadedwallpaper.jpgA thumbs/glitterwalls.jpgA thumbs/goldleafwallpaper.jpgA thumbs/greenwallcoverings.jpgA thumbs/handcraftedwallpaper.jpgA thumbs/healthcarewallpaper.jpgA tour-report.jsonA tour.mjs
Diff
commit 916a5f91141a8763355433309fa8fda566ce9dbd
Author: Steve <steve@designerwallcoverings.com>
Date: Wed May 13 05:27:38 2026 -0700
initial scaffold
---
.gitignore | 8 +
build-index.mjs | 242 +++++++++
clips/01-1800swallpaper.webm | Bin 0 -> 816194 bytes
clips/02-1890swallpaper.webm | Bin 0 -> 1360449 bytes
clips/03-1900swallpaper.webm | Bin 0 -> 963806 bytes
clips/04-1920swallpaper.webm | Bin 0 -> 757471 bytes
clips/05-1930swallpaper.webm | Bin 0 -> 1259597 bytes
clips/06-1940swallpaper.webm | Bin 0 -> 1334931 bytes
clips/07-1950swallpaper.webm | Bin 0 -> 1502962 bytes
clips/08-1960swallpaper.webm | Bin 0 -> 1192622 bytes
clips/09-1970swallpaper.webm | Bin 0 -> 1333899 bytes
clips/10-1980swallpaper.webm | Bin 0 -> 1383553 bytes
clips/11-agedwallpaper.webm | Bin 0 -> 1182952 bytes
clips/12-apartmentwallpaper.webm | Bin 0 -> 1449487 bytes
clips/13-architecturalwallcoverings.webm | Bin 0 -> 1366944 bytes
clips/14-bestwallpaperplace.webm | Bin 0 -> 112445 bytes
clips/15-blockprintedwallpaper.webm | Bin 0 -> 981940 bytes
clips/16-carmelwallpapers.webm | Bin 0 -> 1749216 bytes
clips/17-chinoiseriewallpaper.webm | Bin 0 -> 1605670 bytes
clips/18-contractwallpaper.webm | Bin 0 -> 1591776 bytes
clips/19-corkwallcovering.webm | Bin 0 -> 1582642 bytes
clips/20-designerwallcoverings.webm | Bin 0 -> 1501005 bytes
clips/21-embroideredwallpaper.webm | Bin 0 -> 1292563 bytes
clips/22-fabricwallpaper.webm | Bin 0 -> 1201822 bytes
clips/23-flockedwallpaper.webm | Bin 0 -> 1434550 bytes
clips/24-glassbeadedwallpaper.webm | Bin 0 -> 1475485 bytes
clips/25-glitterwalls.webm | Bin 0 -> 1223877 bytes
clips/26-goldleafwallpaper.webm | Bin 0 -> 1105628 bytes
clips/27-grassclothwallpaper.webm | Bin 0 -> 2742069 bytes
clips/28-greenwallcoverings.webm | Bin 0 -> 1276252 bytes
clips/29-handcraftedwallpaper.webm | Bin 0 -> 902998 bytes
clips/30-healthcarewallpaper.webm | Bin 0 -> 1077722 bytes
clips/31-hospitalitywallcoverings.webm | Bin 0 -> 1483098 bytes
clips/32-hospitalitywallpaper.webm | Bin 0 -> 1227505 bytes
clips/33-hotelwallcoverings.webm | Bin 0 -> 1422524 bytes
clips/34-jutewallpaper.webm | Bin 0 -> 1453768 bytes
clips/35-linenwallpaper.webm | Bin 0 -> 1675873 bytes
clips/36-madagascarwallpaper.webm | Bin 0 -> 1295249 bytes
clips/37-metallicwallpaper.webm | Bin 0 -> 1528901 bytes
clips/38-micawallpaper.webm | Bin 0 -> 2883733 bytes
clips/39-museumwallpaper.webm | Bin 0 -> 1453727 bytes
clips/40-mylarwallpaper.webm | Bin 0 -> 1133239 bytes
clips/41-naturaltextilewallpaper.webm | Bin 0 -> 1968052 bytes
clips/42-naturalwallcoverings.webm | Bin 0 -> 2098898 bytes
clips/43-pastelwallpaper.webm | Bin 0 -> 1516576 bytes
clips/44-raffiawallcoverings.webm | Bin 0 -> 457700 bytes
clips/45-raffiawalls.webm | Bin 0 -> 457692 bytes
clips/46-recycledwallpaper.webm | Bin 0 -> 390919 bytes
clips/47-restaurantwallpaper.webm | Bin 0 -> 1031275 bytes
clips/48-restorationwallpaper.webm | Bin 0 -> 2251695 bytes
clips/49-retrowalls.webm | Bin 0 -> 4133 bytes
clips/50-saloonwallpaper.webm | Bin 0 -> 2327028 bytes
clips/51-screenprintedwallpaper.webm | Bin 0 -> 1389843 bytes
clips/52-selfadhesivewallpaper.webm | Bin 0 -> 1044997 bytes
clips/53-silkwallcoverings.webm | Bin 0 -> 513026 bytes
clips/54-silkwallpaper.webm | Bin 0 -> 828712 bytes
clips/55-silverleafwallpaper.webm | Bin 0 -> 1274916 bytes
clips/56-stringwallpaper.webm | Bin 0 -> 1875295 bytes
clips/57-suedewallpaper.webm | Bin 0 -> 1283406 bytes
clips/58-textilewallpaper.webm | Bin 0 -> 937420 bytes
clips/59-vinylwallpaper.webm | Bin 0 -> 815520 bytes
clips/60-wallpapercanada.webm | Bin 0 -> 1182150 bytes
clips/61-wallpapercontractor.webm | Bin 0 -> 610885 bytes
clips/62-wallpaperdefinitions.webm | Bin 0 -> 1020417 bytes
clips/63-wallpaperdictionary.webm | Bin 0 -> 2655772 bytes
clips/64-wallpaperdistributors.webm | Bin 0 -> 247369 bytes
clips/65-wallpaperestimator.webm | Bin 0 -> 302433 bytes
clips/66-wallpaperglossary.webm | Bin 0 -> 947530 bytes
clips/67-wallpaperhistory.webm | Bin 0 -> 1199675 bytes
clips/68-wallpapersback.webm | Bin 0 -> 1580548 bytes
clips/69-wallpaperwednesday.webm | Bin 0 -> 917131 bytes
clips/70-wallsandfabrics.webm | Bin 0 -> 177208 bytes
index.html | 902 +++++++++++++++++++++++++++++++
inventory.json | 834 ++++++++++++++++++++++++++++
live-urls.txt | 70 +++
preflight-results.json | 429 +++++++++++++++
preflight.mjs | 66 +++
projects.txt | 71 +++
smoketest.mjs | 13 +
stitch.mjs | 63 +++
thumbs/1800swallpaper.jpg | Bin 0 -> 2562 bytes
thumbs/1890swallpaper.jpg | Bin 0 -> 13118 bytes
thumbs/1900swallpaper.jpg | Bin 0 -> 13814 bytes
thumbs/1920swallpaper.jpg | Bin 0 -> 783 bytes
thumbs/1930swallpaper.jpg | Bin 0 -> 783 bytes
thumbs/1940swallpaper.jpg | Bin 0 -> 783 bytes
thumbs/1950swallpaper.jpg | Bin 0 -> 21205 bytes
thumbs/1960swallpaper.jpg | Bin 0 -> 13183 bytes
thumbs/1970swallpaper.jpg | Bin 0 -> 2495 bytes
thumbs/1980swallpaper.jpg | Bin 0 -> 12894 bytes
thumbs/agedwallpaper.jpg | Bin 0 -> 10420 bytes
thumbs/apartmentwallpaper.jpg | Bin 0 -> 1983 bytes
thumbs/architecturalwallcoverings.jpg | Bin 0 -> 13817 bytes
thumbs/bestwallpaperplace.jpg | Bin 0 -> 2645 bytes
thumbs/blockprintedwallpaper.jpg | Bin 0 -> 2635 bytes
thumbs/carmelwallpapers.jpg | Bin 0 -> 7832 bytes
thumbs/chinoiseriewallpaper.jpg | Bin 0 -> 8255 bytes
thumbs/contractwallpaper.jpg | Bin 0 -> 2788 bytes
thumbs/corkwallcovering.jpg | Bin 0 -> 2403 bytes
thumbs/designerwallcoverings.jpg | Bin 0 -> 22249 bytes
thumbs/embroideredwallpaper.jpg | Bin 0 -> 12190 bytes
thumbs/fabricwallpaper.jpg | Bin 0 -> 3681 bytes
thumbs/flockedwallpaper.jpg | Bin 0 -> 12295 bytes
thumbs/glassbeadedwallpaper.jpg | Bin 0 -> 11239 bytes
thumbs/glitterwalls.jpg | Bin 0 -> 9562 bytes
thumbs/goldleafwallpaper.jpg | Bin 0 -> 3821 bytes
thumbs/greenwallcoverings.jpg | Bin 0 -> 16833 bytes
thumbs/handcraftedwallpaper.jpg | Bin 0 -> 783 bytes
thumbs/healthcarewallpaper.jpg | Bin 0 -> 7382 bytes
tour-report.json | 614 +++++++++++++++++++++
tour.mjs | 185 +++++++
111 files changed, 3497 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/build-index.mjs b/build-index.mjs
new file mode 100644
index 0000000..40e8797
--- /dev/null
+++ b/build-index.mjs
@@ -0,0 +1,242 @@
+#!/usr/bin/env node
+// Build a clickable index page of all wallpaper/wallcover/walls projects:
+// • public URL liveness (HEAD with 6s timeout)
+// • local pm2 port if managed
+// • thumbnail (first frame of the captured clip if available)
+import fs from 'node:fs';
+import path from 'node:path';
+import os from 'node:os';
+import { execFile } from 'node:child_process';
+import { fileURLToPath } from 'node:url';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const PROJECTS = path.join(os.homedir(), 'Projects');
+
+// 1) Discover dirs
+const dirs = fs.readdirSync(PROJECTS, { withFileTypes: true })
+ .filter(d => d.isDirectory() && /wallpaper|wallcover|walls/i.test(d.name))
+ .map(d => d.name).sort();
+
+// 2) pm2 lookup
+const pm2 = await new Promise((res) => {
+ execFile('pm2', ['jlist'], { maxBuffer: 16 * 1024 * 1024 }, (err, stdout) => {
+ if (err) return res([]);
+ try { res(JSON.parse(stdout)); } catch { res([]); }
+ });
+});
+const pm2ByName = new Map(pm2.map(p => [p.name, p]));
+
+// 3) HEAD each candidate URL
+async function head(url) {
+ const t0 = Date.now();
+ try {
+ const r = await fetch(url, { method: 'GET', signal: AbortSignal.timeout(6000), redirect: 'manual' });
+ return { ok: r.status < 500, status: r.status, ms: Date.now() - t0 };
+ } catch (e) {
+ return { ok: false, status: 0, ms: Date.now() - t0, error: e.code || (e.name === 'TimeoutError' ? 'timeout' : 'err') };
+ }
+}
+
+// concurrent HEAD pool
+async function pmap(items, fn, limit = 8) {
+ const out = new Array(items.length); let i = 0;
+ await Promise.all(Array.from({ length: limit }, async () => {
+ while (true) {
+ const idx = i++; if (idx >= items.length) return;
+ out[idx] = await fn(items[idx], idx);
+ }
+ }));
+ return out;
+}
+
+console.log(`Checking ${dirs.length} sites…`);
+const rows = await pmap(dirs, async (name) => {
+ const url = `https://${name.replace(/[^a-z0-9-]/gi, '')}.com`;
+ const pm2p = pm2ByName.get(name);
+ const port = pm2p?.pm2_env?.PORT ?? pm2p?.pm2_env?.env?.PORT ?? null;
+ const status = pm2p?.pm2_env?.status ?? null;
+ const probe = await head(url);
+ return {
+ name, public_url: url,
+ pm2: !!pm2p, pm2_status: status, port,
+ public_live: probe.ok, public_status: probe.status, public_ms: probe.ms,
+ };
+});
+
+// 4) Check for thumbnails (first frame of captured clip via ffmpeg)
+const clipsDir = path.join(__dirname, 'clips');
+const thumbDir = path.join(__dirname, 'thumbs');
+fs.mkdirSync(thumbDir, { recursive: true });
+const existingThumbs = new Set(fs.existsSync(thumbDir) ? fs.readdirSync(thumbDir) : []);
+
+for (const row of rows) {
+ const clipMatch = fs.existsSync(clipsDir)
+ ? fs.readdirSync(clipsDir).find(f => f.endsWith(`-${row.name}.webm`))
+ : null;
+ if (clipMatch) {
+ const thumbName = `${row.name}.jpg`;
+ if (!existingThumbs.has(thumbName)) {
+ const src = path.join(clipsDir, clipMatch);
+ const dst = path.join(thumbDir, thumbName);
+ await new Promise((res) => {
+ execFile('ffmpeg', ['-y', '-ss', '2', '-i', src, '-vframes', '1', '-vf', 'scale=400:-1', '-q:v', '4', dst],
+ { timeout: 10000 }, () => res());
+ });
+ }
+ if (fs.existsSync(path.join(thumbDir, thumbName))) row.thumb = `thumbs/${thumbName}`;
+ }
+}
+
+// 5) Categorize
+const cat = (n) => {
+ if (/^\d{4}s/.test(n)) return 'era';
+ if (/contract|hospital|hotel|healthcar|restaurant|architect/.test(n)) return 'commercial';
+ if (/silk|cork|linen|jute|suede|raffia|mica|metallic|silverleaf|goldleaf|grasscloth|natural|recycled|textile|fabric|stringwallpaper|hyggewallpaper/.test(n)) return 'material';
+ if (/glass|glitter|embroidered|flocked|mylar|pastel|aged|restoration/.test(n)) return 'finish';
+ if (/wallpaper(dictionary|definitions|glossary|history|wednesday|sback|estimator|contractor|distributors|canada)/.test(n) || /^bestwallpaperplace|wallsandfabrics$/.test(n)) return 'editorial';
+ if (/madagascar|carmel|hollywood/.test(n)) return 'place';
+ return 'other';
+};
+
+rows.forEach(r => r.category = cat(r.name));
+rows.sort((a, b) => a.category.localeCompare(b.category) || a.name.localeCompare(b.name));
+
+// 6) Render
+const counts = rows.reduce((a, r) => (a[r.category] = (a[r.category] || 0) + 1, a), {});
+const live = rows.filter(r => r.public_live).length;
+const pm2Count = rows.filter(r => r.pm2).length;
+
+const html = `<!doctype html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>DW Wallpaper Fleet — ${rows.length} sites</title>
+<style>
+ :root{--bg:#0a0a0c;--panel:#15151a;--rule:#26262e;--ink:#eee6d8;--mute:#8c8680;--gold:#c9a96a;--ok:#7ad27a;--bad:#e26d6d;}
+ *{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.5 -apple-system,'SF Pro Text',sans-serif}
+ header{padding:24px 32px;border-bottom:1px solid var(--rule);background:var(--panel);display:flex;justify-content:space-between;align-items:baseline}
+ h1{margin:0;font:300 32px/1 'SF Pro Display',serif;letter-spacing:.04em}
+ h1 b{color:var(--gold);font-weight:500}
+ .stats{font-family:'SF Mono',monospace;font-size:12px;color:var(--mute);display:flex;gap:18px}
+ .stats b{color:var(--ink);font-weight:600;font-size:15px;letter-spacing:0}
+ .cat-tabs{padding:14px 32px;display:flex;gap:8px;border-bottom:1px solid var(--rule);background:var(--panel);overflow-x:auto;align-items:center;flex-wrap:wrap}
+ .tab{background:transparent;border:1px solid var(--rule);color:var(--ink);padding:6px 12px;font:600 11px 'SF Pro Text';text-transform:uppercase;letter-spacing:.1em;border-radius:3px;cursor:pointer}
+ .tab.active{background:var(--gold);color:#000;border-color:var(--gold)}
+ .divider{width:1px;height:24px;background:var(--rule);margin:0 6px}
+ .range{display:flex;align-items:center;gap:10px;font:600 11px 'SF Pro Text';text-transform:uppercase;letter-spacing:.1em;color:var(--mute)}
+ .range input[type="number"]{width:50px;background:#0e0e10;border:1px solid var(--rule);color:var(--ink);padding:5px 6px;border-radius:3px;font:600 12px 'SF Mono',monospace;text-align:center}
+ .range input[type="number"]:focus{outline:none;border-color:var(--gold)}
+ .range .dash{color:var(--mute)}
+ .range-presets{display:flex;gap:4px}
+ .range-presets button{background:transparent;border:1px solid var(--rule);color:var(--mute);padding:5px 9px;font:600 10px 'SF Mono',monospace;border-radius:2px;cursor:pointer;text-transform:uppercase;letter-spacing:.06em}
+ .range-presets button:hover{color:var(--gold);border-color:var(--gold)}
+ .range-presets button.active{background:var(--gold);color:#000;border-color:var(--gold)}
+ .grid{padding:24px 32px;display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:14px}
+ .card{background:var(--panel);border:1px solid var(--rule);border-radius:6px;overflow:hidden;text-decoration:none;color:inherit;transition:border-color .15s,transform .15s}
+ .card:hover{border-color:var(--gold);transform:translateY(-2px)}
+ .thumb{aspect-ratio:16/9;background:#0e0e10;display:flex;align-items:center;justify-content:center;overflow:hidden;position:relative}
+ .thumb img{width:100%;height:100%;object-fit:cover}
+ .thumb .placeholder{color:var(--mute);font:300 32px 'SF Pro Display',serif}
+ .meta{padding:12px 14px}
+ .name{font:500 14px 'SF Pro Display';letter-spacing:.01em;margin:0 0 4px;display:flex;justify-content:space-between;align-items:center;gap:6px}
+ .sub{font:400 11px 'SF Mono',monospace;color:var(--mute);letter-spacing:.02em;display:flex;justify-content:space-between}
+ .pill{font:600 9px/1 'SF Mono',monospace;text-transform:uppercase;letter-spacing:.08em;padding:3px 5px;border-radius:2px;flex-shrink:0}
+ .pill.ok{background:rgba(122,210,122,.12);color:var(--ok);border:1px solid rgba(122,210,122,.3)}
+ .pill.bad{background:rgba(226,109,109,.12);color:var(--bad);border:1px solid rgba(226,109,109,.3)}
+ .footer{padding:18px 32px;color:var(--mute);font-size:11px;text-align:center;border-top:1px solid var(--rule);font-family:'SF Mono',monospace}
+ .hidden{display:none}
+</style>
+</head>
+<body>
+<header>
+ <h1>DW <b>WALLPAPER FLEET</b></h1>
+ <div class="stats">
+ <span><b>${rows.length}</b> sites</span>
+ <span><b>${live}</b> live public</span>
+ <span><b>${pm2Count}</b> pm2-managed</span>
+ </div>
+</header>
+<div class="cat-tabs">
+ <button class="tab active" data-cat="all">All (${rows.length})</button>
+ ${Object.entries(counts).sort().map(([c, n]) => `<button class="tab" data-cat="${c}">${c} (${n})</button>`).join('')}
+ <div class="divider"></div>
+ <div class="range">
+ <span>Range</span>
+ <input type="number" id="rStart" min="1" max="${rows.length}" value="1">
+ <span class="dash">–</span>
+ <input type="number" id="rEnd" min="1" max="${rows.length}" value="${rows.length}">
+ <div class="range-presets">
+ <button data-r="1-10">1–10</button>
+ <button data-r="1-25">1–25</button>
+ <button data-r="1-50">1–50</button>
+ <button data-r="1-${rows.length}" class="active">All</button>
+ </div>
+ <span id="rCount" style="color:var(--gold);font-family:'SF Mono',monospace">${rows.length} shown</span>
+ </div>
+</div>
+<div class="grid" id="grid">
+${rows.map(r => ` <a class="card" data-cat="${r.category}" href="${r.public_url}" target="_blank">
+ <div class="thumb">
+ ${r.thumb ? `<img src="${r.thumb}" loading="lazy" alt="${r.name}">` : `<span class="placeholder">${r.name.slice(0,1).toUpperCase()}</span>`}
+ </div>
+ <div class="meta">
+ <div class="name">${r.name} <span class="pill ${r.public_live?'ok':'bad'}">${r.public_live?'LIVE':'DOWN'}</span></div>
+ <div class="sub"><span>${r.public_url.replace('https://','')}</span><span>${r.port?(':'+r.port):''}</span></div>
+ </div>
+ </a>`).join('\n')}
+</div>
+<div class="footer">~/Projects/wallpaper-tour/index.html · ${new Date().toISOString()} · sorted by ${Object.keys(counts).sort().join(' · ')}</div>
+<script>
+ const TOTAL = ${rows.length};
+ const state = { cat: 'all', start: 1, end: TOTAL };
+
+ function applyFilters() {
+ const cards = [...document.querySelectorAll('.card')];
+ // First, get cards matching category
+ const matchingCat = cards.filter(c => state.cat === 'all' || c.dataset.cat === state.cat);
+ // Then, slice by range (1-indexed inclusive)
+ const visible = new Set(matchingCat.slice(state.start - 1, state.end));
+ for (const c of cards) c.classList.toggle('hidden', !visible.has(c));
+ document.getElementById('rCount').textContent = visible.size + ' shown';
+ }
+
+ for (const t of document.querySelectorAll('.tab')) {
+ t.onclick = () => {
+ document.querySelectorAll('.tab').forEach(x => x.classList.toggle('active', x === t));
+ state.cat = t.dataset.cat;
+ applyFilters();
+ };
+ }
+
+ const rStart = document.getElementById('rStart');
+ const rEnd = document.getElementById('rEnd');
+ function readRange() {
+ state.start = Math.max(1, Math.min(TOTAL, parseInt(rStart.value) || 1));
+ state.end = Math.max(state.start, Math.min(TOTAL, parseInt(rEnd.value) || TOTAL));
+ rStart.value = state.start; rEnd.value = state.end;
+ // clear preset highlights when user types custom range
+ document.querySelectorAll('.range-presets button').forEach(b => {
+ b.classList.toggle('active', b.dataset.r === state.start + '-' + state.end);
+ });
+ applyFilters();
+ }
+ rStart.addEventListener('input', readRange);
+ rEnd.addEventListener('input', readRange);
+
+ for (const btn of document.querySelectorAll('.range-presets button')) {
+ btn.onclick = () => {
+ const [s, e] = btn.dataset.r.split('-').map(Number);
+ rStart.value = s; rEnd.value = e;
+ readRange();
+ };
+ }
+</script>
+</body>
+</html>`;
+
+const outPath = path.join(__dirname, 'index.html');
+fs.writeFileSync(outPath, html);
+fs.writeFileSync(path.join(__dirname, 'inventory.json'), JSON.stringify(rows, null, 2));
+console.log(`\n✓ ${outPath}`);
+console.log(` ${rows.length} sites · ${live} live · ${pm2Count} pm2`);
+console.log(` categories: ${Object.entries(counts).map(([c,n])=>`${c}=${n}`).join(' ')}`);
diff --git a/clips/01-1800swallpaper.webm b/clips/01-1800swallpaper.webm
new file mode 100644
index 0000000..157c874
Binary files /dev/null and b/clips/01-1800swallpaper.webm differ
diff --git a/clips/02-1890swallpaper.webm b/clips/02-1890swallpaper.webm
new file mode 100644
index 0000000..6aacec6
Binary files /dev/null and b/clips/02-1890swallpaper.webm differ
diff --git a/clips/03-1900swallpaper.webm b/clips/03-1900swallpaper.webm
new file mode 100644
index 0000000..fc43bc5
Binary files /dev/null and b/clips/03-1900swallpaper.webm differ
diff --git a/clips/04-1920swallpaper.webm b/clips/04-1920swallpaper.webm
new file mode 100644
index 0000000..6d44abd
Binary files /dev/null and b/clips/04-1920swallpaper.webm differ
diff --git a/clips/05-1930swallpaper.webm b/clips/05-1930swallpaper.webm
new file mode 100644
index 0000000..6352a28
Binary files /dev/null and b/clips/05-1930swallpaper.webm differ
diff --git a/clips/06-1940swallpaper.webm b/clips/06-1940swallpaper.webm
new file mode 100644
index 0000000..8e492d0
Binary files /dev/null and b/clips/06-1940swallpaper.webm differ
diff --git a/clips/07-1950swallpaper.webm b/clips/07-1950swallpaper.webm
new file mode 100644
index 0000000..b0e0937
Binary files /dev/null and b/clips/07-1950swallpaper.webm differ
diff --git a/clips/08-1960swallpaper.webm b/clips/08-1960swallpaper.webm
new file mode 100644
index 0000000..2c3e367
Binary files /dev/null and b/clips/08-1960swallpaper.webm differ
diff --git a/clips/09-1970swallpaper.webm b/clips/09-1970swallpaper.webm
new file mode 100644
index 0000000..c44462a
Binary files /dev/null and b/clips/09-1970swallpaper.webm differ
diff --git a/clips/10-1980swallpaper.webm b/clips/10-1980swallpaper.webm
new file mode 100644
index 0000000..f8f9228
Binary files /dev/null and b/clips/10-1980swallpaper.webm differ
diff --git a/clips/11-agedwallpaper.webm b/clips/11-agedwallpaper.webm
new file mode 100644
index 0000000..9dc6afa
Binary files /dev/null and b/clips/11-agedwallpaper.webm differ
diff --git a/clips/12-apartmentwallpaper.webm b/clips/12-apartmentwallpaper.webm
new file mode 100644
index 0000000..9d78dbb
Binary files /dev/null and b/clips/12-apartmentwallpaper.webm differ
diff --git a/clips/13-architecturalwallcoverings.webm b/clips/13-architecturalwallcoverings.webm
new file mode 100644
index 0000000..d42a874
Binary files /dev/null and b/clips/13-architecturalwallcoverings.webm differ
diff --git a/clips/14-bestwallpaperplace.webm b/clips/14-bestwallpaperplace.webm
new file mode 100644
index 0000000..a78fe05
Binary files /dev/null and b/clips/14-bestwallpaperplace.webm differ
diff --git a/clips/15-blockprintedwallpaper.webm b/clips/15-blockprintedwallpaper.webm
new file mode 100644
index 0000000..e9f780f
Binary files /dev/null and b/clips/15-blockprintedwallpaper.webm differ
diff --git a/clips/16-carmelwallpapers.webm b/clips/16-carmelwallpapers.webm
new file mode 100644
index 0000000..142edf7
Binary files /dev/null and b/clips/16-carmelwallpapers.webm differ
diff --git a/clips/17-chinoiseriewallpaper.webm b/clips/17-chinoiseriewallpaper.webm
new file mode 100644
index 0000000..0103fac
Binary files /dev/null and b/clips/17-chinoiseriewallpaper.webm differ
diff --git a/clips/18-contractwallpaper.webm b/clips/18-contractwallpaper.webm
new file mode 100644
index 0000000..f5cbb34
Binary files /dev/null and b/clips/18-contractwallpaper.webm differ
diff --git a/clips/19-corkwallcovering.webm b/clips/19-corkwallcovering.webm
new file mode 100644
index 0000000..c9d6c76
Binary files /dev/null and b/clips/19-corkwallcovering.webm differ
diff --git a/clips/20-designerwallcoverings.webm b/clips/20-designerwallcoverings.webm
new file mode 100644
index 0000000..7431d6c
Binary files /dev/null and b/clips/20-designerwallcoverings.webm differ
diff --git a/clips/21-embroideredwallpaper.webm b/clips/21-embroideredwallpaper.webm
new file mode 100644
index 0000000..3e341e4
Binary files /dev/null and b/clips/21-embroideredwallpaper.webm differ
diff --git a/clips/22-fabricwallpaper.webm b/clips/22-fabricwallpaper.webm
new file mode 100644
index 0000000..2e66210
Binary files /dev/null and b/clips/22-fabricwallpaper.webm differ
diff --git a/clips/23-flockedwallpaper.webm b/clips/23-flockedwallpaper.webm
new file mode 100644
index 0000000..49cdf76
Binary files /dev/null and b/clips/23-flockedwallpaper.webm differ
diff --git a/clips/24-glassbeadedwallpaper.webm b/clips/24-glassbeadedwallpaper.webm
new file mode 100644
index 0000000..00ca096
Binary files /dev/null and b/clips/24-glassbeadedwallpaper.webm differ
diff --git a/clips/25-glitterwalls.webm b/clips/25-glitterwalls.webm
new file mode 100644
index 0000000..bdfedc2
Binary files /dev/null and b/clips/25-glitterwalls.webm differ
diff --git a/clips/26-goldleafwallpaper.webm b/clips/26-goldleafwallpaper.webm
new file mode 100644
index 0000000..7d939af
Binary files /dev/null and b/clips/26-goldleafwallpaper.webm differ
diff --git a/clips/27-grassclothwallpaper.webm b/clips/27-grassclothwallpaper.webm
new file mode 100644
index 0000000..dbccb59
Binary files /dev/null and b/clips/27-grassclothwallpaper.webm differ
diff --git a/clips/28-greenwallcoverings.webm b/clips/28-greenwallcoverings.webm
new file mode 100644
index 0000000..30d3d48
Binary files /dev/null and b/clips/28-greenwallcoverings.webm differ
diff --git a/clips/29-handcraftedwallpaper.webm b/clips/29-handcraftedwallpaper.webm
new file mode 100644
index 0000000..1e52dd7
Binary files /dev/null and b/clips/29-handcraftedwallpaper.webm differ
diff --git a/clips/30-healthcarewallpaper.webm b/clips/30-healthcarewallpaper.webm
new file mode 100644
index 0000000..be4a355
Binary files /dev/null and b/clips/30-healthcarewallpaper.webm differ
diff --git a/clips/31-hospitalitywallcoverings.webm b/clips/31-hospitalitywallcoverings.webm
new file mode 100644
index 0000000..f1bc896
Binary files /dev/null and b/clips/31-hospitalitywallcoverings.webm differ
diff --git a/clips/32-hospitalitywallpaper.webm b/clips/32-hospitalitywallpaper.webm
new file mode 100644
index 0000000..3efed08
Binary files /dev/null and b/clips/32-hospitalitywallpaper.webm differ
diff --git a/clips/33-hotelwallcoverings.webm b/clips/33-hotelwallcoverings.webm
new file mode 100644
index 0000000..aebc28f
Binary files /dev/null and b/clips/33-hotelwallcoverings.webm differ
diff --git a/clips/34-jutewallpaper.webm b/clips/34-jutewallpaper.webm
new file mode 100644
index 0000000..36a97aa
Binary files /dev/null and b/clips/34-jutewallpaper.webm differ
diff --git a/clips/35-linenwallpaper.webm b/clips/35-linenwallpaper.webm
new file mode 100644
index 0000000..19b2256
Binary files /dev/null and b/clips/35-linenwallpaper.webm differ
diff --git a/clips/36-madagascarwallpaper.webm b/clips/36-madagascarwallpaper.webm
new file mode 100644
index 0000000..301e740
Binary files /dev/null and b/clips/36-madagascarwallpaper.webm differ
diff --git a/clips/37-metallicwallpaper.webm b/clips/37-metallicwallpaper.webm
new file mode 100644
index 0000000..a36d0ab
Binary files /dev/null and b/clips/37-metallicwallpaper.webm differ
diff --git a/clips/38-micawallpaper.webm b/clips/38-micawallpaper.webm
new file mode 100644
index 0000000..da78bc2
Binary files /dev/null and b/clips/38-micawallpaper.webm differ
diff --git a/clips/39-museumwallpaper.webm b/clips/39-museumwallpaper.webm
new file mode 100644
index 0000000..b98642e
Binary files /dev/null and b/clips/39-museumwallpaper.webm differ
diff --git a/clips/40-mylarwallpaper.webm b/clips/40-mylarwallpaper.webm
new file mode 100644
index 0000000..c874aca
Binary files /dev/null and b/clips/40-mylarwallpaper.webm differ
diff --git a/clips/41-naturaltextilewallpaper.webm b/clips/41-naturaltextilewallpaper.webm
new file mode 100644
index 0000000..35b8815
Binary files /dev/null and b/clips/41-naturaltextilewallpaper.webm differ
diff --git a/clips/42-naturalwallcoverings.webm b/clips/42-naturalwallcoverings.webm
new file mode 100644
index 0000000..592fd74
Binary files /dev/null and b/clips/42-naturalwallcoverings.webm differ
diff --git a/clips/43-pastelwallpaper.webm b/clips/43-pastelwallpaper.webm
new file mode 100644
index 0000000..1585b2c
Binary files /dev/null and b/clips/43-pastelwallpaper.webm differ
diff --git a/clips/44-raffiawallcoverings.webm b/clips/44-raffiawallcoverings.webm
new file mode 100644
index 0000000..3634d30
Binary files /dev/null and b/clips/44-raffiawallcoverings.webm differ
diff --git a/clips/45-raffiawalls.webm b/clips/45-raffiawalls.webm
new file mode 100644
index 0000000..139fab9
Binary files /dev/null and b/clips/45-raffiawalls.webm differ
diff --git a/clips/46-recycledwallpaper.webm b/clips/46-recycledwallpaper.webm
new file mode 100644
index 0000000..1ea3ad9
Binary files /dev/null and b/clips/46-recycledwallpaper.webm differ
diff --git a/clips/47-restaurantwallpaper.webm b/clips/47-restaurantwallpaper.webm
new file mode 100644
index 0000000..5646908
Binary files /dev/null and b/clips/47-restaurantwallpaper.webm differ
diff --git a/clips/48-restorationwallpaper.webm b/clips/48-restorationwallpaper.webm
new file mode 100644
index 0000000..0f0ee01
Binary files /dev/null and b/clips/48-restorationwallpaper.webm differ
diff --git a/clips/49-retrowalls.webm b/clips/49-retrowalls.webm
new file mode 100644
index 0000000..d9e9031
Binary files /dev/null and b/clips/49-retrowalls.webm differ
diff --git a/clips/50-saloonwallpaper.webm b/clips/50-saloonwallpaper.webm
new file mode 100644
index 0000000..a98a9bd
Binary files /dev/null and b/clips/50-saloonwallpaper.webm differ
diff --git a/clips/51-screenprintedwallpaper.webm b/clips/51-screenprintedwallpaper.webm
new file mode 100644
index 0000000..f418e4a
Binary files /dev/null and b/clips/51-screenprintedwallpaper.webm differ
diff --git a/clips/52-selfadhesivewallpaper.webm b/clips/52-selfadhesivewallpaper.webm
new file mode 100644
index 0000000..f66b559
Binary files /dev/null and b/clips/52-selfadhesivewallpaper.webm differ
diff --git a/clips/53-silkwallcoverings.webm b/clips/53-silkwallcoverings.webm
new file mode 100644
index 0000000..2151e01
Binary files /dev/null and b/clips/53-silkwallcoverings.webm differ
diff --git a/clips/54-silkwallpaper.webm b/clips/54-silkwallpaper.webm
new file mode 100644
index 0000000..4a5fd4a
Binary files /dev/null and b/clips/54-silkwallpaper.webm differ
diff --git a/clips/55-silverleafwallpaper.webm b/clips/55-silverleafwallpaper.webm
new file mode 100644
index 0000000..76f243f
Binary files /dev/null and b/clips/55-silverleafwallpaper.webm differ
diff --git a/clips/56-stringwallpaper.webm b/clips/56-stringwallpaper.webm
new file mode 100644
index 0000000..8da6e51
Binary files /dev/null and b/clips/56-stringwallpaper.webm differ
diff --git a/clips/57-suedewallpaper.webm b/clips/57-suedewallpaper.webm
new file mode 100644
index 0000000..fbf153a
Binary files /dev/null and b/clips/57-suedewallpaper.webm differ
diff --git a/clips/58-textilewallpaper.webm b/clips/58-textilewallpaper.webm
new file mode 100644
index 0000000..35aa45c
Binary files /dev/null and b/clips/58-textilewallpaper.webm differ
diff --git a/clips/59-vinylwallpaper.webm b/clips/59-vinylwallpaper.webm
new file mode 100644
index 0000000..f085498
Binary files /dev/null and b/clips/59-vinylwallpaper.webm differ
diff --git a/clips/60-wallpapercanada.webm b/clips/60-wallpapercanada.webm
new file mode 100644
index 0000000..75fdc51
Binary files /dev/null and b/clips/60-wallpapercanada.webm differ
diff --git a/clips/61-wallpapercontractor.webm b/clips/61-wallpapercontractor.webm
new file mode 100644
index 0000000..8c49dd0
Binary files /dev/null and b/clips/61-wallpapercontractor.webm differ
diff --git a/clips/62-wallpaperdefinitions.webm b/clips/62-wallpaperdefinitions.webm
new file mode 100644
index 0000000..94a5a3d
Binary files /dev/null and b/clips/62-wallpaperdefinitions.webm differ
diff --git a/clips/63-wallpaperdictionary.webm b/clips/63-wallpaperdictionary.webm
new file mode 100644
index 0000000..d841123
Binary files /dev/null and b/clips/63-wallpaperdictionary.webm differ
diff --git a/clips/64-wallpaperdistributors.webm b/clips/64-wallpaperdistributors.webm
new file mode 100644
index 0000000..775560b
Binary files /dev/null and b/clips/64-wallpaperdistributors.webm differ
diff --git a/clips/65-wallpaperestimator.webm b/clips/65-wallpaperestimator.webm
new file mode 100644
index 0000000..37baff0
Binary files /dev/null and b/clips/65-wallpaperestimator.webm differ
diff --git a/clips/66-wallpaperglossary.webm b/clips/66-wallpaperglossary.webm
new file mode 100644
index 0000000..b99bdbd
Binary files /dev/null and b/clips/66-wallpaperglossary.webm differ
diff --git a/clips/67-wallpaperhistory.webm b/clips/67-wallpaperhistory.webm
new file mode 100644
index 0000000..0df33c9
Binary files /dev/null and b/clips/67-wallpaperhistory.webm differ
diff --git a/clips/68-wallpapersback.webm b/clips/68-wallpapersback.webm
new file mode 100644
index 0000000..527b7d2
Binary files /dev/null and b/clips/68-wallpapersback.webm differ
diff --git a/clips/69-wallpaperwednesday.webm b/clips/69-wallpaperwednesday.webm
new file mode 100644
index 0000000..7689ed9
Binary files /dev/null and b/clips/69-wallpaperwednesday.webm differ
diff --git a/clips/70-wallsandfabrics.webm b/clips/70-wallsandfabrics.webm
new file mode 100644
index 0000000..3cdbdb3
Binary files /dev/null and b/clips/70-wallsandfabrics.webm differ
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..26d6ab2
--- /dev/null
+++ b/index.html
@@ -0,0 +1,902 @@
+<!doctype html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title>DW Wallpaper Fleet — 73 sites</title>
+<style>
+ :root{--bg:#0a0a0c;--panel:#15151a;--rule:#26262e;--ink:#eee6d8;--mute:#8c8680;--gold:#c9a96a;--ok:#7ad27a;--bad:#e26d6d;}
+ *{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.5 -apple-system,'SF Pro Text',sans-serif}
+ header{padding:24px 32px;border-bottom:1px solid var(--rule);background:var(--panel);display:flex;justify-content:space-between;align-items:baseline}
+ h1{margin:0;font:300 32px/1 'SF Pro Display',serif;letter-spacing:.04em}
+ h1 b{color:var(--gold);font-weight:500}
+ .stats{font-family:'SF Mono',monospace;font-size:12px;color:var(--mute);display:flex;gap:18px}
+ .stats b{color:var(--ink);font-weight:600;font-size:15px;letter-spacing:0}
+ .cat-tabs{padding:14px 32px;display:flex;gap:8px;border-bottom:1px solid var(--rule);background:var(--panel);overflow-x:auto;align-items:center;flex-wrap:wrap}
+ .tab{background:transparent;border:1px solid var(--rule);color:var(--ink);padding:6px 12px;font:600 11px 'SF Pro Text';text-transform:uppercase;letter-spacing:.1em;border-radius:3px;cursor:pointer}
+ .tab.active{background:var(--gold);color:#000;border-color:var(--gold)}
+ .divider{width:1px;height:24px;background:var(--rule);margin:0 6px}
+ .range{display:flex;align-items:center;gap:10px;font:600 11px 'SF Pro Text';text-transform:uppercase;letter-spacing:.1em;color:var(--mute)}
+ .range input[type="number"]{width:50px;background:#0e0e10;border:1px solid var(--rule);color:var(--ink);padding:5px 6px;border-radius:3px;font:600 12px 'SF Mono',monospace;text-align:center}
+ .range input[type="number"]:focus{outline:none;border-color:var(--gold)}
+ .range .dash{color:var(--mute)}
+ .range-presets{display:flex;gap:4px}
+ .range-presets button{background:transparent;border:1px solid var(--rule);color:var(--mute);padding:5px 9px;font:600 10px 'SF Mono',monospace;border-radius:2px;cursor:pointer;text-transform:uppercase;letter-spacing:.06em}
+ .range-presets button:hover{color:var(--gold);border-color:var(--gold)}
+ .range-presets button.active{background:var(--gold);color:#000;border-color:var(--gold)}
+ .grid{padding:24px 32px;display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:14px}
+ .card{background:var(--panel);border:1px solid var(--rule);border-radius:6px;overflow:hidden;text-decoration:none;color:inherit;transition:border-color .15s,transform .15s}
+ .card:hover{border-color:var(--gold);transform:translateY(-2px)}
+ /* Uniform chip size — carmelwallpaper canonical (feedback_chip_image_uniform_size). */
+ .thumb{aspect-ratio:1/1.15;background:#0e0e10;display:flex;align-items:center;justify-content:center;overflow:hidden;position:relative}
+ .thumb img{width:100%;height:100%;object-fit:cover}
+
+ /* Logo UL + hamburger UR — applied to every DW page per feedback_logo_ul_hamburger_ur_all_tabs. */
+ .dw-chrome-logo{position:fixed;top:18px;left:22px;z-index:1000;font:600 14px/1 'SF Pro Text',sans-serif;letter-spacing:.18em;text-transform:uppercase;color:var(--gold);text-decoration:none;background:rgba(10,10,12,.7);padding:8px 12px;border:1px solid var(--rule);border-radius:3px;backdrop-filter:blur(6px)}
+ .dw-chrome-logo b{color:var(--ink);font-weight:300;margin-left:6px;letter-spacing:.04em}
+ .dw-burger{position:fixed;top:18px;right:22px;z-index:1000;width:44px;height:44px;border-radius:50%;border:1px solid var(--rule);background:rgba(10,10,12,.7);backdrop-filter:blur(6px);cursor:pointer;display:flex;flex-direction:column;justify-content:center;align-items:center;gap:5px;padding:0}
+ .dw-burger:hover{border-color:var(--gold)}
+ .dw-burger span{display:block;width:18px;height:2px;background:var(--ink);transition:transform .2s,opacity .2s}
+ .dw-burger.open span:nth-child(1){transform:translateY(7px) rotate(45deg)}
+ .dw-burger.open span:nth-child(2){opacity:0}
+ .dw-burger.open span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
+ .dw-burger-panel{position:fixed;top:74px;right:22px;z-index:999;background:var(--panel);border:1px solid var(--rule);border-radius:6px;padding:14px;min-width:220px;display:none;backdrop-filter:blur(8px)}
+ .dw-burger-panel.open{display:block}
+ .dw-burger-panel a{display:block;padding:8px 12px;color:var(--ink);text-decoration:none;border-radius:3px;font-size:13px}
+ .dw-burger-panel a:hover{background:var(--rule);color:var(--gold)}
+
+ /* Sort + density above the grid (sort-skill canonical). */
+ .grid-controls{padding:10px 32px 0;display:flex;gap:14px;align-items:center;flex-wrap:wrap;font:600 11px 'SF Pro Text';text-transform:uppercase;letter-spacing:.1em;color:var(--mute)}
+ .grid-controls select,.grid-controls input[type=range]{background:#0e0e10;border:1px solid var(--rule);color:var(--ink);padding:6px 8px;border-radius:3px;font:600 12px 'SF Mono',monospace}
+ .grid-controls input[type=range]{padding:0;width:140px}
+ .grid-controls label{display:flex;align-items:center;gap:8px}
+ .thumb .placeholder{color:var(--mute);font:300 32px 'SF Pro Display',serif}
+ .meta{padding:12px 14px}
+ .name{font:500 14px 'SF Pro Display';letter-spacing:.01em;margin:0 0 4px;display:flex;justify-content:space-between;align-items:center;gap:6px}
+ .sub{font:400 11px 'SF Mono',monospace;color:var(--mute);letter-spacing:.02em;display:flex;justify-content:space-between}
+ .pill{font:600 9px/1 'SF Mono',monospace;text-transform:uppercase;letter-spacing:.08em;padding:3px 5px;border-radius:2px;flex-shrink:0}
+ .pill.ok{background:rgba(122,210,122,.12);color:var(--ok);border:1px solid rgba(122,210,122,.3)}
+ .pill.bad{background:rgba(226,109,109,.12);color:var(--bad);border:1px solid rgba(226,109,109,.3)}
+ .footer{padding:18px 32px;color:var(--mute);font-size:11px;text-align:center;border-top:1px solid var(--rule);font-family:'SF Mono',monospace}
+ .hidden{display:none}
+</style>
+</head>
+<body>
+<!-- DW chrome — logo UL + hamburger UR per standing rule (every tab). -->
+<a class="dw-chrome-logo" href="#">DW <b>FLEET</b></a>
+<button class="dw-burger" id="dwBurger" aria-label="Menu" aria-expanded="false">
+ <span></span><span></span><span></span>
+</button>
+<nav class="dw-burger-panel" id="dwBurgerPanel" aria-label="Site sections">
+ <a href="#" data-jump="cat-all">All sites</a>
+ <a href="#" data-jump="cat-commercial">Commercial</a>
+ <a href="#" data-jump="cat-editorial">Editorial</a>
+ <a href="#" data-jump="cat-era">Era</a>
+ <a href="#" data-jump="cat-finish">Finish</a>
+ <a href="#" data-jump="cat-material">Material</a>
+ <a href="#" data-jump="cat-other">Other</a>
+ <a href="#" data-jump="cat-place">Place</a>
+</nav>
+
+<header style="padding-left:88px;padding-right:88px">
+ <h1>DW <b>WALLPAPER FLEET</b></h1>
+ <div class="stats">
+ <span><b>73</b> sites</span>
+ <span><b>68</b> live public</span>
+ <span><b>52</b> pm2-managed</span>
+ </div>
+</header>
+
+<div class="grid-controls">
+ <label>Sort
+ <select id="sortBy">
+ <option value="default">Default (category, name)</option>
+ <option value="name-asc">Name A→Z</option>
+ <option value="name-desc">Name Z→A</option>
+ <option value="live-first">Live first</option>
+ <option value="offline-first">Offline first</option>
+ <option value="category">Category</option>
+ </select>
+ </label>
+ <label>Density
+ <input type="range" id="density" min="200" max="380" step="10" value="260">
+ <span id="densityVal" style="font:600 12px 'SF Mono',monospace;color:var(--gold)">260px</span>
+ </label>
+</div>
+<div class="cat-tabs">
+ <button class="tab active" data-cat="all">All (73)</button>
+ <button class="tab" data-cat="commercial">commercial (8)</button><button class="tab" data-cat="editorial">editorial (10)</button><button class="tab" data-cat="era">era (10)</button><button class="tab" data-cat="finish">finish (8)</button><button class="tab" data-cat="material">material (20)</button><button class="tab" data-cat="other">other (14)</button><button class="tab" data-cat="place">place (3)</button>
+ <div class="divider"></div>
+ <div class="range">
+ <span>Range</span>
+ <input type="number" id="rStart" min="1" max="73" value="1">
+ <span class="dash">–</span>
+ <input type="number" id="rEnd" min="1" max="73" value="73">
+ <div class="range-presets">
+ <button data-r="1-10">1–10</button>
+ <button data-r="1-25">1–25</button>
+ <button data-r="1-50">1–50</button>
+ <button data-r="1-73" class="active">All</button>
+ </div>
+ <span id="rCount" style="color:var(--gold);font-family:'SF Mono',monospace">73 shown</span>
+ </div>
+</div>
+<div class="grid" id="grid">
+ <a class="card" data-cat="commercial" href="https://architecturalwallcoverings.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/architecturalwallcoverings.jpg" loading="lazy" alt="architecturalwallcoverings">
+ </div>
+ <div class="meta">
+ <div class="name">architecturalwallcoverings <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>architecturalwallcoverings.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="commercial" href="https://contractwallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/contractwallpaper.jpg" loading="lazy" alt="contractwallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">contractwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>contractwallpaper.com</span><span>:9849</span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="commercial" href="https://healthcarewallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/healthcarewallpaper.jpg" loading="lazy" alt="healthcarewallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">healthcarewallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>healthcarewallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="commercial" href="https://hospitalitywallcoverings.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">H</span>
+ </div>
+ <div class="meta">
+ <div class="name">hospitalitywallcoverings <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>hospitalitywallcoverings.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="commercial" href="https://hospitalitywallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">H</span>
+ </div>
+ <div class="meta">
+ <div class="name">hospitalitywallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>hospitalitywallpaper.com</span><span>:9855</span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="commercial" href="https://hotelwallcoverings.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">H</span>
+ </div>
+ <div class="meta">
+ <div class="name">hotelwallcoverings <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>hotelwallcoverings.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="commercial" href="https://restaurantwallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">R</span>
+ </div>
+ <div class="meta">
+ <div class="name">restaurantwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>restaurantwallpaper.com</span><span>:9854</span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="commercial" href="https://wallpapercontractor.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">W</span>
+ </div>
+ <div class="meta">
+ <div class="name">wallpapercontractor <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>wallpapercontractor.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="editorial" href="https://bestwallpaperplace.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/bestwallpaperplace.jpg" loading="lazy" alt="bestwallpaperplace">
+ </div>
+ <div class="meta">
+ <div class="name">bestwallpaperplace <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>bestwallpaperplace.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="editorial" href="https://wallpapercanada.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">W</span>
+ </div>
+ <div class="meta">
+ <div class="name">wallpapercanada <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>wallpapercanada.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="editorial" href="https://wallpaperdefinitions.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">W</span>
+ </div>
+ <div class="meta">
+ <div class="name">wallpaperdefinitions <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>wallpaperdefinitions.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="editorial" href="https://wallpaperdictionary.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">W</span>
+ </div>
+ <div class="meta">
+ <div class="name">wallpaperdictionary <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>wallpaperdictionary.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="editorial" href="https://wallpaperdistributors.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">W</span>
+ </div>
+ <div class="meta">
+ <div class="name">wallpaperdistributors <span class="pill bad">DOWN</span></div>
+ <div class="sub"><span>wallpaperdistributors.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="editorial" href="https://wallpaperestimator.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">W</span>
+ </div>
+ <div class="meta">
+ <div class="name">wallpaperestimator <span class="pill bad">DOWN</span></div>
+ <div class="sub"><span>wallpaperestimator.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="editorial" href="https://wallpaperglossary.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">W</span>
+ </div>
+ <div class="meta">
+ <div class="name">wallpaperglossary <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>wallpaperglossary.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="editorial" href="https://wallpaperhistory.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">W</span>
+ </div>
+ <div class="meta">
+ <div class="name">wallpaperhistory <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>wallpaperhistory.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="editorial" href="https://wallpapersback.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">W</span>
+ </div>
+ <div class="meta">
+ <div class="name">wallpapersback <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>wallpapersback.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="editorial" href="https://wallpaperwednesday.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">W</span>
+ </div>
+ <div class="meta">
+ <div class="name">wallpaperwednesday <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>wallpaperwednesday.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="era" href="https://1800swallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/1800swallpaper.jpg" loading="lazy" alt="1800swallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">1800swallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>1800swallpaper.com</span><span>:9856</span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="era" href="https://1890swallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/1890swallpaper.jpg" loading="lazy" alt="1890swallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">1890swallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>1890swallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="era" href="https://1900swallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/1900swallpaper.jpg" loading="lazy" alt="1900swallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">1900swallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>1900swallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="era" href="https://1920swallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/1920swallpaper.jpg" loading="lazy" alt="1920swallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">1920swallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>1920swallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="era" href="https://1930swallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/1930swallpaper.jpg" loading="lazy" alt="1930swallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">1930swallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>1930swallpaper.com</span><span>:9837</span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="era" href="https://1940swallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/1940swallpaper.jpg" loading="lazy" alt="1940swallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">1940swallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>1940swallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="era" href="https://1950swallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/1950swallpaper.jpg" loading="lazy" alt="1950swallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">1950swallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>1950swallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="era" href="https://1960swallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/1960swallpaper.jpg" loading="lazy" alt="1960swallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">1960swallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>1960swallpaper.com</span><span>:9839</span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="era" href="https://1970swallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/1970swallpaper.jpg" loading="lazy" alt="1970swallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">1970swallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>1970swallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="era" href="https://1980swallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/1980swallpaper.jpg" loading="lazy" alt="1980swallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">1980swallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>1980swallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="finish" href="https://agedwallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/agedwallpaper.jpg" loading="lazy" alt="agedwallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">agedwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>agedwallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="finish" href="https://embroideredwallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/embroideredwallpaper.jpg" loading="lazy" alt="embroideredwallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">embroideredwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>embroideredwallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="finish" href="https://flockedwallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/flockedwallpaper.jpg" loading="lazy" alt="flockedwallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">flockedwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>flockedwallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="finish" href="https://glassbeadedwallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/glassbeadedwallpaper.jpg" loading="lazy" alt="glassbeadedwallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">glassbeadedwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>glassbeadedwallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="finish" href="https://glitterwalls.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/glitterwalls.jpg" loading="lazy" alt="glitterwalls">
+ </div>
+ <div class="meta">
+ <div class="name">glitterwalls <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>glitterwalls.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="finish" href="https://mylarwallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">M</span>
+ </div>
+ <div class="meta">
+ <div class="name">mylarwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>mylarwallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="finish" href="https://pastelwallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">P</span>
+ </div>
+ <div class="meta">
+ <div class="name">pastelwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>pastelwallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="finish" href="https://restorationwallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">R</span>
+ </div>
+ <div class="meta">
+ <div class="name">restorationwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>restorationwallpaper.com</span><span>:9903</span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="material" href="https://corkwallcovering.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/corkwallcovering.jpg" loading="lazy" alt="corkwallcovering">
+ </div>
+ <div class="meta">
+ <div class="name">corkwallcovering <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>corkwallcovering.com</span><span>:9848</span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="material" href="https://fabricwallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/fabricwallpaper.jpg" loading="lazy" alt="fabricwallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">fabricwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>fabricwallpaper.com</span><span>:9846</span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="material" href="https://goldleafwallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/goldleafwallpaper.jpg" loading="lazy" alt="goldleafwallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">goldleafwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>goldleafwallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="material" href="https://grassclothwallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">G</span>
+ </div>
+ <div class="meta">
+ <div class="name">grassclothwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>grassclothwallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="material" href="https://jutewallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">J</span>
+ </div>
+ <div class="meta">
+ <div class="name">jutewallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>jutewallpaper.com</span><span>:9911</span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="material" href="https://linenwallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">L</span>
+ </div>
+ <div class="meta">
+ <div class="name">linenwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>linenwallpaper.com</span><span>:9842</span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="material" href="https://metallicwallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">M</span>
+ </div>
+ <div class="meta">
+ <div class="name">metallicwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>metallicwallpaper.com</span><span>:9847</span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="material" href="https://micawallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">M</span>
+ </div>
+ <div class="meta">
+ <div class="name">micawallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>micawallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="material" href="https://naturaltextilewallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">N</span>
+ </div>
+ <div class="meta">
+ <div class="name">naturaltextilewallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>naturaltextilewallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="material" href="https://naturalwallcoverings.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">N</span>
+ </div>
+ <div class="meta">
+ <div class="name">naturalwallcoverings <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>naturalwallcoverings.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="material" href="https://raffiawallcoverings.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">R</span>
+ </div>
+ <div class="meta">
+ <div class="name">raffiawallcoverings <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>raffiawallcoverings.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="material" href="https://raffiawalls.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">R</span>
+ </div>
+ <div class="meta">
+ <div class="name">raffiawalls <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>raffiawalls.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="material" href="https://recycledwallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">R</span>
+ </div>
+ <div class="meta">
+ <div class="name">recycledwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>recycledwallpaper.com</span><span>:9902</span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="material" href="https://silkwallcoverings.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">S</span>
+ </div>
+ <div class="meta">
+ <div class="name">silkwallcoverings <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>silkwallcoverings.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="material" href="https://silkwallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">S</span>
+ </div>
+ <div class="meta">
+ <div class="name">silkwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>silkwallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="material" href="https://silverleafwallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">S</span>
+ </div>
+ <div class="meta">
+ <div class="name">silverleafwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>silverleafwallpaper.com</span><span>:9912</span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="material" href="https://stringwallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">S</span>
+ </div>
+ <div class="meta">
+ <div class="name">stringwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>stringwallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="material" href="https://suedewallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">S</span>
+ </div>
+ <div class="meta">
+ <div class="name">suedewallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>suedewallpaper.com</span><span>:9907</span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="material" href="https://textilewallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">T</span>
+ </div>
+ <div class="meta">
+ <div class="name">textilewallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>textilewallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="material" href="https://wallsandfabrics.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">W</span>
+ </div>
+ <div class="meta">
+ <div class="name">wallsandfabrics <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>wallsandfabrics.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="other" href="https://apartmentwallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/apartmentwallpaper.jpg" loading="lazy" alt="apartmentwallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">apartmentwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>apartmentwallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="other" href="https://blockprintedwallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/blockprintedwallpaper.jpg" loading="lazy" alt="blockprintedwallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">blockprintedwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>blockprintedwallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="other" href="https://chinoiseriewallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/chinoiseriewallpaper.jpg" loading="lazy" alt="chinoiseriewallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">chinoiseriewallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>chinoiseriewallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="other" href="https://Designer-Wallcoverings.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">D</span>
+ </div>
+ <div class="meta">
+ <div class="name">Designer-Wallcoverings <span class="pill bad">DOWN</span></div>
+ <div class="sub"><span>Designer-Wallcoverings.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="other" href="https://designerwallcoverings.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/designerwallcoverings.jpg" loading="lazy" alt="designerwallcoverings">
+ </div>
+ <div class="meta">
+ <div class="name">designerwallcoverings <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>designerwallcoverings.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="other" href="https://greenwallcoverings.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/greenwallcoverings.jpg" loading="lazy" alt="greenwallcoverings">
+ </div>
+ <div class="meta">
+ <div class="name">greenwallcoverings <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>greenwallcoverings.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="other" href="https://handcraftedwallpaper.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/handcraftedwallpaper.jpg" loading="lazy" alt="handcraftedwallpaper">
+ </div>
+ <div class="meta">
+ <div class="name">handcraftedwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>handcraftedwallpaper.com</span><span>:9897</span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="other" href="https://museumwallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">M</span>
+ </div>
+ <div class="meta">
+ <div class="name">museumwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>museumwallpaper.com</span><span>:9899</span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="other" href="https://retrowalls.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">R</span>
+ </div>
+ <div class="meta">
+ <div class="name">retrowalls <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>retrowalls.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="other" href="https://saloonwallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">S</span>
+ </div>
+ <div class="meta">
+ <div class="name">saloonwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>saloonwallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="other" href="https://screenprintedwallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">S</span>
+ </div>
+ <div class="meta">
+ <div class="name">screenprintedwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>screenprintedwallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="other" href="https://selfadhesivewallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">S</span>
+ </div>
+ <div class="meta">
+ <div class="name">selfadhesivewallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>selfadhesivewallpaper.com</span><span>:9905</span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="other" href="https://vinylwallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">V</span>
+ </div>
+ <div class="meta">
+ <div class="name">vinylwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>vinylwallpaper.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="other" href="https://wallpaper-tour.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">W</span>
+ </div>
+ <div class="meta">
+ <div class="name">wallpaper-tour <span class="pill bad">DOWN</span></div>
+ <div class="sub"><span>wallpaper-tour.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="place" href="https://carmelwallpapers.com" target="_blank">
+ <div class="thumb">
+ <img src="thumbs/carmelwallpapers.jpg" loading="lazy" alt="carmelwallpapers">
+ </div>
+ <div class="meta">
+ <div class="name">carmelwallpapers <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>carmelwallpapers.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="place" href="https://hollywood-wallcoverings.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">H</span>
+ </div>
+ <div class="meta">
+ <div class="name">hollywood-wallcoverings <span class="pill bad">DOWN</span></div>
+ <div class="sub"><span>hollywood-wallcoverings.com</span><span></span></div>
+ </div>
+ </a>
+ <a class="card" data-cat="place" href="https://madagascarwallpaper.com" target="_blank">
+ <div class="thumb">
+ <span class="placeholder">M</span>
+ </div>
+ <div class="meta">
+ <div class="name">madagascarwallpaper <span class="pill ok">LIVE</span></div>
+ <div class="sub"><span>madagascarwallpaper.com</span><span></span></div>
+ </div>
+ </a>
+</div>
+<div class="footer">~/Projects/wallpaper-tour/index.html · 2026-05-11T06:07:14.885Z · sorted by commercial · editorial · era · finish · material · other · place</div>
+<script>
+ const TOTAL = 73;
+ const state = { cat: 'all', start: 1, end: TOTAL };
+
+ function applyFilters() {
+ const cards = [...document.querySelectorAll('.card')];
+ // First, get cards matching category
+ const matchingCat = cards.filter(c => state.cat === 'all' || c.dataset.cat === state.cat);
+ // Then, slice by range (1-indexed inclusive)
+ const visible = new Set(matchingCat.slice(state.start - 1, state.end));
+ for (const c of cards) c.classList.toggle('hidden', !visible.has(c));
+ document.getElementById('rCount').textContent = visible.size + ' shown';
+ }
+
+ for (const t of document.querySelectorAll('.tab')) {
+ t.onclick = () => {
+ document.querySelectorAll('.tab').forEach(x => x.classList.toggle('active', x === t));
+ state.cat = t.dataset.cat;
+ applyFilters();
+ };
+ }
+
+ const rStart = document.getElementById('rStart');
+ const rEnd = document.getElementById('rEnd');
+ function readRange() {
+ state.start = Math.max(1, Math.min(TOTAL, parseInt(rStart.value) || 1));
+ state.end = Math.max(state.start, Math.min(TOTAL, parseInt(rEnd.value) || TOTAL));
+ rStart.value = state.start; rEnd.value = state.end;
+ // clear preset highlights when user types custom range
+ document.querySelectorAll('.range-presets button').forEach(b => {
+ b.classList.toggle('active', b.dataset.r === state.start + '-' + state.end);
+ });
+ applyFilters();
+ }
+ rStart.addEventListener('input', readRange);
+ rEnd.addEventListener('input', readRange);
+
+ for (const btn of document.querySelectorAll('.range-presets button')) {
+ btn.onclick = () => {
+ const [s, e] = btn.dataset.r.split('-').map(Number);
+ rStart.value = s; rEnd.value = e;
+ readRange();
+ };
+ }
+
+ // ─── Hamburger menu (chrome upper-right) ────────────────────────────
+ const burger = document.getElementById('dwBurger');
+ const panel = document.getElementById('dwBurgerPanel');
+ burger.addEventListener('click', () => {
+ const open = !burger.classList.contains('open');
+ burger.classList.toggle('open', open);
+ panel.classList.toggle('open', open);
+ burger.setAttribute('aria-expanded', open ? 'true' : 'false');
+ });
+ document.addEventListener('click', (e) => {
+ if (!burger.contains(e.target) && !panel.contains(e.target) && burger.classList.contains('open')) {
+ burger.classList.remove('open'); panel.classList.remove('open');
+ burger.setAttribute('aria-expanded', 'false');
+ }
+ });
+ panel.querySelectorAll('a[data-jump]').forEach(a => {
+ a.addEventListener('click', (e) => {
+ e.preventDefault();
+ const cat = a.dataset.jump.replace('cat-', '');
+ const tab = [...document.querySelectorAll('.tab')].find(t => t.dataset.cat === cat);
+ if (tab) tab.click();
+ burger.classList.remove('open'); panel.classList.remove('open');
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+ });
+ });
+
+ // ─── Sort + density (sort-skill canonical, localStorage-persisted) ──
+ const sortSel = document.getElementById('sortBy');
+ const dens = document.getElementById('density');
+ const densVal = document.getElementById('densityVal');
+ const grid = document.getElementById('grid');
+
+ try {
+ const ss = localStorage.getItem('wpTourSort'); if (ss) sortSel.value = ss;
+ const dd = localStorage.getItem('wpTourDensity'); if (dd) dens.value = dd;
+ } catch {}
+
+ function applyDensity() {
+ grid.style.gridTemplateColumns = `repeat(auto-fill, minmax(${dens.value}px, 1fr))`;
+ densVal.textContent = dens.value + 'px';
+ try { localStorage.setItem('wpTourDensity', dens.value); } catch {}
+ }
+ function applySort() {
+ const mode = sortSel.value;
+ const cards = [...grid.querySelectorAll('.card')];
+ if (mode === 'default') {
+ // Restore the server-rendered order. Each card has its original index
+ // captured at first call; sort by that.
+ cards.forEach((c, i) => { if (!c.dataset.origIdx) c.dataset.origIdx = String(i); });
+ cards.sort((a, b) => Number(a.dataset.origIdx) - Number(b.dataset.origIdx));
+ } else {
+ const nameOf = c => (c.querySelector('.name')?.textContent || '').trim().toLowerCase();
+ const liveOf = c => c.querySelector('.pill.ok') ? 1 : 0;
+ const catOf = c => c.dataset.cat || '';
+ const cmp = {
+ 'name-asc' : (a,b) => nameOf(a).localeCompare(nameOf(b)),
+ 'name-desc' : (a,b) => nameOf(b).localeCompare(nameOf(a)),
+ 'live-first' : (a,b) => liveOf(b) - liveOf(a) || nameOf(a).localeCompare(nameOf(b)),
+ 'offline-first': (a,b) => liveOf(a) - liveOf(b) || nameOf(a).localeCompare(nameOf(b)),
+ 'category' : (a,b) => catOf(a).localeCompare(catOf(b)) || nameOf(a).localeCompare(nameOf(b))
+ }[mode];
+ if (cmp) cards.sort(cmp);
+ }
+ cards.forEach(c => grid.appendChild(c)); // re-append in new order
+ try { localStorage.setItem('wpTourSort', mode); } catch {}
+ applyFilters(); // re-evaluate range after re-order
+ }
+
+ sortSel.addEventListener('change', applySort);
+ dens.addEventListener('input', applyDensity);
+ applyDensity();
+ applySort();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/inventory.json b/inventory.json
new file mode 100644
index 0000000..f9acdb7
--- /dev/null
+++ b/inventory.json
@@ -0,0 +1,834 @@
+[
+ {
+ "name": "architecturalwallcoverings",
+ "public_url": "https://architecturalwallcoverings.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 2441,
+ "thumb": "thumbs/architecturalwallcoverings.jpg",
+ "category": "commercial"
+ },
+ {
+ "name": "contractwallpaper",
+ "public_url": "https://contractwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": "9849",
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 452,
+ "thumb": "thumbs/contractwallpaper.jpg",
+ "category": "commercial"
+ },
+ {
+ "name": "healthcarewallpaper",
+ "public_url": "https://healthcarewallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 1712,
+ "thumb": "thumbs/healthcarewallpaper.jpg",
+ "category": "commercial"
+ },
+ {
+ "name": "hospitalitywallcoverings",
+ "public_url": "https://hospitalitywallcoverings.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 681,
+ "category": "commercial"
+ },
+ {
+ "name": "hospitalitywallpaper",
+ "public_url": "https://hospitalitywallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": "9855",
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 711,
+ "category": "commercial"
+ },
+ {
+ "name": "hotelwallcoverings",
+ "public_url": "https://hotelwallcoverings.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 1448,
+ "category": "commercial"
+ },
+ {
+ "name": "restaurantwallpaper",
+ "public_url": "https://restaurantwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": "9854",
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 2889,
+ "category": "commercial"
+ },
+ {
+ "name": "wallpapercontractor",
+ "public_url": "https://wallpapercontractor.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 4161,
+ "category": "commercial"
+ },
+ {
+ "name": "bestwallpaperplace",
+ "public_url": "https://bestwallpaperplace.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 3033,
+ "thumb": "thumbs/bestwallpaperplace.jpg",
+ "category": "editorial"
+ },
+ {
+ "name": "wallpapercanada",
+ "public_url": "https://wallpapercanada.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 2037,
+ "category": "editorial"
+ },
+ {
+ "name": "wallpaperdefinitions",
+ "public_url": "https://wallpaperdefinitions.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 3116,
+ "category": "editorial"
+ },
+ {
+ "name": "wallpaperdictionary",
+ "public_url": "https://wallpaperdictionary.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 4530,
+ "category": "editorial"
+ },
+ {
+ "name": "wallpaperdistributors",
+ "public_url": "https://wallpaperdistributors.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": false,
+ "public_status": 0,
+ "public_ms": 6318,
+ "category": "editorial"
+ },
+ {
+ "name": "wallpaperestimator",
+ "public_url": "https://wallpaperestimator.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": false,
+ "public_status": 0,
+ "public_ms": 6313,
+ "category": "editorial"
+ },
+ {
+ "name": "wallpaperglossary",
+ "public_url": "https://wallpaperglossary.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 6245,
+ "category": "editorial"
+ },
+ {
+ "name": "wallpaperhistory",
+ "public_url": "https://wallpaperhistory.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 4740,
+ "category": "editorial"
+ },
+ {
+ "name": "wallpapersback",
+ "public_url": "https://wallpapersback.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 4744,
+ "category": "editorial"
+ },
+ {
+ "name": "wallpaperwednesday",
+ "public_url": "https://wallpaperwednesday.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 3626,
+ "category": "editorial"
+ },
+ {
+ "name": "1800swallpaper",
+ "public_url": "https://1800swallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": "9856",
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 4248,
+ "thumb": "thumbs/1800swallpaper.jpg",
+ "category": "era"
+ },
+ {
+ "name": "1890swallpaper",
+ "public_url": "https://1890swallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 353,
+ "thumb": "thumbs/1890swallpaper.jpg",
+ "category": "era"
+ },
+ {
+ "name": "1900swallpaper",
+ "public_url": "https://1900swallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 496,
+ "thumb": "thumbs/1900swallpaper.jpg",
+ "category": "era"
+ },
+ {
+ "name": "1920swallpaper",
+ "public_url": "https://1920swallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 486,
+ "thumb": "thumbs/1920swallpaper.jpg",
+ "category": "era"
+ },
+ {
+ "name": "1930swallpaper",
+ "public_url": "https://1930swallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": "9837",
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 564,
+ "thumb": "thumbs/1930swallpaper.jpg",
+ "category": "era"
+ },
+ {
+ "name": "1940swallpaper",
+ "public_url": "https://1940swallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 572,
+ "thumb": "thumbs/1940swallpaper.jpg",
+ "category": "era"
+ },
+ {
+ "name": "1950swallpaper",
+ "public_url": "https://1950swallpaper.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 581,
+ "thumb": "thumbs/1950swallpaper.jpg",
+ "category": "era"
+ },
+ {
+ "name": "1960swallpaper",
+ "public_url": "https://1960swallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": "9839",
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 1201,
+ "thumb": "thumbs/1960swallpaper.jpg",
+ "category": "era"
+ },
+ {
+ "name": "1970swallpaper",
+ "public_url": "https://1970swallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 905,
+ "thumb": "thumbs/1970swallpaper.jpg",
+ "category": "era"
+ },
+ {
+ "name": "1980swallpaper",
+ "public_url": "https://1980swallpaper.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 937,
+ "thumb": "thumbs/1980swallpaper.jpg",
+ "category": "era"
+ },
+ {
+ "name": "agedwallpaper",
+ "public_url": "https://agedwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 2589,
+ "thumb": "thumbs/agedwallpaper.jpg",
+ "category": "finish"
+ },
+ {
+ "name": "embroideredwallpaper",
+ "public_url": "https://embroideredwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 301,
+ "public_ms": 322,
+ "thumb": "thumbs/embroideredwallpaper.jpg",
+ "category": "finish"
+ },
+ {
+ "name": "flockedwallpaper",
+ "public_url": "https://flockedwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 219,
+ "thumb": "thumbs/flockedwallpaper.jpg",
+ "category": "finish"
+ },
+ {
+ "name": "glassbeadedwallpaper",
+ "public_url": "https://glassbeadedwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 227,
+ "thumb": "thumbs/glassbeadedwallpaper.jpg",
+ "category": "finish"
+ },
+ {
+ "name": "glitterwalls",
+ "public_url": "https://glitterwalls.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 1685,
+ "thumb": "thumbs/glitterwalls.jpg",
+ "category": "finish"
+ },
+ {
+ "name": "mylarwallpaper",
+ "public_url": "https://mylarwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 3529,
+ "category": "finish"
+ },
+ {
+ "name": "pastelwallpaper",
+ "public_url": "https://pastelwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 3565,
+ "category": "finish"
+ },
+ {
+ "name": "restorationwallpaper",
+ "public_url": "https://restorationwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": "9903",
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 3006,
+ "category": "finish"
+ },
+ {
+ "name": "corkwallcovering",
+ "public_url": "https://corkwallcovering.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": "9848",
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 603,
+ "thumb": "thumbs/corkwallcovering.jpg",
+ "category": "material"
+ },
+ {
+ "name": "fabricwallpaper",
+ "public_url": "https://fabricwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": "9846",
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 311,
+ "thumb": "thumbs/fabricwallpaper.jpg",
+ "category": "material"
+ },
+ {
+ "name": "goldleafwallpaper",
+ "public_url": "https://goldleafwallpaper.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 1232,
+ "thumb": "thumbs/goldleafwallpaper.jpg",
+ "category": "material"
+ },
+ {
+ "name": "grassclothwallpaper",
+ "public_url": "https://grassclothwallpaper.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 1286,
+ "category": "material"
+ },
+ {
+ "name": "jutewallpaper",
+ "public_url": "https://jutewallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": "9911",
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 812,
+ "category": "material"
+ },
+ {
+ "name": "linenwallpaper",
+ "public_url": "https://linenwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": "9842",
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 583,
+ "category": "material"
+ },
+ {
+ "name": "metallicwallpaper",
+ "public_url": "https://metallicwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": "9847",
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 1107,
+ "category": "material"
+ },
+ {
+ "name": "micawallpaper",
+ "public_url": "https://micawallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 1064,
+ "category": "material"
+ },
+ {
+ "name": "naturaltextilewallpaper",
+ "public_url": "https://naturaltextilewallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 854,
+ "category": "material"
+ },
+ {
+ "name": "naturalwallcoverings",
+ "public_url": "https://naturalwallcoverings.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 3627,
+ "category": "material"
+ },
+ {
+ "name": "raffiawallcoverings",
+ "public_url": "https://raffiawallcoverings.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 3063,
+ "category": "material"
+ },
+ {
+ "name": "raffiawalls",
+ "public_url": "https://raffiawalls.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 2913,
+ "category": "material"
+ },
+ {
+ "name": "recycledwallpaper",
+ "public_url": "https://recycledwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": "9902",
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 5218,
+ "category": "material"
+ },
+ {
+ "name": "silkwallcoverings",
+ "public_url": "https://silkwallcoverings.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 371,
+ "category": "material"
+ },
+ {
+ "name": "silkwallpaper",
+ "public_url": "https://silkwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 397,
+ "category": "material"
+ },
+ {
+ "name": "silverleafwallpaper",
+ "public_url": "https://silverleafwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": "9912",
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 249,
+ "category": "material"
+ },
+ {
+ "name": "stringwallpaper",
+ "public_url": "https://stringwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 301,
+ "public_ms": 273,
+ "category": "material"
+ },
+ {
+ "name": "suedewallpaper",
+ "public_url": "https://suedewallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": "9907",
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 251,
+ "category": "material"
+ },
+ {
+ "name": "textilewallpaper",
+ "public_url": "https://textilewallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 215,
+ "category": "material"
+ },
+ {
+ "name": "wallsandfabrics",
+ "public_url": "https://wallsandfabrics.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 2667,
+ "category": "material"
+ },
+ {
+ "name": "apartmentwallpaper",
+ "public_url": "https://apartmentwallpaper.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": true,
+ "public_status": 302,
+ "public_ms": 2421,
+ "thumb": "thumbs/apartmentwallpaper.jpg",
+ "category": "other"
+ },
+ {
+ "name": "blockprintedwallpaper",
+ "public_url": "https://blockprintedwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 302,
+ "public_ms": 2357,
+ "thumb": "thumbs/blockprintedwallpaper.jpg",
+ "category": "other"
+ },
+ {
+ "name": "chinoiseriewallpaper",
+ "public_url": "https://chinoiseriewallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 2090,
+ "thumb": "thumbs/chinoiseriewallpaper.jpg",
+ "category": "other"
+ },
+ {
+ "name": "Designer-Wallcoverings",
+ "public_url": "https://Designer-Wallcoverings.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": false,
+ "public_status": 0,
+ "public_ms": 3114,
+ "category": "other"
+ },
+ {
+ "name": "designerwallcoverings",
+ "public_url": "https://designerwallcoverings.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 301,
+ "public_ms": 1775,
+ "thumb": "thumbs/designerwallcoverings.jpg",
+ "category": "other"
+ },
+ {
+ "name": "greenwallcoverings",
+ "public_url": "https://greenwallcoverings.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 1447,
+ "thumb": "thumbs/greenwallcoverings.jpg",
+ "category": "other"
+ },
+ {
+ "name": "handcraftedwallpaper",
+ "public_url": "https://handcraftedwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": "9897",
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 1780,
+ "thumb": "thumbs/handcraftedwallpaper.jpg",
+ "category": "other"
+ },
+ {
+ "name": "museumwallpaper",
+ "public_url": "https://museumwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": "9899",
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 1126,
+ "category": "other"
+ },
+ {
+ "name": "retrowalls",
+ "public_url": "https://retrowalls.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 597,
+ "category": "other"
+ },
+ {
+ "name": "saloonwallpaper",
+ "public_url": "https://saloonwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 477,
+ "category": "other"
+ },
+ {
+ "name": "screenprintedwallpaper",
+ "public_url": "https://screenprintedwallpaper.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": true,
+ "public_status": 302,
+ "public_ms": 319,
+ "category": "other"
+ },
+ {
+ "name": "selfadhesivewallpaper",
+ "public_url": "https://selfadhesivewallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": "9905",
+ "public_live": true,
+ "public_status": 302,
+ "public_ms": 383,
+ "category": "other"
+ },
+ {
+ "name": "vinylwallpaper",
+ "public_url": "https://vinylwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 253,
+ "category": "other"
+ },
+ {
+ "name": "wallpaper-tour",
+ "public_url": "https://wallpaper-tour.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": false,
+ "public_status": 0,
+ "public_ms": 161,
+ "category": "other"
+ },
+ {
+ "name": "carmelwallpapers",
+ "public_url": "https://carmelwallpapers.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 2339,
+ "thumb": "thumbs/carmelwallpapers.jpg",
+ "category": "place"
+ },
+ {
+ "name": "hollywood-wallcoverings",
+ "public_url": "https://hollywood-wallcoverings.com",
+ "pm2": false,
+ "pm2_status": null,
+ "port": null,
+ "public_live": false,
+ "public_status": 0,
+ "public_ms": 1113,
+ "category": "place"
+ },
+ {
+ "name": "madagascarwallpaper",
+ "public_url": "https://madagascarwallpaper.com",
+ "pm2": true,
+ "pm2_status": "online",
+ "port": null,
+ "public_live": true,
+ "public_status": 200,
+ "public_ms": 536,
+ "category": "place"
+ }
+]
\ No newline at end of file
diff --git a/live-urls.txt b/live-urls.txt
new file mode 100644
index 0000000..930dcd0
--- /dev/null
+++ b/live-urls.txt
@@ -0,0 +1,70 @@
+1800swallpaper https://1800swallpaper.com/
+1890swallpaper https://1890swallpaper.com/
+1900swallpaper https://1900swallpaper.com/
+1920swallpaper https://1920swallpaper.com/
+1930swallpaper https://1930swallpaper.com/
+1940swallpaper https://1940swallpaper.com/
+1950swallpaper https://1950swallpaper.com/
+1960swallpaper https://1960swallpaper.com/
+1970swallpaper https://1970swallpaper.com/
+1980swallpaper https://1980swallpaper.com/
+agedwallpaper https://agedwallpaper.com/
+apartmentwallpaper https://wallpapersback.com/
+architecturalwallcoverings https://architecturalwallcoverings.com/
+bestwallpaperplace https://bestwallpaperplace.com/
+blockprintedwallpaper https://handcraftedwallpaper.com/
+carmelwallpapers https://carmelwallpapers.com/
+chinoiseriewallpaper https://chinoiseriewallpaper.com/
+contractwallpaper https://contractwallpaper.com/
+corkwallcovering https://corkwallcovering.com/
+designerwallcoverings https://www.designerwallcoverings.com/
+embroideredwallpaper https://fabricwallpaper.com/
+fabricwallpaper https://fabricwallpaper.com/
+flockedwallpaper https://flockedwallpaper.com/
+glassbeadedwallpaper https://glassbeadedwallpaper.com/
+glitterwalls https://glitterwalls.com/
+goldleafwallpaper https://goldleafwallpaper.com/
+grassclothwallpaper https://grassclothwallpaper.com/
+greenwallcoverings https://greenwallcoverings.com/
+handcraftedwallpaper https://handcraftedwallpaper.com/
+healthcarewallpaper https://healthcarewallpaper.com/
+hospitalitywallcoverings https://hospitalitywallcoverings.com/
+hospitalitywallpaper https://hospitalitywallpaper.com/
+hotelwallcoverings https://hotelwallcoverings.com/
+jutewallpaper https://jutewallpaper.com/
+linenwallpaper https://linenwallpaper.com/
+madagascarwallpaper https://madagascarwallpaper.com/
+metallicwallpaper https://metallicwallpaper.com/
+micawallpaper https://micawallpaper.com/
+museumwallpaper https://museumwallpaper.com/
+mylarwallpaper https://mylarwallpaper.com/
+naturaltextilewallpaper https://naturaltextilewallpaper.com/
+naturalwallcoverings https://naturalwallcoverings.com/
+pastelwallpaper https://pastelwallpaper.com/
+raffiawallcoverings https://raffiawallcoverings.com/
+raffiawalls https://raffiawalls.com/
+recycledwallpaper https://recycledwallpaper.com/
+restaurantwallpaper https://restaurantwallpaper.com/
+restorationwallpaper https://restorationwallpaper.com/
+retrowalls https://retrowalls.com/
+saloonwallpaper https://saloonwallpaper.com/
+screenprintedwallpaper https://handcraftedwallpaper.com/
+selfadhesivewallpaper https://wallpapersback.com/
+silkwallcoverings https://silkwallcoverings.com/
+silkwallpaper https://silkwallpaper.com/
+silverleafwallpaper https://silverleafwallpaper.com/
+stringwallpaper https://linenwallpaper.com/
+suedewallpaper https://suedewallpaper.com/
+textilewallpaper https://textilewallpaper.com/
+vinylwallpaper https://vinylwallpaper.com/
+wallpapercanada https://wallpapercanada.com/
+wallpapercontractor https://wallpapercontractor.com/
+wallpaperdefinitions https://wallpaperdefinitions.com/
+wallpaperdictionary https://wallpaperdictionary.com/
+wallpaperdistributors https://wallpaperdistributors.com/
+wallpaperestimator https://wallpaperestimator.com/
+wallpaperglossary https://wallpaperglossary.com/
+wallpaperhistory https://wallpaperhistory.com/
+wallpapersback https://wallpapersback.com/
+wallpaperwednesday https://wallpaperwednesday.com/
+wallsandfabrics https://wallsandfabrics.com/
diff --git a/preflight-results.json b/preflight-results.json
new file mode 100644
index 0000000..7dd9a39
--- /dev/null
+++ b/preflight-results.json
@@ -0,0 +1,429 @@
+[
+ {
+ "name": "1800swallpaper",
+ "url": "https://1800swallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "1890swallpaper",
+ "url": "https://1890swallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "1900swallpaper",
+ "url": "https://1900swallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "1920swallpaper",
+ "url": "https://1920swallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "1930swallpaper",
+ "url": "https://1930swallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "1940swallpaper",
+ "url": "https://1940swallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "1950swallpaper",
+ "url": "https://1950swallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "1960swallpaper",
+ "url": "https://1960swallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "1970swallpaper",
+ "url": "https://1970swallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "1980swallpaper",
+ "url": "https://1980swallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "agedwallpaper",
+ "url": "https://agedwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "apartmentwallpaper",
+ "url": "https://wallpapersback.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "architecturalwallcoverings",
+ "url": "https://architecturalwallcoverings.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "bestwallpaperplace",
+ "url": "https://bestwallpaperplace.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "blockprintedwallpaper",
+ "url": "https://handcraftedwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "carmelwallpapers",
+ "url": "https://carmelwallpapers.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "chinoiseriewallpaper",
+ "url": "https://chinoiseriewallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "contractwallpaper",
+ "url": "https://contractwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "corkwallcovering",
+ "url": "https://corkwallcovering.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "designerwallcoverings",
+ "url": "https://www.designerwallcoverings.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "embroideredwallpaper",
+ "url": "https://fabricwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "fabricwallpaper",
+ "url": "https://fabricwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "flockedwallpaper",
+ "url": "https://flockedwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "glassbeadedwallpaper",
+ "url": "https://glassbeadedwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "glitterwalls",
+ "url": "https://glitterwalls.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "goldleafwallpaper",
+ "url": "https://goldleafwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "grassclothwallpaper",
+ "url": "https://grassclothwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "greenwallcoverings",
+ "url": "https://greenwallcoverings.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "handcraftedwallpaper",
+ "url": "https://handcraftedwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "healthcarewallpaper",
+ "url": "https://healthcarewallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "hollywood-wallcoverings",
+ "url": "https://hollywood-wallcoverings.com",
+ "status": 0,
+ "live": false,
+ "error": "fetch failed"
+ },
+ {
+ "name": "hospitalitywallcoverings",
+ "url": "https://hospitalitywallcoverings.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "hospitalitywallpaper",
+ "url": "https://hospitalitywallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "hotelwallcoverings",
+ "url": "https://hotelwallcoverings.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "jutewallpaper",
+ "url": "https://jutewallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "linenwallpaper",
+ "url": "https://linenwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "madagascarwallpaper",
+ "url": "https://madagascarwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "metallicwallpaper",
+ "url": "https://metallicwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "micawallpaper",
+ "url": "https://micawallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "museumwallpaper",
+ "url": "https://museumwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "mylarwallpaper",
+ "url": "https://mylarwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "naturaltextilewallpaper",
+ "url": "https://naturaltextilewallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "naturalwallcoverings",
+ "url": "https://naturalwallcoverings.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "pastelwallpaper",
+ "url": "https://pastelwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "raffiawallcoverings",
+ "url": "https://raffiawallcoverings.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "raffiawalls",
+ "url": "https://raffiawalls.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "recycledwallpaper",
+ "url": "https://recycledwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "restaurantwallpaper",
+ "url": "https://restaurantwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "restorationwallpaper",
+ "url": "https://restorationwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "retrowalls",
+ "url": "https://retrowalls.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "saloonwallpaper",
+ "url": "https://saloonwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "screenprintedwallpaper",
+ "url": "https://handcraftedwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "selfadhesivewallpaper",
+ "url": "https://wallpapersback.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "silkwallcoverings",
+ "url": "https://silkwallcoverings.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "silkwallpaper",
+ "url": "https://silkwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "silverleafwallpaper",
+ "url": "https://silverleafwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "stringwallpaper",
+ "url": "https://linenwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "suedewallpaper",
+ "url": "https://suedewallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "textilewallpaper",
+ "url": "https://textilewallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "vinylwallpaper",
+ "url": "https://vinylwallpaper.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "wallpapercanada",
+ "url": "https://wallpapercanada.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "wallpapercontractor",
+ "url": "https://wallpapercontractor.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "wallpaperdefinitions",
+ "url": "https://wallpaperdefinitions.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "wallpaperdictionary",
+ "url": "https://wallpaperdictionary.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "wallpaperdistributors",
+ "url": "https://wallpaperdistributors.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "wallpaperestimator",
+ "url": "https://wallpaperestimator.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "wallpaperglossary",
+ "url": "https://wallpaperglossary.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "wallpaperhistory",
+ "url": "https://wallpaperhistory.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "wallpapersback",
+ "url": "https://wallpapersback.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "wallpaperwednesday",
+ "url": "https://wallpaperwednesday.com/",
+ "status": 200,
+ "live": true
+ },
+ {
+ "name": "wallsandfabrics",
+ "url": "https://wallsandfabrics.com/",
+ "status": 200,
+ "live": true
+ }
+]
\ No newline at end of file
diff --git a/preflight.mjs b/preflight.mjs
new file mode 100644
index 0000000..6e837c5
--- /dev/null
+++ b/preflight.mjs
@@ -0,0 +1,66 @@
+#!/usr/bin/env node
+// Pre-flight: HEAD each https://<name>.com with 8s timeout, keep live ones.
+import fs from 'node:fs';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const projects = fs.readFileSync(path.join(__dirname, 'projects.txt'), 'utf8')
+ .split('\n').map(s => s.trim()).filter(Boolean);
+
+const TIMEOUT_MS = 8000;
+
+async function checkUrl(name) {
+ const url = `https://${name}.com`;
+ const ctrl = new AbortController();
+ const timer = setTimeout(() => ctrl.abort(), TIMEOUT_MS);
+ try {
+ // Try HEAD first; some servers reject HEAD, fall back to GET range
+ let res;
+ try {
+ res = await fetch(url, { method: 'HEAD', redirect: 'follow', signal: ctrl.signal });
+ } catch (_) {
+ res = await fetch(url, { method: 'GET', redirect: 'follow', signal: ctrl.signal, headers: { Range: 'bytes=0-1023' } });
+ }
+ clearTimeout(timer);
+ if (res.status >= 200 && res.status < 400) {
+ return { name, url: res.url || url, status: res.status, live: true };
+ }
+ // 405 (method not allowed) usually means HEAD-rejected — try GET
+ if (res.status === 405) {
+ const ctrl2 = new AbortController();
+ const t2 = setTimeout(() => ctrl2.abort(), TIMEOUT_MS);
+ try {
+ const r2 = await fetch(url, { method: 'GET', redirect: 'follow', signal: ctrl2.signal });
+ clearTimeout(t2);
+ if (r2.status >= 200 && r2.status < 400) return { name, url: r2.url || url, status: r2.status, live: true };
+ return { name, url, status: r2.status, live: false };
+ } catch (e) {
+ clearTimeout(t2);
+ return { name, url, status: 0, live: false, error: e.message };
+ }
+ }
+ return { name, url, status: res.status, live: false };
+ } catch (e) {
+ clearTimeout(timer);
+ return { name, url, status: 0, live: false, error: e.message };
+ }
+}
+
+const results = [];
+// Sequential to be gentle (Steve says load avg ~500)
+let i = 0;
+for (const name of projects) {
+ i++;
+ process.stdout.write(`[${i}/${projects.length}] ${name} ... `);
+ const r = await checkUrl(name);
+ results.push(r);
+ console.log(r.live ? `LIVE (${r.status})` : `DEAD (${r.status}${r.error ? ' ' + r.error : ''})`);
+}
+
+const live = results.filter(r => r.live);
+fs.writeFileSync(path.join(__dirname, 'live-urls.txt'), live.map(r => `${r.name}\t${r.url}`).join('\n') + '\n');
+fs.writeFileSync(path.join(__dirname, 'preflight-results.json'), JSON.stringify(results, null, 2));
+
+console.log(`\nLIVE: ${live.length}/${results.length}`);
+console.log(`Wrote ${path.join(__dirname, 'live-urls.txt')}`);
diff --git a/projects.txt b/projects.txt
new file mode 100644
index 0000000..173a336
--- /dev/null
+++ b/projects.txt
@@ -0,0 +1,71 @@
+1800swallpaper
+1890swallpaper
+1900swallpaper
+1920swallpaper
+1930swallpaper
+1940swallpaper
+1950swallpaper
+1960swallpaper
+1970swallpaper
+1980swallpaper
+agedwallpaper
+apartmentwallpaper
+architecturalwallcoverings
+bestwallpaperplace
+blockprintedwallpaper
+carmelwallpapers
+chinoiseriewallpaper
+contractwallpaper
+corkwallcovering
+designerwallcoverings
+embroideredwallpaper
+fabricwallpaper
+flockedwallpaper
+glassbeadedwallpaper
+glitterwalls
+goldleafwallpaper
+grassclothwallpaper
+greenwallcoverings
+handcraftedwallpaper
+healthcarewallpaper
+hollywood-wallcoverings
+hospitalitywallcoverings
+hospitalitywallpaper
+hotelwallcoverings
+jutewallpaper
+linenwallpaper
+madagascarwallpaper
+metallicwallpaper
+micawallpaper
+museumwallpaper
+mylarwallpaper
+naturaltextilewallpaper
+naturalwallcoverings
+pastelwallpaper
+raffiawallcoverings
+raffiawalls
+recycledwallpaper
+restaurantwallpaper
+restorationwallpaper
+retrowalls
+saloonwallpaper
+screenprintedwallpaper
+selfadhesivewallpaper
+silkwallcoverings
+silkwallpaper
+silverleafwallpaper
+stringwallpaper
+suedewallpaper
+textilewallpaper
+vinylwallpaper
+wallpapercanada
+wallpapercontractor
+wallpaperdefinitions
+wallpaperdictionary
+wallpaperdistributors
+wallpaperestimator
+wallpaperglossary
+wallpaperhistory
+wallpapersback
+wallpaperwednesday
+wallsandfabrics
diff --git a/smoketest.mjs b/smoketest.mjs
new file mode 100644
index 0000000..0d20c3f
--- /dev/null
+++ b/smoketest.mjs
@@ -0,0 +1,13 @@
+// Verify Playwright launches via createRequire (CJS bridge)
+import { createRequire } from 'node:module';
+const require = createRequire('/Users/stevestudio2/Projects/animals/');
+const { chromium } = require('playwright');
+console.log('Imported playwright OK');
+const b = await chromium.launch({ headless: true });
+console.log('Browser launched');
+const ctx = await b.newContext({ viewport: { width: 1600, height: 900 } });
+const p = await ctx.newPage();
+await p.goto('https://silkwallpaper.com', { waitUntil: 'domcontentloaded', timeout: 15000 });
+console.log('Loaded:', await p.title());
+await b.close();
+console.log('OK');
diff --git a/stitch.mjs b/stitch.mjs
new file mode 100644
index 0000000..7a5813c
--- /dev/null
+++ b/stitch.mjs
@@ -0,0 +1,63 @@
+#!/usr/bin/env node
+// Stitch all clips/*.webm into one MP4 with title overlay per clip.
+// Usage: node stitch.mjs [outname]
+import fs from 'node:fs';
+import path from 'node:path';
+import { execFile } from 'node:child_process';
+import { fileURLToPath } from 'node:url';
+import os from 'node:os';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const OUTNAME = process.argv[2] || 'wallpaper-portfolio-tour.mp4';
+const OUT = path.join(__dirname, OUTNAME);
+const SHIPPED = path.join(os.homedir(), 'Videos', 'shipped-apps');
+
+const CLIPS_DIR_ = path.join(__dirname, 'clips');
+const clips = fs.readdirSync(CLIPS_DIR_)
+ .filter(f => f.endsWith('.webm'))
+ .map(f => ({ f, full: path.join(CLIPS_DIR_, f), size: fs.statSync(path.join(CLIPS_DIR_, f)).size }))
+ .filter(c => c.size > 100_000) // skip corrupt tiny clips
+ .sort((a, b) => a.f.localeCompare(b.f, undefined, { numeric: true }))
+ .map(c => c.full);
+
+if (!clips.length) { console.error('no clips'); process.exit(1); }
+console.log(`stitching ${clips.length} clips → ${OUT}`);
+
+const concatFile = path.join(__dirname, '.concat.txt');
+fs.writeFileSync(concatFile, clips.map(c => `file '${c}'`).join('\n'));
+
+const args = [
+ '-f', 'concat', '-safe', '0', '-i', concatFile,
+ '-vf', 'scale=1600:900:force_original_aspect_ratio=decrease,pad=1600:900:(ow-iw)/2:(oh-ih)/2:color=black,fps=30',
+ '-c:v', 'libx264', '-preset', 'fast', '-crf', '23',
+ '-pix_fmt', 'yuv420p',
+ '-an',
+ '-y', OUT
+];
+
+const t0 = Date.now();
+const child = execFile('ffmpeg', args, { maxBuffer: 64 * 1024 * 1024 }, (err, _, stderr) => {
+ fs.unlinkSync(concatFile);
+ if (err) {
+ console.error('ffmpeg failed:', err.message);
+ console.error(stderr.toString().slice(-1000));
+ process.exit(1);
+ }
+ const ms = Date.now() - t0;
+ const stat = fs.statSync(OUT);
+ console.log(`✓ ${OUT}`);
+ console.log(` ${(stat.size / 1024 / 1024).toFixed(1)} MB in ${(ms / 1000).toFixed(1)}s`);
+
+ // Stage to ~/Videos/shipped-apps for auto-publish to videos.agentabrams.com
+ fs.mkdirSync(SHIPPED, { recursive: true });
+ const staged = path.join(SHIPPED, OUTNAME);
+ fs.copyFileSync(OUT, staged);
+ console.log(`✓ staged for videos.agentabrams.com: ${staged}`);
+});
+
+child.stderr?.on('data', (d) => {
+ const s = d.toString();
+ // ffmpeg progress lines start with "frame=" — print the latest only
+ const m = s.match(/frame=\s*\d+.*?time=\s*[\d:.]+\s*bitrate=\s*[\d.]+\s*\w+\/?\w*\s*speed=\s*[\d.]+x?/);
+ if (m) process.stdout.write(`\r ${m[0]} `);
+});
diff --git a/thumbs/1800swallpaper.jpg b/thumbs/1800swallpaper.jpg
new file mode 100644
index 0000000..8e35775
Binary files /dev/null and b/thumbs/1800swallpaper.jpg differ
diff --git a/thumbs/1890swallpaper.jpg b/thumbs/1890swallpaper.jpg
new file mode 100644
index 0000000..f6b2c71
Binary files /dev/null and b/thumbs/1890swallpaper.jpg differ
diff --git a/thumbs/1900swallpaper.jpg b/thumbs/1900swallpaper.jpg
new file mode 100644
index 0000000..c53b795
Binary files /dev/null and b/thumbs/1900swallpaper.jpg differ
diff --git a/thumbs/1920swallpaper.jpg b/thumbs/1920swallpaper.jpg
new file mode 100644
index 0000000..2b403e1
Binary files /dev/null and b/thumbs/1920swallpaper.jpg differ
diff --git a/thumbs/1930swallpaper.jpg b/thumbs/1930swallpaper.jpg
new file mode 100644
index 0000000..2b403e1
Binary files /dev/null and b/thumbs/1930swallpaper.jpg differ
diff --git a/thumbs/1940swallpaper.jpg b/thumbs/1940swallpaper.jpg
new file mode 100644
index 0000000..2b403e1
Binary files /dev/null and b/thumbs/1940swallpaper.jpg differ
diff --git a/thumbs/1950swallpaper.jpg b/thumbs/1950swallpaper.jpg
new file mode 100644
index 0000000..815ad07
Binary files /dev/null and b/thumbs/1950swallpaper.jpg differ
diff --git a/thumbs/1960swallpaper.jpg b/thumbs/1960swallpaper.jpg
new file mode 100644
index 0000000..7051e2f
Binary files /dev/null and b/thumbs/1960swallpaper.jpg differ
diff --git a/thumbs/1970swallpaper.jpg b/thumbs/1970swallpaper.jpg
new file mode 100644
index 0000000..a0d1463
Binary files /dev/null and b/thumbs/1970swallpaper.jpg differ
diff --git a/thumbs/1980swallpaper.jpg b/thumbs/1980swallpaper.jpg
new file mode 100644
index 0000000..666831b
Binary files /dev/null and b/thumbs/1980swallpaper.jpg differ
diff --git a/thumbs/agedwallpaper.jpg b/thumbs/agedwallpaper.jpg
new file mode 100644
index 0000000..1439cbe
Binary files /dev/null and b/thumbs/agedwallpaper.jpg differ
diff --git a/thumbs/apartmentwallpaper.jpg b/thumbs/apartmentwallpaper.jpg
new file mode 100644
index 0000000..8be6cc1
Binary files /dev/null and b/thumbs/apartmentwallpaper.jpg differ
diff --git a/thumbs/architecturalwallcoverings.jpg b/thumbs/architecturalwallcoverings.jpg
new file mode 100644
index 0000000..1f663c9
Binary files /dev/null and b/thumbs/architecturalwallcoverings.jpg differ
diff --git a/thumbs/bestwallpaperplace.jpg b/thumbs/bestwallpaperplace.jpg
new file mode 100644
index 0000000..31173ee
Binary files /dev/null and b/thumbs/bestwallpaperplace.jpg differ
diff --git a/thumbs/blockprintedwallpaper.jpg b/thumbs/blockprintedwallpaper.jpg
new file mode 100644
index 0000000..8c0393d
Binary files /dev/null and b/thumbs/blockprintedwallpaper.jpg differ
diff --git a/thumbs/carmelwallpapers.jpg b/thumbs/carmelwallpapers.jpg
new file mode 100644
index 0000000..68a7318
Binary files /dev/null and b/thumbs/carmelwallpapers.jpg differ
diff --git a/thumbs/chinoiseriewallpaper.jpg b/thumbs/chinoiseriewallpaper.jpg
new file mode 100644
index 0000000..44baf02
Binary files /dev/null and b/thumbs/chinoiseriewallpaper.jpg differ
diff --git a/thumbs/contractwallpaper.jpg b/thumbs/contractwallpaper.jpg
new file mode 100644
index 0000000..3225267
Binary files /dev/null and b/thumbs/contractwallpaper.jpg differ
diff --git a/thumbs/corkwallcovering.jpg b/thumbs/corkwallcovering.jpg
new file mode 100644
index 0000000..99a9334
Binary files /dev/null and b/thumbs/corkwallcovering.jpg differ
diff --git a/thumbs/designerwallcoverings.jpg b/thumbs/designerwallcoverings.jpg
new file mode 100644
index 0000000..d938bcc
Binary files /dev/null and b/thumbs/designerwallcoverings.jpg differ
diff --git a/thumbs/embroideredwallpaper.jpg b/thumbs/embroideredwallpaper.jpg
new file mode 100644
index 0000000..0074b34
Binary files /dev/null and b/thumbs/embroideredwallpaper.jpg differ
diff --git a/thumbs/fabricwallpaper.jpg b/thumbs/fabricwallpaper.jpg
new file mode 100644
index 0000000..9752bdf
Binary files /dev/null and b/thumbs/fabricwallpaper.jpg differ
diff --git a/thumbs/flockedwallpaper.jpg b/thumbs/flockedwallpaper.jpg
new file mode 100644
index 0000000..9b2d286
Binary files /dev/null and b/thumbs/flockedwallpaper.jpg differ
diff --git a/thumbs/glassbeadedwallpaper.jpg b/thumbs/glassbeadedwallpaper.jpg
new file mode 100644
index 0000000..f19ff70
Binary files /dev/null and b/thumbs/glassbeadedwallpaper.jpg differ
diff --git a/thumbs/glitterwalls.jpg b/thumbs/glitterwalls.jpg
new file mode 100644
index 0000000..776dcc0
Binary files /dev/null and b/thumbs/glitterwalls.jpg differ
diff --git a/thumbs/goldleafwallpaper.jpg b/thumbs/goldleafwallpaper.jpg
new file mode 100644
index 0000000..dfff2ce
Binary files /dev/null and b/thumbs/goldleafwallpaper.jpg differ
diff --git a/thumbs/greenwallcoverings.jpg b/thumbs/greenwallcoverings.jpg
new file mode 100644
index 0000000..4f643a2
Binary files /dev/null and b/thumbs/greenwallcoverings.jpg differ
diff --git a/thumbs/handcraftedwallpaper.jpg b/thumbs/handcraftedwallpaper.jpg
new file mode 100644
index 0000000..2b403e1
Binary files /dev/null and b/thumbs/handcraftedwallpaper.jpg differ
diff --git a/thumbs/healthcarewallpaper.jpg b/thumbs/healthcarewallpaper.jpg
new file mode 100644
index 0000000..fe64b96
Binary files /dev/null and b/thumbs/healthcarewallpaper.jpg differ
diff --git a/tour-report.json b/tour-report.json
new file mode 100644
index 0000000..e72ebd0
--- /dev/null
+++ b/tour-report.json
@@ -0,0 +1,614 @@
+[
+ {
+ "index": 1,
+ "name": "1800swallpaper",
+ "url": "https://1800swallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/01-1800swallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 2,
+ "name": "1890swallpaper",
+ "url": "https://1890swallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/02-1890swallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 3,
+ "name": "1900swallpaper",
+ "url": "https://1900swallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/03-1900swallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 4,
+ "name": "1920swallpaper",
+ "url": "https://1920swallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/04-1920swallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 5,
+ "name": "1930swallpaper",
+ "url": "https://1930swallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/05-1930swallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 6,
+ "name": "1940swallpaper",
+ "url": "https://1940swallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/06-1940swallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 7,
+ "name": "1950swallpaper",
+ "url": "https://1950swallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/07-1950swallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 8,
+ "name": "1960swallpaper",
+ "url": "https://1960swallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/08-1960swallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 9,
+ "name": "1970swallpaper",
+ "url": "https://1970swallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/09-1970swallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 10,
+ "name": "1980swallpaper",
+ "url": "https://1980swallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/10-1980swallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 11,
+ "name": "agedwallpaper",
+ "url": "https://agedwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/11-agedwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 12,
+ "name": "apartmentwallpaper",
+ "url": "https://wallpapersback.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/12-apartmentwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 13,
+ "name": "architecturalwallcoverings",
+ "url": "https://architecturalwallcoverings.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/13-architecturalwallcoverings.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 14,
+ "name": "bestwallpaperplace",
+ "url": "https://bestwallpaperplace.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/14-bestwallpaperplace.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 15,
+ "name": "blockprintedwallpaper",
+ "url": "https://handcraftedwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/15-blockprintedwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 16,
+ "name": "carmelwallpapers",
+ "url": "https://carmelwallpapers.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/16-carmelwallpapers.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 17,
+ "name": "chinoiseriewallpaper",
+ "url": "https://chinoiseriewallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/17-chinoiseriewallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 18,
+ "name": "contractwallpaper",
+ "url": "https://contractwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/18-contractwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 19,
+ "name": "corkwallcovering",
+ "url": "https://corkwallcovering.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/19-corkwallcovering.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 20,
+ "name": "designerwallcoverings",
+ "url": "https://www.designerwallcoverings.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/20-designerwallcoverings.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 21,
+ "name": "embroideredwallpaper",
+ "url": "https://fabricwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/21-embroideredwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 22,
+ "name": "fabricwallpaper",
+ "url": "https://fabricwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/22-fabricwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 23,
+ "name": "flockedwallpaper",
+ "url": "https://flockedwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/23-flockedwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 24,
+ "name": "glassbeadedwallpaper",
+ "url": "https://glassbeadedwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/24-glassbeadedwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 25,
+ "name": "glitterwalls",
+ "url": "https://glitterwalls.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/25-glitterwalls.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 26,
+ "name": "goldleafwallpaper",
+ "url": "https://goldleafwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/26-goldleafwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 27,
+ "name": "grassclothwallpaper",
+ "url": "https://grassclothwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/27-grassclothwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 28,
+ "name": "greenwallcoverings",
+ "url": "https://greenwallcoverings.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/28-greenwallcoverings.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 29,
+ "name": "handcraftedwallpaper",
+ "url": "https://handcraftedwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/29-handcraftedwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 30,
+ "name": "healthcarewallpaper",
+ "url": "https://healthcarewallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/30-healthcarewallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 31,
+ "name": "hospitalitywallcoverings",
+ "url": "https://hospitalitywallcoverings.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/31-hospitalitywallcoverings.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 32,
+ "name": "hospitalitywallpaper",
+ "url": "https://hospitalitywallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/32-hospitalitywallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 33,
+ "name": "hotelwallcoverings",
+ "url": "https://hotelwallcoverings.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/33-hotelwallcoverings.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 34,
+ "name": "jutewallpaper",
+ "url": "https://jutewallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/34-jutewallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 35,
+ "name": "linenwallpaper",
+ "url": "https://linenwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/35-linenwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 36,
+ "name": "madagascarwallpaper",
+ "url": "https://madagascarwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/36-madagascarwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 37,
+ "name": "metallicwallpaper",
+ "url": "https://metallicwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/37-metallicwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 38,
+ "name": "micawallpaper",
+ "url": "https://micawallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/38-micawallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 39,
+ "name": "museumwallpaper",
+ "url": "https://museumwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/39-museumwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 40,
+ "name": "mylarwallpaper",
+ "url": "https://mylarwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/40-mylarwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 41,
+ "name": "naturaltextilewallpaper",
+ "url": "https://naturaltextilewallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/41-naturaltextilewallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 42,
+ "name": "naturalwallcoverings",
+ "url": "https://naturalwallcoverings.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/42-naturalwallcoverings.webm",
+ "error": null,
+ "ms": 151010
+ },
+ {
+ "index": 43,
+ "name": "pastelwallpaper",
+ "url": "https://pastelwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/43-pastelwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 44,
+ "name": "raffiawallcoverings",
+ "url": "https://raffiawallcoverings.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/44-raffiawallcoverings.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 45,
+ "name": "raffiawalls",
+ "url": "https://raffiawalls.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/45-raffiawalls.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 46,
+ "name": "recycledwallpaper",
+ "url": "https://recycledwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/46-recycledwallpaper.webm",
+ "error": null,
+ "ms": 42234
+ },
+ {
+ "index": 47,
+ "name": "restaurantwallpaper",
+ "url": "https://restaurantwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/47-restaurantwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 48,
+ "name": "restorationwallpaper",
+ "url": "https://restorationwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/48-restorationwallpaper.webm",
+ "error": null,
+ "ms": 96431
+ },
+ {
+ "index": 49,
+ "name": "retrowalls",
+ "url": "https://retrowalls.com/",
+ "clip": null,
+ "error": "page.goto: Timeout 20000ms exceeded.\nCall log:\n\u001b[2m - navigating to \"https://retrowalls.com/\", waiting until \"domcontentloaded\"\u001b[22m\n",
+ "ms": 263212
+ },
+ {
+ "index": 50,
+ "name": "saloonwallpaper",
+ "url": "https://saloonwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/50-saloonwallpaper.webm",
+ "error": null,
+ "ms": 126765
+ },
+ {
+ "index": 51,
+ "name": "screenprintedwallpaper",
+ "url": "https://handcraftedwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/51-screenprintedwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 52,
+ "name": "selfadhesivewallpaper",
+ "url": "https://wallpapersback.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/52-selfadhesivewallpaper.webm",
+ "error": null,
+ "ms": 31173
+ },
+ {
+ "index": 53,
+ "name": "silkwallcoverings",
+ "url": "https://silkwallcoverings.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/53-silkwallcoverings.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 54,
+ "name": "silkwallpaper",
+ "url": "https://silkwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/54-silkwallpaper.webm",
+ "error": null,
+ "ms": 27388
+ },
+ {
+ "index": 55,
+ "name": "silverleafwallpaper",
+ "url": "https://silverleafwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/55-silverleafwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 56,
+ "name": "stringwallpaper",
+ "url": "https://linenwallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/56-stringwallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 57,
+ "name": "suedewallpaper",
+ "url": "https://suedewallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/57-suedewallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 58,
+ "name": "textilewallpaper",
+ "url": "https://textilewallpaper.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/58-textilewallpaper.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ },
+ {
+ "index": 59,
+ "name": "vinylwallpaper",
+ "url": "https://vinylwallpaper.com/",
+ "clip": null,
+ "error": "ENOENT: no such file or directory, scandir '/Users/stevestudio2/Projects/wallpaper-tour/clips/_raw-59-vinylwallpaper'",
+ "ms": 40057
+ },
+ {
+ "index": 60,
+ "name": "wallpapercanada",
+ "url": "https://wallpapercanada.com/",
+ "clip": null,
+ "error": "ENOENT: no such file or directory, scandir '/Users/stevestudio2/Projects/wallpaper-tour/clips/_raw-60-wallpapercanada'",
+ "ms": 20822
+ },
+ {
+ "index": 61,
+ "name": "wallpapercontractor",
+ "url": "https://wallpapercontractor.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/61-wallpapercontractor.webm",
+ "error": null,
+ "ms": 38421
+ },
+ {
+ "index": 62,
+ "name": "wallpaperdefinitions",
+ "url": "https://wallpaperdefinitions.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/62-wallpaperdefinitions.webm",
+ "error": null,
+ "ms": 16112
+ },
+ {
+ "index": 63,
+ "name": "wallpaperdictionary",
+ "url": "https://wallpaperdictionary.com/",
+ "clip": null,
+ "error": "ENOENT: no such file or directory, scandir '/Users/stevestudio2/Projects/wallpaper-tour/clips/_raw-63-wallpaperdictionary'",
+ "ms": 118229
+ },
+ {
+ "index": 64,
+ "name": "wallpaperdistributors",
+ "url": "https://wallpaperdistributors.com/",
+ "clip": null,
+ "error": "ENOENT: no such file or directory, scandir '/Users/stevestudio2/Projects/wallpaper-tour/clips/_raw-64-wallpaperdistributors'",
+ "ms": 21142
+ },
+ {
+ "index": 65,
+ "name": "wallpaperestimator",
+ "url": "https://wallpaperestimator.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/65-wallpaperestimator.webm",
+ "error": null,
+ "ms": 20010
+ },
+ {
+ "index": 66,
+ "name": "wallpaperglossary",
+ "url": "https://wallpaperglossary.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/66-wallpaperglossary.webm",
+ "error": null,
+ "ms": 21932
+ },
+ {
+ "index": 67,
+ "name": "wallpaperhistory",
+ "url": "https://wallpaperhistory.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/67-wallpaperhistory.webm",
+ "error": null,
+ "ms": 25984
+ },
+ {
+ "index": 68,
+ "name": "wallpapersback",
+ "url": "https://wallpapersback.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/68-wallpapersback.webm",
+ "error": null,
+ "ms": 22303
+ },
+ {
+ "index": 69,
+ "name": "wallpaperwednesday",
+ "url": "https://wallpaperwednesday.com/",
+ "clip": null,
+ "error": "ENOENT: no such file or directory, scandir '/Users/stevestudio2/Projects/wallpaper-tour/clips/_raw-69-wallpaperwednesday'",
+ "ms": 281731
+ },
+ {
+ "index": 70,
+ "name": "wallsandfabrics",
+ "url": "https://wallsandfabrics.com/",
+ "clip": "/Users/stevestudio2/Projects/wallpaper-tour/clips/70-wallsandfabrics.webm",
+ "error": null,
+ "ms": 0,
+ "resumed": true
+ }
+]
\ No newline at end of file
diff --git a/tour.mjs b/tour.mjs
new file mode 100644
index 0000000..b8ef8f8
--- /dev/null
+++ b/tour.mjs
@@ -0,0 +1,185 @@
+#!/usr/bin/env node
+// Wallpaper portfolio tour — sequential Playwright video capture
+import fs from 'node:fs';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+
+// Use the playwright already installed under ~/Projects/animals (CJS bridge)
+import { createRequire } from 'node:module';
+const require = createRequire('/Users/stevestudio2/Projects/animals/');
+const { chromium } = require('playwright');
+
+const live = fs.readFileSync(path.join(__dirname, 'live-urls.txt'), 'utf8')
+ .split('\n').map(s => s.trim()).filter(Boolean)
+ .map(line => {
+ const [name, url] = line.split('\t');
+ return { name, url };
+ });
+
+const CLIPS_DIR = path.join(__dirname, 'clips');
+fs.mkdirSync(CLIPS_DIR, { recursive: true });
+
+const VIEWPORT = { width: 1600, height: 900 };
+const NAV_TIMEOUT_MS = 20000;
+const HARD_FIRST_PAINT_MS = 25000;
+const HERO_SETTLE_MS = 1000;
+const FRONT_PAGE_DWELL_MS = 3500;
+const POST_CLICK_DWELL_MS = 2500;
+const RUN_BUDGET_MS = 60 * 60 * 1000; // 60 minutes (resume budget)
+
+const startTime = Date.now();
+const report = [];
+
+function safeName(s) {
+ return s.replace(/[^a-z0-9-]/gi, '_');
+}
+
+async function launchBrowserWithRetry() {
+ for (let attempt = 1; attempt <= 2; attempt++) {
+ try {
+ return await chromium.launch({ headless: true });
+ } catch (e) {
+ console.log(` Playwright launch failed (attempt ${attempt}): ${e.message}`);
+ if (attempt === 2) throw e;
+ await new Promise(r => setTimeout(r, 5000));
+ }
+ }
+}
+
+async function captureSite(browser, site, index) {
+ const t0 = Date.now();
+ const tag = `${String(index).padStart(2, '0')}-${safeName(site.name)}`;
+ const finalClip = path.join(CLIPS_DIR, `${tag}.webm`);
+ // Resume: if final clip already exists with reasonable size, skip
+ if (fs.existsSync(finalClip) && fs.statSync(finalClip).size > 100_000) {
+ return { index, name: site.name, url: site.url, clip: finalClip, error: null, ms: 0, resumed: true };
+ }
+ const siteClipDir = path.join(CLIPS_DIR, `_raw-${tag}`);
+ // Clean any prior _raw dir from interrupted runs
+ try { fs.rmSync(siteClipDir, { recursive: true, force: true }); } catch {}
+ fs.mkdirSync(siteClipDir, { recursive: true });
+
+ let ctx, page;
+ const result = { index, name: site.name, url: site.url, clip: null, error: null, ms: 0 };
+ try {
+ ctx = await browser.newContext({
+ viewport: VIEWPORT,
+ deviceScaleFactor: 2,
+ recordVideo: { dir: siteClipDir, size: VIEWPORT },
+ userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
+ });
+ page = await ctx.newPage();
+
+ // Hard cap on first paint
+ const navPromise = page.goto(site.url, { waitUntil: 'domcontentloaded', timeout: NAV_TIMEOUT_MS });
+ const hardCap = new Promise((_, rej) => setTimeout(() => rej(new Error('hard 30s cap')), HARD_FIRST_PAINT_MS));
+ await Promise.race([navPromise, hardCap]);
+
+ // Hero settle
+ await page.waitForTimeout(HERO_SETTLE_MS);
+
+ // Stay on front page
+ await page.waitForTimeout(FRONT_PAGE_DWELL_MS);
+
+ // Try to find a clickable interesting element (don't navigate away to a 3rd party)
+ let clicked = false;
+ const selectors = [
+ 'main a[href]:not([href^="#"]):not([href^="mailto"]):not([href^="tel"]) img',
+ '.product-card a, .product a, [class*="product"] a img',
+ 'a.product-link, a[href*="/products/"]',
+ 'main img + *, main a img',
+ 'button:has-text("View Collection")',
+ 'a:has-text("View Collection")',
+ 'a:has-text("Shop")',
+ 'a:has-text("Explore")',
+ 'main a[href^="/"]:not([href="/"])',
+ ];
+
+ for (const sel of selectors) {
+ try {
+ const el = await page.locator(sel).first();
+ if (await el.count() > 0 && await el.isVisible({ timeout: 1000 }).catch(() => false)) {
+ await el.scrollIntoViewIfNeeded({ timeout: 2000 }).catch(() => {});
+ await el.click({ timeout: 3000, noWaitAfter: true });
+ clicked = true;
+ break;
+ }
+ } catch (_) { /* try next selector */ }
+ }
+
+ if (!clicked) {
+ // Fallback: scroll down 600px so the video shows more of the page
+ await page.evaluate(() => window.scrollBy({ top: 600, behavior: 'smooth' })).catch(() => {});
+ }
+
+ await page.waitForTimeout(POST_CLICK_DWELL_MS);
+
+ // If a click navigated, dwell a bit more on the destination
+ if (clicked) {
+ try { await page.waitForLoadState('domcontentloaded', { timeout: 3000 }); } catch (_) {}
+ await page.waitForTimeout(1500);
+ }
+
+ await page.close();
+ await ctx.close();
+
+ // Find the saved video file in siteClipDir (Playwright names it randomly)
+ const files = fs.readdirSync(siteClipDir).filter(f => f.endsWith('.webm'));
+ if (files.length > 0) {
+ const src = path.join(siteClipDir, files[0]);
+ const dst = path.join(CLIPS_DIR, `${tag}.webm`);
+ fs.renameSync(src, dst);
+ result.clip = dst;
+ }
+ // Clean up the per-site dir
+ try { fs.rmSync(siteClipDir, { recursive: true, force: true }); } catch (_) {}
+ } catch (e) {
+ result.error = e.message;
+ try { if (page) await page.close(); } catch (_) {}
+ try { if (ctx) await ctx.close(); } catch (_) {}
+ // Save any partial clip
+ try {
+ const files = fs.existsSync(siteClipDir) ? fs.readdirSync(siteClipDir).filter(f => f.endsWith('.webm')) : [];
+ if (files.length > 0) {
+ const src = path.join(siteClipDir, files[0]);
+ const stat = fs.statSync(src);
+ if (stat.size > 50_000) {
+ const dst = path.join(CLIPS_DIR, `${tag}.webm`);
+ fs.renameSync(src, dst);
+ result.clip = dst;
+ }
+ }
+ } catch (_) {}
+ try { fs.rmSync(siteClipDir, { recursive: true, force: true }); } catch (_) {}
+ }
+ result.ms = Date.now() - t0;
+ return result;
+}
+
+const browser = await launchBrowserWithRetry();
+console.log(`Playwright launched. ${live.length} sites to capture.\n`);
+
+for (let i = 0; i < live.length; i++) {
+ if (Date.now() - startTime > RUN_BUDGET_MS) {
+ console.log(`\n!! 90m budget exceeded — stopping at ${i}/${live.length}`);
+ break;
+ }
+ const site = live[i];
+ process.stdout.write(`[${i + 1}/${live.length}] ${site.name} ... `);
+ const r = await captureSite(browser, site, i + 1);
+ report.push(r);
+ if (r.error) console.log(`FAIL ${r.ms}ms (${r.error.slice(0, 80)})${r.clip ? ' [partial saved]' : ''}`);
+ else console.log(`OK ${r.ms}ms ${r.clip ? path.basename(r.clip) : '(no clip)'}`);
+
+ // tiny breather to be gentle on overloaded box
+ await new Promise(r => setTimeout(r, 250));
+}
+
+await browser.close();
+
+fs.writeFileSync(path.join(__dirname, 'tour-report.json'), JSON.stringify(report, null, 2));
+const ok = report.filter(r => r.clip);
+console.log(`\nCAPTURED ${ok.length}/${report.length} clips`);
+console.log(`Report: ${path.join(__dirname, 'tour-report.json')}`);
(oldest)
·
back to Wallpaper Tour
·
Add rel=noopener noreferrer to all 73 external target=_blank ce6149d →