← back to Carmelwallpapers
Add linked Carmel disclosures and crawlable catalog sitemap
1342913ae5a09e0010faf8282e45665fb66930fc · 2026-09-10 10:35:35 -0700 · Steve Abrams
Files touched
A public/about.htmlA public/privacy.htmlM server.jsA verification/TK-11380/carmel-privacy-local.pngA verification/TK-11380/hospitality-privacy-local.pngA verification/TK-11380/verify-disclosures.cjs
Diff
commit 1342913ae5a09e0010faf8282e45665fb66930fc
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Sep 10 10:35:35 2026 -0700
Add linked Carmel disclosures and crawlable catalog sitemap
---
public/about.html | 8 +++
public/privacy.html | 10 ++++
server.js | 10 ++++
verification/TK-11380/carmel-privacy-local.png | Bin 0 -> 242508 bytes
.../TK-11380/hospitality-privacy-local.png | Bin 0 -> 249877 bytes
verification/TK-11380/verify-disclosures.cjs | 55 +++++++++++++++++++++
6 files changed, 83 insertions(+)
diff --git a/public/about.html b/public/about.html
new file mode 100644
index 0000000..7d9747d
--- /dev/null
+++ b/public/about.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>About — Carmel Wallpapers</title><meta name="description" content="Carmel Wallpapers is a Designer Wallcoverings service for coastal California projects, with product browsing, memo samples and trade assistance."><style>body{margin:0;background:#f7f3ec;color:#26221d;font:18px/1.7 system-ui,sans-serif}main{max-width:760px;margin:auto;padding:36px 24px}a{color:#60492c}h1,h2{line-height:1.2}nav{margin-bottom:36px}h2{margin-top:32px}</style></head>
+<body><main><nav><a href="/">Carmel Wallpapers</a> · <a href="/privacy">Privacy & cookies</a></nav><h1>About Carmel Wallpapers</h1>
+<p>Carmel Wallpapers is a <a href="https://designerwallcoverings.com">Designer Wallcoverings</a> service for residential and trade projects in Carmel-by-the-Sea, Pebble Beach and Big Sur. The catalog brings together wallcoverings that suit coastal interiors, including grasscloth, linen textures, botanicals and other natural-looking finishes.</p>
+<h2>Explore the catalog</h2><p>Browse by aesthetic, search a pattern or use the sort and density controls to compare products. Product pages show available imagery and provide links to the full product and memo-sample request at Designer Wallcoverings. Photography and screen settings affect how color appears, so a physical sample is useful before making a selection.</p>
+<h2>Plan for the room</h2><p>Consider the room's light, the wall condition and the finish you want. A quiet linen texture can provide a background for art and furnishings; botanical patterns can create a focal point; grasscloth adds variation and texture. Natural materials can vary between panels and production runs. Review samples and product information with your designer or installer before ordering.</p>
+<h2>Samples and project assistance</h2><p>For help narrowing a selection, finding product details, requesting samples or discussing trade service, contact <a href="mailto:info@carmelwallpapers.com">info@carmelwallpapers.com</a>. Include the product name or page link and the room or project you are planning. Availability and ordering details are confirmed through the linked store or through our team.</p>
+<h2>Contact</h2><p><a href="mailto:info@carmelwallpapers.com">info@carmelwallpapers.com</a> · <a href="https://designerwallcoverings.com">Designer Wallcoverings</a></p><p><a href="/">Browse wallcoverings</a></p></main></body></html>
diff --git a/public/privacy.html b/public/privacy.html
new file mode 100644
index 0000000..555af57
--- /dev/null
+++ b/public/privacy.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Privacy & cookies — Carmel Wallpapers</title><meta name="description" content="Carmel Wallpapers privacy notice: inquiries, browser preferences, analytics, advertising and your choices."><style>body{margin:0;background:#f7f3ec;color:#26221d;font:18px/1.7 system-ui,sans-serif}main{max-width:760px;margin:auto;padding:36px 24px}a{color:#60492c}h1,h2{line-height:1.2}nav{margin-bottom:36px}h2{margin-top:32px}</style></head>
+<body><main><nav><a href="/">Carmel Wallpapers</a> · <a href="/about">About</a></nav><h1>Privacy & cookies</h1><p>Updated September 10, 2026</p>
+<p>Carmel Wallpapers is a Designer Wallcoverings service. This notice describes information used when you browse our catalog or contact us.</p>
+<h2>Inquiries and catalog browsing</h2><p>If you email us or request help with a project, we use the information you provide to respond and assist with product selection. Orders and sample requests follow links to Designer Wallcoverings; the destination site's privacy information applies to those transactions. Contact <a href="mailto:info@carmelwallpapers.com">info@carmelwallpapers.com</a> with questions about information you have provided.</p>
+<h2>Preferences and technical information</h2><p>Your browser uses local storage to remember display preferences, including theme, sort and grid density. You can clear these settings through your browser. Hosting and security systems process page requests, which may include an IP address, browser details, requested page and request time, to deliver the service and investigate problems.</p>
+<h2>Analytics and advertising</h2><p>The site includes Google Analytics, Google AdSense and Meta Pixel technology. These services may receive page addresses, IP addresses, browser information and interactions, and may read or store cookies. Learn about <a href="https://policies.google.com/technologies/partner-sites">Google's use of information on partner sites</a> and <a href="https://www.facebook.com/privacy/policy/">Meta's privacy practices</a>.</p>
+<p>Advertising providers, including Google, may select ads using cookies associated with previous visits to this site or other websites. Google's advertising cookies can help Google and its partners personalize advertising across participating sites. Manage Google ad personalization at <a href="https://myadcenter.google.com/">My Ad Center</a>, Meta preferences through <a href="https://www.facebook.com/adpreferences/">Meta Ad Preferences</a>, and participating providers through <a href="https://optout.aboutads.info/">YourAdChoices</a>. Your browser settings can also restrict or remove cookies.</p>
+<h2>Other websites</h2><p>Linked retailers and external services have their own policies. This notice describes this site and does not control their practices.</p>
+<h2>Contact</h2><p>For privacy questions or requests concerning information you have shared with us, email <a href="mailto:info@carmelwallpapers.com">info@carmelwallpapers.com</a>.</p><p><a href="/">Return to the catalog</a></p></main></body></html>
diff --git a/server.js b/server.js
index ceccc42..071f55d 100644
--- a/server.js
+++ b/server.js
@@ -253,6 +253,14 @@ async function loadData() {
const app = express();
app.get('/ads.txt', (req, res) => res.type('text/plain').send('google.com, pub-5278231299883833, DIRECT, f08c47fec0942fa0'));
+app.get('/privacy', (req, res) => res.sendFile(path.join(__dirname, 'public', 'privacy.html')));
+app.get('/about', (req, res) => res.sendFile(path.join(__dirname, 'public', 'about.html')));
+app.get('/sitemap.xml', (req, res) => {
+ const routes = ['/', '/about', '/privacy', ...PRODUCTS.map(p => '/p/' + encodeURIComponent(redactVendorSlug(p.handle)))];
+ res.type('application/xml').send('<?xml version="1.0" encoding="UTF-8"?>' +
+ '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' +
+ [...new Set(routes)].map(p => '<url><loc>https://carmelwallpapers.com' + p + '</loc></url>').join('') + '</urlset>');
+});
app.set('trust proxy', 1);
// ── Static guard: never serve snapshot files (*.bak / *.pre-*) ────────────────
@@ -511,6 +519,7 @@ ${HEADER_HTML()}
<div class="footer-inner">
<p>Carmel Wallpapers — a Designer Wallcoverings service</p>
<p><a href="mailto:info@carmelwallpapers.com">info@carmelwallpapers.com</a></p>
+ <p><a href="/about">About</a> · <a href="/privacy">Privacy & cookies</a></p>
<p class="footer-fine">Serving Carmel-by-the-Sea, Pebble Beach & Big Sur</p>
</div>
</footer>
@@ -626,6 +635,7 @@ ${HEADER_HTML(q, sort, density)}
<strong>Carmel Wallpapers</strong>
<p>A Designer Wallcoverings service for the Carmel Coast.</p>
<p><a href="mailto:info@carmelwallpapers.com">info@carmelwallpapers.com</a></p>
+ <p><a href="/about">About</a> · <a href="/privacy">Privacy & cookies</a></p>
</div>
<div class="footer-col">
<strong>Our Service</strong>
diff --git a/verification/TK-11380/carmel-privacy-local.png b/verification/TK-11380/carmel-privacy-local.png
new file mode 100644
index 0000000..18452f7
Binary files /dev/null and b/verification/TK-11380/carmel-privacy-local.png differ
diff --git a/verification/TK-11380/hospitality-privacy-local.png b/verification/TK-11380/hospitality-privacy-local.png
new file mode 100644
index 0000000..ab169ec
Binary files /dev/null and b/verification/TK-11380/hospitality-privacy-local.png differ
diff --git a/verification/TK-11380/verify-disclosures.cjs b/verification/TK-11380/verify-disclosures.cjs
new file mode 100644
index 0000000..a0df421
--- /dev/null
+++ b/verification/TK-11380/verify-disclosures.cjs
@@ -0,0 +1,55 @@
+const assert = require('node:assert/strict');
+const fs = require('node:fs');
+const { chromium } = require('/Users/macstudio3/Projects/Designer-Wallcoverings/node_modules/playwright');
+const live = process.argv.includes('--live');
+const sites = [
+ { name: 'carmel', base: live ? 'https://carmelwallpapers.com' : 'http://127.0.0.1:19961', domain: 'carmelwallpapers.com', minimum: 1000, product: '/p/' },
+ { name: 'hospitality', base: live ? 'https://hospitalitydesignreps.com' : 'http://127.0.0.1:19963', domain: 'hospitalitydesignreps.com', minimum: 60, product: '/rep/' },
+];
+(async () => {
+ const browser = await chromium.launch({ executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', headless: true });
+ const proof = { timestamp: new Date().toISOString(), environment: live ? 'live' : 'local', sites: [] };
+ try {
+ for (const site of sites) {
+ const get = async path => {
+ const response = await fetch(site.base + path, { headers: { 'User-Agent': 'TK-11380-readiness-check' } });
+ assert.equal(response.status, 200, site.name + path);
+ return response.text();
+ };
+ const home = await get('/');
+ assert.ok(home.includes('href="/privacy"'));
+ const privacy = await get('/privacy');
+ assert.match(privacy, /My Ad Center/);
+ assert.match(privacy, /YourAdChoices/);
+ assert.ok(privacy.replace(/<[^>]+>/g, ' ').split(/\s+/).length > 150);
+ const about = await get('/about');
+ assert.doesNotMatch(about, /there is no advertising on the site/);
+ const robots = await get('/robots.txt');
+ assert.ok(robots.includes('https://' + site.domain + '/sitemap.xml'));
+ const xml = await get('/sitemap.xml');
+ const urls = [...xml.matchAll(/<loc>(.*?)<\/loc>/g)].map(m => m[1]);
+ assert.ok(urls.length >= site.minimum);
+ const first = urls.find(u => new URL(u).pathname.startsWith(site.product));
+ assert.ok(first);
+ await get(new URL(first).pathname);
+ const page = await browser.newPage();
+ const errors = [];
+ page.on('pageerror', e => errors.push(e.message));
+ // No advertising execution/impressions during readiness verification.
+ await page.route('**/*', route => {
+ const url = new URL(route.request().url());
+ return url.origin === new URL(site.base).origin ? route.continue() : route.abort();
+ });
+ await page.goto(site.base, { waitUntil: 'domcontentloaded' });
+ await page.locator('a[href="/privacy"]').first().click();
+ await page.waitForURL('**/privacy');
+ await page.locator('h1').filter({ hasText: 'Privacy' }).waitFor();
+ await page.screenshot({ path: __dirname + '/' + site.name + '-privacy-' + proof.environment + '.png', fullPage: true });
+ assert.deepEqual(errors, []);
+ proof.sites.push({ name: site.name, paths: 'PASS', publicPrivacyLink: 'PASS', privacyRender: 'PASS', sitemapUrls: urls.length, sampledProduct: new URL(first).pathname, jsErrors: errors, adRequestsBlocked: true });
+ await page.close();
+ }
+ fs.writeFileSync(__dirname + '/disclosures-' + proof.environment + '.json', JSON.stringify(proof, null, 2) + '\n');
+ console.log(JSON.stringify(proof));
+ } finally { await browser.close(); }
+})().catch(err => { console.error(err); process.exitCode = 1; });
← 14d2417 auto-data-snapshot: 2026-09-10T10:34:55 (2 data files) — pub
·
back to Carmelwallpapers
·
Record live disclosures, ownership verification and Carmel r acba1fb →