← back to Wallco Ai
Design Coordinate on /design/:id — palette-mode call to pairs.designerwallcoverings.com
769ab6cdf03892908581e5efd5b2715a12da32c1 · 2026-05-13 11:27:08 -0700 · SteveStudio2
Files touched
Diff
commit 769ab6cdf03892908581e5efd5b2715a12da32c1
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Wed May 13 11:27:08 2026 -0700
Design Coordinate on /design/:id — palette-mode call to pairs.designerwallcoverings.com
---
server.js | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 160 insertions(+)
diff --git a/server.js b/server.js
index 25127d9..dbb0b21 100644
--- a/server.js
+++ b/server.js
@@ -6950,6 +6950,166 @@ ${htmlHeader('/designs')}
</div>
</div>
+ <!-- Design Coordinate — pulls coordinating stripes/plaids from the DW catalog
+ that use the design's palette, plus AI-generated coordinates. -->
+ <section
+ id="pairs-well-with"
+ class="dc-section"
+ data-pairs-endpoint="https://pairs.designerwallcoverings.com/api/pairs"
+ data-ai-endpoint="https://pairs.designerwallcoverings.com/api/ai-coordinates"
+ data-source-palette="${design.dominant_hex || ''}"
+ data-source-title="${(design.title || '').replace(/"/g, '"')}"
+ data-source-image="${design.image_url || ''}"
+ style="margin: 48px auto 0; max-width: 1200px; padding: 0 24px; clear: both;"
+ hidden
+ >
+ <button type="button" class="pairs-well__cta" aria-expanded="false">
+ <span class="pairs-well__cta-text">Design Coordinate</span>
+ <span class="pairs-well__cta-icon" aria-hidden="true">+</span>
+ </button>
+
+ <div class="pairs-well__source-palette" id="pairs-well-source-palette" hidden></div>
+ <div class="pairs-well__vendor-picker" id="pairs-well-vendor-picker" hidden>
+ <h4>Paint brands</h4>
+ <div class="chips">
+ <label><input type="checkbox" value="sw" checked> Sherwin-Williams</label>
+ <label><input type="checkbox" value="de" checked> Dunn-Edwards</label>
+ <label><input type="checkbox" value="bm" checked> Benjamin Moore</label>
+ <label><input type="checkbox" value="fb" checked> Farrow & Ball</label>
+ <label><input type="checkbox" value="behr" > Behr</label>
+ <label><input type="checkbox" value="ppg" > PPG</label>
+ <label><input type="checkbox" value="ral" > RAL</label>
+ </div>
+ </div>
+
+ <div class="pairs-well__catalog" id="pairs-well-catalog" hidden>
+ <div class="pairs-well__panel-head">
+ <h3 style="font:500 22px/1.2 ui-serif,Georgia,serif;">Stripes & Plaids from our catalog <span class="count" id="pairs-well-catalog-count"></span></h3>
+ <div class="pairs-well__controls">
+ <label for="pairs-well-cols-catalog">Columns</label>
+ <input id="pairs-well-cols-catalog" type="range" min="3" max="12" value="8" step="1">
+ <span class="pairs-well__cols-val" id="pairs-well-cols-catalog-val">8</span>
+ </div>
+ </div>
+ <div id="pairs-well-grid" class="pairs-well__grid"></div>
+ </div>
+
+ <div class="pairs-well__ai" id="pairs-well-ai" hidden>
+ <div class="pairs-well__panel-head">
+ <h3 style="font:500 22px/1.2 ui-serif,Georgia,serif;">AI Generated Coordinates <span class="ai-badge">Preview · 24 options</span></h3>
+ <div class="pairs-well__controls">
+ <label for="pairs-well-cols-ai">Columns</label>
+ <input id="pairs-well-cols-ai" type="range" min="3" max="12" value="8" step="1">
+ <span class="pairs-well__cols-val" id="pairs-well-cols-ai-val">8</span>
+ </div>
+ </div>
+ <div id="pairs-well-ai-grid" class="pairs-well__grid pairs-well__grid--ai"></div>
+ </div>
+ </section>
+
+ <style>
+ #pairs-well-with { --cols-catalog: 8; --cols-ai: 8; font-family: inherit; }
+ .pairs-well__cta { display:flex; align-items:center; justify-content:space-between; width:100%; padding:18px 24px; margin:0; background:#111; color:#fff; border:0; border-radius:999px; font-size:15px; font-weight:500; letter-spacing:.06em; text-transform:uppercase; cursor:pointer; transition: background 160ms ease, transform 120ms ease; }
+ .pairs-well__cta:hover { background:#2a2a2a; }
+ .pairs-well__cta:active { transform:scale(.995); }
+ .pairs-well__cta[aria-expanded="true"] .pairs-well__cta-icon { transform: rotate(45deg); }
+ .pairs-well__cta-icon { display:inline-block; font-size:22px; line-height:1; transition: transform 200ms ease; }
+ .pairs-well__source-palette { margin: 18px 0 0; }
+ .pairs-well__source-palette h4 { margin: 0 0 8px; font-size: 11px; letter-spacing:.08em; text-transform: uppercase; color:#777; }
+ .pairs-well__source-palette table { border-collapse: collapse; width: 100%; font-size: 12px; }
+ .pairs-well__source-palette th, .pairs-well__source-palette td { text-align: left; padding: 6px 8px; border-bottom: 1px solid #f0ebe2; }
+ .pairs-well__source-palette th { font-weight: 500; color: #777; font-size: 11px; letter-spacing:.06em; text-transform: uppercase; }
+ .pairs-well__source-palette td.swatch { width: 28px; }
+ .pw-dot { display: inline-block; width: 18px; height: 18px; border-radius: 50%; border: 1px solid rgba(0,0,0,0.12); vertical-align: middle; }
+ .pw-square { display: inline-block; width: 13px; height: 13px; border-radius: 3px; border: 1px solid rgba(0,0,0,0.12); vertical-align: middle; margin-right: 4px; }
+ .pw-code { color: #888; font-variant-numeric: tabular-nums; font-size: 11px; }
+ .pairs-well__vendor-picker { margin: 18px 0 0; }
+ .pairs-well__vendor-picker h4 { margin: 0 0 8px; font-size: 11px; letter-spacing:.08em; text-transform: uppercase; color: #777; }
+ .pairs-well__vendor-picker .chips { display: flex; flex-wrap: wrap; gap: 6px; }
+ .pairs-well__vendor-picker label { font-size: 12px; padding: 5px 10px; border: 1px solid #e7e7e7; border-radius: 999px; cursor: pointer; background: #fff; user-select: none; }
+ .pairs-well__vendor-picker label:has(input:checked) { background: #1a1a1a; color: #fff; border-color: #1a1a1a; }
+ .pairs-well__vendor-picker input { display: none; }
+ .pairs-well__panel-head { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; margin: 24px 0 12px; }
+ .pairs-well__panel-head h3 { margin: 0; }
+ .pairs-well__panel-head .count { color: #888; font-size: 12px; margin-left: 6px; font-family: ui-sans-serif, system-ui, sans-serif; font-weight: 400; }
+ .pairs-well__panel-head .ai-badge { display: inline-block; padding: 3px 8px; background: #2a2a2a; color: #fff; border-radius: 999px; font-size: 10px; letter-spacing:.08em; text-transform: uppercase; margin-left: 8px; font-family: ui-sans-serif, system-ui, sans-serif; font-weight: 600; }
+ .pairs-well__controls { display: flex; align-items: center; gap: 10px; font-size: 11px; color: #777; text-transform: uppercase; letter-spacing:.06em; }
+ .pairs-well__controls input[type=range] { width: 160px; accent-color: #1a1a1a; }
+ .pairs-well__cols-val { font-variant-numeric: tabular-nums; color: #1a1a1a; min-width: 1.5em; text-align: right; font-weight: 600; }
+ .pairs-well__grid { display: grid; gap: 14px; margin-top: 6px; grid-template-columns: repeat(var(--cols-catalog), minmax(0, 1fr)); }
+ .pairs-well__grid--ai { grid-template-columns: repeat(var(--cols-ai), minmax(0, 1fr)); }
+ @media (max-width: 1100px) { .pairs-well__grid { grid-template-columns: repeat(min(6, var(--cols-catalog)), minmax(0, 1fr)); } .pairs-well__grid--ai { grid-template-columns: repeat(min(6, var(--cols-ai)), minmax(0, 1fr)); } }
+ @media (max-width: 900px) { .pairs-well__grid { grid-template-columns: repeat(min(4, var(--cols-catalog)), minmax(0, 1fr)); } .pairs-well__grid--ai { grid-template-columns: repeat(min(4, var(--cols-ai)), minmax(0, 1fr)); } }
+ @media (max-width: 600px) { .pairs-well__grid { grid-template-columns: repeat(min(2, var(--cols-catalog)), minmax(0, 1fr)); } .pairs-well__grid--ai { grid-template-columns: repeat(min(2, var(--cols-ai)), minmax(0, 1fr)); } }
+ .pairs-well__card { display: block; text-decoration: none; color: inherit; background: #fff; border: 1px solid #e7e7e7; border-radius: 6px; overflow: hidden; transition: box-shadow 160ms ease, transform 160ms ease; }
+ .pairs-well__card:hover { box-shadow: 0 6px 20px rgba(0,0,0,0.08); transform: translateY(-2px); }
+ .pairs-well__card--ai { border-style: dashed; border-color: #d9c7a8; }
+ .pairs-well__img-wrap { aspect-ratio: 1 / 1; background: #f4f4f4; overflow: hidden; }
+ .pairs-well__img { width: 100%; height: 100%; object-fit: cover; display: block; }
+ .pairs-well__meta { padding: 10px 12px 12px; }
+ .pairs-well__title { margin: 0 0 4px; font-size: 13px; font-weight: 600; line-height: 1.3; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
+ .pairs-well__vendor { margin: 0 0 6px; font-size: 10px; color: #777; letter-spacing:.04em; text-transform: uppercase; }
+ .pairs-well__why { display: inline-block; padding: 2px 7px; background: #f4ede4; color: #5a4628; border-radius: 999px; font-size: 10px; line-height: 1.4; margin-bottom: 8px; }
+ .pairs-well__card--ai .pairs-well__why { background: #efe6f5; color: #5b3478; }
+ .pairs-well__palette { display: flex; gap: 3px; margin: 0 0 8px; flex-wrap: wrap; }
+ .pairs-well__palette .pw-dot { width: 12px; height: 12px; }
+ .pairs-well__paints { border-top: 1px dashed #ebe4d8; margin-top: 4px; padding-top: 6px; font-size: 10px; line-height: 1.5; color: #444; }
+ .pairs-well__paints .label { color: #888; font-weight: 600; margin-right: 4px; letter-spacing:.04em; }
+ .pairs-well__paints .row { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+ .pairs-well__skeleton { background: linear-gradient(90deg, #eee 0%, #f6f6f6 50%, #eee 100%); background-size: 200% 100%; animation: pwShim 1.2s linear infinite; aspect-ratio: 1 / 1; border-radius: 6px; }
+ @keyframes pwShim { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
+ .pairs-well__empty { padding: 18px; color: #777; font-size: 14px; grid-column: 1 / -1; }
+ </style>
+
+ <script>
+ (function () {
+ var root = document.getElementById('pairs-well-with'); if (!root) return;
+ var endpointPairs = root.getAttribute('data-pairs-endpoint');
+ var endpointAi = root.getAttribute('data-ai-endpoint');
+ var palette = root.getAttribute('data-source-palette');
+ var sourceTitle = root.getAttribute('data-source-title') || '';
+ var sourceImage = root.getAttribute('data-source-image') || '';
+ if (!palette) return;
+ root.hidden = false;
+
+ var cta = root.querySelector('.pairs-well__cta');
+ var catalogPanel = document.getElementById('pairs-well-catalog');
+ var catalogGrid = document.getElementById('pairs-well-grid');
+ var catalogCols = document.getElementById('pairs-well-cols-catalog');
+ var catalogColsVal = document.getElementById('pairs-well-cols-catalog-val');
+ var catalogCount = document.getElementById('pairs-well-catalog-count');
+ var aiPanel = document.getElementById('pairs-well-ai');
+ var aiGrid = document.getElementById('pairs-well-ai-grid');
+ var aiCols = document.getElementById('pairs-well-cols-ai');
+ var aiColsVal = document.getElementById('pairs-well-cols-ai-val');
+ var sourcePalette = document.getElementById('pairs-well-source-palette');
+ var vendorPicker = document.getElementById('pairs-well-vendor-picker');
+ var vendorChecks = vendorPicker.querySelectorAll('input[type=checkbox]');
+ var loaded=false, cachedPairs=null, cachedAi=null, cachedSource=null;
+ var VENDOR_LABEL={sw:'Sherwin-Williams',de:'Dunn-Edwards',bm:'Benjamin Moore',fb:'Farrow & Ball',behr:'Behr',ppg:'PPG',ral:'RAL'};
+ var VENDOR_SHORT={sw:'SW',de:'DE',bm:'BM',fb:'F&B',behr:'Behr',ppg:'PPG',ral:'RAL'};
+ var DEFAULT_VENDORS=['sw','de','bm','fb'];
+ function selectedVendors(){var o=[];vendorChecks.forEach(function(c){if(c.checked)o.push(c.value)});return o.length?o:DEFAULT_VENDORS.slice()}
+ try{var st=JSON.parse(localStorage.getItem('pw.vendors')||'null');if(Array.isArray(st)&&st.length)vendorChecks.forEach(function(c){c.checked=st.indexOf(c.value)!==-1})}catch(_){}
+ function persistVendors(){try{localStorage.setItem('pw.vendors',JSON.stringify(selectedVendors()))}catch(_){}}
+ vendorChecks.forEach(function(c){c.addEventListener('change',function(){persistVendors();if(cachedSource)renderSourcePalette(cachedSource.palette);if(cachedPairs&&!catalogPanel.hidden)renderGrid(catalogGrid,cachedPairs,false)})});
+ function wireSlider(input,valEl,varName,storageKey){function apply(){var n=Math.max(3,Math.min(12,parseInt(input.value,10)||8));valEl.textContent=n;root.style.setProperty('--'+varName,n);try{localStorage.setItem(storageKey,n)}catch(_){}}try{var s=parseInt(localStorage.getItem(storageKey),10);if(!isNaN(s)&&s>=3&&s<=12)input.value=s}catch(_){}input.addEventListener('input',apply);apply()}
+ wireSlider(catalogCols,catalogColsVal,'cols-catalog','pw.cols.catalog');
+ wireSlider(aiCols,aiColsVal,'cols-ai','pw.cols.ai');
+ function esc(s){return String(s==null?'':s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,''')}
+ function renderSkeletons(g,n){var h='';for(var i=0;i<n;i++)h+='<div class="pairs-well__skeleton"></div>';g.innerHTML=h}
+ function renderSourcePalette(pal){if(!pal||!pal.length){sourcePalette.hidden=true;vendorPicker.hidden=true;return}var vs=selectedVendors();var head='<th></th>'+vs.map(function(v){return '<th>'+esc(VENDOR_LABEL[v])+'</th>'}).join('');var body=pal.map(function(c){var hex=typeof c==='string'?c:c.hex;var cells='<td class="swatch"><span class="pw-dot" style="background:'+esc(hex)+'"></span></td>';vs.forEach(function(v){var p=c&&c.paints&&c.paints[v];cells+='<td>'+(p?'<span class="pw-square" style="background:'+esc(p.hex)+'"></span>'+esc(p.name)+' <span class="pw-code">'+esc(p.code)+'</span>':'—')+'</td>'});return '<tr>'+cells+'</tr>'}).join('');sourcePalette.innerHTML='<h4>Color story</h4><table><thead><tr>'+head+'</tr></thead><tbody>'+body+'</tbody></table>';sourcePalette.hidden=false;vendorPicker.hidden=false}
+ function renderCard(p,isAi){var img=p.image_url?esc(p.image_url):'';var why=(p.why&&p.why[0])?esc(p.why[0]):(p.motif?'AI '+esc(p.motif):'');var pal=(p.palette||[]).map(function(c){var hex=typeof c==='string'?c:c.hex;var sw=c&&c.paints&&c.paints.sw,de=c&&c.paints&&c.paints.de;var tip=(sw?'SW '+sw.name+' '+sw.code:'')+(de?' · DE '+de.name+' '+de.code:'');return '<span class="pw-dot" style="background:'+esc(hex)+'" title="'+esc(tip)+'"></span>'}).join('');var dom=(p.palette&&p.palette[0])||null;var pb='';if(dom&&dom.paints){var vs=selectedVendors();var rows=vs.map(function(v){var pt=dom.paints[v];if(!pt)return '';return '<span class="row"><span class="label">'+esc(VENDOR_SHORT[v])+'</span><span class="pw-square" style="background:'+esc(pt.hex)+'"></span>'+esc(pt.name)+' <span class="pw-code">'+esc(pt.code)+'</span></span>'}).filter(Boolean).join('');if(rows)pb='<div class="pairs-well__paints">'+rows+'</div>'}var href=p.url?esc(p.url):(p.handle?'https://www.designerwallcoverings.com/products/'+esc(p.handle):'#');return '<a class="pairs-well__card'+(isAi?' pairs-well__card--ai':'')+'" target="_blank" rel="noopener" href="'+href+'" title="'+esc((p.why||[]).join(' · '))+'"><div class="pairs-well__img-wrap">'+(img?'<img class="pairs-well__img" src="'+img+'" alt="'+esc(p.title)+'" loading="lazy">':'')+'</div><div class="pairs-well__meta"><p class="pairs-well__title">'+esc(p.title||'')+'</p><p class="pairs-well__vendor">'+esc(p.vendor||(isAi?'AI-Generated':''))+'</p>'+(why?'<span class="pairs-well__why">'+why+'</span>':'')+(pal?'<div class="pairs-well__palette">'+pal+'</div>':'')+pb+'</div></a>'}
+ function renderGrid(g,items,isAi){if(!items||!items.length){g.innerHTML='<div class="pairs-well__empty">No coordinates found.</div>';return}g.innerHTML=items.map(function(p){return renderCard(p,isAi)}).join('')}
+ function buildUrl(base,limit){var qs=['palette='+encodeURIComponent(palette)];if(sourceTitle)qs.push('title='+encodeURIComponent(sourceTitle));if(sourceImage)qs.push('image_url='+encodeURIComponent(sourceImage));if(limit)qs.push('limit='+limit);return base+'?'+qs.join('&')}
+ function load(){if(loaded)return Promise.resolve();loaded=true;renderSkeletons(catalogGrid,12);renderSkeletons(aiGrid,12);catalogPanel.hidden=false;aiPanel.hidden=false;
+ var pp=fetch(buildUrl(endpointPairs,24),{credentials:'omit'}).then(function(r){return r.ok?r.json():Promise.reject(new Error('http '+r.status))}).then(function(d){if(!d||d.ok===false)throw new Error((d&&d.error)||'bad payload');cachedSource=d.source||null;cachedPairs=d.pairs||[];if(cachedSource)renderSourcePalette(cachedSource.palette||[]);renderGrid(catalogGrid,cachedPairs,false);catalogCount.textContent='('+cachedPairs.length+')';if(!cachedPairs.length&&(!cachedSource||!cachedSource.palette||!cachedSource.palette.length))root.hidden=true}).catch(function(){catalogGrid.innerHTML='<div class="pairs-well__empty">Could not load coordinates right now.</div>'});
+ var ap=fetch(buildUrl(endpointAi,24),{credentials:'omit'}).then(function(r){return r.ok?r.json():Promise.reject(new Error('http '+r.status))}).then(function(d){if(!d||d.ok===false)throw new Error('bad payload');cachedAi=d.items||[];renderGrid(aiGrid,cachedAi,true)}).catch(function(){aiGrid.innerHTML='<div class="pairs-well__empty">AI generator not ready.</div>'});
+ return Promise.all([pp,ap])}
+ cta.addEventListener('click',function(){var op=cta.getAttribute('aria-expanded')==='true';if(op){cta.setAttribute('aria-expanded','false');sourcePalette.hidden=true;vendorPicker.hidden=true;catalogPanel.hidden=true;aiPanel.hidden=true;return}cta.setAttribute('aria-expanded','true');load()});
+ })();
+ </script>
+
<!-- Generated Rooms gallery — hydrates from /api/rooms?design_id={{id}} -->
<section id="rooms-gallery" class="rooms-gallery" data-design-id="${design.id}" style="margin:48px auto 0;max-width:1200px;padding:0 24px;">
<div class="rg-head" style="display:flex;align-items:baseline;gap:16px;border-top:1px solid var(--rule,#e5e5e0);padding-top:24px;margin-bottom:18px;">
← 7e452f1 drunk-animals: drop overnight gate, fire 24/7 like stoned
·
back to Wallco Ai
·
wallco.ai homepage: pin newest 6 butterflies above Featured, da2aabc →