← back to Atomic50 Onboard
public/index.html
427 lines
<!doctype html><html lang="en"><head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>Atomic 50 — A Designer Wallcoverings Collection</title>
<meta name="description" content="Atomic 50 pressed-tin ceilings & wall panels — a Designer Wallcoverings collection.">
<link rel="canonical" href="https://atomic50.designerwallcoverings.com/">
<link rel="icon" href="/favicon.ico">
<style>
:root{
--bg:#efece6;--paper:#fbf9f4;--ink:#20211d;--accent:#7a6a4c;--gold:#9a7c3f;--line:#ddd6c9;--taupe:#a89e8a;
--cols:4;--fs:1;
--white:#f4f4f2;--grey:#9aa0aa;--black:#1c1e24;--orange:#e08a3c;--gold-c:#c9a75a;--green:#5f8f5c;--blue:#4f7fb5;--purple:#8a6fb5;--pink:#f3a8c0;--red:#d0453e;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);
font:15px/1.55 "Georgia","Times New Roman",serif}
a{color:inherit}
.sans{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
/* ── house chrome ── */
header.house{position:sticky;top:0;z-index:20;background:var(--paper);border-bottom:1px solid var(--line);
display:flex;align-items:center;justify-content:space-between;padding:14px 26px}
.wordmark{font-size:15px;font-weight:700;letter-spacing:.14em;text-transform:uppercase}
.wordmark small{display:block;font-size:9px;letter-spacing:.24em;color:var(--taupe);font-weight:600}
nav.house{display:flex;gap:22px;font-size:12px;letter-spacing:.06em;text-transform:uppercase}
nav.house a{text-decoration:none;color:var(--ink);opacity:.8}
nav.house a:hover{opacity:1;color:var(--accent)}
/* ── hero ── */
.hero{position:relative;min-height:52vh;display:flex;align-items:flex-end;
background:#20211d center/cover no-repeat;color:#fff;padding:48px 30px 40px}
.hero::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,#0000 30%,#000000c9);z-index:0}
.hero .hz{position:relative;z-index:1;max-width:760px}
.hero .eyebrow{font-size:11px;letter-spacing:.28em;text-transform:uppercase;opacity:.9;margin:0 0 14px}
.hero h1{font-size:clamp(30px,5vw,54px);line-height:1.04;margin:0 0 12px;font-weight:400}
.hero .kicker{font-size:12px;letter-spacing:.18em;text-transform:uppercase;opacity:.85;margin:0 0 10px}
.hero .tag{font-size:17px;max-width:600px;opacity:.95;margin:0}
/* ── about ── */
.about{background:var(--paper);border-bottom:1px solid var(--line);padding:34px 30px}
.about .in{max-width:820px;margin:0 auto}
.about p{margin:0 0 14px;color:#3a3a34}
.about .collab{font-style:italic;color:var(--accent);border-top:1px solid var(--line);padding-top:14px;margin-top:6px}
/* ── controls bar ── */
.bar{position:sticky;top:53px;z-index:15;background:var(--bg);border-bottom:1px solid var(--line);
display:flex;align-items:center;gap:18px;flex-wrap:wrap;padding:12px 26px}
.bar .count{font-family:-apple-system,sans-serif;font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--taupe)}
.ctl{display:flex;align-items:center;gap:8px;font-family:-apple-system,sans-serif;font-size:12px}
.ctl label{letter-spacing:.06em;text-transform:uppercase;color:var(--taupe)}
select,input[type=text]{background:var(--paper);color:var(--ink);border:1px solid var(--line);border-radius:4px;padding:6px 9px;font-size:13px;font-family:-apple-system,sans-serif}
input[type=range]{accent-color:var(--accent)}
.chipf{display:flex;gap:6px;flex-wrap:wrap}
.chipf a.chip-a{text-decoration:none;display:inline-flex}
.chipf button{background:var(--paper);border:1px solid var(--line);border-radius:999px;padding:4px 12px;font-size:12px;
font-family:-apple-system,sans-serif;cursor:pointer;color:#4a463c;letter-spacing:.02em}
.chipf button:hover{border-color:var(--accent)}
.chipf button.on{background:var(--ink);color:#fff;border-color:var(--ink)}
.chipf button .dot{width:9px;height:9px;border-radius:50%;display:inline-block;margin-right:5px;vertical-align:middle;border:1px solid #0003}
/* ── grid ── */
.grid{display:grid;grid-template-columns:repeat(var(--cols),1fr);gap:14px;padding:22px 26px 70px;max-width:1500px;margin:0 auto}
.card{background:var(--paper);border:1px solid var(--line);border-radius:3px;overflow:hidden;
display:flex;flex-direction:column;cursor:pointer;transition:box-shadow .15s,border-color .15s}
.card:hover{border-color:var(--accent);box-shadow:0 6px 22px #0000001a}
.imgwrap{position:relative;width:100%;aspect-ratio:1/1;background:#e6e1d8}
.imgwrap img{width:100%;height:100%;object-fit:cover;display:block}
.imgwrap img.spin{position:absolute;inset:0;opacity:0;transition:opacity .2s}
.imgwrap:hover img.spin{opacity:1}
.imgwrap .spinbadge{position:absolute;top:8px;right:8px;background:#000a;color:#fff;font-size:9px;font-weight:700;
border-radius:4px;padding:2px 7px;letter-spacing:.06em;font-family:-apple-system,sans-serif;z-index:2}
/* ── finish-swatch hover gallery ── */
.imgwrap .finishbadge{position:absolute;top:8px;left:8px;background:#000a;color:#fff;font-size:9px;font-weight:700;
border-radius:4px;padding:2px 7px;letter-spacing:.06em;font-family:-apple-system,sans-serif;z-index:3;pointer-events:none}
.imgwrap .dots{position:absolute;left:0;right:0;bottom:7px;display:flex;justify-content:center;gap:5px;z-index:3;pointer-events:none}
.imgwrap .dots i{width:6px;height:6px;border-radius:50%;background:#fff;opacity:.45;box-shadow:0 0 0 1px #0006;transition:opacity .15s,transform .15s}
.imgwrap .dots i.on{opacity:1;transform:scale(1.25)}
.imgwrap.multi{cursor:pointer}
.imgwrap.noimg{background:repeating-linear-gradient(45deg,#e6e1d8,#e6e1d8 8px,#ded8cc 8px,#ded8cc 16px)}
.imgwrap.noimg::after{content:'no image';position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
color:var(--taupe);font-size:12px;font-family:-apple-system,sans-serif}
.card .b{padding:calc(11px*var(--fs)) calc(13px*var(--fs)) calc(13px*var(--fs));display:flex;flex-direction:column;gap:calc(4px*var(--fs))}
.card .kind{font-family:-apple-system,sans-serif;font-size:calc(9.5px*var(--fs));letter-spacing:.12em;text-transform:uppercase;color:var(--gold)}
.card .name{font-size:calc(16px*var(--fs));line-height:1.25}
.card .sub{font-family:-apple-system,sans-serif;font-size:calc(11px*var(--fs));color:var(--taupe)}
.card .cta{font-family:-apple-system,sans-serif;font-size:calc(11px*var(--fs));letter-spacing:.08em;text-transform:uppercase;color:var(--accent);margin-top:2px}
a.drill{color:inherit;text-decoration:none;cursor:pointer}
a.drill:hover{text-decoration:underline;text-decoration-color:var(--accent);text-underline-offset:2px}
a.drill.kind:hover{color:var(--gold)}
#empty{padding:70px;text-align:center;color:var(--taupe);font-family:-apple-system,sans-serif}
footer.house{background:var(--ink);color:#cfc9bb;padding:34px 30px;font-family:-apple-system,sans-serif;font-size:12.5px;text-align:center;letter-spacing:.04em}
footer.house a{color:#fff;text-decoration:none;border-bottom:1px solid #ffffff40}
@media(max-width:820px){.grid{grid-template-columns:repeat(2,1fr)}.ctl.dens{display:none}nav.house{display:none}}
/* ── List view ── */
.grid.list{display:flex;flex-direction:column;gap:8px;padding:16px 26px 70px;max-width:1500px;margin:0 auto}
.grid.list .card{flex-direction:row;align-items:stretch;border-radius:3px}
.grid.list .card:hover{transform:none}
.grid.list .imgwrap{flex:0 0 110px;width:110px;aspect-ratio:1/1}
.grid.list .card .b{flex:1;padding:12px 16px;flex-direction:row;align-items:center;gap:18px;flex-wrap:wrap}
.grid.list .card .kind{font-size:10px}
.grid.list .card .name{font-size:14px;flex:1;min-width:180px}
.grid.list .card .sub{font-size:11px}
.grid.list .card .cta{font-size:11px;white-space:nowrap}
/* ── View toggle ── */
.view-btn{background:var(--paper);border:1px solid var(--line);color:var(--taupe);border-radius:4px;padding:6px 11px;cursor:pointer;font-size:12px;font-family:-apple-system,sans-serif;letter-spacing:.03em}
.view-btn:hover{border-color:var(--accent)}
.view-btn.active{border-color:var(--accent);color:var(--accent)}
/* ── Fields ▾ popover ── */
.fieldmenu{position:relative}
.fbtn{background:var(--paper);border:1px solid var(--line);color:var(--taupe);border-radius:4px;padding:6px 11px;cursor:pointer;font-size:12px;font-family:-apple-system,sans-serif;white-space:nowrap}
.fbtn:hover{border-color:var(--accent);color:var(--ink)}
.fpop{position:absolute;top:calc(100% + 6px);left:0;background:var(--paper);border:1px solid var(--line);border-radius:3px;box-shadow:0 10px 28px rgba(22,20,18,.14);padding:8px 4px;z-index:30;min-width:180px}
.fpop[hidden]{display:none}
.fpop label{display:flex;align-items:center;gap:9px;padding:7px 14px;font-size:12px;font-family:-apple-system,sans-serif;letter-spacing:.04em;color:var(--taupe);cursor:pointer;white-space:nowrap}
.fpop label:hover{background:var(--bg)}
.fpop input{accent-color:var(--accent);margin:0}
/* ── field hide classes (grid only) ── */
body.hf-img .grid:not(.list) .imgwrap{display:none}
body.hf-kind .grid:not(.list) .card .kind{display:none}
body.hf-name .grid:not(.list) .card .name{display:none}
body.hf-sub .grid:not(.list) .card .sub{display:none}
body.hf-cta .grid:not(.list) .card .cta{display:none}
</style></head>
<body>
<header class="house">
<a href="/" style="text-decoration:none"><span class="wordmark" id="wm">Designer Wallcoverings<small id="wmsub">A Designer Wallcoverings Collection</small></span></a>
<nav class="house" id="nav"></nav>
</header>
<section class="hero" id="hero">
<div class="hz">
<p class="eyebrow" id="eyebrow"></p>
<h1 id="htitle"></h1>
<p class="kicker" id="kicker"></p>
<p class="tag" id="tag"></p>
</div>
</section>
<section class="about" id="aboutsec"><div class="in" id="about"></div></section>
<div class="bar" id="collection">
<span class="count" id="count">…</span>
<div class="ctl"><label>Sort</label>
<select id="sort">
<option value="newest">Newest</option>
<option value="color">Color</option>
<option value="style">Style</option>
<option value="type">Type</option>
<option value="pattern">Pattern A→Z</option>
<option value="sku">SKU A→Z</option>
<option value="name">Name A→Z</option>
<option value="collection">Collection A→Z</option>
<option value="finish">Finish A→Z</option>
</select></div>
<div class="ctl dens"><label>Density</label>
<input type="range" id="density" min="2" max="8" step="1"></div>
<input type="text" id="q" placeholder="Search pattern / sku…" style="min-width:180px">
<div class="chipf" id="typef"></div>
<div class="chipf" id="colorf"></div>
<button class="view-btn" id="viewgrid" title="Grid view">⊞ Grid</button>
<button class="view-btn" id="viewlist" title="List view">☰ List</button>
<div class="fieldmenu">
<button class="fbtn" id="fieldbtn">Fields ▾</button>
<div class="fpop" id="fpop" hidden>
<label><input type="checkbox" data-af="img" checked> Image</label>
<label><input type="checkbox" data-af="kind" checked> Type / Collection</label>
<label><input type="checkbox" data-af="name" checked> Name</label>
<label><input type="checkbox" data-af="sub" checked> Style · Pattern · Color</label>
<label><input type="checkbox" data-af="cta" checked> CTA</label>
</div>
</div>
</div>
<div class="grid" id="grid"></div>
<div id="empty" style="display:none">No products match.</div>
<footer class="house" id="footer"></footer>
<script>
const $=s=>document.querySelector(s);
const grid=$('#grid');
let ALL=[],CFG=null,FACETS=null,VIEW=[];
let FTYPE='',FCOLOR='';
const BUCKET={white:'#f4f4f2',grey:'#9aa0aa',black:'#1c1e24',orange:'#e08a3c',gold:'#c9a75a',green:'#5f8f5c',blue:'#4f7fb5',purple:'#8a6fb5',pink:'#f3a8c0',red:'#d0453e'};
function esc(s){return String(s==null?'':s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));}
function esc0(s){return encodeURIComponent(String(s==null?'':s));}
// ── href-drill primitives ──────────────────────────────────────────
// Every displayed data point (type / collection / color / style / pattern)
// is an <a href> to its filtered view; filter state is URL-addressable so the
// grid is shareable, deep-linkable and back-button-friendly.
// Drill-only facets (not wired to a visible control) live in FIL.
const FIL={collection:'',style:'',pattern:''};
// qstr(over): build the query string from the live controls + FIL, then
// override with the fields in `over`. Empty values are omitted.
function qstr(over){
const st={type:FTYPE,color:FCOLOR,collection:FIL.collection,style:FIL.style,pattern:FIL.pattern,
q:$('#q').value.trim(),sort:$('#sort').value};
Object.assign(st,over||{});
const parts=[];
for(const k of ['type','color','collection','style','pattern','q','sort']){
let v=st[k];
if(k==='sort'&&v==='newest')v=''; // default sort → clean URL
if(v)parts.push(k+'='+esc0(v));
}
return parts.length?('?'+parts.join('&')):location.pathname;
}
function drill(field,val,label,cls){
return `<a class="drill ${cls||''}" data-filter="${esc(field)}" data-v="${esc(val)}" href="${esc(qstr({[field]:val}))}">${esc(label)}</a>`;
}
// readURL: hydrate controls + FIL from location.search (deep-link / back).
function readURL(){
const u=new URLSearchParams(location.search);
FTYPE=u.get('type')||'';
FCOLOR=u.get('color')||'';
FIL.collection=u.get('collection')||'';
FIL.style=u.get('style')||'';
FIL.pattern=u.get('pattern')||'';
$('#q').value=u.get('q')||'';
const so=u.get('sort'); if(so)$('#sort').value=so;
}
// writeURL: reflect the current filter state into the address bar.
function writeURL(push){
const url=qstr();
if(push)history.pushState(null,'',url); else history.replaceState(null,'',url);
}
// persisted prefs (CLAUDE.md: sort + density persist)
$('#sort').value=localStorage.getItem('a50.sort')||'newest';
function setDensity(v){v=Number(v);
const fs=Math.max(.72,Math.min(1.25, v<=4 ? 1+(4-v)*.08 : 1-(v-4)*.055));
document.documentElement.style.setProperty('--cols',v);
document.documentElement.style.setProperty('--fs',fs.toFixed(3));}
const dens=localStorage.getItem('a50.density')||'4';
$('#density').value=dens;setDensity(dens);
$('#density').oninput=()=>{setDensity($('#density').value);localStorage.setItem('a50.density',$('#density').value);};
// finish-swatch set for a product: capped `swatches` from the feed, else the
// single swatch. De-dupe defensively; cap to 12 so the cycle stays snappy.
function finishSet(p){
const raw=(Array.isArray(p.swatches)&&p.swatches.length?p.swatches:(p.swatch?[p.swatch]:[]));
const out=[];for(const u of raw){if(u&&!out.includes(u)){out.push(u);if(out.length===12)break;}}
return out;
}
function card(p){
const name=esc(p.display_name||p.sku||'(untitled)');
const set=finishSet(p);
// Each filterable data point is a drill <a>. The "kind" chip drills on the
// product type (or collection when it's the Architectural line); color/style
// /pattern each drill to their filtered view.
const kindLabel=(p.product_type==='Architectural Wallcoverings')?(p.collection||'Pressed Tin'):p.product_type;
const kindHTML=(p.product_type==='Architectural Wallcoverings')
? (p.collection?drill('collection',p.collection,kindLabel,'kind'):`<span class="kind">${esc(kindLabel)}</span>`)
: (p.product_type?drill('type',p.product_type,kindLabel,'kind'):`<span class="kind">${esc(kindLabel)}</span>`);
const facetBits=[];
if(p.style)facetBits.push(drill('style',p.style,p.style,'sub'));
if(p.pattern)facetBits.push(drill('pattern',p.pattern,p.pattern,'sub'));
if(p.color_bucket)facetBits.push(drill('color',p.color_bucket,p.color_bucket,'sub'));
const facetHTML=`<span class="sub">${facetBits.join(' · ')||esc(p.finish||'')}</span>`;
if(!set.length) return `<div class="card" data-href="/product/${encodeURIComponent(p.handle)}">
<div class="imgwrap noimg"></div><div class="b">
${kindHTML}
<div class="name">${name}</div>${facetHTML}
<span class="cta">Request a Quote →</span></div></div>`;
const multi=set.length>1;
const dots=multi?`<div class="dots">${set.map((_,i)=>`<i class="${i===0?'on':''}"></i>`).join('')}</div>`:'';
const badge=multi?`<span class="finishbadge">${set.length} FINISHES</span>`:'';
return `<div class="card" data-href="/product/${encodeURIComponent(p.handle)}">
<div class="imgwrap${multi?' multi':''}" data-swatches='${esc(JSON.stringify(set))}' data-i="0">
<img loading="lazy" src="${esc(set[0])}" alt="${name}" onerror="this.style.display='none';this.parentNode.classList.add('noimg')">
${badge}${dots}
</div><div class="b">
${kindHTML}
<div class="name">${name}</div>
${facetHTML}
<span class="cta">${multi?'Cycle finishes · ':''}Request a Quote →</span>
</div></div>`;
}
// ── swatch-cycle wiring ──
// Desktop: mouse-move across the image maps X-position → finish index (scrub).
// Touch: tap the image advances to the next finish (and never navigates).
// Non-image areas / non-multi cards navigate to the PDP as before.
const isTouch=matchMedia('(hover:none),(pointer:coarse)').matches;
function setSwatch(wrap,i){
let set;try{set=JSON.parse(wrap.dataset.swatches||'[]');}catch{set=[];}
if(!set.length)return;
i=((i%set.length)+set.length)%set.length;
if(String(i)===wrap.dataset.i)return;
wrap.dataset.i=String(i);
const img=wrap.querySelector('img');if(img){img.src=set[i];img.style.display='';wrap.classList.remove('noimg');}
const dots=wrap.querySelectorAll('.dots i');dots.forEach((d,k)=>d.classList.toggle('on',k===i));
}
grid.addEventListener('mousemove',e=>{
if(isTouch)return;
const wrap=e.target.closest('.imgwrap.multi');if(!wrap)return;
const r=wrap.getBoundingClientRect();let set;try{set=JSON.parse(wrap.dataset.swatches||'[]');}catch{set=[];}
if(!set.length)return;
const frac=Math.min(.999,Math.max(0,(e.clientX-r.left)/r.width));
setSwatch(wrap,Math.floor(frac*set.length));
});
grid.addEventListener('mouseleave',e=>{
const wrap=e.target&&e.target.closest?e.target.closest('.imgwrap.multi'):null;if(wrap)setSwatch(wrap,0);
},true);
grid.addEventListener('click',e=>{
// Drill link — filter in place (unless a modifier => open href in new tab).
const d=e.target.closest('a.drill');
if(d){
if(e.metaKey||e.ctrlKey||e.shiftKey||e.button===1)return; // let the href open
e.preventDefault();
const f=d.dataset.filter,v=d.dataset.v;
if(f==='type')FTYPE=FTYPE===v?'':v;
else if(f==='color')FCOLOR=FCOLOR===v?'':v;
else if(f in FIL)FIL[f]=FIL[f]===v?'':v;
writeURL(true);renderChips();apply();return;
}
const wrap=e.target.closest('.imgwrap.multi');
if(isTouch&&wrap){ // tap-to-advance on touch — do NOT navigate
e.preventDefault();setSwatch(wrap,(Number(wrap.dataset.i)||0)+1);return;
}
const c=e.target.closest('.card[data-href]');if(c)location.href=c.dataset.href;
});
// Back/forward button restores the exact filtered set.
window.addEventListener('popstate',()=>{readURL();renderChips();apply();});
function apply(){
const q=$('#q').value.trim().toLowerCase();
VIEW=ALL.filter(p=>{
if(FTYPE&&String(p.product_type||'')!==FTYPE)return false;
if(FCOLOR&&String(p.color_bucket||'')!==FCOLOR)return false;
if(FIL.collection&&String(p.collection||'')!==FIL.collection)return false;
if(FIL.style&&String(p.style||'')!==FIL.style)return false;
if(FIL.pattern&&String(p.pattern||'')!==FIL.pattern)return false;
if(q&&![p.display_name,p.sku,p.pattern,p.style,p.collection].some(v=>v&&String(v).toLowerCase().includes(q)))return false;
return true;
});
const s=$('#sort').value,cmp={
newest:(a,b)=>new Date(b.created_at||0)-new Date(a.created_at||0),
color:(a,b)=>(a.hue??999)-(b.hue??999),
style:(a,b)=>String(a.style||'~').localeCompare(String(b.style||'~')),
type:(a,b)=>String(a.product_type||'').localeCompare(String(b.product_type||'')),
pattern:(a,b)=>String(a.pattern||'~').localeCompare(String(b.pattern||'~')),
sku:(a,b)=>String(a.sku||a.handle).localeCompare(String(b.sku||b.handle),undefined,{numeric:true}),
name:(a,b)=>String(a.display_name||'').localeCompare(String(b.display_name||''),undefined,{numeric:true}),
collection:(a,b)=>String(a.collection||'~').localeCompare(String(b.collection||'~')),
finish:(a,b)=>String(a.finish||'~').localeCompare(String(b.finish||'~')),
}[s];
if(cmp)VIEW=[...VIEW].sort(cmp);
grid.innerHTML=VIEW.map(card).join('');
$('#count').textContent=VIEW.length+' of '+ALL.length+' designs';
$('#empty').style.display=VIEW.length?'none':'block';
}
$('#sort').onchange=()=>{localStorage.setItem('a50.sort',$('#sort').value);writeURL(true);apply();};
let t;$('#q').oninput=()=>{clearTimeout(t);t=setTimeout(()=>{writeURL(false);apply();},250);};
// Chips are real <a href> drill links too — clicking toggles the facet, but
// they carry a shareable href (the state with that chip applied).
function renderChips(){
$('#typef').innerHTML=(FACETS.types||[]).map(o=>{
const on=FTYPE===o.value; const href=esc(qstr({type:on?'':o.value}));
return `<a class="chip-a" href="${href}"><button data-t="${esc(o.value)}" class="${on?'on':''}">${esc(o.value)} · ${o.count}</button></a>`;
}).join('');
$('#colorf').innerHTML=(FACETS.colors||[]).map(o=>{
const on=FCOLOR===o.value; const href=esc(qstr({color:on?'':o.value}));
return `<a class="chip-a" href="${href}"><button data-c="${esc(o.value)}" class="${on?'on':''}"><span class="dot" style="background:${BUCKET[o.value]||'#999'}"></span>${esc(o.value)} · ${o.count}</button></a>`;
}).join('');
$('#typef').querySelectorAll('a.chip-a').forEach(a=>a.onclick=e=>{
if(e.metaKey||e.ctrlKey||e.shiftKey||e.button===1)return; e.preventDefault();
const b=a.querySelector('button'); FTYPE=FTYPE===b.dataset.t?'':b.dataset.t; writeURL(true); renderChips(); apply();
});
$('#colorf').querySelectorAll('a.chip-a').forEach(a=>a.onclick=e=>{
if(e.metaKey||e.ctrlKey||e.shiftKey||e.button===1)return; e.preventDefault();
const b=a.querySelector('button'); FCOLOR=FCOLOR===b.dataset.c?'':b.dataset.c; writeURL(true); renderChips(); apply();
});
}
// ── VIEW TOGGLE ────────────────────────────────────────────────────
function setA50View(v){
grid.classList.toggle('list',v==='list');
$('#viewgrid').classList.toggle('active',v==='grid');
$('#viewlist').classList.toggle('active',v==='list');
$('#density').parentElement.style.opacity=v==='list'?'.35':'1';
$('#density').disabled=v==='list';
try{localStorage.setItem('a50.view',v);}catch(_){}
}
$('#viewgrid').addEventListener('click',()=>setA50View('grid'));
$('#viewlist').addEventListener('click',()=>setA50View('list'));
setA50View(localStorage.getItem('a50.view')||'grid');
// ── FIELDS MENU ───────────────────────────────────────────────────
const A50F_DEF={img:true,kind:true,name:true,sub:true,cta:true};
let A50F=Object.assign({},A50F_DEF);
try{const s=localStorage.getItem('a50.fields');if(s)A50F=Object.assign({},A50F_DEF,JSON.parse(s));}catch(_){}
function applyA50Fields(){
for(const k of Object.keys(A50F_DEF)){document.body.classList.toggle('hf-'+k,!A50F[k]);}
document.querySelectorAll('#fpop input[data-af]').forEach(cb=>{cb.checked=!!A50F[cb.dataset.af];});
try{localStorage.setItem('a50.fields',JSON.stringify(A50F));}catch(_){}
}
document.querySelectorAll('#fpop input[data-af]').forEach(cb=>{
cb.addEventListener('change',()=>{A50F[cb.dataset.af]=cb.checked;applyA50Fields();});
});
applyA50Fields();
$('#fieldbtn').addEventListener('click',e=>{e.stopPropagation();$('#fpop').hidden=!$('#fpop').hidden;});
document.addEventListener('click',e=>{if(!e.target.closest('.fieldmenu'))$('#fpop').hidden=true;});
async function boot(){
const [cfg,pd,fc]=await Promise.all([
fetch(location.origin+'/api/config').then(r=>r.json()),
fetch(location.origin+'/api/products').then(r=>r.json()),
fetch(location.origin+'/api/facets').then(r=>r.json())]);
CFG=cfg;ALL=pd.products;FACETS=fc;
document.title=cfg.title;
$('#wm').firstChild.textContent=cfg.wordmark;
$('#wmsub').textContent=cfg.eyebrow;
$('#eyebrow').textContent=cfg.eyebrow;
$('#htitle').textContent=cfg.line;
$('#kicker').textContent=cfg.kicker;
$('#tag').textContent=cfg.tagline;
$('#nav').innerHTML=(cfg.nav||[]).map(n=>`<a href="${esc(n.href)}">${esc(n.label)}</a>`).join('');
$('#about').innerHTML=(cfg.about.paragraphs||[]).map(p=>`<p>${esc(p)}</p>`).join('')
+`<p class="collab">${esc(cfg.about.collab)}</p>`;
// hero image = first product with a swatch
const heroP=ALL.find(p=>p.swatch);
if(heroP)$('#hero').style.backgroundImage=`url("${heroP.swatch}")`;
$('#footer').innerHTML=`Atomic 50 — a collection of <a href="${esc(cfg.houseUrl)}">Designer Wallcoverings</a>. `
+`Pressed-tin ceilings & wall panels, to the trade. Request a quote or order a $${cfg.samplePrice.toFixed(2)} sample on any pattern.`;
readURL(); // deep-link: hydrate filter state from the URL first
renderChips();apply();
}
boot();
</script>
</body></html>