[object Object]

← back to Wallco Ai

add professional-palette reference + suffix learned from dw_unified

df08ff3f4158b677b18c083f0a5aec4ebf8bc43d · 2026-05-19 23:32:31 -0700 · SteveStudio2

Files touched

Diff

commit df08ff3f4158b677b18c083f0a5aec4ebf8bc43d
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Tue May 19 23:32:31 2026 -0700

    add professional-palette reference + suffix learned from dw_unified
---
 data/professional-palette-reference.json        | 904 ++++++++++++++++++++++++
 data/professional-palette-suffix.txt            |   1 +
 scripts/build_professional_palette_reference.js | 404 +++++++++++
 3 files changed, 1309 insertions(+)

diff --git a/data/professional-palette-reference.json b/data/professional-palette-reference.json
new file mode 100644
index 0000000..4e0783a
--- /dev/null
+++ b/data/professional-palette-reference.json
@@ -0,0 +1,904 @@
+{
+  "source": "dw_unified.shopify_color_enrichment",
+  "ts": "2026-05-20T06:32:03.507Z",
+  "vendors_surveyed": [
+    {
+      "vendor": "Kravet",
+      "product_count": 2532
+    },
+    {
+      "vendor": "Koroseal",
+      "product_count": 2522
+    },
+    {
+      "vendor": "Phillip Jeffries",
+      "product_count": 2503
+    },
+    {
+      "vendor": "Schumacher",
+      "product_count": 1690
+    },
+    {
+      "vendor": "Thibaut Wallpaper",
+      "product_count": 1456
+    },
+    {
+      "vendor": "Cole & Son",
+      "product_count": 1246
+    },
+    {
+      "vendor": "Scalamandre Wallpaper",
+      "product_count": 1102
+    },
+    {
+      "vendor": "Osborne & Little",
+      "product_count": 1021
+    },
+    {
+      "vendor": "Wolf Gordon",
+      "product_count": 938
+    },
+    {
+      "vendor": "Designers Guild",
+      "product_count": 861
+    },
+    {
+      "vendor": "Nina Campbell",
+      "product_count": 780
+    },
+    {
+      "vendor": "Maya Romanoff",
+      "product_count": 639
+    },
+    {
+      "vendor": "Anna French",
+      "product_count": 625
+    },
+    {
+      "vendor": "Harlequin",
+      "product_count": 583
+    },
+    {
+      "vendor": "Arte International",
+      "product_count": 581
+    },
+    {
+      "vendor": "Brunschwig & Fils",
+      "product_count": 569
+    },
+    {
+      "vendor": "William Morris",
+      "product_count": 526
+    },
+    {
+      "vendor": "Lee Jofa",
+      "product_count": 465
+    },
+    {
+      "vendor": "Romo",
+      "product_count": 450
+    },
+    {
+      "vendor": "Innovations USA",
+      "product_count": 436
+    },
+    {
+      "vendor": "Thibaut",
+      "product_count": 417
+    },
+    {
+      "vendor": "Clarke And Clarke",
+      "product_count": 368
+    },
+    {
+      "vendor": "Gaston Y Daniela",
+      "product_count": 278
+    },
+    {
+      "vendor": "GP & J Baker",
+      "product_count": 267
+    },
+    {
+      "vendor": "Ralph Lauren",
+      "product_count": 254
+    },
+    {
+      "vendor": "Elitis",
+      "product_count": 244
+    },
+    {
+      "vendor": "Christian Lacroix Europe",
+      "product_count": 170
+    },
+    {
+      "vendor": "Donghia",
+      "product_count": 160
+    },
+    {
+      "vendor": "Mulberry",
+      "product_count": 131
+    },
+    {
+      "vendor": "Pierre Frey",
+      "product_count": 3
+    }
+  ],
+  "product_count": 23817,
+  "hex_instance_count": 62732,
+  "distinct_hex_count": 24212,
+  "top_50_hexes": [
+    {
+      "hex": "#FFFFFF",
+      "pct_of_products": 0.1014,
+      "appearances": 2414,
+      "avg_share_in_design": 21.8,
+      "hsl": {
+        "h": 0,
+        "s": 0,
+        "l": 100
+      },
+      "hue_bucket": "neutral-white",
+      "common_name": "ivory"
+    },
+    {
+      "hex": "#6B382C",
+      "pct_of_products": 0.0742,
+      "appearances": 1768,
+      "avg_share_in_design": 98,
+      "hsl": {
+        "h": 11.4,
+        "s": 41.7,
+        "l": 29.6
+      },
+      "hue_bucket": "red",
+      "common_name": "cocoa"
+    },
+    {
+      "hex": "#D3D3D3",
+      "pct_of_products": 0.0607,
+      "appearances": 1445,
+      "avg_share_in_design": 38.4,
+      "hsl": {
+        "h": 0,
+        "s": 0,
+        "l": 82.7
+      },
+      "hue_bucket": "neutral-gray",
+      "common_name": "oyster"
+    },
+    {
+      "hex": "#F5F5DC",
+      "pct_of_products": 0.0571,
+      "appearances": 1359,
+      "avg_share_in_design": 38.9,
+      "hsl": {
+        "h": 60,
+        "s": 55.6,
+        "l": 91.2
+      },
+      "hue_bucket": "orange-yellow",
+      "common_name": "cream"
+    },
+    {
+      "hex": "#F5F5F5",
+      "pct_of_products": 0.0353,
+      "appearances": 841,
+      "avg_share_in_design": 42.8,
+      "hsl": {
+        "h": 0,
+        "s": 0,
+        "l": 96.1
+      },
+      "hue_bucket": "neutral-white",
+      "common_name": "off-white"
+    },
+    {
+      "hex": "#C0C0C0",
+      "pct_of_products": 0.0304,
+      "appearances": 725,
+      "avg_share_in_design": 41.3,
+      "hsl": {
+        "h": 0,
+        "s": 0,
+        "l": 75.3
+      },
+      "hue_bucket": "neutral-gray",
+      "common_name": "dove"
+    },
+    {
+      "hex": "#D2B48C",
+      "pct_of_products": 0.0264,
+      "appearances": 628,
+      "avg_share_in_design": 35.9,
+      "hsl": {
+        "h": 34.3,
+        "s": 43.7,
+        "l": 68.6
+      },
+      "hue_bucket": "red-orange",
+      "common_name": "fawn"
+    },
+    {
+      "hex": "#000000",
+      "pct_of_products": 0.0256,
+      "appearances": 610,
+      "avg_share_in_design": 22.4,
+      "hsl": {
+        "h": 0,
+        "s": 0,
+        "l": 0
+      },
+      "hue_bucket": "neutral-black",
+      "common_name": "noir"
+    },
+    {
+      "hex": "#F2F2F2",
+      "pct_of_products": 0.0228,
+      "appearances": 542,
+      "avg_share_in_design": 35,
+      "hsl": {
+        "h": 0,
+        "s": 0,
+        "l": 94.9
+      },
+      "hue_bucket": "neutral-white",
+      "common_name": "off-white"
+    },
+    {
+      "hex": "#F0F0F0",
+      "pct_of_products": 0.0174,
+      "appearances": 415,
+      "avg_share_in_design": 54.4,
+      "hsl": {
+        "h": 0,
+        "s": 0,
+        "l": 94.1
+      },
+      "hue_bucket": "neutral-white",
+      "common_name": "cream"
+    },
+    {
+      "hex": "#4A4A4A",
+      "pct_of_products": 0.013,
+      "appearances": 310,
+      "avg_share_in_design": 42.4,
+      "hsl": {
+        "h": 0,
+        "s": 0,
+        "l": 29
+      },
+      "hue_bucket": "neutral-gray",
+      "common_name": "umber"
+    },
+    {
+      "hex": "#F5F3EE",
+      "pct_of_products": 0.013,
+      "appearances": 309,
+      "avg_share_in_design": 43.7,
+      "hsl": {
+        "h": 42.9,
+        "s": 25.9,
+        "l": 94.7
+      },
+      "hue_bucket": "red-orange",
+      "common_name": "off-white"
+    },
+    {
+      "hex": "#6B3827",
+      "pct_of_products": 0.0123,
+      "appearances": 292,
+      "avg_share_in_design": 96.8,
+      "hsl": {
+        "h": 15,
+        "s": 46.6,
+        "l": 28.6
+      },
+      "hue_bucket": "red",
+      "common_name": "cocoa"
+    },
+    {
+      "hex": "#F0E68C",
+      "pct_of_products": 0.0114,
+      "appearances": 272,
+      "avg_share_in_design": 32.4,
+      "hsl": {
+        "h": 54,
+        "s": 76.9,
+        "l": 74.5
+      },
+      "hue_bucket": "orange-yellow",
+      "common_name": "butter"
+    },
+    {
+      "hex": "#F5F0E1",
+      "pct_of_products": 0.0114,
+      "appearances": 271,
+      "avg_share_in_design": 54.4,
+      "hsl": {
+        "h": 45,
+        "s": 50,
+        "l": 92.2
+      },
+      "hue_bucket": "red-orange",
+      "common_name": "cream"
+    },
+    {
+      "hex": "#F5F5F0",
+      "pct_of_products": 0.0113,
+      "appearances": 269,
+      "avg_share_in_design": 50.9,
+      "hsl": {
+        "h": 60,
+        "s": 20,
+        "l": 95.1
+      },
+      "hue_bucket": "orange-yellow",
+      "common_name": "off-white"
+    },
+    {
+      "hex": "#333333",
+      "pct_of_products": 0.0111,
+      "appearances": 264,
+      "avg_share_in_design": 44.6,
+      "hsl": {
+        "h": 0,
+        "s": 0,
+        "l": 20
+      },
+      "hue_bucket": "neutral-gray",
+      "common_name": "charcoal"
+    },
+    {
+      "hex": "#A9A9A9",
+      "pct_of_products": 0.0109,
+      "appearances": 260,
+      "avg_share_in_design": 35.1,
+      "hsl": {
+        "h": 0,
+        "s": 0,
+        "l": 66.3
+      },
+      "hue_bucket": "neutral-gray",
+      "common_name": "stone"
+    },
+    {
+      "hex": "#363636",
+      "pct_of_products": 0.0108,
+      "appearances": 257,
+      "avg_share_in_design": 43.4,
+      "hsl": {
+        "h": 0,
+        "s": 0,
+        "l": 21.2
+      },
+      "hue_bucket": "neutral-gray",
+      "common_name": "charcoal"
+    },
+    {
+      "hex": "#F5F3ED",
+      "pct_of_products": 0.0107,
+      "appearances": 256,
+      "avg_share_in_design": 46.6,
+      "hsl": {
+        "h": 45,
+        "s": 28.6,
+        "l": 94.5
+      },
+      "hue_bucket": "red-orange",
+      "common_name": "off-white"
+    },
+    {
+      "hex": "#F2E8D9",
+      "pct_of_products": 0.0107,
+      "appearances": 254,
+      "avg_share_in_design": 46.3,
+      "hsl": {
+        "h": 36,
+        "s": 49,
+        "l": 90
+      },
+      "hue_bucket": "red-orange",
+      "common_name": "pearl"
+    },
+    {
+      "hex": "#36454F",
+      "pct_of_products": 0.0102,
+      "appearances": 243,
+      "avg_share_in_design": 29.1,
+      "hsl": {
+        "h": 204,
+        "s": 18.8,
+        "l": 26.1
+      },
+      "hue_bucket": "blue",
+      "common_name": "charcoal"
+    },
+    {
+      "hex": "#6B3A2A",
+      "pct_of_products": 0.0101,
+      "appearances": 240,
+      "avg_share_in_design": 96.5,
+      "hsl": {
+        "h": 14.8,
+        "s": 43.6,
+        "l": 29.2
+      },
+      "hue_bucket": "red",
+      "common_name": "cocoa"
+    },
+    {
+      "hex": "#808080",
+      "pct_of_products": 0.0096,
+      "appearances": 229,
+      "avg_share_in_design": 30.6,
+      "hsl": {
+        "h": 0,
+        "s": 0,
+        "l": 50.2
+      },
+      "hue_bucket": "neutral-gray",
+      "common_name": "slate"
+    },
+    {
+      "hex": "#008080",
+      "pct_of_products": 0.009,
+      "appearances": 215,
+      "avg_share_in_design": 36.1,
+      "hsl": {
+        "h": 180,
+        "s": 100,
+        "l": 25.1
+      },
+      "hue_bucket": "blue-green",
+      "common_name": "malachite"
+    },
+    {
+      "hex": "#483C32",
+      "pct_of_products": 0.009,
+      "appearances": 214,
+      "avg_share_in_design": 27.7,
+      "hsl": {
+        "h": 27.3,
+        "s": 18,
+        "l": 23.9
+      },
+      "hue_bucket": "red-orange",
+      "common_name": "walnut"
+    },
+    {
+      "hex": "#F2E7D6",
+      "pct_of_products": 0.0086,
+      "appearances": 204,
+      "avg_share_in_design": 59.5,
+      "hsl": {
+        "h": 36.4,
+        "s": 51.9,
+        "l": 89.4
+      },
+      "hue_bucket": "red-orange",
+      "common_name": "pearl"
+    },
+    {
+      "hex": "#B0C4DE",
+      "pct_of_products": 0.0084,
+      "appearances": 199,
+      "avg_share_in_design": 42.2,
+      "hsl": {
+        "h": 213.9,
+        "s": 41.1,
+        "l": 78
+      },
+      "hue_bucket": "blue",
+      "common_name": "sky"
+    },
+    {
+      "hex": "#87CEEB",
+      "pct_of_products": 0.0076,
+      "appearances": 182,
+      "avg_share_in_design": 29.8,
+      "hsl": {
+        "h": 197.4,
+        "s": 71.4,
+        "l": 72.5
+      },
+      "hue_bucket": "blue",
+      "common_name": "robin egg"
+    },
+    {
+      "hex": "#F8F8F8",
+      "pct_of_products": 0.0074,
+      "appearances": 177,
+      "avg_share_in_design": 27.5,
+      "hsl": {
+        "h": 0,
+        "s": 0,
+        "l": 97.3
+      },
+      "hue_bucket": "neutral-white",
+      "common_name": "off-white"
+    },
+    {
+      "hex": "#F5F3E8",
+      "pct_of_products": 0.0067,
+      "appearances": 160,
+      "avg_share_in_design": 47.2,
+      "hsl": {
+        "h": 50.8,
+        "s": 39.4,
+        "l": 93.5
+      },
+      "hue_bucket": "orange-yellow",
+      "common_name": "cream"
+    },
+    {
+      "hex": "#ADD8E6",
+      "pct_of_products": 0.0067,
+      "appearances": 160,
+      "avg_share_in_design": 32,
+      "hsl": {
+        "h": 194.7,
+        "s": 53.3,
+        "l": 79
+      },
+      "hue_bucket": "blue-green",
+      "common_name": "powder blue"
+    },
+    {
+      "hex": "#BDB76B",
+      "pct_of_products": 0.0066,
+      "appearances": 158,
+      "avg_share_in_design": 39.8,
+      "hsl": {
+        "h": 55.6,
+        "s": 38.3,
+        "l": 58
+      },
+      "hue_bucket": "orange-yellow",
+      "common_name": "fawn"
+    },
+    {
+      "hex": "#F8F8FF",
+      "pct_of_products": 0.0064,
+      "appearances": 153,
+      "avg_share_in_design": 36.7,
+      "hsl": {
+        "h": 240,
+        "s": 100,
+        "l": 98.6
+      },
+      "hue_bucket": "blue-purple",
+      "common_name": "off-white"
+    },
+    {
+      "hex": "#6495ED",
+      "pct_of_products": 0.0063,
+      "appearances": 150,
+      "avg_share_in_design": 40.1,
+      "hsl": {
+        "h": 218.5,
+        "s": 79.2,
+        "l": 66.1
+      },
+      "hue_bucket": "blue",
+      "common_name": "cornflower"
+    },
+    {
+      "hex": "#6B8E23",
+      "pct_of_products": 0.006,
+      "appearances": 143,
+      "avg_share_in_design": 36.7,
+      "hsl": {
+        "h": 79.6,
+        "s": 60.5,
+        "l": 34.7
+      },
+      "hue_bucket": "yellow-green",
+      "common_name": "moss"
+    },
+    {
+      "hex": "#BDBDBD",
+      "pct_of_products": 0.0055,
+      "appearances": 132,
+      "avg_share_in_design": 39.6,
+      "hsl": {
+        "h": 0,
+        "s": 0,
+        "l": 74.1
+      },
+      "hue_bucket": "neutral-gray",
+      "common_name": "dove"
+    },
+    {
+      "hex": "#808000",
+      "pct_of_products": 0.0052,
+      "appearances": 124,
+      "avg_share_in_design": 26.5,
+      "hsl": {
+        "h": 60,
+        "s": 100,
+        "l": 25.1
+      },
+      "hue_bucket": "orange-yellow",
+      "common_name": "bronze"
+    },
+    {
+      "hex": "#C2B280",
+      "pct_of_products": 0.0051,
+      "appearances": 122,
+      "avg_share_in_design": 30.2,
+      "hsl": {
+        "h": 45.5,
+        "s": 35.1,
+        "l": 63.1
+      },
+      "hue_bucket": "orange-yellow",
+      "common_name": "fawn"
+    },
+    {
+      "hex": "#E5DDCB",
+      "pct_of_products": 0.0051,
+      "appearances": 121,
+      "avg_share_in_design": 43.9,
+      "hsl": {
+        "h": 41.5,
+        "s": 33.3,
+        "l": 84.7
+      },
+      "hue_bucket": "red-orange",
+      "common_name": "bone"
+    },
+    {
+      "hex": "#F2F0E6",
+      "pct_of_products": 0.005,
+      "appearances": 119,
+      "avg_share_in_design": 41.4,
+      "hsl": {
+        "h": 50,
+        "s": 31.6,
+        "l": 92.5
+      },
+      "hue_bucket": "orange-yellow",
+      "common_name": "cream"
+    },
+    {
+      "hex": "#696969",
+      "pct_of_products": 0.0049,
+      "appearances": 117,
+      "avg_share_in_design": 42,
+      "hsl": {
+        "h": 0,
+        "s": 0,
+        "l": 41.2
+      },
+      "hue_bucket": "neutral-gray",
+      "common_name": "fern"
+    },
+    {
+      "hex": "#708090",
+      "pct_of_products": 0.0047,
+      "appearances": 112,
+      "avg_share_in_design": 31.8,
+      "hsl": {
+        "h": 210,
+        "s": 12.6,
+        "l": 50.2
+      },
+      "hue_bucket": "neutral-gray",
+      "common_name": "slate"
+    },
+    {
+      "hex": "#F0E6D2",
+      "pct_of_products": 0.0047,
+      "appearances": 111,
+      "avg_share_in_design": 36.4,
+      "hsl": {
+        "h": 40,
+        "s": 50,
+        "l": 88.2
+      },
+      "hue_bucket": "red-orange",
+      "common_name": "parchment"
+    },
+    {
+      "hex": "#FFFFF0",
+      "pct_of_products": 0.0045,
+      "appearances": 107,
+      "avg_share_in_design": 39.6,
+      "hsl": {
+        "h": 60,
+        "s": 100,
+        "l": 97.1
+      },
+      "hue_bucket": "orange-yellow",
+      "common_name": "ivory"
+    },
+    {
+      "hex": "#E0E0E0",
+      "pct_of_products": 0.0043,
+      "appearances": 103,
+      "avg_share_in_design": 34.1,
+      "hsl": {
+        "h": 0,
+        "s": 0,
+        "l": 87.8
+      },
+      "hue_bucket": "neutral-white",
+      "common_name": "linen"
+    },
+    {
+      "hex": "#E8E8E8",
+      "pct_of_products": 0.0043,
+      "appearances": 102,
+      "avg_share_in_design": 31.8,
+      "hsl": {
+        "h": 0,
+        "s": 0,
+        "l": 91
+      },
+      "hue_bucket": "neutral-white",
+      "common_name": "pearl"
+    },
+    {
+      "hex": "#F08080",
+      "pct_of_products": 0.0043,
+      "appearances": 102,
+      "avg_share_in_design": 24.6,
+      "hsl": {
+        "h": 0,
+        "s": 78.9,
+        "l": 72.2
+      },
+      "hue_bucket": "red",
+      "common_name": "coral"
+    },
+    {
+      "hex": "#4682B4",
+      "pct_of_products": 0.0042,
+      "appearances": 100,
+      "avg_share_in_design": 33.1,
+      "hsl": {
+        "h": 207.3,
+        "s": 44,
+        "l": 49
+      },
+      "hue_bucket": "blue",
+      "common_name": "denim"
+    },
+    {
+      "hex": "#E0D8C4",
+      "pct_of_products": 0.004,
+      "appearances": 95,
+      "avg_share_in_design": 38.7,
+      "hsl": {
+        "h": 42.9,
+        "s": 31.1,
+        "l": 82.4
+      },
+      "hue_bucket": "red-orange",
+      "common_name": "bone"
+    }
+  ],
+  "hue_distribution": {
+    "red-orange": 0.2781,
+    "neutral-gray": 0.1945,
+    "orange-yellow": 0.1244,
+    "blue": 0.0875,
+    "red": 0.0871,
+    "neutral-white": 0.0851,
+    "blue-green": 0.0446,
+    "yellow-green": 0.0402,
+    "green": 0.0193,
+    "neutral-black": 0.0167,
+    "magenta-pink": 0.0099,
+    "blue-purple": 0.0093,
+    "purple": 0.0033
+  },
+  "saturation_distribution": {
+    "0-20": 0.3668,
+    "20-40": 0.2838,
+    "40-60": 0.2382,
+    "60-80": 0.0759,
+    "80-100": 0.0353
+  },
+  "lightness_distribution": {
+    "0-20": 0.0225,
+    "20-40": 0.1689,
+    "40-60": 0.1847,
+    "60-80": 0.2828,
+    "80-100": 0.3411
+  },
+  "hexes_per_design_distribution": [
+    {
+      "distinct_hexes": 1,
+      "product_count": 1525,
+      "pct": 0.064
+    },
+    {
+      "distinct_hexes": 2,
+      "product_count": 11026,
+      "pct": 0.4629
+    },
+    {
+      "distinct_hexes": 3,
+      "product_count": 8258,
+      "pct": 0.3467
+    },
+    {
+      "distinct_hexes": 4,
+      "product_count": 1543,
+      "pct": 0.0648
+    },
+    {
+      "distinct_hexes": 5,
+      "product_count": 681,
+      "pct": 0.0286
+    },
+    {
+      "distinct_hexes": 6,
+      "product_count": 718,
+      "pct": 0.0301
+    },
+    {
+      "distinct_hexes": 7,
+      "product_count": 42,
+      "pct": 0.0018
+    },
+    {
+      "distinct_hexes": 8,
+      "product_count": 18,
+      "pct": 0.0008
+    },
+    {
+      "distinct_hexes": 9,
+      "product_count": 3,
+      "pct": 0.0001
+    },
+    {
+      "distinct_hexes": 10,
+      "product_count": 2,
+      "pct": 0.0001
+    },
+    {
+      "distinct_hexes": 11,
+      "product_count": 1,
+      "pct": 0
+    }
+  ],
+  "professional_palette_words": [
+    "ivory",
+    "cocoa",
+    "oyster",
+    "cream",
+    "off-white",
+    "dove",
+    "fawn",
+    "noir",
+    "umber",
+    "butter",
+    "charcoal",
+    "stone",
+    "pearl",
+    "slate",
+    "malachite",
+    "walnut",
+    "sky",
+    "robin egg",
+    "powder blue",
+    "cornflower",
+    "moss",
+    "bronze",
+    "bone",
+    "fern",
+    "parchment",
+    "linen",
+    "coral",
+    "denim"
+  ],
+  "methodology": {
+    "table": "shopify_color_enrichment",
+    "filter": "vendor IN PRO_VENDORS AND hex_codes is non-empty jsonb array",
+    "counting": "one vote per product per distinct hex (deduped within a product)",
+    "naming": "nearest weighted-RGB match against a curated 70-name designer-paint vocab",
+    "hue_bins": "11 hue bins (30°-ish) + neutral-white / neutral-gray / neutral-black when S<15%"
+  }
+}
\ No newline at end of file
diff --git a/data/professional-palette-suffix.txt b/data/professional-palette-suffix.txt
new file mode 100644
index 0000000..816b637
--- /dev/null
+++ b/data/professional-palette-suffix.txt
@@ -0,0 +1 @@
+Professional designer-wallpaper palette grounded in real-world high-end vendor catalogs (Schumacher, Maya Romanoff, Thibaut, Arte International, Cole & Son, Brunschwig & Fils, Scalamandre, Phillip Jeffries, Koroseal, Romo, Pierre Frey, Designers Guild, Osborne & Little — 23,817 real products analyzed in dw_unified.shopify_color_enrichment). Use ONLY colors from this learned palette: #FFFFFF (ivory), #6B382C (cocoa), #D3D3D3 (oyster), #F5F5DC (cream), #F5F5F5 (off-white), #C0C0C0 (dove), #D2B48C (fawn), #000000 (noir), #F2F2F2 (off-white), #F0F0F0 (cream), #4A4A4A (umber), #F5F3EE (off-white), #6B3827 (cocoa), #F0E68C (butter), #F5F0E1 (cream), #F5F5F0 (off-white), #333333 (charcoal), #A9A9A9 (stone), #363636 (charcoal), #F5F3ED (off-white), #F2E8D9 (pearl), #36454F (charcoal), #6B3A2A (cocoa), #808080 (slate), #008080 (malachite). Saturation distribution observed in real professional wallpaper: S<20% = 36.7% of all color usage, S 20–40% = 28.4%, S>60% = 11.1%. Most professional wallpapers use a low-saturation, tone-on-tone or 2–3 color story. Median distinct hues per design = 2; 90th percentile = 4. Keep total distinct hues per design ≤ 5. Never use neon, fluorescent, rainbow, primary-saturated red/yellow/blue, hot pink, or electric green. Prefer aged, slightly-grayed, archival pigments over pure chromatic colors.
diff --git a/scripts/build_professional_palette_reference.js b/scripts/build_professional_palette_reference.js
new file mode 100644
index 0000000..60d491d
--- /dev/null
+++ b/scripts/build_professional_palette_reference.js
@@ -0,0 +1,404 @@
+#!/usr/bin/env node
+/**
+ * Build professional-palette-reference.json + professional-palette-suffix.txt
+ * from REAL dw_unified.shopify_color_enrichment rows for high-end vendors.
+ *
+ * Source: dw_unified.shopify_color_enrichment (jsonb hex_codes + colors[{hex,name,percentage}])
+ *
+ * Steve's rule: stop using made-up "ivory/taupe/sand" lists. Use REAL
+ * Schumacher / Maya Romanoff / Thibaut / Arte / Cole & Son palettes.
+ */
+const { execSync } = require('child_process');
+const fs = require('fs');
+const path = require('path');
+
+// Canonical "professional / luxury wallpaper" vendors that actually exist in
+// dw_unified.shopify_color_enrichment (verified before adding here).
+const PRO_VENDORS = [
+  'Schumacher',
+  'Maya Romanoff',
+  'Thibaut',
+  'Thibaut Wallpaper',
+  'Arte International',
+  'Cole & Son',
+  'Brunschwig & Fils',
+  'Scalamandre',
+  'Scalamandre Wallpaper',
+  'Phillip Jeffries',
+  'Koroseal',
+  'Romo',
+  'Designtex',
+  'Pierre Frey',
+  'Elitis',
+  'Élitis',
+  'Hermès',
+  'Fromental',
+  'de Gournay',
+  'Nina Campbell',
+  'Osborne & Little',
+  'Designers Guild',
+  'Harlequin',
+  'Anna French',
+  'Lee Jofa',
+  'GP & J Baker',
+  'Mulberry',
+  'Ralph Lauren',
+  'William Morris',
+  'Donghia',
+  'Kravet',
+  'Wolf Gordon',
+  'Innovations USA',
+  'Christian Lacroix Europe',
+  'Gaston Y Daniela',
+  'Clarke And Clarke',
+];
+
+// ---------- color math helpers ----------
+function hexToRgb(hex) {
+  if (typeof hex !== 'string') return null;
+  let h = hex.trim().replace(/^#/, '');
+  if (h.length === 3) h = h.split('').map((c) => c + c).join('');
+  if (h.length !== 6 || /[^0-9a-f]/i.test(h)) return null;
+  const n = parseInt(h, 16);
+  return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 };
+}
+
+function rgbToHsl({ r, g, b }) {
+  const R = r / 255, G = g / 255, B = b / 255;
+  const max = Math.max(R, G, B), min = Math.min(R, G, B);
+  const l = (max + min) / 2;
+  let h = 0, s = 0;
+  if (max !== min) {
+    const d = max - min;
+    s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
+    switch (max) {
+      case R: h = (G - B) / d + (G < B ? 6 : 0); break;
+      case G: h = (B - R) / d + 2; break;
+      case B: h = (R - G) / d + 4; break;
+    }
+    h *= 60;
+  }
+  return { h, s: s * 100, l: l * 100 };
+}
+
+// 30° bins on hue + neutral bucket when saturation <15%
+const HUE_BINS = [
+  [  0,  15, 'red'],
+  [ 15,  45, 'red-orange'],
+  [ 45,  75, 'orange-yellow'],
+  [ 75, 105, 'yellow-green'],
+  [105, 150, 'green'],
+  [150, 195, 'blue-green'],
+  [195, 240, 'blue'],
+  [240, 285, 'blue-purple'],
+  [285, 315, 'purple'],
+  [315, 345, 'magenta-pink'],
+  [345, 360, 'red'],
+];
+function hueBucket(hsl) {
+  if (hsl.s < 15) {
+    if (hsl.l < 20) return 'neutral-black';
+    if (hsl.l > 85) return 'neutral-white';
+    return 'neutral-gray';
+  }
+  for (const [a, b, label] of HUE_BINS) {
+    if (hsl.h >= a && hsl.h < b) return label;
+  }
+  return 'red';
+}
+
+// Nearest plain-English name from a small designer-paint vocabulary.
+// Curated specifically for wallpaper trade colors — names a designer says,
+// not a Crayola box.
+const COLOR_VOCAB = [
+  // neutrals
+  ['ivory',         '#FFFFF0'],
+  ['cream',         '#F5F0E1'],
+  ['ecru',          '#E6DDC2'],
+  ['parchment',     '#EFE4C8'],
+  ['linen',         '#E8DFD0'],
+  ['bone',          '#E3DAC9'],
+  ['oyster',        '#D6CFC2'],
+  ['stone',         '#A8A294'],
+  ['mushroom',      '#A99F8C'],
+  ['taupe',         '#9C8E7D'],
+  ['greige',        '#A89A87'],
+  ['fawn',          '#C2B091'],
+  ['dove',          '#C9C5BC'],
+  ['pearl',         '#EAE6DF'],
+  ['ash',           '#9B9B95'],
+  ['charcoal',      '#3A3A3A'],
+  ['ink',           '#1B1B1B'],
+  ['noir',          '#0A0A0A'],
+  ['off-white',     '#FAF7F0'],
+  // greens
+  ['sage',          '#9CAE92'],
+  ['celadon',       '#A7C3A4'],
+  ['olive',         '#6B6B3A'],
+  ['moss',          '#7A8450'],
+  ['fern',          '#5C7050'],
+  ['forest',        '#2E4A2F'],
+  ['eucalyptus',    '#A3B8A0'],
+  ['hunter',        '#355E3B'],
+  ['emerald',       '#1F6E4A'],
+  ['malachite',     '#0F6B43'],
+  ['jade',          '#3F7368'],
+  // blues
+  ['robin egg',     '#9AC0CD'],
+  ['powder blue',   '#B8C9D8'],
+  ['cornflower',    '#6A8FC5'],
+  ['sky',           '#A6C5DA'],
+  ['slate',         '#6C7B89'],
+  ['denim',         '#5076A0'],
+  ['indigo',        '#2A3E66'],
+  ['midnight',      '#152238'],
+  ['navy',          '#1B2A47'],
+  ['teal',          '#2F6970'],
+  ['peacock',       '#1E5A6D'],
+  ['cerulean',      '#2E5C8A'],
+  ['delft blue',    '#385E8A'],
+  // purples
+  ['lavender',      '#B7A6C9'],
+  ['mauve',         '#A88A99'],
+  ['plum',          '#5E3B5C'],
+  ['aubergine',     '#3D2B3E'],
+  ['heather',       '#9B8AA0'],
+  // reds / pinks
+  ['blush',         '#E5C2BC'],
+  ['rose',          '#C99393'],
+  ['coral',         '#D58A75'],
+  ['terracotta',    '#B8634B'],
+  ['rust',          '#9C5234'],
+  ['oxblood',       '#5A1F22'],
+  ['burgundy',      '#5C1A2B'],
+  ['claret',        '#6E2334'],
+  ['cinnabar',      '#A33A2B'],
+  ['venetian red',  '#A33A2B'],
+  // yellows / browns
+  ['butter',        '#EDDFA6'],
+  ['straw',         '#D8C58A'],
+  ['mustard',       '#B58A2D'],
+  ['ochre',         '#B68642'],
+  ['gold',          '#A98740'],
+  ['bronze',        '#8C6E3F'],
+  ['caramel',       '#8E5D32'],
+  ['cocoa',         '#5E3F2C'],
+  ['walnut',        '#4E3A2A'],
+  ['chocolate',     '#3D2A1E'],
+  ['umber',         '#5A4232'],
+  ['sienna',        '#8A4A30'],
+];
+
+function colorDist(a, b) {
+  // simple weighted RGB; fine for "closest plain-English label"
+  const dr = a.r - b.r, dg = a.g - b.g, db = a.b - b.b;
+  return dr * dr * 0.3 + dg * dg * 0.59 + db * db * 0.11;
+}
+function nearestName(hex) {
+  const rgb = hexToRgb(hex);
+  if (!rgb) return 'unknown';
+  let best = null, bestD = Infinity;
+  for (const [name, vhex] of COLOR_VOCAB) {
+    const v = hexToRgb(vhex);
+    const d = colorDist(rgb, v);
+    if (d < bestD) { bestD = d; best = name; }
+  }
+  return best;
+}
+
+// ---------- main ----------
+(async () => {
+  const vendorList = PRO_VENDORS.map(v => v.replace(/'/g, "''")).map(v => `'${v}'`).join(',');
+  const sql = `
+    SELECT coalesce(json_agg(row_to_json(t)), '[]') FROM (
+      SELECT vendor, hex_codes, colors
+      FROM shopify_color_enrichment
+      WHERE vendor IN (${vendorList})
+        AND hex_codes IS NOT NULL
+        AND jsonb_typeof(hex_codes) = 'array'
+        AND jsonb_array_length(hex_codes) > 0
+    ) t
+  `;
+  const raw = execSync(
+    `psql -d dw_unified -tA -c ${JSON.stringify(sql.replace(/\s+/g, ' '))}`,
+    { encoding: 'utf8', maxBuffer: 256 << 20 }
+  );
+  const rows = JSON.parse(raw.trim());
+  console.log(`[pro-palette] surveyed ${rows.length.toLocaleString()} professional products`);
+
+  const vendorCounts = {};
+  const hexFreq = new Map();              // hex -> product count (one vote per product)
+  const hexProducts = new Map();          // hex -> Set(rowIdx) for pct math
+  const hexPercentSum = new Map();        // hex -> sum of "percentage" across rows when colors{} present
+  const hexPercentN   = new Map();        // hex -> n contributing
+  let hexInstancesTotal = 0;
+  const hueBucketCount = {};
+  const satBuckets = { '0-20':0, '20-40':0, '40-60':0, '60-80':0, '80-100':0 };
+  const lightBuckets = { '0-20':0, '20-40':0, '40-60':0, '60-80':0, '80-100':0 };
+  let colorsPerDesignHist = {}; // n distinct hexes per product
+
+  rows.forEach((row, idx) => {
+    vendorCounts[row.vendor] = (vendorCounts[row.vendor] || 0) + 1;
+    const hexes = Array.isArray(row.hex_codes) ? row.hex_codes : [];
+    const named = Array.isArray(row.colors) ? row.colors : [];
+    // dedupe within a product so we count "products that contain hex" not "instances"
+    const uniq = new Set();
+    hexes.forEach((h) => {
+      if (typeof h !== 'string') return;
+      const norm = h.trim().toUpperCase();
+      if (!/^#[0-9A-F]{6}$/.test(norm)) return;
+      uniq.add(norm);
+    });
+    const n = uniq.size;
+    colorsPerDesignHist[n] = (colorsPerDesignHist[n] || 0) + 1;
+    uniq.forEach((h) => {
+      hexFreq.set(h, (hexFreq.get(h) || 0) + 1);
+      hexInstancesTotal++;
+      const rgb = hexToRgb(h);
+      if (rgb) {
+        const hsl = rgbToHsl(rgb);
+        const bucket = hueBucket(hsl);
+        hueBucketCount[bucket] = (hueBucketCount[bucket] || 0) + 1;
+        const sBin = Math.min(Math.floor(hsl.s / 20), 4);
+        const lBin = Math.min(Math.floor(hsl.l / 20), 4);
+        const sKey = `${sBin*20}-${(sBin+1)*20}`;
+        const lKey = `${lBin*20}-${(lBin+1)*20}`;
+        satBuckets[sKey] = (satBuckets[sKey] || 0) + 1;
+        lightBuckets[lKey] = (lightBuckets[lKey] || 0) + 1;
+      }
+    });
+    // accumulate named percentage info
+    named.forEach((c) => {
+      if (!c || typeof c.hex !== 'string') return;
+      const norm = c.hex.trim().toUpperCase();
+      if (!/^#[0-9A-F]{6}$/.test(norm)) return;
+      const pct = Number(c.percentage);
+      if (!Number.isFinite(pct)) return;
+      hexPercentSum.set(norm, (hexPercentSum.get(norm) || 0) + pct);
+      hexPercentN.set(norm, (hexPercentN.get(norm) || 0) + 1);
+    });
+  });
+
+  const ranked = Array.from(hexFreq.entries())
+    .sort((a, b) => b[1] - a[1])
+    .slice(0, 50)
+    .map(([hex, count]) => {
+      const rgb = hexToRgb(hex);
+      const hsl = rgbToHsl(rgb);
+      return {
+        hex,
+        pct_of_products: +(count / rows.length).toFixed(4),
+        appearances: count,
+        avg_share_in_design: hexPercentN.get(hex)
+          ? +(hexPercentSum.get(hex) / hexPercentN.get(hex)).toFixed(1)
+          : null,
+        hsl: { h: +hsl.h.toFixed(1), s: +hsl.s.toFixed(1), l: +hsl.l.toFixed(1) },
+        hue_bucket: hueBucket(hsl),
+        common_name: nearestName(hex),
+      };
+    });
+
+  // normalize hue distribution as a percentage
+  const totalHex = hexInstancesTotal;
+  const hueDist = {};
+  Object.entries(hueBucketCount)
+    .sort((a, b) => b[1] - a[1])
+    .forEach(([k, v]) => { hueDist[k] = +(v / totalHex).toFixed(4); });
+
+  const satDist = {};
+  const lightDist = {};
+  Object.entries(satBuckets).forEach(([k, v]) => satDist[k] = +(v / totalHex).toFixed(4));
+  Object.entries(lightBuckets).forEach(([k, v]) => lightDist[k] = +(v / totalHex).toFixed(4));
+
+  const hexesPerDesign = Object.entries(colorsPerDesignHist)
+    .sort((a, b) => Number(a[0]) - Number(b[0]))
+    .map(([n, c]) => ({ distinct_hexes: Number(n), product_count: c, pct: +(c/rows.length).toFixed(4) }));
+
+  const reference = {
+    source: 'dw_unified.shopify_color_enrichment',
+    ts: new Date().toISOString(),
+    vendors_surveyed: Object.entries(vendorCounts).sort((a,b)=>b[1]-a[1]).map(([v,c]) => ({ vendor: v, product_count: c })),
+    product_count: rows.length,
+    hex_instance_count: hexInstancesTotal,
+    distinct_hex_count: hexFreq.size,
+    top_50_hexes: ranked,
+    hue_distribution: hueDist,
+    saturation_distribution: satDist,
+    lightness_distribution: lightDist,
+    hexes_per_design_distribution: hexesPerDesign,
+    professional_palette_words: Array.from(new Set(ranked.map(r => r.common_name))),
+    methodology: {
+      table: 'shopify_color_enrichment',
+      filter: 'vendor IN PRO_VENDORS AND hex_codes is non-empty jsonb array',
+      counting: 'one vote per product per distinct hex (deduped within a product)',
+      naming: 'nearest weighted-RGB match against a curated 70-name designer-paint vocab',
+      hue_bins: '11 hue bins (30°-ish) + neutral-white / neutral-gray / neutral-black when S<15%',
+    },
+  };
+
+  // ---------- write reference json ----------
+  const outDir = path.join(__dirname, '..', 'data');
+  const refPath = path.join(outDir, 'professional-palette-reference.json');
+  fs.writeFileSync(refPath, JSON.stringify(reference, null, 2));
+  console.log(`[pro-palette] wrote ${refPath}`);
+
+  // ---------- build the prompt suffix ----------
+  // Pick the top ~25 hexes for the suffix (any more and prompts get bloated).
+  const topForSuffix = ranked.slice(0, 25);
+  const hexList = topForSuffix.map(r => `${r.hex} (${r.common_name})`).join(', ');
+
+  const satLow = ((satDist['0-20'] || 0) * 100).toFixed(1);
+  const satMid = ((satDist['20-40'] || 0) * 100).toFixed(1);
+  const satHigh = (((satDist['60-80'] || 0) + (satDist['80-100'] || 0)) * 100).toFixed(1);
+
+  // observed median number of distinct hexes per product
+  const sorted = [];
+  hexesPerDesign.forEach(({ distinct_hexes, product_count }) => {
+    for (let i = 0; i < product_count; i++) sorted.push(distinct_hexes);
+  });
+  sorted.sort((a, b) => a - b);
+  const median = sorted[Math.floor(sorted.length / 2)] || 0;
+  const p90 = sorted[Math.floor(sorted.length * 0.9)] || 0;
+
+  const suffix =
+`Professional designer-wallpaper palette grounded in real-world high-end vendor catalogs ` +
+`(Schumacher, Maya Romanoff, Thibaut, Arte International, Cole & Son, Brunschwig & Fils, Scalamandre, ` +
+`Phillip Jeffries, Koroseal, Romo, Pierre Frey, Designers Guild, Osborne & Little — ${rows.length.toLocaleString()} ` +
+`real products analyzed in dw_unified.shopify_color_enrichment). ` +
+`Use ONLY colors from this learned palette: ${hexList}. ` +
+`Saturation distribution observed in real professional wallpaper: S<20% = ${satLow}% of all color usage, ` +
+`S 20–40% = ${satMid}%, S>60% = ${satHigh}%. ` +
+`Most professional wallpapers use a low-saturation, tone-on-tone or 2–3 color story. ` +
+`Median distinct hues per design = ${median}; 90th percentile = ${p90}. ` +
+`Keep total distinct hues per design ≤ ${Math.max(p90, 5)}. ` +
+`Never use neon, fluorescent, rainbow, primary-saturated red/yellow/blue, hot pink, or electric green. ` +
+`Prefer aged, slightly-grayed, archival pigments over pure chromatic colors.`;
+
+  const suffixPath = path.join(outDir, 'professional-palette-suffix.txt');
+  fs.writeFileSync(suffixPath, suffix + '\n');
+  console.log(`[pro-palette] wrote ${suffixPath}`);
+
+  // ---------- console headline ----------
+  console.log('\n=== HEADLINE ===');
+  console.log(`vendors surveyed:       ${Object.keys(vendorCounts).length}`);
+  console.log(`products surveyed:      ${rows.length.toLocaleString()}`);
+  console.log(`distinct hexes:         ${hexFreq.size.toLocaleString()}`);
+  console.log(`hex instances total:    ${hexInstancesTotal.toLocaleString()}`);
+  console.log(`S<20%:                  ${satLow}% of color usage`);
+  console.log(`S 20–40%:               ${satMid}%`);
+  console.log(`S>60%:                  ${satHigh}%`);
+  console.log(`median hues/design:     ${median}`);
+  console.log(`p90 hues/design:        ${p90}`);
+  console.log('\nTop 10 hexes:');
+  ranked.slice(0, 10).forEach((r, i) => {
+    console.log(`  ${String(i+1).padStart(2)}. ${r.hex}  ${String(r.common_name).padEnd(14)} ${(r.pct_of_products*100).toFixed(1)}% of products  bucket=${r.hue_bucket}`);
+  });
+  console.log('\nHue distribution:');
+  Object.entries(hueDist).slice(0, 12).forEach(([k, v]) => {
+    console.log(`  ${k.padEnd(16)} ${(v*100).toFixed(1)}%`);
+  });
+
+})().catch((e) => {
+  console.error(e);
+  process.exit(1);
+});

← eee8638 aesthetic purge: log 1007 designs to do-not-want.jsonl (neon  ·  back to Wallco Ai  ·  generator hardening: tone-on-tone suffix + neon/rainbow nega dec2cf7 →