[object Object]

← back to Wallco Ai

pairings: Dunn-Edwards + Sherwin-Williams paint chip suggestions

e54cb626af7220d5d6ad5d1a080dc1177d9deb0f · 2026-05-13 08:27:51 -0700 · SteveStudio2

Adds 'paint' suggestion type to the per-design pairing panel. For every
design's dominant_hex, finds the 2 closest Dunn-Edwards chips + 2 closest
Sherwin-Williams chips by HSL distance — paintable wall behind the
wallcovering, or trim/ceiling pickup.

- data/paint_chips.json: curated ~110 popular chips with public hex codes
  (DE Whisper / Swiss Coffee / Antique Brass / Plunge Pool / Bison Beige
  / Heliotrope...; SW Alabaster / Agreeable Gray / Tricorn Black / Naval
  / Real Red / Hidden Garden / Garden Spot...). Source: dunnedwards.com
  + sherwin-williams.com public color libraries. Expand over time.
- src/review.js: paintChipMatches(hex) + cache loader; wired into
  generatePairings() between colorSwatches() and materialSuggestions().
  pinKey('paint') = p:brand:code so moodboard pins persist.
- server.js render: new pair-card variant 'paint-card' with brand-coded
  left stripe (DE red, SW navy), chip code badge bottom-left of thumb.
  Mini fan-deck aesthetic.

Cache busted (pairings.json -> {}) so all designs regenerate with paint
chips on next /api/pairings/:id hit.

Smoke-tested design 2681 (#413b31): DE Ottertail #3e342a + DE Bison
Beige + SW Iron Ore + SW Virtual Taupe — close on the dominant, with
2 brand-name trim options at higher saturation.

Files touched

Diff

commit e54cb626af7220d5d6ad5d1a080dc1177d9deb0f
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Wed May 13 08:27:51 2026 -0700

    pairings: Dunn-Edwards + Sherwin-Williams paint chip suggestions
    
    Adds 'paint' suggestion type to the per-design pairing panel. For every
    design's dominant_hex, finds the 2 closest Dunn-Edwards chips + 2 closest
    Sherwin-Williams chips by HSL distance — paintable wall behind the
    wallcovering, or trim/ceiling pickup.
    
    - data/paint_chips.json: curated ~110 popular chips with public hex codes
      (DE Whisper / Swiss Coffee / Antique Brass / Plunge Pool / Bison Beige
      / Heliotrope...; SW Alabaster / Agreeable Gray / Tricorn Black / Naval
      / Real Red / Hidden Garden / Garden Spot...). Source: dunnedwards.com
      + sherwin-williams.com public color libraries. Expand over time.
    - src/review.js: paintChipMatches(hex) + cache loader; wired into
      generatePairings() between colorSwatches() and materialSuggestions().
      pinKey('paint') = p:brand:code so moodboard pins persist.
    - server.js render: new pair-card variant 'paint-card' with brand-coded
      left stripe (DE red, SW navy), chip code badge bottom-left of thumb.
      Mini fan-deck aesthetic.
    
    Cache busted (pairings.json -> {}) so all designs regenerate with paint
    chips on next /api/pairings/:id hit.
    
    Smoke-tested design 2681 (#413b31): DE Ottertail #3e342a + DE Bison
    Beige + SW Iron Ore + SW Virtual Taupe — close on the dominant, with
    2 brand-name trim options at higher saturation.
---
 data/paint_chips.json | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++
 server.js             |  18 +++++++
 src/review.js         |  50 +++++++++++++++++--
 3 files changed, 195 insertions(+), 3 deletions(-)

diff --git a/data/paint_chips.json b/data/paint_chips.json
new file mode 100644
index 0000000..f49a4b7
--- /dev/null
+++ b/data/paint_chips.json
@@ -0,0 +1,130 @@
+{
+  "note": "Curated popular Dunn-Edwards + Sherwin-Williams paint colors with public hex codes. Used by /design/:id pairing panel to surface nearest paint chips to a design's dominant_hex. Expand over time.",
+  "sources": ["dunnedwards.com color collection", "sherwin-williams.com color library"],
+  "colors": [
+    { "brand": "Dunn-Edwards", "code": "DEW380", "name": "Whisper",            "hex": "#f4ecdf" },
+    { "brand": "Dunn-Edwards", "code": "DEW340", "name": "Swiss Coffee",       "hex": "#efeae0" },
+    { "brand": "Dunn-Edwards", "code": "DEW341", "name": "White Sand",         "hex": "#f0e8d6" },
+    { "brand": "Dunn-Edwards", "code": "DEW360", "name": "Cool December",      "hex": "#e8e6dd" },
+    { "brand": "Dunn-Edwards", "code": "DET625", "name": "Pebbled Path",       "hex": "#cfc3a8" },
+    { "brand": "Dunn-Edwards", "code": "DEC793", "name": "Birch Bark",         "hex": "#d9cdb6" },
+    { "brand": "Dunn-Edwards", "code": "DET668", "name": "Antique Brass",      "hex": "#b69a64" },
+    { "brand": "Dunn-Edwards", "code": "DET636", "name": "Toasted Almond",     "hex": "#b08d65" },
+    { "brand": "Dunn-Edwards", "code": "DEC761", "name": "Sand Trail",         "hex": "#bca57f" },
+    { "brand": "Dunn-Edwards", "code": "DE6121","name": "Soft Pumice",         "hex": "#8a8475" },
+    { "brand": "Dunn-Edwards", "code": "DE6391","name": "Bullion Bag",         "hex": "#867159" },
+    { "brand": "Dunn-Edwards", "code": "DE6118","name": "Bison Beige",         "hex": "#8b7a5e" },
+    { "brand": "Dunn-Edwards", "code": "DE6076","name": "Bonsai Trunk",        "hex": "#594433" },
+    { "brand": "Dunn-Edwards", "code": "DEC783", "name": "Ottertail",          "hex": "#3e342a" },
+    { "brand": "Dunn-Edwards", "code": "DE6386","name": "Black Bean",          "hex": "#262321" },
+
+    { "brand": "Dunn-Edwards", "code": "DET694", "name": "Tea Bag",            "hex": "#8c5c40" },
+    { "brand": "Dunn-Edwards", "code": "DET684", "name": "Ground Sienna",      "hex": "#a55a3f" },
+    { "brand": "Dunn-Edwards", "code": "DE5125","name": "Spiced Cider",        "hex": "#a8533b" },
+    { "brand": "Dunn-Edwards", "code": "DE5081","name": "Red Gerbera",         "hex": "#b73a35" },
+    { "brand": "Dunn-Edwards", "code": "DEA138","name": "Anjou Pear",          "hex": "#b6a458" },
+    { "brand": "Dunn-Edwards", "code": "DET498", "name": "Bistro Olive",       "hex": "#7b6b3a" },
+    { "brand": "Dunn-Edwards", "code": "DE5559","name": "Caper Berry",         "hex": "#5d6939" },
+    { "brand": "Dunn-Edwards", "code": "DET504", "name": "Tatami Mat",         "hex": "#b9a36a" },
+    { "brand": "Dunn-Edwards", "code": "DEA177","name": "Treasure Trove",      "hex": "#d6b569" },
+
+    { "brand": "Dunn-Edwards", "code": "DE5566","name": "Sage Tea",            "hex": "#8d9b80" },
+    { "brand": "Dunn-Edwards", "code": "DET515", "name": "Coastal Trail",      "hex": "#7e8a6e" },
+    { "brand": "Dunn-Edwards", "code": "DE5621","name": "Aphid Green",         "hex": "#a3ad7d" },
+    { "brand": "Dunn-Edwards", "code": "DE5728","name": "Mountain Range",      "hex": "#5f6e5e" },
+    { "brand": "Dunn-Edwards", "code": "DE5740","name": "Hidden Forest",       "hex": "#465040" },
+    { "brand": "Dunn-Edwards", "code": "DE5635","name": "Dill Seed",           "hex": "#a7a567" },
+    { "brand": "Dunn-Edwards", "code": "DET543", "name": "Hidden Meadow",      "hex": "#6c8568" },
+
+    { "brand": "Dunn-Edwards", "code": "DE5759","name": "Spruced Up",          "hex": "#4d6c63" },
+    { "brand": "Dunn-Edwards", "code": "DE5770","name": "Plunge Pool",         "hex": "#2c5a5d" },
+    { "brand": "Dunn-Edwards", "code": "DET561", "name": "Open Seas",          "hex": "#4b6f7a" },
+    { "brand": "Dunn-Edwards", "code": "DE5818","name": "Splashy",             "hex": "#5e7a8e" },
+    { "brand": "Dunn-Edwards", "code": "DET587", "name": "Watery",             "hex": "#a0bcc7" },
+    { "brand": "Dunn-Edwards", "code": "DET602", "name": "Blue Caspian",       "hex": "#365e7a" },
+    { "brand": "Dunn-Edwards", "code": "DEC798", "name": "Below Zero",         "hex": "#c4d5d8" },
+    { "brand": "Dunn-Edwards", "code": "DEA170","name": "Tropical Skies",      "hex": "#3a8aa8" },
+
+    { "brand": "Dunn-Edwards", "code": "DE5921","name": "Deep River",          "hex": "#283a55" },
+    { "brand": "Dunn-Edwards", "code": "DE5871","name": "Egyptian Nile",       "hex": "#2c557a" },
+    { "brand": "Dunn-Edwards", "code": "DE5897","name": "Royal Navy",          "hex": "#2c3a5e" },
+    { "brand": "Dunn-Edwards", "code": "DE5908","name": "Twilight Zone",       "hex": "#16203a" },
+
+    { "brand": "Dunn-Edwards", "code": "DET582", "name": "Lavender Beads",     "hex": "#9b8db2" },
+    { "brand": "Dunn-Edwards", "code": "DEA135","name": "Hippie Pink",         "hex": "#c25b86" },
+    { "brand": "Dunn-Edwards", "code": "DE5044","name": "Roman Plaster",       "hex": "#e9c0a8" },
+    { "brand": "Dunn-Edwards", "code": "DE5005","name": "Just Peachy",         "hex": "#e69d8a" },
+    { "brand": "Dunn-Edwards", "code": "DEA122","name": "Burnt Sienna",        "hex": "#9c4b2b" },
+    { "brand": "Dunn-Edwards", "code": "DE5012","name": "Apple Wine",          "hex": "#8a3a45" },
+    { "brand": "Dunn-Edwards", "code": "DE6041","name": "Heliotrope",          "hex": "#604168" },
+
+    { "brand": "Dunn-Edwards", "code": "DEW398", "name": "Beach House",        "hex": "#e2dad0" },
+    { "brand": "Dunn-Edwards", "code": "DEC754", "name": "Greta Garbo",        "hex": "#cbc4b5" },
+    { "brand": "Dunn-Edwards", "code": "DEC756", "name": "Hopsack",            "hex": "#b6ad95" },
+
+    { "brand": "Sherwin-Williams", "code": "SW7008", "name": "Alabaster",            "hex": "#ede9df" },
+    { "brand": "Sherwin-Williams", "code": "SW7005", "name": "Pure White",           "hex": "#edece4" },
+    { "brand": "Sherwin-Williams", "code": "SW7551", "name": "Greek Villa",          "hex": "#e9e0ce" },
+    { "brand": "Sherwin-Williams", "code": "SW7012", "name": "Creamy",               "hex": "#ecdfca" },
+    { "brand": "Sherwin-Williams", "code": "SW6385", "name": "Dover White",          "hex": "#ece1c6" },
+    { "brand": "Sherwin-Williams", "code": "SW7036", "name": "Accessible Beige",     "hex": "#cdc1ab" },
+    { "brand": "Sherwin-Williams", "code": "SW7044", "name": "Amazing Gray",         "hex": "#aaa194" },
+    { "brand": "Sherwin-Williams", "code": "SW7029", "name": "Agreeable Gray",       "hex": "#d0c8b8" },
+    { "brand": "Sherwin-Williams", "code": "SW7039", "name": "Virtual Taupe",        "hex": "#8b7c69" },
+    { "brand": "Sherwin-Williams", "code": "SW7641", "name": "Collonade Gray",       "hex": "#bcb3a3" },
+    { "brand": "Sherwin-Williams", "code": "SW7016", "name": "Mindful Gray",         "hex": "#a59e91" },
+    { "brand": "Sherwin-Williams", "code": "SW7018", "name": "Dovetail",             "hex": "#71696a" },
+    { "brand": "Sherwin-Williams", "code": "SW7068", "name": "Grizzle Gray",         "hex": "#666260" },
+    { "brand": "Sherwin-Williams", "code": "SW7069", "name": "Iron Ore",             "hex": "#4a4845" },
+    { "brand": "Sherwin-Williams", "code": "SW6258", "name": "Tricorn Black",        "hex": "#22201f" },
+
+    { "brand": "Sherwin-Williams", "code": "SW6093", "name": "Familiar Beige",       "hex": "#c4a78a" },
+    { "brand": "Sherwin-Williams", "code": "SW6118", "name": "Leather Bound",        "hex": "#6e503c" },
+    { "brand": "Sherwin-Williams", "code": "SW6041", "name": "Otter",                "hex": "#594538" },
+    { "brand": "Sherwin-Williams", "code": "SW6048", "name": "Terra Brun",           "hex": "#3a2a23" },
+    { "brand": "Sherwin-Williams", "code": "SW6328", "name": "Fireweed",             "hex": "#a3422e" },
+    { "brand": "Sherwin-Williams", "code": "SW6332", "name": "Coral Reef",           "hex": "#d97a5e" },
+    { "brand": "Sherwin-Williams", "code": "SW6868", "name": "Real Red",             "hex": "#bc2c2f" },
+    { "brand": "Sherwin-Williams", "code": "SW7589", "name": "Habanero Chile",       "hex": "#7e2e26" },
+
+    { "brand": "Sherwin-Williams", "code": "SW6406", "name": "Honey Bees",           "hex": "#e6c465" },
+    { "brand": "Sherwin-Williams", "code": "SW6398", "name": "Lemon Twist",          "hex": "#f0c93e" },
+    { "brand": "Sherwin-Williams", "code": "SW6385", "name": "Dover White",          "hex": "#ece1c6" },
+    { "brand": "Sherwin-Williams", "code": "SW6128", "name": "Blonde",               "hex": "#e6d3a9" },
+
+    { "brand": "Sherwin-Williams", "code": "SW6206", "name": "Oyster Bay",           "hex": "#8aa087" },
+    { "brand": "Sherwin-Williams", "code": "SW6184", "name": "Austere Gray",         "hex": "#a8a695" },
+    { "brand": "Sherwin-Williams", "code": "SW6438", "name": "Garden Gate",          "hex": "#506b46" },
+    { "brand": "Sherwin-Williams", "code": "SW6432", "name": "Garden Spot",          "hex": "#5d7a4e" },
+    { "brand": "Sherwin-Williams", "code": "SW6453", "name": "Argyle",               "hex": "#3a4e3e" },
+    { "brand": "Sherwin-Williams", "code": "SW6177", "name": "Softened Green",       "hex": "#9aa78d" },
+    { "brand": "Sherwin-Williams", "code": "SW6189", "name": "Opaline",              "hex": "#a9b5a1" },
+    { "brand": "Sherwin-Williams", "code": "SW6451", "name": "Hidden Garden",        "hex": "#86997a" },
+    { "brand": "Sherwin-Williams", "code": "SW6452", "name": "Inland",               "hex": "#577557" },
+    { "brand": "Sherwin-Williams", "code": "SW7741", "name": "Willow Tree",          "hex": "#9da280" },
+
+    { "brand": "Sherwin-Williams", "code": "SW6231", "name": "Rock Candy",           "hex": "#cad9d8" },
+    { "brand": "Sherwin-Williams", "code": "SW7621", "name": "Silvermist",           "hex": "#a5b7b7" },
+    { "brand": "Sherwin-Williams", "code": "SW9135", "name": "Niebla Azul",          "hex": "#7e94a0" },
+    { "brand": "Sherwin-Williams", "code": "SW6230", "name": "Rainwashed",           "hex": "#a7c1c4" },
+    { "brand": "Sherwin-Williams", "code": "SW6225", "name": "Sleepy Blue",          "hex": "#a3c0c4" },
+    { "brand": "Sherwin-Williams", "code": "SW6244", "name": "Naval",                "hex": "#2c3e4e" },
+    { "brand": "Sherwin-Williams", "code": "SW6243", "name": "Distance",             "hex": "#3a5066" },
+    { "brand": "Sherwin-Williams", "code": "SW6250", "name": "Granite Peak",         "hex": "#56676d" },
+
+    { "brand": "Sherwin-Williams", "code": "SW6961", "name": "Indulgent",            "hex": "#1f2c4a" },
+    { "brand": "Sherwin-Williams", "code": "SW6962", "name": "Hyper Blue",           "hex": "#1d3b6e" },
+    { "brand": "Sherwin-Williams", "code": "SW6258", "name": "Tricorn Black",        "hex": "#22201f" },
+
+    { "brand": "Sherwin-Williams", "code": "SW6296", "name": "Plumage",              "hex": "#634a6e" },
+    { "brand": "Sherwin-Williams", "code": "SW6286", "name": "Mature Grape",         "hex": "#4f3d56" },
+    { "brand": "Sherwin-Williams", "code": "SW6555", "name": "Loyal Blue",           "hex": "#2a4a7e" },
+    { "brand": "Sherwin-Williams", "code": "SW6840", "name": "Exuberant Pink",       "hex": "#cd397b" },
+    { "brand": "Sherwin-Williams", "code": "SW6606", "name": "Begonia",              "hex": "#e57e7f" },
+    { "brand": "Sherwin-Williams", "code": "SW6346", "name": "Sockeye",              "hex": "#d36043" },
+    { "brand": "Sherwin-Williams", "code": "SW6604", "name": "Youthful Coral",       "hex": "#ec8b86" },
+    { "brand": "Sherwin-Williams", "code": "SW6063", "name": "Quartzite",            "hex": "#aa8270" },
+    { "brand": "Sherwin-Williams", "code": "SW9176", "name": "Iron Mountain",        "hex": "#564f4d" },
+    { "brand": "Sherwin-Williams", "code": "SW7607", "name": "Rosy Outlook",         "hex": "#e1a89b" }
+  ]
+}
diff --git a/server.js b/server.js
index f96bd08..4e3e532 100644
--- a/server.js
+++ b/server.js
@@ -3129,6 +3129,12 @@ ${_isAdmin ? `
   .pair-card .pair-pin:hover { color:#d2b15c; border-color:#d2b15c; }
   .pair-card.pinned { border-color:var(--chip-hot); background:#1f1b13; }
   .pair-card.pinned .pair-pin { color:#d2b15c; background:#d2b15c33; border-color:#d2b15c; }
+  /* Paint-chip variant — left thumb shows the chip color with brand code laid */
+  /* on top in mini-fan-deck style. Brand stripe accents on the left edge.    */
+  .pair-card.paint-card .paint-thumb { position:relative; box-shadow: inset 0 0 0 1px rgba(0,0,0,.18); }
+  .pair-card.paint-card .paint-code { position:absolute; left:4px; bottom:3px; font-family:ui-monospace,monospace; font-size:9px; padding:1px 4px; background:rgba(0,0,0,.55); color:#fff; border-radius:2px; letter-spacing:.04em; }
+  .pair-card.paint-card.paint-de { border-left:3px solid #c84a3a; }
+  .pair-card.paint-card.paint-sw { border-left:3px solid #1a4f9c; }
   .pair-card .pair-vision-badge { display:inline-block; font-size:9px; padding:0 5px; border:1px solid #3a3631; border-radius:2px; color:#888; margin-left:6px; text-transform:uppercase; letter-spacing:.05em; }
   #pair-vision-read { background:#1a1816; padding:8px 12px; margin:0 18px 8px; border:1px solid #2a2825; border-radius:2px; font-size:11px; color:#bba; line-height:1.45; font-style:italic; }
   #pair-vision-read.hidden { display:none; }
@@ -3563,6 +3569,18 @@ ${_isAdmin ? `
                '<div class="pair-title">' + escapeHTML(s.label) + '</div>' +
                '<div class="pair-reason">' + escapeHTML(s.reason) + '</div></div>' + pinBtn + '</div>';
       }
+      if (s.type === 'paint') {
+        // Paint chip — left half is the chip color block with the brand-style
+        // label tucked underneath. Lets the room be specified end-to-end
+        // (wallcovering + matching wall paint).
+        const brandClass = (s.brand || '').toLowerCase().includes('dunn') ? 'paint-de' : 'paint-sw';
+        return '<div class="pair-card paint-card ' + brandClass + pinned + '" data-pin-key="' + escapeAttr(s.pin_key) + '">' +
+               '<div class="pair-thumb paint-thumb" style="background:' + (s.hex||'#888') + '">' +
+                 '<span class="paint-code">' + escapeHTML(s.code||'') + '</span></div>' +
+               '<div class="pair-body"><span class="pair-type">paint · ' + escapeHTML(s.brand||'') + '</span>' +
+               '<div class="pair-title">' + escapeHTML(s.name||'') + '<span class="pair-hexcode">'+escapeHTML(s.hex||'')+'</span></div>' +
+               '<div class="pair-reason">' + escapeHTML(s.reason||'') + '</div></div>' + pinBtn + '</div>';
+      }
       // material / object
       return '<div class="pair-card' + pinned + '" data-pin-key="' + escapeAttr(s.pin_key) + '">' +
              '<div class="pair-thumb material">' + (s.icon || '✦') + '</div>' +
diff --git a/src/review.js b/src/review.js
index 1cd7a4a..e944a11 100644
--- a/src/review.js
+++ b/src/review.js
@@ -252,6 +252,48 @@ function catalogMatches(design, designs) {
   return out;
 }
 
+// paint chip matches — nearest 2 Dunn-Edwards + nearest 2 Sherwin-Williams
+// from data/paint_chips.json, ranked by HSL distance. Surfaces brand-name
+// paint chips as part of the pairing panel so the room can be specified
+// end-to-end: wallcovering + matching wall paint.
+let _PAINT_CHIPS_CACHE = null;
+function loadPaintChips() {
+  if (_PAINT_CHIPS_CACHE) return _PAINT_CHIPS_CACHE;
+  try {
+    const file = path.join(__dirname, '..', 'data', 'paint_chips.json');
+    _PAINT_CHIPS_CACHE = JSON.parse(fs.readFileSync(file, 'utf8')).colors || [];
+  } catch { _PAINT_CHIPS_CACHE = []; }
+  return _PAINT_CHIPS_CACHE;
+}
+function paintChipMatches(hex) {
+  if (!hex) return [];
+  const chips = loadPaintChips();
+  if (!chips.length) return [];
+  const ranked = chips
+    .map(c => ({ ...c, _d: hexDistance(c.hex, hex) }))
+    .sort((a, b) => a._d - b._d);
+  const top = { 'Dunn-Edwards': [], 'Sherwin-Williams': [] };
+  for (const r of ranked) {
+    if (top[r.brand] && top[r.brand].length < 2) top[r.brand].push(r);
+    if (top['Dunn-Edwards'].length === 2 && top['Sherwin-Williams'].length === 2) break;
+  }
+  const out = [];
+  for (const brand of ['Dunn-Edwards', 'Sherwin-Williams']) {
+    for (const c of top[brand] || []) {
+      out.push({
+        type: 'paint',
+        brand: c.brand,
+        code: c.code,
+        name: c.name,
+        hex: c.hex,
+        label: `${c.brand} ${c.code} · ${c.name}`,
+        reason: `Nearest ${c.brand} chip — paintable wall behind the wallcovering, or trim/ceiling pickup`,
+      });
+    }
+  }
+  return out;
+}
+
 // color swatches: complement + analogous
 function colorSwatches(hex) {
   if (!hex) return [];
@@ -339,15 +381,16 @@ Respond as JSON only, no preamble: [{"label":"specific material/finish (≤5 wor
 }
 
 async function generatePairings(design, allDesigns) {
-  const [matches, colors, materials] = await Promise.all([
+  const [matches, colors, paints, materials] = await Promise.all([
     Promise.resolve(catalogMatches(design, allDesigns)),
     Promise.resolve(colorSwatches(design.dominant_hex)),
+    Promise.resolve(paintChipMatches(design.dominant_hex)),
     materialSuggestions(design),
   ]);
   // load existing moodboard for this design so pins survive cache regen
   const moodboards = loadJSON(MOODBOARD_FILE, {});
   const pinnedKeys = new Set((moodboards[design.id] || []).map(p => p.pin_key));
-  const allSuggestions = [...matches, ...colors, ...materials].map((s, i) => ({
+  const allSuggestions = [...matches, ...colors, ...paints, ...materials].map((s, i) => ({
     ...s,
     idx: i,
     pin_key: pinKey(s, design.id),
@@ -366,7 +409,8 @@ async function generatePairings(design, allDesigns) {
 // Stable identifier for a suggestion so pins survive regenerations.
 function pinKey(s, designId) {
   if (s.type === 'design') return `d:${s.design_id}`;
-  if (s.type === 'color') return `c:${(s.hex || '').toLowerCase()}`;
+  if (s.type === 'color')  return `c:${(s.hex || '').toLowerCase()}`;
+  if (s.type === 'paint')  return `p:${(s.brand||'').toLowerCase()}:${(s.code||'').toLowerCase()}`;
   return `m:${(s.label || '').toLowerCase().replace(/[^a-z0-9]+/g,'-')}`;
 }
 

← d8b7aa1 stoned-animals v2 — every animal must have joint AT lips  ·  back to Wallco Ai  ·  /designs: infinite scroll, replace pagination links 65dcd74 →