← 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 &amp; 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">&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 &mdash; 3,600&#43; 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&rsquo;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&rsquo;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 &mdash; 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 (&ldquo;ochre botanical,&rdquo; &ldquo;putty linen&rdquo;), 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 &mdash; terracotta walls next to terracotta wovens &mdash; 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 &mdash; 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 &mdash; 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 &amp; 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,'&amp;').replace(/</g,'&lt;');
    // 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>