← back to Designer Wallcoverings
auto-save: 2026-07-08T12:35:41 (6 files) — pending-approval/boost-filter-consolidation-2026-06-25 shopify/scripts/catalog-push-to-kamatera.sh vendor-scrapers/china-seas-refresh shopify/scripts/audits/harmonize-wallquest-units-before.jsonl shopify/scripts/harmonize-wallquest-length-units.py
8db74464490b88e96b1c21942933ea9e1214e2d7 · 2026-07-08 12:35:46 -0700 · Steve Abrams
Files touched
A shopify/scripts/audits/harmonize-wallquest-units-before.jsonlM shopify/scripts/catalog-push-to-kamatera.shA shopify/scripts/harmonize-wallquest-length-units.pyA shopify/theme-LIVE-591-pull-20260708/sections/main-product-high-variant.liquidA shopify/theme-LIVE-591-pull-20260708/snippets/color-dots.liquidA shopify/theme-LIVE-591-pull-20260708/snippets/color-palette.liquidA shopify/theme-LIVE-591-pull-20260708/snippets/colorway-siblings.liquidA shopify/theme-LIVE-591-pull-20260708/snippets/other-colorways.liquidA shopify/theme-LIVE-591-pull-20260708/snippets/product-gallery.liquid
Diff
commit 8db74464490b88e96b1c21942933ea9e1214e2d7
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Jul 8 12:35:46 2026 -0700
auto-save: 2026-07-08T12:35:41 (6 files) — pending-approval/boost-filter-consolidation-2026-06-25 shopify/scripts/catalog-push-to-kamatera.sh vendor-scrapers/china-seas-refresh shopify/scripts/audits/harmonize-wallquest-units-before.jsonl shopify/scripts/harmonize-wallquest-length-units.py
---
.../audits/harmonize-wallquest-units-before.jsonl | 107 +++++++++
shopify/scripts/catalog-push-to-kamatera.sh | 5 +-
.../scripts/harmonize-wallquest-length-units.py | 113 ++++++++++
.../sections/main-product-high-variant.liquid | 50 +++++
.../snippets/color-dots.liquid | 245 +++++++++++++++++++++
.../snippets/color-palette.liquid | 205 +++++++++++++++++
.../snippets/colorway-siblings.liquid | 117 ++++++++++
.../snippets/other-colorways.liquid | 163 ++++++++++++++
.../snippets/product-gallery.liquid | 226 +++++++++++++++++++
9 files changed, 1229 insertions(+), 2 deletions(-)
diff --git a/shopify/scripts/audits/harmonize-wallquest-units-before.jsonl b/shopify/scripts/audits/harmonize-wallquest-units-before.jsonl
new file mode 100644
index 00000000..25b20779
--- /dev/null
+++ b/shopify/scripts/audits/harmonize-wallquest-units-before.jsonl
@@ -0,0 +1,107 @@
+{"id": "gid://shopify/Product/1497990135920", "handle": "yagishiri-island-koi-fish-wallpaper-cda-96718", "sr": "16.5'", "dr": "33'", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.5\" \u00d7 16.5'. Double roll: 20.5\" \u00d7 33'."}
+{"id": "gid://shopify/Product/1497993674864", "handle": "mitsu-gingko-tree-wallpaper-cda-96728", "sr": "16.5'", "dr": "33'", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.5\" \u00d7 16.5'. Double roll: 20.5\" \u00d7 33'."}
+{"id": "gid://shopify/Product/1498001834096", "handle": "rebun-island-lattice-trellis-wallpaper-cda-96752", "sr": "16.5'", "dr": "33'", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27\" \u00d7 16.5'. Double roll: 27\" \u00d7 33'."}
+{"id": "gid://shopify/Product/7292603596851", "handle": "dwqw-57545-handle", "sr": "4.425ft", "dr": "8.85ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 98.4in x 4.425ft \u00d7 4.425ft. Double roll: 98.4in x 4.425ft \u00d7 8.85ft."}
+{"id": "gid://shopify/Product/7297268744243", "handle": "dwqw-58288-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.5ft \u00d7 16.5ft. Double roll: 20.9in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297268973619", "handle": "dwqw-58290-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.5ft \u00d7 16.5ft. Double roll: 20.9in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297269137459", "handle": "dwqw-58291-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.5ft \u00d7 16.5ft. Double roll: 20.9in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297269301299", "handle": "dwqw-58292-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.5ft \u00d7 16.5ft. Double roll: 20.9in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297269497907", "handle": "dwqw-58293-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.5ft \u00d7 16.5ft. Double roll: 20.9in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297269694515", "handle": "dwqw-58294-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.5ft \u00d7 16.5ft. Double roll: 20.9in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297269825587", "handle": "dwqw-58295-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.5ft \u00d7 16.5ft. Double roll: 20.9in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297269956659", "handle": "dwqw-58296-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.5ft \u00d7 16.5ft. Double roll: 20.9in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297270218803", "handle": "dwqw-58297-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.5ft \u00d7 16.5ft. Double roll: 20.9in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297270579251", "handle": "dwqw-58300-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.5ft \u00d7 16.5ft. Double roll: 20.9in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297271103539", "handle": "dwqw-58304-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.5ft \u00d7 16.5ft. Double roll: 20.9in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297281392691", "handle": "dwqw-58372-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.5ft \u00d7 16.5ft. Double roll: 20.9in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297281490995", "handle": "dwqw-58373-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.5ft \u00d7 16.5ft. Double roll: 20.9in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297281589299", "handle": "dwqw-58374-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.5ft \u00d7 16.5ft. Double roll: 20.9in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297281785907", "handle": "dwqw-58376-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.5ft \u00d7 16.5ft. Double roll: 20.9in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297281916979", "handle": "dwqw-58377-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.5ft \u00d7 16.5ft. Double roll: 20.9in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297282048051", "handle": "dwqw-58378-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.5ft \u00d7 16.5ft. Double roll: 20.9in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297282179123", "handle": "dwqw-58379-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.5ft \u00d7 16.5ft. Double roll: 20.9in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297282310195", "handle": "dwqw-58380-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.5ft \u00d7 16.5ft. Double roll: 20.9in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297294794803", "handle": "dwqw-58442-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297295089715", "handle": "dwqw-58443-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297295351859", "handle": "dwqw-58444-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297295614003", "handle": "dwqw-58445-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297296302131", "handle": "dwqw-58448-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297296498739", "handle": "dwqw-58449-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297296728115", "handle": "dwqw-58450-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297296957491", "handle": "dwqw-58451-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297297186867", "handle": "dwqw-58452-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297297350707", "handle": "dwqw-58453-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297297580083", "handle": "dwqw-58454-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297298137139", "handle": "dwqw-58457-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297298464819", "handle": "dwqw-58459-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297298595891", "handle": "dwqw-58460-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297298759731", "handle": "dwqw-58461-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297298989107", "handle": "dwqw-58462-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297299120179", "handle": "dwqw-58463-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297299349555", "handle": "dwqw-58464-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297299513395", "handle": "dwqw-58465-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297300529203", "handle": "dwqw-58469-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297300856883", "handle": "dwqw-58470-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297301184563", "handle": "dwqw-58471-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297301381171", "handle": "dwqw-58472-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297301610547", "handle": "dwqw-58473-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297301872691", "handle": "dwqw-58474-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297302102067", "handle": "dwqw-58475-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297302265907", "handle": "dwqw-58476-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297302593587", "handle": "dwqw-58477-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297302855731", "handle": "dwqw-58478-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297303117875", "handle": "dwqw-58479-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297303445555", "handle": "dwqw-58480-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297303838771", "handle": "dwqw-58481-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297304035379", "handle": "dwqw-58482-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297304592435", "handle": "dwqw-58484-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297304789043", "handle": "dwqw-58485-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297305051187", "handle": "dwqw-58486-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297305280563", "handle": "dwqw-58487-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297305509939", "handle": "dwqw-58488-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297305804851", "handle": "dwqw-58489-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297306001459", "handle": "dwqw-58490-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297306230835", "handle": "dwqw-58491-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297306558515", "handle": "dwqw-58492-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297306820659", "handle": "dwqw-58493-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297311834163", "handle": "dwqw-58508-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297312129075", "handle": "dwqw-58509-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297312522291", "handle": "dwqw-58510-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297312817203", "handle": "dwqw-58511-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297313112115", "handle": "dwqw-58512-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297313374259", "handle": "dwqw-58513-handle", "sr": "16.4041666666667ft", "dr": "32.808ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 20.9in x 16.4041666666667ft \u00d7 16.4041666666667ft. Double roll: 20.9in x 16.4041666666667ft \u00d7 32.808ft."}
+{"id": "gid://shopify/Product/7297328283699", "handle": "dwqw-58564-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297328611379", "handle": "dwqw-58565-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297329004595", "handle": "dwqw-58566-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297329365043", "handle": "dwqw-58567-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297329725491", "handle": "dwqw-58568-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297330118707", "handle": "dwqw-58569-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297330479155", "handle": "dwqw-58570-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297330741299", "handle": "dwqw-58571-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297330937907", "handle": "dwqw-58572-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297331200051", "handle": "dwqw-58573-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297331429427", "handle": "dwqw-58574-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297331626035", "handle": "dwqw-58575-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297332019251", "handle": "dwqw-58576-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297332314163", "handle": "dwqw-58577-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297332609075", "handle": "dwqw-58578-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297332871219", "handle": "dwqw-58579-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297333133363", "handle": "dwqw-58580-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297333362739", "handle": "dwqw-58581-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297333592115", "handle": "dwqw-58582-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297333854259", "handle": "dwqw-58583-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297334575155", "handle": "dwqw-58586-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297334870067", "handle": "dwqw-58587-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297335197747", "handle": "dwqw-58588-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297335459891", "handle": "dwqw-58589-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297335689267", "handle": "dwqw-58590-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297336016947", "handle": "dwqw-58591-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297336279091", "handle": "dwqw-58592-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297336803379", "handle": "dwqw-58594-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297336999987", "handle": "dwqw-58595-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297337196595", "handle": "dwqw-58596-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297337524275", "handle": "dwqw-58597-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297337819187", "handle": "dwqw-58598-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297338081331", "handle": "dwqw-58599-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297338441779", "handle": "dwqw-58600-handle", "sr": "16.5ft", "dr": "33ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.5ft \u00d7 16.5ft. Double roll: 27.5in x 16.5ft \u00d7 33ft."}
+{"id": "gid://shopify/Product/7297338703923", "handle": "dwqw-58601-handle", "sr": "16.485ft", "dr": "32.97ft", "uom": "Priced per single roll \u2014 packaged in double rolls only. Single roll: 27.5in x 16.485ft \u00d7 16.485ft. Double roll: 27.5in x 16.485ft \u00d7 32.97ft."}
diff --git a/shopify/scripts/catalog-push-to-kamatera.sh b/shopify/scripts/catalog-push-to-kamatera.sh
index 998debc7..70a884b4 100755
--- a/shopify/scripts/catalog-push-to-kamatera.sh
+++ b/shopify/scripts/catalog-push-to-kamatera.sh
@@ -28,8 +28,9 @@ KAM='postgresql://dw_admin@127.0.0.1:5432/dw_unified'
psql "$MAC3" -tAc 'select 1' >/dev/null 2>&1 || { echo "mac3 dw_unified unreachable — abort"; exit 1; }
ssh -o ConnectTimeout=15 my-server "psql '$KAM' -tAc 'select 1'" >/dev/null 2>&1 || { echo "Kamatera unreachable — abort"; exit 1; }
-# every *_catalog table on mac3
-mapfile -t TABLES < <(psql "$MAC3" -tAc "select tablename from pg_tables where schemaname='public' and tablename ~ '^[a-z0-9_]+_catalog\$' order by 1")
+# every *_catalog table on mac3 (bash 3.2-safe array read — macOS /bin/bash lacks mapfile)
+TABLES=()
+while IFS= read -r line; do [ -n "$line" ] && TABLES+=("$line"); done < <(psql "$MAC3" -tAc "select tablename from pg_tables where schemaname='public' and tablename ~ '^[a-z0-9_]+_catalog\$' order by 1")
[ "${#TABLES[@]}" -eq 0 ] && { echo "no *_catalog tables on mac3 — abort"; exit 1; }
ARGS=(); for t in "${TABLES[@]}"; do ARGS+=(-t "$t"); done
echo "syncing ${#TABLES[@]} catalog tables"
diff --git a/shopify/scripts/harmonize-wallquest-length-units.py b/shopify/scripts/harmonize-wallquest-length-units.py
new file mode 100644
index 00000000..090bebf6
--- /dev/null
+++ b/shopify/scripts/harmonize-wallquest-length-units.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python3
+"""
+Harmonize WallQuest (Malibu Wallpaper) S/R + D/R length UNITS to yards (DTD verdict A, 2026-07-08).
+
+The double-roll length pass left units mixed: most rows show yards ('5.5 yd / 11 yd'), a CDA subset
+shows the equivalent in feet ('16.5\\' / 33\\'') because their source length field held a "W x L"
+string in feet. Values are correct; this converts feet -> yards (÷3) so the whole line is consistent.
+
+Idempotent: only touches single_roll_length / double_roll_length that are expressed in FEET (a
+trailing ' or 'ft'/'feet', no 'yd'). Recomputes the unit_of_measure copy from the yard values.
+Values only change UNIT, not magnitude (16.5 ft = 5.5 yd). Before-state -> audits/ for rollback.
+
+Default --dry ; --live to apply. Run: SHOPIFY_ADMIN_TOKEN=... python3 harmonize-wallquest-length-units.py [--live]
+"""
+import json, os, re, sys, time, urllib.request, urllib.error
+
+TOKEN = os.environ["SHOPIFY_ADMIN_TOKEN"]
+SHOP = os.environ.get("SHOPIFY_SHOP", "designer-laboratory-sandbox.myshopify.com")
+GQL = f"https://{SHOP}/admin/api/2024-10/graphql.json"
+LIVE = "--live" in sys.argv
+VENDOR = "Malibu Wallpaper"
+HERE = os.path.dirname(os.path.abspath(__file__))
+BEFORE = os.path.join(HERE, "audits", "harmonize-wallquest-units-before.jsonl")
+os.makedirs(os.path.dirname(BEFORE), exist_ok=True)
+
+# a length in feet: a number followed by ' or ft/feet (and NOT already yards)
+FEET_RE = re.compile(r"^\s*\d+(?:\.\d+)?\s*(?:'|ft|feet)\s*$", re.I)
+
+def gql(q, v=None):
+ body = json.dumps({"query": q, "variables": v or {}}).encode()
+ req = urllib.request.Request(GQL, data=body, headers={"X-Shopify-Access-Token": TOKEN, "Content-Type": "application/json"})
+ for _ in range(8):
+ try:
+ with urllib.request.urlopen(req) as r: d = json.load(r)
+ if d.get("errors"):
+ s = json.dumps(d["errors"])
+ if "THROTTLED" in s: time.sleep(2.5); continue
+ raise SystemExit("GQL ERR: " + s)
+ return d["data"]
+ except urllib.error.HTTPError as e:
+ if e.code == 429: time.sleep(2.5); continue
+ raise
+ raise SystemExit("retries exhausted")
+
+def fmt(x):
+ return str(int(x)) if abs(x-round(x)) < 1e-9 else f"{round(x,3):g}"
+
+def feet_to_yards(s):
+ """'16.5\\'' / '33 ft' -> '5.5 yd' / '11 yd'. None if not a feet value."""
+ if not s or not FEET_RE.match(str(s)): return None
+ m = re.search(r"(\d+(?:\.\d+)?)", str(s))
+ return f"{fmt(float(m.group(1))/3)} yd"
+
+PAGE = """
+query($v:String!,$cursor:String){
+ products(first:80, after:$cursor, query:$v){
+ pageInfo{ hasNextPage endCursor }
+ nodes{ id handle
+ w: metafield(namespace:"global", key:"width"){ value }
+ sr: metafield(namespace:"global", key:"single_roll_length"){ value }
+ dr: metafield(namespace:"global", key:"double_roll_length"){ value }
+ uom:metafield(namespace:"global", key:"unit_of_measure"){ value } }
+ }
+}"""
+SET = """mutation($mfs:[MetafieldsSetInput!]!){ metafieldsSet(metafields:$mfs){ userErrors{ field message } } }"""
+
+def mf(owner, key, val):
+ return {"ownerId": owner, "namespace": "global", "key": key, "type": "single_line_text_field", "value": val}
+
+def main():
+ prods, cursor = [], None
+ vq = f"vendor:'{VENDOR}' status:active,draft"
+ while True:
+ d = gql(PAGE, {"v": vq, "cursor": cursor})["products"]
+ prods.extend(d["nodes"]); sys.stderr.write(f"\rscanned {len(prods)}")
+ if not d["pageInfo"]["hasNextPage"]: break
+ cursor = d["pageInfo"]["endCursor"]; time.sleep(0.25)
+ sys.stderr.write("\n")
+
+ inputs, converted, samples = [], 0, []
+ bf = open(BEFORE, "w")
+ for p in prods:
+ sr = (p["sr"] or {}).get("value"); dr = (p["dr"] or {}).get("value")
+ nsr = feet_to_yards(sr); ndr = feet_to_yards(dr)
+ if not (nsr or ndr): continue
+ converted += 1
+ bf.write(json.dumps({"id": p["id"], "handle": p["handle"], "sr": sr, "dr": dr, "uom": (p["uom"] or {}).get("value")}) + "\n")
+ fsr = nsr or sr; fdr = ndr or dr
+ if nsr: inputs.append(mf(p["id"], "single_roll_length", nsr))
+ if ndr: inputs.append(mf(p["id"], "double_roll_length", ndr))
+ W = (p["w"] or {}).get("value") or ""
+ copy = (f"Priced per single roll — packaged in double rolls only. "
+ f"Single roll: {W} × {fsr}. Double roll: {W} × {fdr}.")
+ if (p["uom"] or {}).get("value") != copy:
+ inputs.append(mf(p["id"], "unit_of_measure", copy))
+ if len(samples) < 8: samples.append(f"{p['handle'][:42]:42} S/R {sr!r}->{nsr!r} D/R {dr!r}->{ndr!r}")
+ bf.close()
+
+ print(f"{VENDOR}: scanned {len(prods)} | feet->yd products {converted} | writes {len(inputs)}")
+ for s in samples: print(" ", s)
+ print("before-state:", BEFORE)
+ if not LIVE:
+ print("\nDRY RUN — no writes. Re-run with --live to apply."); return
+ done = errs = 0
+ for i in range(0, len(inputs), 25):
+ b = inputs[i:i+25]; d = gql(SET, {"mfs": b})
+ ue = d["metafieldsSet"]["userErrors"]
+ if ue: errs += len(ue); print("userErrors:", ue[:3])
+ done += len(b); time.sleep(0.3)
+ print(f"DONE. wrote {done} metafields, userErrors={errs}")
+
+if __name__ == "__main__":
+ main()
diff --git a/shopify/theme-LIVE-591-pull-20260708/sections/main-product-high-variant.liquid b/shopify/theme-LIVE-591-pull-20260708/sections/main-product-high-variant.liquid
new file mode 100644
index 00000000..1ef0cf5a
--- /dev/null
+++ b/shopify/theme-LIVE-591-pull-20260708/sections/main-product-high-variant.liquid
@@ -0,0 +1,50 @@
+{% render 'breadcrumbs' %}
+
+{% capture slot_gallery %}
+ {% content_for 'block', type: '_section-flex-pdp-media-gallery', id: 'section-flex-pdp-media-gallery' %}
+{% endcapture %}
+
+{% capture slot_details %}
+ {% content_for 'blocks' %}
+{% endcapture %}
+
+{%- render 'section.flex-pdp', slot_gallery: slot_gallery, slot_details: slot_details -%}
+
+{% schema %}
+{
+ "name": "t:flex_pdp.section.high_variant.name",
+ "blocks": [
+ {"type": "@app"},
+ {"type": "@theme"},
+ {"type": "_section-flex-pdp-buy-buttons"},
+ {"type": "_section-flex-pdp-description"},
+ {"type": "_section-flex-pdp-divider"},
+ {"type": "_section-flex-pdp-policies"},
+ {"type": "_section-flex-pdp-price"},
+ {"type": "_section-flex-pdp-quantity-picker"},
+ {"type": "_section-flex-pdp-sku"},
+ {"type": "_section-flex-pdp-title"},
+ {"type": "_section-flex-pdp-variant-picker"},
+ {"type": "_section-flex-pdp-vendor"},
+ {"type": "_section-flex-pdp-pick-up"},
+ {"type": "_section-flex-pdp-installments"}
+ ],
+ "presets": [
+ {
+ "name": "t:flex_pdp.section.high_variant.name",
+ "blocks": [
+ {"type": "_section-flex-pdp-title"},
+ {"type": "_section-flex-pdp-vendor"},
+ {"type": "_section-flex-pdp-price"},
+ {"type": "_section-flex-pdp-installments"},
+ {"type": "_section-flex-pdp-divider"},
+ {"type": "_section-flex-pdp-variant-picker"},
+ {"type": "_section-flex-pdp-quantity-picker"},
+ {"type": "_section-flex-pdp-buy-buttons"},
+ {"type": "_section-flex-pdp-pick-up"},
+ {"type": "_section-flex-pdp-description"}
+ ]
+ }
+ ]
+}
+{% endschema %}
\ No newline at end of file
diff --git a/shopify/theme-LIVE-591-pull-20260708/snippets/color-dots.liquid b/shopify/theme-LIVE-591-pull-20260708/snippets/color-dots.liquid
new file mode 100644
index 00000000..3b97e2c2
--- /dev/null
+++ b/shopify/theme-LIVE-591-pull-20260708/snippets/color-dots.liquid
@@ -0,0 +1,245 @@
+{% assign color_dots_json_val = product.metafields.custom.color_dots_json.value %}
+{% assign color_details_val = product.metafields.custom.color_details.value %}
+{% if color_dots_json_val != blank %}
+ {% assign color_details_raw = color_dots_json_val | json %}
+{% elsif color_details_val != blank %}
+ {% assign color_details_raw = color_details_val | json %}
+{% else %}
+ {% assign color_details_raw = blank %}
+{% endif %}
+{% assign color_hex = product.metafields.custom.color_hex.value %}
+{% assign primary_hex = product.metafields.custom.primary_color_hex.value %}
+{% assign ai_colors_raw = product.metafields.dwc.ai_generated_colors.value | json %}
+{% assign color_name = product.metafields.custom.color_name.value %}
+
+{%- comment -%}
+ Color Dots v3 — 3-tier color system
+ Tier 1: color_details metafield (Gemini-enriched)
+ Tier 2: primary_color_hex + ai_generated_colors
+ Tier 3: Canvas pixel extraction from product image (onload)
+{%- endcomment -%}
+
+<div class="dw-color-dots" id="dw-color-dots-wrapper" style="display:flex;justify-content:center;gap:10px;padding:12px 0 8px;flex-wrap:wrap;min-height:30px;">
+ <div id="dw-color-dots-container" style="display:flex;justify-content:center;gap:10px;flex-wrap:wrap;"
+ {% if color_details_raw != blank and color_details_raw != 'null' %}data-colors='{{ color_details_raw | json }}'{% endif %}
+ {% if primary_hex != blank %}data-primary-hex="{{ primary_hex }}"{% endif %}
+ {% if ai_colors_raw != blank and ai_colors_raw != 'null' %}data-ai-colors='{{ ai_colors_raw }}'{% endif %}
+ {% if color_name != blank %}data-color-name="{{ color_name }}"{% endif %}
+ {% if color_hex != blank %}data-color-hex="{{ color_hex }}"{% endif %}
+ {% if product.featured_image %}data-product-image="{{ product.featured_image | img_url: '200x200' }}"{% endif %}
+ ></div>
+</div>
+
+<script>
+(function(){
+ function init() {
+ try {
+ var container = document.getElementById('dw-color-dots-container');
+ if (!container || container.dataset.rendered) return;
+ container.dataset.rendered = '1';
+
+ var C = {
+ 'red':'red-wallpaper-collection','crimson':'red-wallpaper-collection','burgundy':'red-wallpaper-collection','wine':'red-wallpaper-collection','maroon':'red-wallpaper-collection',
+ 'blue':'blue-wallpaper-collection','navy':'blue-wallpaper-collection','cobalt':'blue-wallpaper-collection','indigo':'blue-wallpaper-collection','sky blue':'blue-wallpaper-collection','steel blue':'blue-wallpaper-collection','royal blue':'blue-wallpaper-collection',
+ 'green':'green-wallpaper-collection','sage':'green-wallpaper-collection','olive':'green-wallpaper-collection','forest green':'green-wallpaper-collection','emerald':'green-wallpaper-collection','mint':'green-wallpaper-collection','moss':'green-wallpaper-collection',
+ 'gold':'gold-wallpaper','golden':'gold-wallpaper','amber':'gold-wallpaper',
+ 'yellow':'yellow-wallpaper-collection','mustard':'yellow-wallpaper-collection',
+ 'orange':'orange','rust':'orange','terracotta':'orange',
+ 'pink':'pink-wallcovering-2','blush':'pink-wallcovering-2','rose':'pink-wallcovering-2','salmon':'pink-wallcovering-2','fuchsia':'pink-wallcovering-2',
+ 'purple':'purple','violet':'purple','plum':'purple','lavender':'purple','lilac':'purple','mauve':'purple',
+ 'black':'black','charcoal':'black','ebony':'black','onyx':'black',
+ 'white':'white-wallpaper-collection','ivory':'white-wallpaper-collection','cream':'white-wallpaper-collection','pearl':'white-wallpaper-collection','off-white':'white-wallpaper-collection',
+ 'gray':'grey-wallcovering','grey':'grey-wallcovering','silver':'grey-silver','slate':'grey-wallcovering',
+ 'brown':'brown-wallpaper-collection','chocolate':'brown-wallpaper-collection','espresso':'brown-wallpaper-collection','walnut':'brown-wallpaper-collection','mocha':'brown-wallpaper-collection','sienna':'brown-wallpaper-collection',
+ 'beige':'beige-cream-wallpaper-collection','tan':'beige-cream-wallpaper-collection','taupe':'beige-cream-wallpaper-collection','sand':'beige-cream-wallpaper-collection','khaki':'beige-cream-wallpaper-collection','camel':'beige-cream-wallpaper-collection','oatmeal':'beige-cream-wallpaper-collection','linen':'beige-cream-wallpaper-collection',
+ 'coral':'coral','peach':'coral',
+ 'teal':'turquoise','aqua':'turquoise','turquoise':'turquoise','cyan':'turquoise',
+ 'metallic':'metallic-wallpaper-collections','copper':'metallic-wallpaper-collections','bronze':'metallic-wallpaper-collections'
+ };
+
+ var N = {
+ 'red':'#DC2626','crimson':'#DC143C','burgundy':'#800020','wine':'#722F37','maroon':'#800000',
+ 'blue':'#2563EB','navy':'#1B2A4A','cobalt':'#0047AB','indigo':'#3F51B5','sky blue':'#87CEEB','steel blue':'#4682B4','royal blue':'#4169E1',
+ 'green':'#16A34A','sage':'#87AE73','olive':'#808000','forest green':'#228B22','emerald':'#50C878','mint':'#98FF98','moss':'#8A9A5B',
+ 'gold':'#D4AF37','golden':'#FFD700','amber':'#FFBF00',
+ 'yellow':'#EAB308','mustard':'#FFDB58',
+ 'orange':'#EA580C','rust':'#B7410E','terracotta':'#E2725B',
+ 'pink':'#EC4899','blush':'#DE5D83','rose':'#FF007F','salmon':'#FA8072','fuchsia':'#FF00FF',
+ 'purple':'#9333EA','violet':'#7C3AED','plum':'#8E4585','lavender':'#E6E6FA','lilac':'#C8A2C8','mauve':'#E0B0FF',
+ 'black':'#1A1A1A','charcoal':'#36454F','ebony':'#555D50','onyx':'#353839',
+ 'white':'#FAFAFA','ivory':'#FFFFF0','cream':'#FFFDD0','pearl':'#F0EAD6','off-white':'#FAF9F6',
+ 'gray':'#6B7280','grey':'#6B7280','silver':'#C0C0C0','slate':'#708090',
+ 'brown':'#92400E','chocolate':'#7B3F00','espresso':'#3C1414','walnut':'#5C3A1D','mocha':'#967969','sienna':'#A0522D',
+ 'beige':'#F5F5DC','tan':'#D2B48C','taupe':'#483C32','sand':'#C2B280','khaki':'#F0E68C','camel':'#C19A6B','oatmeal':'#B8AB8C','linen':'#FAF0E6',
+ 'coral':'#FF6F61','peach':'#FFCBA4',
+ 'teal':'#0D9488','aqua':'#00FFFF','turquoise':'#40E0D0','cyan':'#06B6D4',
+ 'metallic':'#AAA9AD','copper':'#B87333','bronze':'#CD7F32',
+ 'deep indigo':'#1B2A4A','dark navy':'#1B2A4A','midnight':'#191970'
+ };
+
+ function findColl(name) {
+ var l = (name||'').toLowerCase();
+ if (C[l]) return C[l];
+ for (var k in C) { if (l.indexOf(k) > -1) return C[k]; }
+ return null;
+ }
+
+ function makeDot(hex, name, pct) {
+ try {
+ var coll = findColl(name);
+ var a = document.createElement('a');
+ a.href = coll ? '/collections/' + coll + '?sort_by=created-descending' : '/search?q=' + encodeURIComponent(name||hex) + '&sort_by=created-descending';
+ a.title = (coll?'Shop ':'Search ') + (name||'color') + ' (' + hex + ')';
+ a.style.textDecoration = 'none';
+ var d = document.createElement('div');
+ var p = (pct == null ? 20 : pct);
+ var sz=Math.max(22,Math.min(52,16+p*0.6));d.style.cssText='width:'+sz+'px;height:'+sz+'px;border-radius:50%;background:'+hex+';border:2px solid #eee;box-shadow:0 1px 3px rgba(0,0,0,0.1);cursor:pointer;transition:transform 0.15s,box-shadow 0.15s;flex-shrink:0;';
+ d.onmouseenter = function(){this.style.transform='scale(1.3)';this.style.boxShadow='0 2px 8px rgba(0,0,0,0.2)';};
+ d.onmouseleave = function(){this.style.transform='scale(1)';this.style.boxShadow='0 1px 3px rgba(0,0,0,0.1)';};
+ a.appendChild(d);
+ return a;
+ } catch(e) { return null; }
+ }
+
+ function renderDots(colors) {
+ colors.forEach(function(c){ var el = makeDot(c.hex, c.name, (c.pct || c.percentage)); if (el) container.appendChild(el); });
+ }
+
+ /* Canvas color refs */
+ var REF = {
+ 'red':[220,38,38],'navy':[27,42,74],'blue':[37,99,235],'green':[22,163,74],'sage':[135,174,115],
+ 'gold':[212,175,55],'yellow':[234,179,8],'orange':[234,88,12],'pink':[236,72,153],
+ 'purple':[147,51,234],'black':[26,26,26],'charcoal':[54,69,79],'white':[250,250,250],
+ 'ivory':[255,255,240],'cream':[255,253,208],'gray':[107,114,128],'silver':[192,192,192],
+ 'brown':[146,64,14],'beige':[245,245,220],'tan':[210,180,140],'taupe':[72,60,50],
+ 'teal':[13,148,136],'coral':[255,111,97],'olive':[128,128,0],'rust':[183,65,14],
+ 'burgundy':[128,0,32],'slate':[112,128,144],'sand':[194,178,128],'mint':[152,255,152],
+ 'turquoise':[64,224,208],'copper':[184,115,51],'bronze':[205,127,50],'mauve':[224,176,255]
+ };
+
+ function closestName(r,g,b) {
+ var best='',bestD=1e9;
+ for(var n in REF){var c=REF[n],d=Math.sqrt(Math.pow(r-c[0],2)+Math.pow(g-c[1],2)+Math.pow(b-c[2],2));if(d<bestD){bestD=d;best=n;}}
+ return best;
+ }
+ function rgbHex(r,g,b){return '#'+[r,g,b].map(function(x){return x.toString(16).padStart(2,'0');}).join('');}
+
+ function extractFromCanvas(imgUrl) {
+ var img = new Image();
+ img.crossOrigin = 'anonymous';
+ img.onload = function() {
+ try {
+ var cv = document.createElement('canvas'), sz = 50;
+ cv.width=sz; cv.height=sz;
+ var ctx=cv.getContext('2d');
+ ctx.drawImage(img,0,0,sz,sz);
+ var px=ctx.getImageData(0,0,sz,sz).data;
+ var bk={};
+ for(var i=0;i<px.length;i+=4){
+ var kr=Math.round(px[i]/32)*32,kg=Math.round(px[i+1]/32)*32,kb=Math.round(px[i+2]/32)*32;
+ var key=kr+','+kg+','+kb;
+ if(!bk[key])bk[key]={r:0,g:0,b:0,n:0};
+ bk[key].r+=px[i];bk[key].g+=px[i+1];bk[key].b+=px[i+2];bk[key].n++;
+ }
+ var sorted=Object.values(bk).sort(function(a,b){return b.n-a.n;});
+ var tot=sz*sz,colors=[],seen={};
+ for(var j=0;j<Math.min(sorted.length,8);j++){
+ var s=sorted[j],ar=Math.round(s.r/s.n),ag=Math.round(s.g/s.n),ab=Math.round(s.b/s.n);
+ var nm=closestName(ar,ag,ab);
+ if(seen[nm])continue; seen[nm]=true;
+ colors.push({name:nm,hex:rgbHex(ar,ag,ab),percentage:Math.round(s.n/tot*100)});
+ if(colors.length>=5)break;
+ }
+ if(colors.length>0) renderDots(colors);
+ } catch(e) {
+ /* CORS or canvas security error — try without crossOrigin */
+ var img2 = new Image();
+ img2.onload = function() {
+ try {
+ var cv2 = document.createElement('canvas'), sz2 = 50;
+ cv2.width=sz2; cv2.height=sz2;
+ var ctx2=cv2.getContext('2d');
+ ctx2.drawImage(img2,0,0,sz2,sz2);
+ /* Cannot getImageData without CORS — use image dominant color via 1x1 scale */
+ cv2.width=1; cv2.height=1;
+ ctx2.drawImage(img2,0,0,1,1);
+ var p=ctx2.getImageData(0,0,1,1).data;
+ var nm=closestName(p[0],p[1],p[2]);
+ renderDots([{name:nm,hex:rgbHex(p[0],p[1],p[2]),percentage:100}]);
+ } catch(e2) {}
+ };
+ img2.src = imgUrl;
+ }
+ };
+ img.onerror = function(){};
+ img.src = imgUrl;
+ }
+
+ function getImageUrl() {
+ var url = container.dataset.productImage;
+ if (url && url.length > 5) return url.indexOf('//') === 0 ? 'https:' + url : url;
+ var sels = [
+ '.product-gallery--viewport img[src]',
+ '.product-gallery img[src]',
+ '[data-product-gallery] img[src]',
+ '.product-gallery--viewport--figure img[src]',
+ '.product__media img[src]',
+ '.product-single__photo img[src]'
+ ];
+ for (var i = 0; i < sels.length; i++) {
+ var el = document.querySelector(sels[i]);
+ if (el && el.src) return el.src;
+ }
+ return null;
+ }
+
+ /* ── Tier 1: color_details metafield ── */
+ var colorsData = container.dataset.colors;
+ if (colorsData) {
+ try {
+ /* Fix Shopify Liquid Ruby hash syntax: => becomes : */
+ var fixed = colorsData.replace(/=>/g, ':');
+ renderDots(JSON.parse(fixed));
+ return;
+ } catch(e) {}
+ }
+
+ /* ── Tier 2: primary_color_hex + ai_generated_colors ── */
+ var ph = container.dataset.primaryHex;
+ var aiRaw = container.dataset.aiColors;
+ if (ph || aiRaw) {
+ var added = {};
+ if (ph) { var pd=makeDot(ph, container.dataset.colorName||'primary'); if(pd)container.appendChild(pd); added[ph.toLowerCase()]=true; }
+ try {
+ var ai = JSON.parse((aiRaw||'[]').replace(/=>/g, ':'));
+ ai.forEach(function(nm){ var h=N[nm.toLowerCase()]; if(h&&!added[h.toLowerCase()]){var ad=makeDot(h,nm); if(ad)container.appendChild(ad);added[h.toLowerCase()]=true;} });
+ } catch(e) {}
+ if (container.children.length > 0) return;
+ }
+
+ /* ── Tier 2.5: single color_hex ── */
+ var sh = container.dataset.colorHex;
+ if (sh) { var shd=makeDot(sh, container.dataset.colorName||'color'); if(shd)container.appendChild(shd); return; }
+
+ /* ── Tier 3: Canvas extraction from product image ── */
+ var imgUrl = getImageUrl();
+ if (imgUrl) {
+ extractFromCanvas(imgUrl);
+ } else {
+ var obs = new MutationObserver(function(m, o) {
+ var url = getImageUrl();
+ if (url) { o.disconnect(); extractFromCanvas(url); }
+ });
+ obs.observe(document.body, { childList: true, subtree: true });
+ setTimeout(function(){ obs.disconnect(); }, 5000);
+ }
+ } catch(e) { /* never let color-dot rendering abort other page init */ }
+ }
+
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', init);
+ } else {
+ init();
+ }
+})();
+</script>
diff --git a/shopify/theme-LIVE-591-pull-20260708/snippets/color-palette.liquid b/shopify/theme-LIVE-591-pull-20260708/snippets/color-palette.liquid
new file mode 100644
index 00000000..37f81db8
--- /dev/null
+++ b/shopify/theme-LIVE-591-pull-20260708/snippets/color-palette.liquid
@@ -0,0 +1,205 @@
+{% comment %}
+ color-palette — "Colors In This Pattern" big-swatch-card breakdown.
+ Shows ALL colors in the pattern with a % coverage under each card.
+
+ Data tiers (mirrors color-dots.liquid, kept in sync):
+ Tier 1 custom.color_dots_json / custom.color_details (Gemini-enriched JSON: [{hex,name,pct}])
+ Tier 2 custom.primary_color_hex + dwc.ai_generated_colors (name list → hex map)
+ Tier 3 custom.color_hex (single)
+ Tier 4 Canvas pixel extraction from the product image (always yields real % — reliable fallback)
+
+ Replaces the small "color-dots" strip. Percentages come from the metafield
+ `pct`/`percentage` field when present, else are computed live from the image.
+{% endcomment %}
+
+{% comment %} Read the authoritative enriched field FIRST (color_details); legacy
+ color_dots_json is only a fallback so it can never shadow fresh enrichment. {% endcomment %}
+{% assign color_dots_json_val = product.metafields.custom.color_dots_json.value %}
+{% assign color_details_val = product.metafields.custom.color_details.value %}
+{% if color_details_val != blank %}
+ {% assign color_details_raw = color_details_val | json %}
+{% elsif color_dots_json_val != blank %}
+ {% assign color_details_raw = color_dots_json_val | json %}
+{% else %}
+ {% assign color_details_raw = blank %}
+{% endif %}
+{% assign color_hex = product.metafields.custom.color_hex.value %}
+{% assign primary_hex = product.metafields.custom.primary_color_hex.value %}
+{% assign ai_colors_raw = product.metafields.dwc.ai_generated_colors.value | json %}
+{% assign color_name = product.metafields.custom.color_name.value %}
+
+<div class="dw-color-palette" data-dw-color-palette
+ {% if color_details_raw != blank and color_details_raw != 'null' %}data-colors='{{ color_details_raw | escape }}'{% endif %}
+ {% if primary_hex != blank %}data-primary-hex="{{ primary_hex }}"{% endif %}
+ {% if ai_colors_raw != blank and ai_colors_raw != 'null' %}data-ai-colors='{{ ai_colors_raw }}'{% endif %}
+ {% if color_name != blank %}data-color-name="{{ color_name }}"{% endif %}
+ {% if color_hex != blank %}data-color-hex="{{ color_hex }}"{% endif %}
+ {% if product.featured_image %}data-product-image="{{ product.featured_image | img_url: '400x400' }}"{% endif %}
+>
+ <p class="dw-color-palette__title">Colors In This Pattern</p>
+ <div class="dw-color-palette__cards" data-dw-color-palette-cards aria-live="polite"></div>
+</div>
+
+<style>
+ .dw-color-palette{padding:16px 0 10px;text-align:center;min-height:10px;}
+ .dw-color-palette__title{font-family:Lora,serif;font-size:15px;font-weight:500;color:#3D4246;margin:0 0 12px;letter-spacing:-0.01em;}
+ /* swatches shrunk to ~1/3 (84px -> 28px), chrome scaled down to match (Steve 2026-07-08) */
+ .dw-color-palette__cards{display:flex;gap:8px;justify-content:center;flex-wrap:wrap;max-width:640px;margin:0 auto;}
+ .dw-color-palette__card{display:flex;flex-direction:column;align-items:center;gap:3px;text-decoration:none;width:auto;min-width:28px;max-width:66px;}
+ a.dw-color-palette__card{cursor:pointer;}
+ .dw-color-palette__swatch{width:28px;height:28px;border-radius:4px;border:1px solid #e5e2dc;box-shadow:0 1px 3px rgba(0,0,0,.06);transition:transform .15s ease,border-color .15s ease,box-shadow .15s ease;position:relative;overflow:hidden;}
+ .dw-color-palette__card:hover .dw-color-palette__swatch{transform:translateY(-1px);border-color:#3D4246;box-shadow:0 3px 8px rgba(0,0,0,.14);}
+ .dw-color-palette__name{font-family:Lora,serif;font-size:10px;color:#3D4246;line-height:1.2;max-width:64px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-transform:capitalize;}
+ .dw-color-palette__pct{font-size:10px;font-weight:600;color:#8a8577;letter-spacing:.02em;}
+ .dw-color-palette__bar{display:flex;height:4px;border-radius:3px;overflow:hidden;max-width:640px;margin:12px auto 0;box-shadow:inset 0 0 0 1px rgba(0,0,0,.04);}
+ .dw-color-palette__bar span{display:block;height:100%;}
+ .dw-color-palette:empty{display:none;}
+ @media (max-width:600px){
+ .dw-color-palette__cards{gap:6px;}
+ .dw-color-palette__card{max-width:56px;}
+ .dw-color-palette__swatch{width:22px;height:22px;}
+ }
+</style>
+
+<script>
+(function(){
+ function init(){
+ var root = document.querySelector('[data-dw-color-palette]');
+ if(!root || root.dataset.rendered) return;
+ var cards = root.querySelector('[data-dw-color-palette-cards]');
+ if(!cards) return;
+ root.dataset.rendered = '1';
+
+ /* name -> hex fallback map (kept in sync with color-dots.liquid) */
+ var N = {
+ 'red':'#DC2626','crimson':'#DC143C','burgundy':'#800020','wine':'#722F37','maroon':'#800000',
+ 'blue':'#2563EB','navy':'#1B2A4A','cobalt':'#0047AB','indigo':'#3F51B5','sky blue':'#87CEEB','steel blue':'#4682B4','royal blue':'#4169E1','blue frost':'#BcorD','deep indigo':'#1B2A4A','dark navy':'#1B2A4A','midnight':'#191970',
+ 'green':'#16A34A','sage':'#87AE73','olive':'#808000','forest green':'#228B22','emerald':'#50C878','mint':'#98FF98','moss':'#8A9A5B','sea glass':'#C6D8CE',
+ 'gold':'#D4AF37','golden':'#FFD700','amber':'#FFBF00',
+ 'yellow':'#EAB308','mustard':'#FFDB58',
+ 'orange':'#EA580C','rust':'#B7410E','terracotta':'#E2725B','melon':'#FDBCB4','soft melon':'#F6C9A8',
+ 'pink':'#EC4899','blush':'#DE5D83','rose':'#FF007F','salmon':'#FA8072','fuchsia':'#FF00FF',
+ 'purple':'#9333EA','violet':'#7C3AED','plum':'#8E4585','lavender':'#E6E6FA','lilac':'#C8A2C8','mauve':'#E0B0FF',
+ 'black':'#1A1A1A','charcoal':'#36454F','ebony':'#555D50','onyx':'#353839',
+ 'white':'#FAFAFA','ivory':'#FFFFF0','cream':'#FFFDD0','pearl':'#F0EAD6','off-white':'#FAF9F6','eggshell':'#F0EAD6',
+ 'gray':'#6B7280','grey':'#6B7280','silver':'#C0C0C0','slate':'#708090','metallic silver':'#BFC1C2',
+ 'brown':'#92400E','chocolate':'#7B3F00','espresso':'#3C1414','walnut':'#5C3A1D','mocha':'#967969','sienna':'#A0522D',
+ 'beige':'#F5F5DC','tan':'#D2B48C','taupe':'#483C32','sand':'#C2B280','khaki':'#F0E68C','camel':'#C19A6B','oatmeal':'#B8AB8C','linen':'#FAF0E6',
+ 'coral':'#FF6F61','peach':'#FFCBA4',
+ 'teal':'#0D9488','aqua':'#00FFFF','turquoise':'#40E0D0','cyan':'#06B6D4',
+ 'metallic':'#AAA9AD','copper':'#B87333','bronze':'#CD7F32'
+ };
+ /* fix bad placeholder above */
+ N['blue frost']='#C4D3E0';
+
+ function esc(s){return (s||'').toString().replace(/[<>&"]/g,function(c){return {'<':'<','>':'>','&':'&','"':'"'}[c];});}
+ function pctOf(c){ var p = (c.pct!=null?c.pct:(c.percentage!=null?c.percentage:null)); if(p==null) return null; p=parseFloat(p); return isNaN(p)?null:Math.round(p); }
+
+ /* Build a card. Receives the WHOLE color object (fixes the color-dots `c` bug). */
+ function makeCard(c){
+ var hex = c.hex || N[(c.name||'').toLowerCase()] || '#ccc';
+ var name = c.name || '';
+ var pct = pctOf(c);
+ /* Link each color to ALL published ("pushed") SKUs of that color via the storefront
+ search — Shopify search only indexes published products, so "if pushed" is inherent.
+ No name -> not linkable, render a plain swatch. (Steve 2026-07-08) */
+ var card;
+ if(name){
+ card = document.createElement('a');
+ card.href = '/search?q=' + encodeURIComponent(name) + '&type=product';
+ card.title = 'View all ' + name + ' wallcoverings' + (pct!=null? ' — '+pct+'% here':'') + ' ('+hex+')';
+ } else {
+ card = document.createElement('div');
+ card.title = (pct!=null? pct+'% ':'') + '('+hex+')';
+ }
+ card.className = 'dw-color-palette__card';
+ var sw = document.createElement('div');
+ sw.className = 'dw-color-palette__swatch';
+ sw.style.background = hex;
+ card.appendChild(sw);
+ if(name){ var nm=document.createElement('span'); nm.className='dw-color-palette__name'; nm.textContent=name; card.appendChild(nm); }
+ if(pct!=null){ var pc=document.createElement('span'); pc.className='dw-color-palette__pct'; pc.textContent=pct+'%'; card.appendChild(pc); }
+ return card;
+ }
+
+ function render(colors){
+ if(!colors || !colors.length) return false;
+ /* de-dupe by hex, sort by pct desc when available */
+ var seen={}, list=[];
+ colors.forEach(function(c){
+ if(!c) return;
+ var key=(c.hex|| c.name ||'').toString().toLowerCase();
+ if(!key || seen[key]) return; seen[key]=1; list.push(c);
+ });
+ if(!list.length) return false;
+ var hasPct = list.some(function(c){return pctOf(c)!=null;});
+ if(hasPct) list.sort(function(a,b){return (pctOf(b)||0)-(pctOf(a)||0);});
+ list = list.slice(0,8);
+ list.forEach(function(c){ cards.appendChild(makeCard(c)); });
+ /* proportion bar when we have percentages */
+ if(hasPct){
+ var bar=document.createElement('div'); bar.className='dw-color-palette__bar';
+ var tot=list.reduce(function(s,c){return s+(pctOf(c)||0);},0)||1;
+ list.forEach(function(c){ var s=document.createElement('span'); s.style.background=c.hex||N[(c.name||'').toLowerCase()]||'#ccc'; s.style.flex=(pctOf(c)||0)/tot; bar.appendChild(s); });
+ root.appendChild(bar);
+ }
+ return true;
+ }
+
+ /* Metafield fallbacks — used ONLY if canvas extraction yields nothing.
+ (A single color_hex alone is a worse answer than a multi-color canvas read
+ for the "all colors with %" goal, so these are demoted below canvas.) */
+ function renderMeta(){
+ var ph=root.dataset.primaryHex, aiRaw=root.dataset.aiColors, sh=root.dataset.colorHex, list2=[];
+ if(ph) list2.push({hex:ph, name:root.dataset.colorName||'Primary'});
+ try { JSON.parse((aiRaw||'[]').replace(/=>/g,':')).forEach(function(nm){ var h=N[(''+nm).toLowerCase()]; if(h) list2.push({hex:h,name:nm}); }); } catch(e){}
+ if(list2.length && render(list2)) return true;
+ if(sh) return render([{hex:sh, name:root.dataset.colorName||'Color'}]);
+ return false;
+ }
+
+ /* ── Tier 1: color_details / color_dots_json metafield (BEST — local-vision enriched) ── */
+ var raw = root.dataset.colors;
+ if(raw){
+ try { if(render(JSON.parse(raw.replace(/=>/g,':')))) return; } catch(e){}
+ }
+
+ /* ── Tier 2: Canvas extraction from the product image (multi-color + %), meta as fallback ── */
+ var REF={'red':[190,55,55],'crimson':[176,40,60],'navy':[30,45,80],'blue':[70,110,175],'slate blue':[95,120,150],'powder blue':[170,196,214],'green':[70,130,80],'sage':[150,168,138],'celadon':[176,192,168],'eucalyptus':[130,146,128],'moss':[120,138,90],'olive':[124,124,70],'gold':[200,168,90],'ochre':[190,150,80],'yellow':[224,186,90],'orange':[220,120,70],'terracotta':[196,110,85],'coral':[224,140,120],'pink':[220,150,165],'blush':[224,190,190],'purple':[140,100,160],'lavender':[190,180,205],'black':[40,40,40],'charcoal':[70,74,78],'white':[248,246,240],'ivory':[240,236,224],'cream':[236,228,206],'eggshell':[224,216,196],'gray':[140,140,142],'greige':[176,168,154],'silver':[196,196,198],'brown':[130,95,70],'walnut':[110,80,55],'taupe':[150,138,120],'beige':[214,202,178],'tan':[196,176,140],'sand':[200,184,150],'stone':[186,180,166],'teal':[70,140,140],'turquoise':[110,180,175]};
+ function closestName(r,g,b){var best='',bd=1e9;for(var n in REF){var c=REF[n],d=Math.pow(r-c[0],2)+Math.pow(g-c[1],2)+Math.pow(b-c[2],2);if(d<bd){bd=d;best=n;}}return best;}
+ function hx(r,g,b){return '#'+[r,g,b].map(function(x){return x.toString(16).padStart(2,'0');}).join('');}
+ function fromCanvas(url){
+ var img=new Image(); img.crossOrigin='anonymous';
+ img.onload=function(){
+ try{
+ var sz=120,cv=document.createElement('canvas');cv.width=sz;cv.height=sz;
+ var ctx=cv.getContext('2d');ctx.drawImage(img,0,0,sz,sz);
+ var px=ctx.getImageData(0,0,sz,sz).data,bk={};
+ for(var i=0;i<px.length;i+=4){var kr=Math.round(px[i]/22)*22,kg=Math.round(px[i+1]/22)*22,kb=Math.round(px[i+2]/22)*22,k=kr+','+kg+','+kb;if(!bk[k])bk[k]={r:0,g:0,b:0,n:0};bk[k].r+=px[i];bk[k].g+=px[i+1];bk[k].b+=px[i+2];bk[k].n++;}
+ var sorted=Object.keys(bk).map(function(k){return bk[k];}).sort(function(a,b){return b.n-a.n;});
+ var tot=sz*sz,out=[],seen={};
+ for(var j=0;j<sorted.length && out.length<8;j++){var s=sorted[j],ar=Math.round(s.r/s.n),ag=Math.round(s.g/s.n),ab=Math.round(s.b/s.n),nm=closestName(ar,ag,ab),pct=Math.round(s.n/tot*100);if(seen[nm]||pct<2)continue;seen[nm]=1;out.push({name:nm,hex:hx(ar,ag,ab),pct:pct});}
+ if(!render(out.slice(0,6))) renderMeta();
+ }catch(e){ renderMeta(); }
+ };
+ img.onerror=function(){ renderMeta(); };
+ img.src=url;
+ }
+ function imgUrl(){
+ var u=root.dataset.productImage;
+ if(u&&u.length>5) return u.indexOf('//')===0?'https:'+u:u;
+ var sels=['.product-gallery--viewport img[src]','.product-gallery img[src]','[data-product-gallery] img[src]','.media-gallery__slide img[src]'];
+ for(var i=0;i<sels.length;i++){var el=document.querySelector(sels[i]);if(el&&el.src)return el.src;}
+ return null;
+ }
+ var u=imgUrl();
+ if(u){ fromCanvas(u); }
+ else {
+ var obs=new MutationObserver(function(m,o){var v=imgUrl();if(v){o.disconnect();fromCanvas(v);}});
+ obs.observe(document.body,{childList:true,subtree:true});
+ setTimeout(function(){ obs.disconnect(); if(!cards.children.length) renderMeta(); },5000);
+ }
+ }
+ if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',init);}else{init();}
+})();
+</script>
diff --git a/shopify/theme-LIVE-591-pull-20260708/snippets/colorway-siblings.liquid b/shopify/theme-LIVE-591-pull-20260708/snippets/colorway-siblings.liquid
new file mode 100644
index 00000000..f91a4a2f
--- /dev/null
+++ b/shopify/theme-LIVE-591-pull-20260708/snippets/colorway-siblings.liquid
@@ -0,0 +1,117 @@
+{% comment %}
+ Colorway Siblings — shows other colors of the same pattern as clickable thumbnails
+ Uses dwc.pattern_name metafield or parses from title
+{% endcomment %}
+
+{%- assign pattern_name = product.metafields.custom.pattern_name.value | default: product.metafields.dwc.pattern_name.value | default: product.metafields.custom.design_name.value -%}
+
+{%- unless pattern_name != blank -%}
+ {%- assign pattern_parts = product.title | split: " - " -%}
+ {%- if pattern_parts.size >= 2 -%}
+ {%- assign pattern_name = pattern_parts[0] | strip -%}
+ {%- else -%}
+ {%- assign pattern_parts = product.title | split: " – " -%}
+ {%- if pattern_parts.size >= 2 -%}
+ {%- assign pattern_name = pattern_parts[0] | strip -%}
+ {%- else -%}
+ {%- assign pipe_parts = product.title | split: " | " -%}
+ {%- if pipe_parts.size >= 2 -%}
+ {%- assign pattern_name = pipe_parts[0] | strip -%}
+ {%- endif -%}
+ {%- endif -%}
+ {%- endif -%}
+{%- endunless -%}
+
+{%- if pattern_name != blank and pattern_name.size > 2 -%}
+ <div id="dw-colorway-siblings" style="display:none;padding:14px 0 8px;text-align:center;">
+ <p style="font-family:Lora,serif;font-size:15px;font-weight:500;color:#3D4246;margin:0 0 10px;letter-spacing:-0.01em;">
+ Other Colorways
+ </p>
+ <div id="dw-colorway-thumbs" style="display:flex;gap:8px;justify-content:center;flex-wrap:wrap;max-width:600px;margin:0 auto;"></div>
+ </div>
+
+ <script>
+ (function() {
+ var currentHandle = {{ product.handle | json }};
+ var currentVendor = {{ product.vendor | json }};
+ var searchTerm = {{ pattern_name | json }};
+ if (!searchTerm || searchTerm.length < 3) return;
+
+ fetch('/search/suggest.json?q=' + encodeURIComponent(searchTerm) + '&resources[type]=product&resources[limit]=24', { credentials: 'same-origin' })
+ .then(function(r) { return r.ok ? r.json() : null; })
+ .then(function(data) {
+ if (!data || !data.resources || !data.resources.results) return;
+ var products = data.resources.results.products || [];
+ var matchLower = searchTerm.toLowerCase();
+ var container = document.getElementById('dw-colorway-thumbs');
+ var section = document.getElementById('dw-colorway-siblings');
+ if (!container || !section) return;
+
+ var siblings = [];
+ for (var i = 0; i < products.length; i++) {
+ var p = products[i];
+ /* Skip current product */
+ if (p.handle === currentHandle) continue;
+ /* Must match pattern name in title */
+ if ((p.title || '').toLowerCase().indexOf(matchLower) < 0) continue;
+ /* Must match vendor to avoid cross-vendor contamination */
+ if (currentVendor && (p.vendor || '').toLowerCase() !== currentVendor.toLowerCase()) continue;
+ /* Must have an image */
+ if (!p.image && !(p.featured_image && p.featured_image.url)) continue;
+
+ var imgUrl = (p.featured_image && p.featured_image.url) ? p.featured_image.url : p.image;
+
+ /* Extract color: remove pattern name and vendor */
+ var label = (p.title || '');
+ var pipeIdx = label.indexOf(' | ');
+ if (pipeIdx > 0) label = label.substring(0, pipeIdx);
+ var dashIdx = label.indexOf(' - ');
+ if (dashIdx > 0) {
+ label = label.substring(dashIdx + 3).trim();
+ } else {
+ var pLower = label.toLowerCase().indexOf(matchLower);
+ if (pLower === 0) label = label.substring(searchTerm.length).trim();
+ }
+ label = label.replace(/\s*(Wallcoverings?|Fabrics?|Murals?|Commercial)\s*/gi, '').trim();
+ if (!label) label = 'View';
+
+ siblings.push({ color: label, image: imgUrl, url: p.url || ('/products/' + p.handle) });
+ }
+
+ if (siblings.length === 0) return;
+ siblings.sort(function(a, b) { return a.color.localeCompare(b.color); });
+
+ for (var j = 0; j < siblings.length; j++) {
+ var s = siblings[j];
+ var a = document.createElement('a');
+ a.href = s.url;
+ a.title = s.color;
+ a.style.cssText = 'display:flex;flex-direction:column;align-items:center;gap:4px;text-decoration:none;width:72px;';
+
+ var wrap = document.createElement('div');
+ wrap.style.cssText = 'width:64px;height:64px;border-radius:8px;overflow:hidden;border:2px solid #e5e2dc;transition:border-color 0.15s,box-shadow 0.15s;';
+ a.onmouseover = function() { this.firstChild.style.borderColor = '#3D4246'; this.firstChild.style.boxShadow = '0 2px 8px rgba(0,0,0,0.1)'; };
+ a.onmouseout = function() { this.firstChild.style.borderColor = '#e5e2dc'; this.firstChild.style.boxShadow = 'none'; };
+
+ var img = document.createElement('img');
+ img.src = s.image;
+ img.alt = s.color;
+ img.loading = 'lazy';
+ img.style.cssText = 'width:100%;height:100%;object-fit:cover;';
+ wrap.appendChild(img);
+ a.appendChild(wrap);
+
+ var nameSpan = document.createElement('span');
+ nameSpan.textContent = s.color;
+ nameSpan.style.cssText = 'font-size:10px;color:#666;line-height:1.2;max-width:72px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;text-align:center;';
+ a.appendChild(nameSpan);
+
+ container.appendChild(a);
+ }
+
+ section.style.display = '';
+ })
+ .catch(function() {});
+ })();
+ </script>
+{%- endif -%}
diff --git a/shopify/theme-LIVE-591-pull-20260708/snippets/other-colorways.liquid b/shopify/theme-LIVE-591-pull-20260708/snippets/other-colorways.liquid
new file mode 100644
index 00000000..7141878e
--- /dev/null
+++ b/shopify/theme-LIVE-591-pull-20260708/snippets/other-colorways.liquid
@@ -0,0 +1,163 @@
+{% comment %}
+ Other Colorways — shows sibling products with the same pattern name
+ Uses Shopify Predictive Search API to find products sharing the pattern.
+ Reads dwc.pattern_name metafield; falls back to parsing title before " - " or " | ".
+{% endcomment %}
+
+{% assign pattern_name = product.metafields.dwc.pattern_name.value %}
+{% unless pattern_name != blank %}
+ {% assign title_parts = product.title | split: ' | ' %}
+ {% assign before_pipe = title_parts[0] %}
+ {% assign dash_parts = before_pipe | split: ' - ' %}
+ {% if dash_parts.size > 1 %}
+ {% assign pattern_name = dash_parts[0] | strip %}
+ {% else %}
+ {% assign pattern_name = before_pipe | strip %}
+ {% endif %}
+{% endunless %}
+
+{% if pattern_name != blank %}
+<div id="dw-other-colorways" data-pattern="{{ pattern_name | escape }}" data-current="{{ product.handle }}" data-vendor="{{ product.vendor | escape }}" style="display:none;">
+ <style>
+ .dw-colorways-section {
+ margin: 24px 0 32px;
+ padding: 0 20px;
+ }
+ .dw-colorways-heading {
+ font-family: 'Lora', serif;
+ font-size: 18px;
+ font-weight: 500;
+ margin: 0 0 14px;
+ color: #1a1a1a;
+ letter-spacing: -0.01em;
+ }
+ .dw-colorways-scroll {
+ display: flex;
+ gap: 12px;
+ overflow-x: auto;
+ padding-bottom: 8px;
+ scroll-snap-type: x mandatory;
+ -webkit-overflow-scrolling: touch;
+ }
+ .dw-colorways-scroll::-webkit-scrollbar { height: 4px; }
+ .dw-colorways-scroll::-webkit-scrollbar-track { background: #f0ede8; border-radius: 2px; }
+ .dw-colorways-scroll::-webkit-scrollbar-thumb { background: #ccc; border-radius: 2px; }
+ .dw-colorway-card {
+ flex: 0 0 140px;
+ scroll-snap-align: start;
+ text-decoration: none;
+ color: inherit;
+ border: 1px solid #e5e2dc;
+ border-radius: 8px;
+ overflow: hidden;
+ background: #fff;
+ transition: border-color 0.15s, box-shadow 0.15s;
+ }
+ .dw-colorway-card:hover {
+ border-color: #bbb;
+ box-shadow: 0 2px 10px rgba(0,0,0,0.06);
+ }
+ .dw-colorway-card.dw-current {
+ border-color: #3D4246;
+ border-width: 2px;
+ }
+ .dw-colorway-card img {
+ width: 100%;
+ aspect-ratio: 1 / 1;
+ object-fit: cover;
+ display: block;
+ background: #f2f0ec;
+ }
+ .dw-colorway-label {
+ padding: 8px 10px;
+ font-size: 12px;
+ line-height: 1.3;
+ color: #3D4246;
+ font-weight: 500;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ </style>
+
+ <div class="dw-colorways-section">
+ <h3 class="dw-colorways-heading">Other Colorways</h3>
+ <div class="dw-colorways-scroll" id="dw-colorways-list"></div>
+ </div>
+</div>
+
+<script>
+(function() {
+ var wrapper = document.getElementById('dw-other-colorways');
+ if (!wrapper) return;
+ var pattern = wrapper.dataset.pattern;
+ var currentHandle = wrapper.dataset.current;
+ var currentVendor = wrapper.dataset.vendor;
+ if (!pattern) return;
+
+ var url = '/search/suggest.json?q=' + encodeURIComponent(pattern) + '&resources[type]=product&resources[limit]=24';
+
+ fetch(url)
+ .then(function(r) { return r.json(); })
+ .then(function(data) {
+ var products = (data.resources && data.resources.results && data.resources.results.products) || [];
+
+ // Filter: same vendor, has image, and title contains the pattern name
+ var patternLower = pattern.toLowerCase();
+ var vendorLower = (currentVendor || '').toLowerCase();
+ var siblings = products.filter(function(p) {
+ if (!p.image) return false;
+ var titleLower = (p.title || '').toLowerCase();
+ if (titleLower.indexOf(patternLower) === -1) return false;
+ // Must match vendor to avoid cross-vendor contamination
+ if (vendorLower) {
+ var pVendor = ((p.vendor || '')).toLowerCase();
+ if (pVendor !== vendorLower) return false;
+ }
+ return true;
+ });
+
+ if (siblings.length < 2) return; // Need at least 2 (including current) to show section
+
+ var container = document.getElementById('dw-colorways-list');
+ siblings.forEach(function(p) {
+ var a = document.createElement('a');
+ a.href = p.url;
+ a.className = 'dw-colorway-card';
+ if (p.url.indexOf('/' + currentHandle) !== -1) {
+ a.className += ' dw-current';
+ }
+
+ var img = document.createElement('img');
+ img.src = p.image.replace(/(\.[a-z]+)(\?|$)/, '_300x300$1$2');
+ img.alt = p.title;
+ img.loading = 'lazy';
+ a.appendChild(img);
+
+ // Extract color from title: "Pattern - Color | Vendor" → "Color"
+ var label = p.title || '';
+ var pipeIdx = label.indexOf(' | ');
+ if (pipeIdx > 0) label = label.substring(0, pipeIdx);
+ var dashIdx = label.indexOf(' - ');
+ if (dashIdx > 0) label = label.substring(dashIdx + 3).trim();
+ else {
+ // Try removing pattern name prefix
+ var pi = label.toLowerCase().indexOf(patternLower);
+ if (pi === 0) label = label.substring(pattern.length).trim();
+ }
+ if (!label) label = p.title;
+
+ var labelDiv = document.createElement('div');
+ labelDiv.className = 'dw-colorway-label';
+ labelDiv.textContent = label;
+ a.appendChild(labelDiv);
+
+ container.appendChild(a);
+ });
+
+ wrapper.style.display = 'block';
+ })
+ .catch(function() {});
+})();
+</script>
+{% endif %}
diff --git a/shopify/theme-LIVE-591-pull-20260708/snippets/product-gallery.liquid b/shopify/theme-LIVE-591-pull-20260708/snippets/product-gallery.liquid
new file mode 100644
index 00000000..988fdc82
--- /dev/null
+++ b/shopify/theme-LIVE-591-pull-20260708/snippets/product-gallery.liquid
@@ -0,0 +1,226 @@
+{% comment %}Updated: 2026-03-23T19:12:17.978750{% endcomment %}
+{% comment %}
+@param images_layout
+ Image layout setting - masonry, slideshow or list
+@param use_masonry
+@param product
+@param variant
+@param selected_media
+@param enable_zoom
+{% endcomment %}
+
+<div
+ class="
+ product-images
+ product-gallery
+ {% if images_layout == 'masonry' %}
+ product-images-masonry
+ product-images-masonry-{{ product.media.size }}
+ {% endif %}
+ "
+ {% if images_layout == 'masonry' and product.media.size > 1 %}
+ data-masonry-gallery
+ {% endif %}
+ data-product-gallery
+ data-product-gallery-layout="{{ images_layout }}"
+ {% if enable_zoom %}data-product-gallery-image-zoom{% endif %}
+>
+ {% if images_layout == 'masonry' and product.media.size > 1 %}
+ <div class="product-image-masonry-sizer" data-masonry-image-sizer></div>
+ {% endif %}
+ <div
+ class="
+ product-gallery--viewport
+ {% if images_layout == 'slideshow' and product.media.size > 1 %}product-gallery--viewport--has-navigation{% endif %}
+ "
+ data-product-gallery-viewport
+ >
+ {% assign models = product.media | where: 'media_type', 'model' %}
+ {%- if models.size > 0 -%}
+ <script>
+ window.ShopifyXR=window.ShopifyXR||function(){(ShopifyXR.q=ShopifyXR.q||[]).push(arguments)}
+ ShopifyXR('addModels', {{ models | json }});
+ </script>
+ {%- endif -%}
+ {% assign first_model = models | first %}
+ {% for media in product.media %}
+
+ {% assign use_large_image = false %}
+ {% if product.media.size == 2 %}
+ {% assign use_large_image = true %}
+ {% elsif forloop.first and use_masonry %}
+ {% assign use_large_image = true %}
+ {% endif %}
+
+ {% if media.media_type == 'model' %}
+ {% assign model = media %}
+ {% else %}
+ {% assign model = first_model %}
+ {% endif %}
+
+ {% if model %}
+ {% capture model_view_button %}
+ <button
+ class="product-gallery--viewinyourspace"
+ data-shopify-xr
+ data-shopify-model3d-id="{{ model.id }}"
+ data-shopify-title="{{ product.title }}"
+ data-shopify-xr-hidden
+ data-viewinyourspace
+ >
+ {% render 'icons', id: 'ar' %}
+ {{ 'products.media.view_in_your_space' | t }}
+ </button>
+ {% endcapture %}
+ {% endif %}
+
+ <figure
+ class="product-gallery--viewport--figure {% if use_large_image %}product-image-wide{% endif %}"
+ tabindex="-1"
+ {% if images_layout != 'list' %}
+ {% if selected_media.id != media.id %}
+ aria-hidden="true"
+ {% else %}
+ aria-hidden="false"
+ {% endif %}
+ {% endif %}
+ data-product-gallery-figure="{{ forloop.index0 }}"
+ {% if images_layout == 'slideshow' %}
+ data-product-gallery-selected="{%- if selected_media.id == media.id -%}true{%- else -%}false{%- endif -%}"
+ {% endif %}
+ data-media="{{ media.id }}"
+ data-media-type="{{ media.media_type }}"
+ >
+ <div class="product-gallery--media-wrapper">
+ {% case media.media_type %}
+ {% when 'image' %}
+ {%- capture data_attr -%}
+ {% if enable_zoom %}data-image-zoom="{{ media.id }}"{% endif %}
+ {%- endcapture -%}
+ {%
+ render 'rimg'
+ img: media.preview_image,
+ size: '1024x1024',
+ attr: data_attr,
+ lazy: true,
+ %}
+ {% if enable_zoom %}
+ <div class="product-zoom" data-product-zoom-id="{{ media.id }}"></div>
+ {% endif %}
+ {% when 'model' %}
+ {{ media | model_viewer_tag: image_size: '1024x', reveal: 'interaction' , toggleable: true, interaction-prompt-threshold: 0 }}
+ {% when 'video' %}
+ {{ media | media_tag: image_size: '1024x' }}
+ {% when 'external_video' %}
+ {{ media | media_tag: image_size: '1024x' }}
+ {% else %}
+ {{ media | media_tag }}
+ {% endcase %}
+ </div>
+ {% if images_layout == 'slideshow' %}
+ {{ model_view_button }}
+ {% endif %}
+ </figure>
+ {% if images_layout == 'list' %}
+ {{ model_view_button }}
+ {% endif %}
+ {% else %}
+ <figure
+ class="product-gallery--viewport--figure"
+ data-product-gallery-selected="true"
+ >
+ {{ 'product-1' | placeholder_svg_tag: 'placeholder-svg' }}
+ </figure>
+ {% endfor %}
+
+ </div>
+
+ {% comment %} Colors In This Pattern — big swatch cards w/ % (replaces color-dots). Sits between the product images and Other Colorways. {% endcomment %}
+ {% render 'color-palette' %}
+ {% render 'colorway-siblings' %}
+
+ {% if images_layout == 'slideshow' and product.media.size > 1 %}
+ <div class="product-gallery-navigation-outer-wrapper">
+ {% if product.media.size > 4 %}
+ <span class="product-thumbnails-navigation-previous"></span>
+ {% endif %}
+ <div class="product-gallery--navigation-wrapper">
+ <div
+ class="product-gallery--navigation {% if product.media.size > 4 %}has-side-scroll{% endif %}"
+ data-product-gallery-navigation
+ >
+ {% for media in product.media %}
+ <button
+ class="
+ product-gallery--media-thumbnail
+ product-gallery--{{ media.media_type }}-thumbnail
+ "
+ type="button"
+ tab-index="0"
+ aria-label="{{ media.alt }} {{ 'general.accessibility.nav_product_thumbnail' | t }}"
+ data-product-gallery-thumbnail="{{ forloop.index0 }}"
+ data-product-gallery-selected="{%- if selected_media.id == media.id -%}true{%- else -%}false{%- endif -%}"
+ data-media="{{ media.id }}"
+ data-media-type="{{ media.media_type }}"
+ >
+ {%
+ render 'rimg'
+ img: media.preview_image,
+ size: '100x100',
+ lazy: true
+ %}
+ {% case media.media_type %}
+ {% when 'model' %}
+ {% render 'icons', id:'model-thumb' %}
+ {% when 'video' %}
+ {% render 'icons', id: 'play-thumb' %}
+ {% when 'external_video' %}
+ {% render 'icons', id: 'play-thumb' %}
+ {% endcase %}
+ </button>
+ {% endfor %}
+ </div>
+ </div>
+ {% if product.media.size > 4 %}
+ <span class="product-thumbnails-navigation-next"></span>
+ {% endif %}
+ </div>
+ {% endif %}
+ <div class="product__social" {{ block.shopify_attributes }}>
+ {% render 'share-buttons' %}
+ </div>
+ <div class="product-content__desktop">
+ <h4 style="margin-bottom: 0.25em;"><strong>Pattern Description:</strong></h4>
+
+ {% if product != blank and product.description != blank %}
+ <div class="product-description rte" itemprop="description" style="margin-top: 0;">
+ {{ product.description }}
+ </div>
+ {% elsif product == blank %}
+ <div class="product-description rte" itemprop="description" style="margin-top: 0;">
+ {{ 'products.product.onboarding.description' | t }}
+ </div>
+ {% endif %}
+ </div>
+</div>
+{% if images_layout == 'masonry' and product.media.size > 1 %}
+ <div class="mobile-slider-nav-wrapper">
+ {{ model_view_button }}
+ <div data-mobile-slider-navigation>
+ <button class="mobile-slider-prev-next" data-mobile-slider-button="prev">
+ {% render 'icons', id: 'prev-button' %}
+ </button>
+ <ol data-mobile-slider-dots>
+ {% for media in product.media %}
+ <li class='dot' aria-label="Page dot {{ forloop.index0 }}" data-mobile-slider-dot data-slide-dot-index="{{ forloop.index0 }}"></li>
+ {% endfor %}
+ </ol>
+ <button class="mobile-slider-prev-next" data-mobile-slider-button="next">
+ {% render 'icons', id: 'next-button' %}
+ </button>
+ </div>
+ </div>
+{% endif %}
+
+
+{% render 'full-monte-express' %}
← 59d5c2f8 standing mac3->Kamatera catalog push: additive nightly sync
·
back to Designer Wallcoverings
·
auto-save: 2026-07-08T13:05:49 (10 files) — pending-approval 36a54f98 →