← back to Wallsandfabrics
public/index.html
557 lines
<!doctype html>
<html lang="en">
<head>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5278231299883833" crossorigin="anonymous"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Walls & Fabrics — matched-set storefront from Designer Wallcoverings</title>
<meta name="description" content="A purpose-built lens on the Designer Wallcoverings catalog. 3,600+ wallcoverings, fabrics, murals, and trims — searchable side-by-side, sortable by color wheel. Memo samples ship next business day.">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<!-- anti-flash theme — must run before paint -->
<script>
(function(){try{var t=localStorage.getItem('waf-theme');if(!t){t=window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light';}document.documentElement.setAttribute('data-theme',t);}catch(e){}})();
</script>
<!-- GA4 gtag (auto) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-429EVNMHVY"></script>
<script>
window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config','G-429EVNMHVY');
</script>
<!-- /GA4 gtag -->
<style>
:root{
--bg:#F4EFE6; --fg:#0E1116; --muted:#5b5042; --line:#d8cfbf;
--paper:#ffffff; --accent:#9A6E3A; --accent2:#46606D;
--shadow:0 1px 0 rgba(0,0,0,.05),0 12px 28px -16px rgba(0,0,0,.18);
--cols:5;
}
:root[data-theme="dark"]{
--bg:#0E1116; --fg:#F0EBE0; --muted:#9aa1ae; --line:#2a3038;
--paper:#171b22; --accent:#C99463; --accent2:#7BA3B3;
--shadow:0 1px 0 rgba(0,0,0,.4),0 14px 30px -14px rgba(0,0,0,.6);
}
*{box-sizing:border-box}
html,body{margin:0;background:var(--bg);color:var(--fg);font-family:Georgia,'Times New Roman',serif}
body{font-size:16px;line-height:1.5}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
.wrap{max-width:1320px;margin:0 auto;padding:0 22px}
header.site{position:sticky;top:0;z-index:20;background:var(--bg);border-bottom:1px solid var(--line)}
header.site .row{display:flex;align-items:center;gap:18px;padding:14px 22px}
.brand{font-family:'Bodoni Moda','Playfair Display',Georgia,serif;font-weight:700;font-size:22px;letter-spacing:.02em}
.brand .amp{color:var(--accent);font-style:italic;padding:0 .12em}
nav.top{display:flex;gap:18px;margin-left:auto;align-items:center;font-size:14px}
nav.top a{color:var(--fg);opacity:.78}
nav.top a:hover{opacity:1}
.theme-btn{background:none;border:1px solid var(--line);color:var(--fg);width:38px;height:38px;border-radius:50%;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:16px}
.theme-btn:hover{border-color:var(--accent)}
.hero{padding:72px 22px 36px;border-bottom:1px solid var(--line)}
.hero h1{font-family:'Bodoni Moda','Playfair Display',Georgia,serif;font-size:clamp(38px,5.4vw,64px);line-height:1.05;margin:0 0 18px;letter-spacing:-.012em;max-width:18ch}
.hero h1 em{font-style:italic;color:var(--accent);font-weight:500}
.hero p.lede{font-size:18px;color:var(--muted);max-width:62ch;margin:0 0 8px;line-height:1.5}
.hero .meta{font-size:12px;color:var(--muted);text-transform:uppercase;letter-spacing:.22em;margin-bottom:18px;font-family:-apple-system,'Helvetica Neue',sans-serif}
.editorial{padding:44px 0 56px;border-bottom:1px solid var(--line);background:linear-gradient(to bottom, transparent 0%, rgba(154,110,58,.04) 100%)}
.editorial .label{font-size:11px;color:var(--accent);text-transform:uppercase;letter-spacing:.26em;margin:0 0 14px;font-family:-apple-system,'Helvetica Neue',sans-serif;font-weight:600}
.editorial h2{font-family:'Bodoni Moda','Playfair Display',Georgia,serif;font-weight:400;font-size:clamp(22px,2.4vw,30px);line-height:1.25;margin:0 0 20px;max-width:38ch;color:var(--fg)}
.editorial .cols{display:grid;grid-template-columns:1fr 1fr;gap:36px;max-width:78ch}
@media (max-width:720px){.editorial .cols{grid-template-columns:1fr;gap:18px}}
.editorial p{font-size:15.5px;line-height:1.65;color:var(--fg);opacity:.86;margin:0 0 14px}
.editorial p:last-child{margin-bottom:0}
.editorial p em{color:var(--accent);font-style:italic}
.howto{padding:54px 0 60px;border-bottom:1px solid var(--line)}
.howto .label{font-size:11px;color:var(--accent);text-transform:uppercase;letter-spacing:.26em;margin:0 0 14px;font-family:-apple-system,'Helvetica Neue',sans-serif;font-weight:600}
.howto h2{font-family:'Bodoni Moda','Playfair Display',Georgia,serif;font-weight:400;font-size:clamp(22px,2.4vw,30px);line-height:1.25;margin:0 0 32px;max-width:34ch;color:var(--fg)}
.howto .steps{display:grid;grid-template-columns:repeat(3,1fr);gap:32px}
@media (max-width:820px){.howto .steps{grid-template-columns:1fr;gap:22px}}
.howto .step{display:flex;flex-direction:column;gap:10px;padding-top:22px;border-top:1px solid var(--line)}
.howto .num{font-family:'Bodoni Moda',Georgia,serif;font-size:13px;letter-spacing:.22em;color:var(--accent);font-style:italic}
.howto .step h3{font-family:-apple-system,'Helvetica Neue',sans-serif;font-weight:600;font-size:15px;margin:0;color:var(--fg);letter-spacing:.01em}
.howto .step p{font-size:14.5px;line-height:1.6;color:var(--fg);opacity:.78;margin:0}
.howto .footnote{margin:28px 0 0;font-size:13px;color:var(--muted);font-style:italic;max-width:62ch}
.surface-tabs{display:flex;flex-wrap:wrap;gap:8px;margin:24px 0 0}
.surface-tab{padding:10px 16px;border:1px solid var(--line);background:var(--paper);color:var(--fg);border-radius:999px;cursor:pointer;font:inherit;font-size:13px;letter-spacing:.05em;text-transform:uppercase}
.surface-tab[aria-pressed="true"]{background:var(--fg);color:var(--bg);border-color:var(--fg)}
.surface-tab .count{opacity:.6;margin-left:6px;font-variant-numeric:tabular-nums}
.toolbar{display:flex;flex-wrap:wrap;gap:14px;align-items:center;padding:18px 0;border-bottom:1px solid var(--line);margin-bottom:24px}
.toolbar .grow{flex:1;min-width:200px}
.toolbar input[type="search"]{width:100%;padding:11px 14px;font:inherit;border:1px solid var(--line);background:var(--paper);color:var(--fg);border-radius:8px}
.toolbar label{font-size:12px;color:var(--muted);text-transform:uppercase;letter-spacing:.12em}
.toolbar select{padding:10px 12px;font:inherit;border:1px solid var(--line);background:var(--paper);color:var(--fg);border-radius:8px}
.toolbar input[type="range"]{accent-color:var(--accent);width:140px}
.toolbar .group{display:flex;align-items:center;gap:8px}
.grid{display:grid;grid-template-columns:repeat(var(--cols),1fr);gap:18px;padding:8px 0 60px}
@media (max-width:1100px){.grid{grid-template-columns:repeat(min(var(--cols),4),1fr)}}
@media (max-width:820px){.grid{grid-template-columns:repeat(min(var(--cols),3),1fr)}}
@media (max-width:560px){.grid{grid-template-columns:repeat(min(var(--cols),2),1fr)}}
.card{background:var(--paper);border:1px solid var(--line);border-radius:10px;overflow:hidden;display:flex;flex-direction:column;transition:transform .15s,box-shadow .15s}
.card:hover{transform:translateY(-2px);box-shadow:var(--shadow)}
.card .img{aspect-ratio:1/1;background:#0001;display:block;background-size:cover;background-position:center}
.card .body{padding:11px 12px 12px}
.card .surface{font-size:10px;letter-spacing:.18em;text-transform:uppercase;color:var(--accent);font-weight:700}
.card .ttl{font-size:14px;line-height:1.35;margin:4px 0 6px;color:var(--fg);min-height:2.6em;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.card .meta{font-size:12px;color:var(--muted);display:flex;justify-content:space-between;gap:8px}
.card a.smp{display:inline-block;margin-top:8px;font-size:12px;letter-spacing:.05em;text-transform:uppercase;color:var(--accent2);border-bottom:1px solid var(--accent2);padding-bottom:1px}
.pager{display:flex;justify-content:center;gap:10px;margin:30px 0 60px}
.pager button{padding:10px 18px;border:1px solid var(--line);background:var(--paper);color:var(--fg);border-radius:6px;cursor:pointer;font:inherit;font-size:14px}
.pager button:disabled{opacity:.4;cursor:not-allowed}
.pager .info{align-self:center;color:var(--muted);font-size:13px;font-variant-numeric:tabular-nums}
.empty{padding:80px 20px;text-align:center;color:var(--muted)}
.empty h2{color:var(--fg);font-family:'Bodoni Moda',Georgia,serif;font-weight:400;margin-bottom:8px}
footer.site{border-top:1px solid var(--line);padding:36px 0;font-size:13px;color:var(--muted);background:var(--paper)}
footer.site .grid-foot{display:grid;grid-template-columns:2fr 1fr 1fr;gap:30px}
@media (max-width:720px){footer.site .grid-foot{grid-template-columns:1fr}}
footer.site h4{font-size:11px;text-transform:uppercase;letter-spacing:.18em;color:var(--fg);margin:0 0 10px}
footer.site a{color:var(--accent2)}
.legal{margin-top:18px;font-size:12px;color:var(--muted)}
/* ── Grid / List view ── */
html.waf-list #results.grid { display:block; padding-bottom:60px; }
html.waf-list .card { display:flex; flex-direction:row; align-items:stretch; border-radius:8px; margin-bottom:10px; }
html.waf-list .card .img { flex:0 0 clamp(72px,14vw,140px); width:clamp(72px,14vw,140px); min-height:72px; aspect-ratio:auto; border-radius:8px 0 0 8px; }
html.waf-list .card .body { flex:1; display:flex; flex-direction:column; justify-content:center; }
html.waf-list .card:hover { transform:none; }
/* ── Per-field hide rules (html.hf-<field>) ── */
html.hf-image .card .img { display:none; }
html.hf-surface [data-f="surface"] { display:none; }
html.hf-title [data-f="title"] { display:none; }
html.hf-meta [data-f="meta"] { display:none; }
html.hf-sample [data-f="sample"] { display:none; }
/* ── Fields popover labels ── */
#wafFpop label { display:flex; align-items:center; gap:10px; padding:7px 14px; font-size:12px; letter-spacing:.05em; text-transform:uppercase; color:var(--fg); cursor:pointer; white-space:nowrap; font-family:inherit; }
#wafFpop label:hover { background:var(--bg); border-radius:4px; }
#wafFpop input[type=checkbox] { accent-color:var(--accent); width:13px; height:13px; cursor:pointer; }
</style>
<!-- Meta Pixel — INERT until set-fb-pixel.sh is run (does NOT load fbevents.js or hit Meta until flipped) -->
<script>
(function(){
var PIXEL_ID = '1431180262113856';
// Inert until flipped — the brace check prevents BOTH the loader IIFE
// AND the init call from running, so fbevents.js is never fetched.
if (PIXEL_ID.indexOf('{{') !== -1) return;
!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
document,'script','https://connect.facebook.net/en_US/fbevents.js');
fbq('init', PIXEL_ID);
fbq('track', 'PageView');
})();
</script>
<!-- noscript fallback is added by set-fb-pixel.sh on flip (omitted here to avoid unflipped img phone-home) -->
<noscript><img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=1431180262113856<!-- End Meta Pixel -->ev=PageView<!-- End Meta Pixel -->noscript=1"/></noscript>
<!-- End Meta Pixel -->
</head>
<body>
<!-- Theme toggle (theme1 / theme2) — query-param-gated preview surface
(Steve 2026-05-29). ?theme=2 in URL flips to theme2 and persists in
localStorage. Toggle UI hidden until theme2 mode active. Same key
('wallco-page-theme') as wallco.ai for cross-site continuity. -->
<script>
(function(){
var qs = new URLSearchParams(location.search);
var fromUrl = qs.get('theme');
var saved = null; try { saved = localStorage.getItem('wallco-page-theme'); } catch(e){}
var theme = null;
if (fromUrl === '2' || fromUrl === 'theme2') theme = 'theme2';
else if (fromUrl === '1' || fromUrl === 'theme1') theme = 'theme1';
else if (saved === 'theme2' || saved === 'theme1') theme = saved;
if (theme) {
document.documentElement.setAttribute('data-page-theme', theme);
if (fromUrl) { try { localStorage.setItem('wallco-page-theme', theme); } catch(e){} }
}
})();
</script>
<style id="page-theme-css-fleet">
[data-page-theme="theme2"] body{
background:#fff !important; color:#0a0a0a !important;
}
[data-page-theme="theme2"] h1,
[data-page-theme="theme2"] h2,
[data-page-theme="theme2"] h3{ letter-spacing:-0.01em; font-weight:500; }
[data-page-theme="theme2"] .card,
[data-page-theme="theme2"] [class*="card"]{
background:#fafafa !important; border:1px solid #e5e5e5 !important; box-shadow:none !important;
}
#page-theme-toggle{ display:none; }
[data-page-theme] #page-theme-toggle{ display:inline-flex; }
#page-theme-toggle{
position:fixed; top:96px; right:14px; z-index:90;
gap:0; align-items:center;
background:rgba(255,255,255,.92); border:1px solid #d8d2c5;
border-radius:999px; padding:3px; backdrop-filter:blur(6px);
box-shadow:0 2px 10px rgba(0,0,0,.08);
font:600 11px ui-sans-serif,system-ui; letter-spacing:.06em; text-transform:uppercase;
}
#page-theme-toggle button{
border:0; background:transparent; color:#5a5048; cursor:pointer;
padding:6px 14px; border-radius:999px; font:inherit; transition:all .15s;
}
#page-theme-toggle button[aria-pressed="true"]{ background:#1a1714; color:#faf7f2; }
#page-theme-toggle button:hover:not([aria-pressed="true"]){ color:#1a1714; }
</style>
<div id="page-theme-toggle" role="group" aria-label="Page theme">
<button type="button" data-theme="theme1" aria-pressed="false">Theme 1</button>
<button type="button" data-theme="theme2" aria-pressed="false">Theme 2</button>
</div>
<script>
(function(){
var cur = document.documentElement.getAttribute('data-page-theme') || 'theme1';
var btns = document.querySelectorAll('#page-theme-toggle button');
function sync(t){ btns.forEach(function(b){ b.setAttribute('aria-pressed', b.dataset.theme === t ? 'true' : 'false'); }); }
sync(cur);
btns.forEach(function(b){
b.addEventListener('click', function(){
var t = b.dataset.theme;
document.documentElement.setAttribute('data-page-theme', t);
try { localStorage.setItem('wallco-page-theme', t); } catch(e){}
sync(t);
});
});
})();
</script>
<header class="site">
<div class="wrap row">
<div class="brand">Walls <span class="amp">&</span> Fabrics</div>
<nav class="top">
<a href="#grid" data-surface="all">All</a>
<a href="#grid" data-surface="wallcovering">Walls</a>
<a href="#grid" data-surface="fabric">Fabrics</a>
<a href="#grid" data-surface="mural">Murals</a>
<a href="#grid" data-surface="trim">Trim</a>
<button id="themeBtn" class="theme-btn" type="button" aria-label="Toggle dark / light">☼</button>
</nav>
</div>
</header>
<section class="hero wrap" id="hero">
<div class="meta">A Designer Wallcoverings property · Est. 2026</div>
<h1>When the wall <em>and</em> the fabric both have to land.</h1>
<p class="lede">A purpose-built lens on the Designer Wallcoverings catalog — 3,600+ wallcoverings, fabrics, murals, and trims, searchable side-by-side and sortable by color wheel.</p>
<div class="surface-tabs" id="surfaceTabs"></div>
</section>
<section class="editorial">
<div class="wrap">
<div class="label">About the Collection</div>
<h2>Most catalogs make you switch contexts — wallpaper here, textiles over there. This one doesn’t.</h2>
<div class="cols">
<p>Designers who work in <em>walls and fabrics</em> together know the problem. A botanical paper needs a drapery that won’t fight it. A grasscloth needs the right woven texture in the cushion. Pulling them from separate catalogs, separate sites, separate spec sheets is the friction that eats an afternoon.</p>
<p>This site is the narrower lens: every wallcovering, fabric, mural, and trim we carry, in one searchable grid. Sort by color wheel and scroll terracotta to teal across both surfaces at once. Tag a paper you like, hold its hue against the coordinating linen, and order the memo samples side-by-side — they ship the next business day from the same warehouse.</p>
</div>
</div>
</section>
<section class="howto">
<div class="wrap">
<div class="label">How matched-set browsing works</div>
<h2>Three small moves that turn a 3,600-SKU catalog into a paired set in an afternoon.</h2>
<div class="steps">
<div class="step">
<div class="num">01 — Filter</div>
<h3>Pick a surface, then narrow.</h3>
<p>Start with Walls or Fabrics, search a pattern name or color word (“ochre botanical,” “putty linen”), and let the grid prune to the working shortlist. Color-wheel sort puts adjacent hues next to each other so a near-match jumps the eye.</p>
</div>
<div class="step">
<div class="num">02 — Pair</div>
<h3>Hold a wall against a fabric in the same hue band.</h3>
<p>Switch the surface filter to the other side without losing your color sort. The grid keeps you in the same chromatic range — terracotta walls next to terracotta wovens — so coordinating picks read together instead of fighting.</p>
</div>
<div class="step">
<div class="num">03 — Memo</div>
<h3>Order both samples in one pass.</h3>
<p>Tap <em>Request memo</em> on each card. Memos for walls and fabrics ship the next business day from the same Sherman Oaks warehouse on a single packing slip — no hunting two vendor portals to reconcile arrival dates.</p>
</div>
</div>
<p class="footnote">Trade pricing, quantity quotes, and CFA cuttings available on request — reach out to <a href="mailto:info@wallsandfabrics.com">info@wallsandfabrics.com</a> or call 888-373-4564.</p>
</div>
</section>
<section class="wrap" id="grid">
<div class="toolbar">
<div class="grow"><input id="q" type="search" placeholder="Search by name, color, surface…" autocomplete="off"></div>
<div class="group"><label for="sort">Sort</label>
<select id="sort">
<option value="newest">Newest</option>
<option value="light-dark">Light → Dark</option>
<option value="dark-light">Dark → Light</option>
<option value="wheel">Color Wheel</option>
<option value="color">Color</option>
<option value="style">Style</option>
<option value="sku">SKU A→Z</option>
<option value="title">Title A→Z</option>
<option value="price-asc">Price ↑</option>
<option value="price-desc">Price ↓</option>
</select>
</div>
<div class="group"><label for="cols">Density</label>
<input id="cols" type="range" min="3" max="8" step="1" value="5">
</div>
<!-- Grid/List view toggle -->
<div class="group" role="group" aria-label="View mode" style="gap:0;border:1px solid var(--line);border-radius:8px;overflow:hidden;">
<button id="wafVGrid" style="padding:9px 14px;background:var(--fg);color:var(--bg);border:0;font-size:12px;letter-spacing:.05em;text-transform:uppercase;cursor:pointer;font-family:inherit;" title="Grid view">Grid</button>
<button id="wafVList" style="padding:9px 14px;background:transparent;color:var(--muted);border:0;border-left:1px solid var(--line);font-size:12px;letter-spacing:.05em;text-transform:uppercase;cursor:pointer;font-family:inherit;" title="List view">List</button>
</div>
<!-- Fields ▾ menu -->
<div class="waf-fieldmenu" style="position:relative;">
<button id="wafFieldsBtn" style="padding:10px 14px;border:1px solid var(--line);background:var(--paper);border-radius:8px;font:inherit;font-size:12px;letter-spacing:.05em;text-transform:uppercase;color:var(--muted);cursor:pointer;" aria-haspopup="true" aria-expanded="false">Fields ▾</button>
<div id="wafFpop" hidden style="position:absolute;top:calc(100% + 8px);right:0;background:var(--paper);border:1px solid var(--line);box-shadow:0 12px 28px rgba(0,0,0,.12);padding:6px 4px;z-index:40;min-width:180px;border-radius:8px;" role="menu"></div>
</div>
</div>
<div id="results" class="grid"></div>
<div class="pager">
<button id="prev" disabled>← Previous</button>
<span class="info" id="info">—</span>
<button id="next" disabled>Next →</button>
</div>
</section>
<footer class="site">
<div class="wrap grid-foot">
<div>
<h4>Walls & Fabrics</h4>
<p>A Designer Wallcoverings sister site. Trade orders, memo samples, and quantity quotes available — same catalog, narrower lens.</p>
<p class="legal">© <span id="yr">2026</span> Designer Wallcoverings · 15442 Ventura Bl #102, Sherman Oaks CA 91403</p>
</div>
<div>
<h4>Contact</h4>
<p><a href="mailto:info@wallsandfabrics.com">info@wallsandfabrics.com</a></p>
<p><a href="tel:+18883734564">888-373-4564</a></p>
</div>
<div>
<h4>Browse</h4>
<p><a href="#" data-surface="wallcovering">Wallcoverings</a></p>
<p><a href="#" data-surface="fabric">Fabrics</a></p>
<p><a href="#" data-surface="mural">Murals</a></p>
<p><a href="#" data-surface="trim">Trim</a></p>
</div>
</div>
<div style="text-align:center;padding:8px 0;font-size:11px"><a href="/privacy.html" style="color:inherit;opacity:.7;text-decoration:none">Privacy Policy</a></div>
</footer>
<style>a.drill{text-decoration:none}.card .surface.drill,.surface-tab.drill{cursor:pointer}.card .surface.drill{border-bottom:1px dotted transparent}.card .surface.drill:hover{border-bottom-color:currentColor;opacity:.85}</style>
<script src="/drill.js"></script>
<script>
(function(){
// theme toggle
var btn=document.getElementById('themeBtn');
function setTheme(t){document.documentElement.setAttribute('data-theme',t);try{localStorage.setItem('waf-theme',t);}catch(e){}btn.textContent=(t==='dark')?'☾':'☼';}
setTheme(document.documentElement.getAttribute('data-theme')||'light');
btn.addEventListener('click',function(){setTheme(document.documentElement.getAttribute('data-theme')==='dark'?'light':'dark');});
// density slider
var sl=document.getElementById('cols');
function applyCols(v){document.documentElement.style.setProperty('--cols',String(v));try{localStorage.setItem('waf-cols',String(v));}catch(e){}}
var savedCols=Number(localStorage.getItem('waf-cols'))||5;
sl.value=String(savedCols); applyCols(savedCols);
sl.addEventListener('input',function(){applyCols(Number(sl.value));});
// sort persistence
var sortSel=document.getElementById('sort');
var savedSort=localStorage.getItem('waf-sort')||'newest';
sortSel.value=savedSort;
sortSel.addEventListener('change',function(){try{localStorage.setItem('waf-sort',sortSel.value);}catch(e){}reload(1);sfWriteURL(false);});
// search debounce
var q=document.getElementById('q'); var qTimer=null;
q.addEventListener('input',function(){clearTimeout(qTimer);qTimer=setTimeout(function(){reload(1);sfWriteURL(false);},250);});
// surface tabs
var SURFACE_LABELS={all:'All',wallcovering:'Walls',fabric:'Fabrics',mural:'Murals',trim:'Trim'};
var state={surface:'all',page:1};
// ── href-to-deeper-data (HARD rule) — mirror the `surface` facet (+ q + sort) into the URL so any
// filtered view is a real shareable/deep-linkable href; surface values become .drill atoms. drill.js
// is loaded for the fleet atom contract + the enforcer canary; the pager logic below is this site's.
function sfQstr(){
var u=new URLSearchParams();
if(state.surface && state.surface!=='all') u.set('surface',state.surface);
var qv=q.value.trim(); if(qv) u.set('q',qv);
if(sortSel.value && sortSel.value!=='newest') u.set('sort',sortSel.value);
if(state.page && state.page>1) u.set('page',String(state.page));
var s=u.toString(); return s?('?'+s):location.pathname;
}
function sfWriteURL(push){ try{ history[push?'pushState':'replaceState']({},'',sfQstr()); }catch(_){} }
function sfReadURL(){
var u=new URLSearchParams(location.search);
state.surface=u.get('surface')||'all';
if(u.get('q')!=null) q.value=u.get('q');
if(u.get('sort')){ sortSel.value=u.get('sort'); try{localStorage.setItem('waf-sort',sortSel.value);}catch(e){} }
state.page=Math.max(1, parseInt(u.get('page')||'1',10)||1);
}
var tabs=document.getElementById('surfaceTabs');
// Set the surface facet, write it into the URL history, and reload the grid (drill-down).
function sfSetSurface(k, scroll){
state.surface=k||'all';
reload(1);
sfWriteURL(true);
if(scroll){ var g=document.getElementById('grid'); if(g) g.scrollIntoView({behavior:'smooth',block:'start'}); }
}
window.sfSetSurface=sfSetSurface;
function buildTabs(counts){
var totalAll=Object.values(counts).reduce(function(a,b){return a+b;},0);
var keys=['all','wallcovering','fabric','mural','trim'];
tabs.innerHTML=keys.map(function(k){
var n=k==='all'?totalAll:(counts[k]||0);
var label=SURFACE_LABELS[k]||k;
// Each surface tab is a REAL href (?surface=k) so every count is a link to its deeper data.
var href=(k==='all')?location.pathname:('?surface='+encodeURIComponent(k));
return '<a class="surface-tab drill" href="'+href+'" data-filter="surface" data-surface="'+k+'" data-v="'+k+'" aria-pressed="'+(state.surface===k?'true':'false')+'">'+label+'<span class="count">'+n.toLocaleString()+'</span></a>';
}).join('');
}
// Delegated: plain click on any surface .drill atom (tabs, nav, footer, in-card) filters in place;
// modifier / middle-click keeps the real href so it opens in a new tab.
document.addEventListener('click',function(e){
var d=e.target.closest('.drill[data-filter="surface"]'); if(!d) return;
if(e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||e.button===1) return;
e.preventDefault(); e.stopPropagation();
sfSetSurface(d.dataset.v||d.dataset.surface, true);
});
addEventListener('popstate',function(){ sfReadURL(); reload(state.page); });
// nav links + footer browse links become real surface hrefs + drill atoms
Array.from(document.querySelectorAll('a[data-surface]')).forEach(function(a){
var k=a.dataset.surface;
a.setAttribute('href', (k==='all')?'/':('?surface='+encodeURIComponent(k)));
a.classList.add('drill'); a.dataset.filter='surface'; a.dataset.v=k;
});
// pager
var prev=document.getElementById('prev'),next=document.getElementById('next'),info=document.getElementById('info');
prev.addEventListener('click',function(){if(state.page>1){reload(state.page-1);sfWriteURL(false);}});
next.addEventListener('click',function(){reload(state.page+1);sfWriteURL(false);});
function fmt$(p){if(!p||!p.max_price)return '';var n=Number(p.max_price);return isFinite(n)?'$'+n.toFixed(0):'';}
function card(p){
var img='<span class="img" style="background-image:url(\''+(p.image_url||'').replace(/'/g,'%27')+'\')"></span>';
var url=p.product_url||'https://designerwallcoverings.com/products/'+encodeURIComponent(p.handle||p.sku||'');
var surface=(p.__surface||'wallcovering');
var label=SURFACE_LABELS[surface]||surface;
var ttl=(p.title||'(untitled)').replace(/&/g,'&').replace(/</g,'<');
// Standing rule — never render DW vendor names on customer surfaces.
var brand='Designer Wallcoverings';
// The surface label is a drill atom (span, NOT a nested <a>) — the card itself is already an <a>.
var surfaceAtom='<span class="surface drill" data-filter="surface" data-surface="'+surface+'" data-v="'+surface+'" title="See all '+label+'">'+label+'</span>';
return '<a href="'+url+'" target="_blank" rel="noopener noreferrer" class="card" data-sku="'+(p.sku||p.handle||'')+'">'
+img
+'<div class="body">'+surfaceAtom+'<div class="ttl" data-f="title">'+ttl+'</div>'
+'<div class="meta" data-f="meta"><span>'+brand+'</span><span>'+fmt$(p)+'</span></div>'
+'<span class="smp" data-f="sample">Request memo →</span></div></a>';
}
function renderEmpty(){
document.getElementById('results').innerHTML='<div class="empty"><h2>No matches</h2><p>Try widening your search or selecting a different surface.</p></div>';
info.textContent='0 results';
prev.disabled=true; next.disabled=true;
}
function reload(page){
state.page=page||1;
var params=new URLSearchParams({sort:sortSel.value,page:String(state.page),limit:'24'});
if(q.value.trim()) params.set('q',q.value.trim());
if(state.surface && state.surface!=='all') params.set('surface',state.surface);
info.textContent='Loading…';
fetch('/api/products?'+params.toString()).then(function(r){return r.json();}).then(function(j){
buildTabs(j.surfaces||{});
if(!j.items || !j.items.length){renderEmpty();return;}
document.getElementById('results').innerHTML=j.items.map(card).join('');
info.textContent=(j.total.toLocaleString())+' results · page '+j.page+' of '+j.pages;
prev.disabled=j.page<=1;
next.disabled=j.page>=j.pages;
}).catch(function(e){info.textContent='Error loading.';console.error(e);});
}
document.getElementById('yr').textContent=new Date().getFullYear();
sfReadURL(); // deep-link / back-button: hydrate surface + q + sort + page from the URL
reload(state.page); // render at the URL-provided state
sfWriteURL(false); // normalize the URL to the canonical filter-set
// ── Grid/List view toggle ─────────────────────────────────────────────────
const WAF_FIELDS = [
['image', 'Image', true],
['surface', 'Surface', true],
['title', 'Title', true],
['meta', 'Price', true],
['sample', 'Sample', true],
];
function wafLoadFields(){
const def = {}; WAF_FIELDS.forEach(([k,,d]) => def[k] = d);
try { return Object.assign(def, JSON.parse(localStorage.getItem('waf-fields') || '{}')); } catch(e){ return def; }
}
var wafFieldState = wafLoadFields();
function wafApplyView(){
var v = localStorage.getItem('waf-view') || 'grid';
document.documentElement.classList.toggle('waf-list', v === 'list');
var gBtn = document.getElementById('wafVGrid'), lBtn = document.getElementById('wafVList');
if(gBtn){ gBtn.style.background = v==='grid'?'var(--fg)':'transparent'; gBtn.style.color = v==='grid'?'var(--bg)':'var(--muted)'; }
if(lBtn){ lBtn.style.background = v==='list'?'var(--fg)':'transparent'; lBtn.style.color = v==='list'?'var(--bg)':'var(--muted)'; }
}
var wafVG = document.getElementById('wafVGrid'), wafVL = document.getElementById('wafVList');
if(wafVG) wafVG.addEventListener('click', function(){ localStorage.setItem('waf-view','grid'); wafApplyView(); });
if(wafVL) wafVL.addEventListener('click', function(){ localStorage.setItem('waf-view','list'); wafApplyView(); });
wafApplyView();
// ── Fields ▾ menu ─────────────────────────────────────────────────────────
function wafApplyFields(){
WAF_FIELDS.forEach(function(f){ document.documentElement.classList.toggle('hf-' + f[0], !wafFieldState[f[0]]); });
}
function wafBuildFieldMenu(){
var pop = document.getElementById('wafFpop'); if(!pop) return;
pop.innerHTML = WAF_FIELDS.map(function(f){ return '<label><input type="checkbox" data-field="'+f[0]+'"'+(wafFieldState[f[0]]?' checked':'')+'> '+f[1]+'</label>'; }).join('');
pop.querySelectorAll('input').forEach(function(cb){ cb.addEventListener('change', function(){
wafFieldState[cb.dataset.field] = cb.checked;
localStorage.setItem('waf-fields', JSON.stringify(wafFieldState));
wafApplyFields();
}); });
}
var wafFBtn = document.getElementById('wafFieldsBtn'), wafFpop = document.getElementById('wafFpop');
if(wafFBtn && wafFpop){
wafFBtn.addEventListener('click', function(e){
e.stopPropagation(); var open = wafFpop.hidden; wafFpop.hidden = !open;
wafFBtn.setAttribute('aria-expanded', String(open));
});
document.addEventListener('click', function(e){
if(!wafFpop.hidden && !e.target.closest('.waf-fieldmenu')){ wafFpop.hidden = true; wafFBtn.setAttribute('aria-expanded','false'); }
});
}
wafBuildFieldMenu();
wafApplyFields();
})();
</script>
<!-- Big Red customer chat — lower-left, woman-avatar, vendor-redacted retail persona. -->
<script data-bigred-widget src="https://chat.designerwallcoverings.com/widget.js"
data-host="https://chat.designerwallcoverings.com"
data-mode="retail"
defer onerror="this.remove()"></script>
<script src="/sku-redact.js" defer></script>
<!-- corner-nav disabled (UL-hamburger rebuild) -->
<script>window.DwHeaderConfig={siteName:"Walls & Fabrics",accent:"#9B7B4F",navLinks:[{name:'Home',url:'/'},{name:'Collections',url:'#shop'},{name:'About',url:'/about'},{name:'Care',url:'/care.html'},{name:'History',url:'/history.html'}],adminLinks:[{name:'Shopify admin',url:'https://admin.shopify.com',external:true},{name:'Edit hero',url:'#'},{name:'Analytics',url:'#'}]};</script>
<script src="/dw-header.js" defer></script>
<script src="/dw-theme-nav.js" defer></script>
<!-- DW network ad slot -->
<script src="https://ads.agentabrams.com/embed.js" data-slot="footer-strip"></script>
</body>
</html>