[object Object]

← back to Designer Wallcoverings

auto-save: 2026-06-23T09:20:41 (16 files) — shopify/scripts/cadence/sdg-trade-login.mjs shopify/scripts/tres-tintas-designer-descriptions.js shopify/tres-tintas-desc-backups/_done.ledger shopify/grid-js-fix/backups/header.liquid.live-142250278963.before shopify/grid-js-fix/backups/product-list-item.liquid.live-142250278963.before

e78be974b60caa3be6a73fe9a2d715be8c30fa54 · 2026-06-23 09:20:47 -0700 · Steve Abrams

Files touched

Diff

commit e78be974b60caa3be6a73fe9a2d715be8c30fa54
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Jun 23 09:20:47 2026 -0700

    auto-save: 2026-06-23T09:20:41 (16 files) — shopify/scripts/cadence/sdg-trade-login.mjs shopify/scripts/tres-tintas-designer-descriptions.js shopify/tres-tintas-desc-backups/_done.ledger shopify/grid-js-fix/backups/header.liquid.live-142250278963.before shopify/grid-js-fix/backups/product-list-item.liquid.live-142250278963.before
---
 .../backups/header.liquid.live-142250278963.before | 483 ++++++++++++++++++++
 ...oduct-list-item.liquid.live-142250278963.before | 174 +++++++
 shopify/grid-js-fix/diagnose-init-throw.js         |  60 +++
 shopify/grid-js-fix/patched/header.liquid          | 503 +++++++++++++++++++++
 .../grid-js-fix/patched/product-list-item.liquid   | 175 +++++++
 shopify/grid-js-fix/probe-contract.js              |  47 ++
 shopify/grid-js-fix/probe-masonry.js               |  41 ++
 shopify/grid-js-fix/verify-dev-fix.js              | 118 +++++
 shopify/grid-js-fix/verify-dev-preview-auth.js     |  49 ++
 shopify/grid-js-fix/verify-final.js                |  84 ++++
 shopify/scripts/cadence/sdg-capture-edi.mjs        |  33 ++
 shopify/scripts/cadence/sdg-fetch-pricing.mjs      |  23 +
 shopify/scripts/cadence/sdg-recon-api.mjs          |  76 ++++
 shopify/scripts/cadence/sdg-trade-login.mjs        |  85 +++-
 .../scripts/tres-tintas-designer-descriptions.js   |  12 +-
 shopify/tres-tintas-desc-backups/_done.ledger      | 161 +------
 shopify/tres-tintas-desc-backups/_done.ledger.tmp  |   0
 17 files changed, 1966 insertions(+), 158 deletions(-)

diff --git a/shopify/grid-js-fix/backups/header.liquid.live-142250278963.before b/shopify/grid-js-fix/backups/header.liquid.live-142250278963.before
new file mode 100644
index 00000000..5990c5d1
--- /dev/null
+++ b/shopify/grid-js-fix/backups/header.liquid.live-142250278963.before
@@ -0,0 +1,483 @@
+<script
+  type="application/json"
+  data-section-type="static-header"
+  data-section-id="{{ section.id }}"
+  data-section-data
+>
+  {
+    "stickyHeader": {{ section.settings.sticky_header }},
+    "compactCenter": "{{ section.settings.header_layout }}",
+    "enable_predictive_search": "{{ section.settings.enable_predictive_search }}"
+  }
+</script>
+
+{%- capture arrow_right -%}
+  {%-
+    render 'icons',
+    id: 'chevron-right'
+  -%}
+{%- endcapture -%}
+
+{% assign mega_nav_handle = section.settings.mega-nav-handle %}
+
+{%- capture header_classes -%}
+  header
+  header-layout-{{ section.settings.header_layout }}
+
+  {% if section.settings.header_full_width %}
+    header-full-width
+  {% else %}
+    header-content-width
+  {% endif %}
+
+  {% if section.settings.sticky_header and section.settings.sticky_header_logo %}
+    header-sticky-show-logo
+  {% endif %}
+
+  {% if section.settings.header_border and template != 'index' %}
+    header-border
+  {% endif %}
+{%- endcapture -%}
+
+<section
+  class="{{ header_classes | replace: '  ', ' ' | strip_newlines }}"
+  {% if section.settings.sticky_header %}data-sticky-header{% endif %}
+  {% if section.settings.header_layout == 'compact-center' %}data-header-compact-center{% endif %}
+  data-section-header
+>
+  <header
+    class="main-header"
+    role="banner"
+    data-header-main
+  >
+    <div class="header-tools-wrapper">
+      <div class="header-tools">
+
+        <div class="aligned-left">
+          <div id="coin-container"></div>
+        </div>
+
+        <div class="aligned-left">
+          {% assign customer_link_blocks = section.blocks | where: 'type', 'header_customer_link' %}
+          {% for block in customer_link_blocks %}
+            <div class="customer-links" {{ block.shopify_attributes }}>
+              <a href="{{ block.settings.link_url }}">{{ block.settings.link_text }}</a>
+            </div>
+          {% endfor %}
+        </div>
+
+        <div class="aligned-right">
+          {% if shop.customer_accounts_enabled %}
+            <div class="customer-links">
+              {% if customer %}
+                <a href="{{ routes.account_url }}">{{ 'layout.header.my_account' | t }}</a>
+
+                <div class="customer-links-inner">
+                  <span class="arrow"></span>
+                  <ul>
+                    <li><a href="{{ routes.account_url }}">{{ 'customers.general.purchases' | t }}</a></li>
+                    <li><a href="{{ routes.account_addresses_url }}">{{ 'customers.addresses.header' | t }}</a></li>
+                    <li>{{ 'layout.header.logout' | t | customer_logout_link }}</li>
+                  </ul>
+                </div>
+              {% else %}
+                <a class="customer-login-link" href="{{ routes.account_login_url }}">{{ 'layout.header.account' | t }}</a>
+              {% endif %}
+            </div>
+          {% endif %}
+
+          <div class="mini-cart-wrapper">
+            <a class="cart-count" href="{{ routes.cart_url }}" data-cart-count>
+              <span class="cart-count-text">{{ 'layout.header.cart' | t }}</span>
+              (<span class="cart-count-number">{{ cart.item_count }}</span>)
+            </a>
+
+            {% render 'mini-cart' %}
+          </div>
+          <form class="checkout-link" action="{{ routes.cart_url }}" method="POST"><button class="checkout-link__button" type="submit" name="checkout">{{ 'layout.header.checkout' | t }}</button></form>
+        </div>
+
+      </div>
+    </div>
+
+    <div class="header-main-content" data-header-content>
+      {% unless section.settings.menu == blank %}
+        <div class="header-content-left">
+          <button
+            class="navigation-toggle navigation-toggle-open"
+            aria-label="{{ 'layout.header.menu' | t }}"
+            data-header-nav-toggle
+          >
+            <span class="navigation-toggle-icon">
+              {%-
+                render 'icons',
+                id: 'menu'
+              -%}
+            </span>
+          </button>
+        </div>
+      {% endunless %}
+
+      <div
+        class="
+          header-branding
+          header-branding-desktop
+          {% if section.settings.logo %}header-branding-desktop--has-logo{% endif %}
+        "
+        data-header-branding
+      >
+        <a class="logo-link" href="{{ routes.root_url }}">
+          {%- if section.settings.logo -%}
+            {%
+              render 'rimg',
+              img: section.settings.logo,
+              class: 'logo-image',
+              size: '200x100',
+              focal_point: section.settings.logo.presentation.focal_point,
+            %}
+          {%- else -%}
+            <h1>{{- shop.name -}}</h1>
+          {%- endif -%}
+        </a>
+        <span
+          class="navigation-toggle navigation-toggle-close"
+          data-header-nav-toggle
+        >
+          {%-
+            render 'icons',
+            id: 'close',
+          -%}
+        </span>
+      </div>
+
+      {% unless section.settings.menu == blank %}
+        <div class="navigation-wrapper" data-navigation-wrapper>
+          <nav class="navigation navigation-desktop navigation-has-mega-nav" data-navigation-content>
+            <ul class="navigation-menu">
+              {% for link in linklists[section.settings.menu].links %}
+                {% assign first_tier_link = link %}
+                {% assign uses_blocks = true %}
+                {% comment %}Inject is-dropdown-or-meganav begin{% endcomment %}
+                {% comment %}
+                  A helper snippet for detecting if a link has a mega nav
+                  or dropdown associated with it.
+                
+                  @param first_tier_link
+                
+                  @param uses_blocks
+                {% endcomment %}
+                
+                {% assign is_dropdown = false %}
+                {% assign is_meganav = true %}
+                
+                {% if first_tier_link.links == blank %}
+                  {% assign is_meganav = false %}
+                {% else %}
+                  {% assign is_dropdown = true %}
+                {% endif %}
+                
+                {% for second_tier_link in first_tier_link.links %}
+                  {% if second_tier_link.links == blank %}
+                    {% assign is_meganav = false %}
+                  {% else %}
+                    {% assign is_dropdown = true %}
+                  {% endif %}
+                {% endfor %}
+                
+                {% if is_meganav %}
+                  {% assign is_dropdown = false %}
+                {% endif %}
+                
+                {% if uses_blocks %}
+                  {% assign found_mega_item_handle = false %}
+                  {% if section != blank and section.blocks != blank %}
+                    {% for block in section.blocks %}
+                      {% if block.type == 'mega_menu' %}
+                        {% assign menu_item_handle = block.settings.menu_item | handle %}
+                        {% if first_tier_link.handle == menu_item_handle %}
+                          {% assign found_mega_item_handle = true %}
+                        {% endif %}
+                      {% endif %}
+                    {% endfor %}
+                  {% endif %}
+                
+                  {% if found_mega_item_handle %}
+                    {% if is_dropdown %}
+                      {% assign is_meganav = true %}
+                      {% assign is_dropdown = false %}
+                    {% endif %}
+                  {% elsif found_mega_item_handle == false %}
+                    {% if is_meganav %}
+                      {% assign is_meganav = false %}
+                      {% assign is_dropdown = true %}
+                    {% endif %}
+                  {% else %}
+                    {% if is_meganav %}
+                      {% assign is_meganav = false %}
+                      {% assign is_dropdown = true %}
+                    {% endif %}
+                  {% endif %}
+                {% endif %}
+                {% comment %}Inject is-dropdown-or-meganav end{% endcomment %}
+
+
+                <li
+                  class="{% if is_dropdown %}has-dropdown{% elsif is_meganav %}has-mega-nav{% endif %}"
+                  {% if is_meganav %}data-mega-nav="true"{% endif %}
+                >
+                  {% if is_dropdown or is_meganav %}
+                    <details data-nav-details>
+                      <summary data-href="{{ link.url }}"
+                  {% else %}
+                    <a href="{{ link.url }}"
+                  {% endif %}
+                    id="main-navigation-{{ link.handle }}"
+                    class="navigation-menu-link"
+                    {% if is_dropdown or is_meganav %}aria-haspopup{% endif %}
+                  >
+                    {{ link.title }}
+                    {% if is_dropdown or is_meganav %}
+                      <span
+                        class="navigation-submenu-toggle"
+                        tabIndex="0"
+                        role="button"
+                        aria-controls="navigation-{{ link.handle }}"
+                        aria-expanded="false"
+                        aria-label="{{ 'general.accessibility.toggle' | t }}"
+                        data-subnav-toggle
+                      >
+                        {{- arrow_right -}}
+                      </span>
+                    {% endif %}
+                  {% if is_dropdown or is_meganav %}
+                    </summary>
+                  {% else %}
+                    </a>
+                  {% endif %}
+
+                  {% if is_dropdown %}
+                    {% assign direction = 'right' %}
+                    {% assign halfway = linklists[section.settings.menu].links.size | divided_by: 2 %}
+                    {% if forloop.index0 > halfway %}
+                      {% assign direction = 'left' %}
+                    {% endif %}
+
+                    {%- render 'dropdown', direction: direction, link: link -%}
+                    </details>
+                  {% elsif is_meganav %}
+                    {%- render 'mega-nav', link: link -%}
+                  {% endif %}
+                </li>
+              {% endfor %}
+
+              {% if shop.customer_accounts_enabled %}
+                {% if customer %}
+                  <li class="mobile-link">
+                    <a href="{{ routes.account_url }}">
+                      {{ 'layout.header.my_account' | t }}
+                    </a>
+                  </li>
+                  <li class="mobile-link">
+                    {{ 'layout.header.logout' | t | customer_logout_link }}
+                  </li>
+                {% else %}
+                  <li class="mobile-link">
+                    <a class="customer-login-link" href="{{ routes.account_login_url }}">{{ 'layout.header.account' | t }}</a>
+                  </li>
+                {% endif %}
+              {% endif %}
+            </ul>
+          </nav>
+          {% render 'mobile-nav', arrow_right: arrow_right %}
+        </div>
+      {% endunless %}
+
+      <div class="header-content-right header-content-right--inline-search" data-header-content-right>
+        <div class="header-search-inline">
+          {%
+            render 'search-form',
+            context: 'header',
+          %}
+          <a href="#" class="visual-search-clickable header-search-inline__visual" aria-controls="Visual Search">
+            <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="30px" height="30px" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 20 20"><path d="M10 8a3 3 0 1 0 0 6a3 3 0 0 0 0-6zm8-3h-2.4a.888.888 0 0 1-.789-.57l-.621-1.861A.89.89 0 0 0 13.4 2H6.6c-.33 0-.686.256-.789.568L5.189 4.43A.889.889 0 0 1 4.4 5H2C.9 5 0 5.9 0 7v9c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-8 11a5 5 0 0 1-5-5a5 5 0 1 1 10 0a5 5 0 0 1-5 5zm7.5-7.8a.7.7 0 1 1 0-1.4a.7.7 0 0 1 0 1.4z" fill="#000000"/></svg>
+          </a>
+        </div>
+
+        <button
+          class="search-toggle"
+          type="button"
+          aria-label="{{ 'general.search.header' | t }}"
+          data-header-search-toggle
+        >
+          {%
+            render 'icons',
+            id: 'search',
+          %}
+        </button>
+
+        {% if section.settings.sticky_header %}
+          <a class="sticky-header-cart-count" href="{{ routes.cart_url }}" data-cart-count>
+            <span class="cart-count-text">{{ 'layout.header.cart' | t }}</span>
+            (<span class="cart-count-number">{{ cart.item_count }}</span>)
+          </a>
+        {% endif %}
+      </div>
+
+      <div class="search-takeover" data-search-takeover>
+        <div class="search-takeover__wrapper">
+          {%
+            render 'search-form',
+            context: 'mobile',
+          %}
+
+          <button
+            class="search-takeover__close-button"
+            type="button"
+            aria-label="{{ 'general.search.close' | t }}"
+            data-search-takeover-close
+          >
+            {%
+              render 'icons',
+              id: 'close',
+            %}
+          </button>
+        </div>
+      </div>      
+    </div>
+  </header>
+</section>
+
+{% schema %}
+{
+  "name": "Header",
+  "settings": [
+    {
+      "type": "header",
+      "content": "Logo"
+    },
+    {
+      "type": "image_picker",
+      "id": "logo",
+      "label": "Logo image",
+      "info": "200px tall recommended"
+    },
+    {
+      "type": "header",
+      "content": "Header"
+    },
+    {
+      "type": "select",
+      "id": "header_layout",
+      "label": "Logo layout",
+      "info": "Logo layouts are only active on screens wider than 1080px",
+      "options": [
+        {
+          "value": "default",
+          "label": "Default"
+        },
+        {
+          "value": "compact-center",
+          "label": "Compact center"
+        },
+        {
+          "value": "compact-left",
+          "label": "Compact left"
+        }
+      ],
+      "default": "compact-left"
+    },
+    {
+      "type": "checkbox",
+      "id": "header_full_width",
+      "label": "Enable full width header ",
+      "default": false
+    },
+    {
+      "type": "checkbox",
+      "id": "header_border",
+      "label": "Show header border",
+      "default": true,
+      "info": "This will appear on all pages except the home page."
+    },
+    {
+      "type": "checkbox",
+      "id": "sticky_header",
+      "label": "Enable sticky header"
+    },
+    {
+      "type": "checkbox",
+      "id": "sticky_header_logo",
+      "default": true,
+      "label": "Show logo image in sticky header"
+    },
+    {
+      "type": "header",
+      "content": "Search behavior"
+    },
+    {
+      "type": "checkbox",
+      "id": "enable_predictive_search",
+      "label": "Enable predictive search",
+      "default": true
+    },
+    {
+      "type": "header",
+      "content": "Navigation"
+    },
+    {
+      "type": "link_list",
+      "id": "menu",
+      "label": "Menu",
+      "default": "main-menu"
+    },
+    {
+      "type": "header",
+      "content": "Mini Cart"
+    },
+    {
+      "type": "checkbox",
+      "id": "enable_checkout_lock_icon",
+      "label": "Show lock icon on checkout button in mini cart",
+      "default": false
+    }
+  ],
+  "blocks": [
+    {
+      "type": "mega_menu",
+      "name": "Mega menu",
+      "settings": [
+        {
+          "type": "text",
+          "id": "menu_item",
+          "label": "Menu item",
+          "info": "Enter menu item to apply a mega menu dropdown. [Learn more](https://support.pixelunion.net/hc/en-us/articles/360022052534-How-to-set-up-your-store-s-navigation)"
+        },
+        {
+          "type": "image_picker",
+          "id": "menu_image",
+          "label": "Featured image",
+          "info": "480 x 1000px recommended"
+        }
+      ]
+    },
+    {
+      "type": "header_customer_link",
+      "name": "Header customer link",
+      "settings": [
+        {
+          "type": "text",
+          "id": "link_text",
+          "label": "Link text",
+          "default": "Contact us: (888) 373-4564"
+        },
+        {
+          "type": "url",
+          "id": "link_url",
+          "label": "Link URL"
+        }
+      ]
+    }
+  ]
+}
+
+{% endschema %}
\ No newline at end of file
diff --git a/shopify/grid-js-fix/backups/product-list-item.liquid.live-142250278963.before b/shopify/grid-js-fix/backups/product-list-item.liquid.live-142250278963.before
new file mode 100644
index 00000000..d1a115fd
--- /dev/null
+++ b/shopify/grid-js-fix/backups/product-list-item.liquid.live-142250278963.before
@@ -0,0 +1,174 @@
+{% comment %}
+  @param product_attributes
+    custom attributes to be applied to the product item
+    Defaults to: blank
+{% endcomment %}
+
+{% liquid
+  assign product_attributes = product_attributes | default: ''
+  assign product_hover = settings.product_hover |  default: 'quick-shop'
+  assign product_stock_level_threshold = settings.product_stock_level_threshold | default: 1
+  assign product_badges = settings.product_badges | default: false
+  assign product_icons = settings.product_badges_icons | default: false
+  assign product_vendor = settings.show_vendor | default: false
+
+  assign item = product
+  if template contains 'search'
+    assign item = item
+  endif
+
+  assign has_quick_shop = false
+  if product_hover == 'quick-shop' and template.name != 'password' and product.variants_count <= 250
+    assign has_quick_shop = true
+  endif
+%}
+
+{% if product_hover == 'stock-level' and item.available %}
+  {% assign total = 0 %}
+  {% assign threshold = product_stock_level_threshold | times: 1 %}
+  {% assign infinity = false %}
+  {% for variant in item.variants %}
+    {% if variant.inventory_management == null %}
+      {% assign infinity = true %}
+    {% elsif variant.inventory_management == '' %}
+      {% assign infinity = true %}
+    {% elsif variant.inventory_management == 'shopify' and variant.inventory_policy == 'continue' %}
+      {% assign infinity = true %}
+    {% elsif infinity == false %}
+      {% capture temp %}{{ total | plus: variant.inventory_quantity }}{% endcapture %}
+      {% assign total = temp | times: 1 %}
+    {% endif %}
+  {% endfor %}
+  {% assign stockText = 'products.product.stock_indicator_message' | t: num: total %}
+{% endif %}
+
+<article
+  class="
+    product-list-item
+    {% if has_quick_shop %} has-quick-shop{% endif %}
+    {% if item.available and infinity == false and total <= threshold %} has-stock-indicator{%endif %}
+  "
+  id="product-list-item-{{ item.id }}"
+  data-product-id="{{ item.id }}"
+  {{ product_attributes }}
+>
+
+  {% assign secondaryImage = false %}
+  {% if item.media.size > 1 and product_hover == 'image-flip' %}
+    {% assign secondaryImage = true %}
+  {% endif %}
+
+  <figure
+    class="
+      product-list-item-thumbnail
+      {% if secondaryImage %}
+        has-secondary-image
+      {% endif %}
+    "
+    data-url="{{ item.url | within: collection }}"
+    {% if secondaryImage %}
+      {%
+        render 'rimg',
+        img: item.media[1].preview_image,
+        alt: item.media[1].preview_image.alt,
+        size: '600x600',
+        background: true,
+        lazy: true
+      %}
+    {% endif %}
+  >
+    <a href="{{ item.url | within: collection }}" aria-label="{{ item.title }}">
+      {% if item.featured_media.preview_image %}
+        {%
+          render 'rimg',
+          img: item.featured_media.preview_image,
+          alt: item.featured_media.preview_image.alt,
+          size: '600x600',
+          lazy: true
+        %}
+      {% else %}
+        {{ 'product-1' | placeholder_svg_tag: 'placeholder-svg' }}
+      {% endif %}
+    </a>
+
+    {% if has_quick_shop %}
+      <span
+        class="quick-shop-modal-trigger"
+        data-product-url="{{ item.url | within: collection }}"
+      >
+        {{ 'products.product.quick_shop_trigger_text' | t }}
+      </span>
+    {% elsif product_hover == 'stock-level' %}
+
+      {% if item.available and infinity == false and total <= threshold %}
+        <a class="product-list-item-inventory" href="{{ item.url }}">{{ stockText }}</a>
+      {% endif %}
+
+    {% endif %}
+
+    {% if product_badges %}
+      {% if item.available != true %}
+        <span class="product-list-item-unavailable{% if product_icons %} product-icons{% endif %}" data-title="{{ 'products.product.sold_out' | t }}"></span>
+      {% elsif item.compare_at_price_min > item.price_min %}
+        <span class="product-list-item-on-sale{% if product_icons %} product-icons{% endif %}" data-title="{{ 'products.product.on_sale' | t }}"></span>
+      {% endif %}
+    {% endif %}
+
+    <!-- Hover overlay: vendor + pattern name only -->
+    <div class="product-list-item-hover-overlay">
+      <div class="product-list-item-hover-content">
+        <p class="product-list-item-hover-vendor">{{ item.vendor }}</p>
+        <h3 class="product-list-item-hover-title">{{ item.title }}</h3>
+      </div>
+    </div>
+  </figure>
+
+  <div class="product-list-item-details" style="display:none;"></div>
+    {% assign variant_for_unit_price = item.variants | sort: 'price' | first %}
+    {% if variant_for_unit_price.unit_price %}
+      {% comment %}Inject unit-price begin{% endcomment %}
+      {% comment %}
+        @param variant_for_unit_price
+          Product variant for price
+        @param tax_text
+          String containing 'tax included' text
+      {% endcomment %}
+      
+      {% capture total_quantity %}
+        <span class="product-price__unit-price-total-quantity" data-unit-price-quantity>
+          {{ variant_for_unit_price.unit_price_measurement.quantity_value }}{{ variant_for_unit_price.unit_price_measurement.quantity_unit }}
+        </span>
+      {% endcapture %}
+      
+      
+      {% capture unit_price %}
+        <span class="product-price__unit-price-amount money" data-unit-price-amount>
+          {{ variant_for_unit_price.unit_price | money }}
+        </span>
+      {% endcapture %}
+      {% capture unit_measure %}
+        <span class="product-price__unit-price-measure" data-unit-price-measure>
+          {%- if variant_for_unit_price.unit_price_measurement.reference_value != 1 -%}
+            {{ variant_for_unit_price.unit_price_measurement.reference_value }}
+          {%- endif %}
+          {{ variant_for_unit_price.unit_price_measurement.reference_unit }}
+        </span>
+      {% endcapture %}
+      
+      <div
+        class="
+          product-price__unit-price
+          {% unless variant_for_unit_price.unit_price_measurement %}hidden{% endunless %}
+        "
+        data-unit-price
+      >
+        {{ 'products.product.price_per_unit_html' | t: total_quantity: total_quantity, unit_price: unit_price, unit_measure: unit_measure | strip_newlines }}
+      </div>
+      
+      {% assign variant_for_unit_price = blank %}
+      {% comment %}Inject unit-price end{% endcomment %}
+
+    {% endif %}
+  </div>
+
+</article>
\ No newline at end of file
diff --git a/shopify/grid-js-fix/diagnose-init-throw.js b/shopify/grid-js-fix/diagnose-init-throw.js
new file mode 100644
index 00000000..03f7a1f7
--- /dev/null
+++ b/shopify/grid-js-fix/diagnose-init-throw.js
@@ -0,0 +1,60 @@
+/*
+ * Pin the EXACT throw that aborts grid.js entry init on the live DW markup.
+ * Loads live home + collection, captures full pageerror stacks (incl. webpack
+ * module frames), and reports the first uncaught error with its stack so we can
+ * identify the null DOM element the entry script dereferences.
+ *
+ * Runs against the live (broken-dev) asset AND the staged prod asset so we see
+ * both stacks.
+ */
+const fs = require('fs');
+const { chromium } = require('playwright');
+const PROD_BUNDLE = fs.readFileSync('/tmp/grid_PROD_candidate.js', 'utf8');
+const PAGES = [
+  { name: 'HOME', url: 'https://www.designerwallcoverings.com/' },
+  { name: 'COLLECTION', url: 'https://www.designerwallcoverings.com/collections/acoustical?pf_t_product_type=Wallcovering&pf_t_color=Green' },
+];
+
+(async () => {
+  const browser = await chromium.launch({ headless: true, executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' });
+
+  for (const variant of ['LIVE-dev-asset', 'STAGED-prod-asset']) {
+    console.log(`\n================ ${variant} ================`);
+    const ctx = await browser.newContext({ viewport: { width: 1440, height: 1200 } });
+    if (variant === 'STAGED-prod-asset') {
+      await ctx.route('**/assets/grid.js**', route => route.fulfill({ status: 200, contentType: 'application/javascript', body: PROD_BUNDLE }));
+    }
+    const page = await ctx.newPage();
+    const fullErrors = [];
+    page.on('pageerror', e => fullErrors.push(e.stack || String(e)));
+    for (const p of PAGES) {
+      fullErrors.length = 0;
+      try { await page.goto(p.url, { waitUntil: 'domcontentloaded', timeout: 45000 }); } catch(e){}
+      await page.waitForTimeout(3000);
+      console.log(`\n  --- [${p.name}] first 2 uncaught stacks ---`);
+      const seen = new Set();
+      let shown = 0;
+      for (const st of fullErrors) {
+        const key = st.split('\n')[0];
+        if (seen.has(key)) continue; seen.add(key);
+        console.log('  >>> ' + st.split('\n').slice(0, 6).join('\n      '));
+        if (++shown >= 2) break;
+      }
+      // Probe presence of elements OOTS Grid entry typically derefs
+      const probe = await page.evaluate(() => {
+        const sel = [
+          '[data-section-type]','[data-section-id]','#PageContainer','#shopify-section-header',
+          '.site-header','[data-header-section]','.header-layout','[data-predictive-search]',
+          '.js-drawer-open','#NavDrawer','[data-collapsible-trigger]','.home-products-content',
+          'main[role="main"]','#MainContent','[data-search-form]','.grid-product__link',
+        ];
+        const out = {};
+        for (const s of sel) out[s] = document.querySelectorAll(s).length;
+        return out;
+      });
+      console.log('  element presence:', JSON.stringify(probe));
+    }
+    await ctx.close();
+  }
+  await browser.close();
+})().catch(e=>{console.error('FATAL',e);process.exit(1);});
diff --git a/shopify/grid-js-fix/patched/header.liquid b/shopify/grid-js-fix/patched/header.liquid
new file mode 100644
index 00000000..af5c69c5
--- /dev/null
+++ b/shopify/grid-js-fix/patched/header.liquid
@@ -0,0 +1,503 @@
+<script
+  type="application/json"
+  data-section-type="static-header"
+  data-section-id="{{ section.id }}"
+  data-section-data
+>
+  {
+    "stickyHeader": {{ section.settings.sticky_header }},
+    "compactCenter": "{{ section.settings.header_layout }}",
+    "enable_predictive_search": "{{ section.settings.enable_predictive_search }}"
+  }
+</script>
+
+{%- capture arrow_right -%}
+  {%-
+    render 'icons',
+    id: 'chevron-right'
+  -%}
+{%- endcapture -%}
+
+{% assign mega_nav_handle = section.settings.mega-nav-handle %}
+
+{%- capture header_classes -%}
+  header
+  header-layout-{{ section.settings.header_layout }}
+
+  {% if section.settings.header_full_width %}
+    header-full-width
+  {% else %}
+    header-content-width
+  {% endif %}
+
+  {% if section.settings.sticky_header and section.settings.sticky_header_logo %}
+    header-sticky-show-logo
+  {% endif %}
+
+  {% if section.settings.header_border and template != 'index' %}
+    header-border
+  {% endif %}
+{%- endcapture -%}
+
+<section
+  class="{{ header_classes | replace: '  ', ' ' | strip_newlines }}"
+  {% if section.settings.sticky_header %}data-sticky-header{% endif %}
+  {% if section.settings.header_layout == 'compact-center' %}data-header-compact-center{% endif %}
+  data-section-header
+>
+  <header
+    class="main-header"
+    role="banner"
+    data-header-main
+  >
+    <div class="header-tools-wrapper">
+      <div class="header-tools">
+
+        <div class="aligned-left">
+          <div id="coin-container"></div>
+        </div>
+
+        <div class="aligned-left">
+          {% assign customer_link_blocks = section.blocks | where: 'type', 'header_customer_link' %}
+          {% for block in customer_link_blocks %}
+            <div class="customer-links" {{ block.shopify_attributes }}>
+              <a href="{{ block.settings.link_url }}">{{ block.settings.link_text }}</a>
+            </div>
+          {% endfor %}
+        </div>
+
+        <div class="aligned-right">
+          {% if shop.customer_accounts_enabled %}
+            <div class="customer-links">
+              {% if customer %}
+                <a href="{{ routes.account_url }}">{{ 'layout.header.my_account' | t }}</a>
+
+                <div class="customer-links-inner">
+                  <span class="arrow"></span>
+                  <ul>
+                    <li><a href="{{ routes.account_url }}">{{ 'customers.general.purchases' | t }}</a></li>
+                    <li><a href="{{ routes.account_addresses_url }}">{{ 'customers.addresses.header' | t }}</a></li>
+                    <li>{{ 'layout.header.logout' | t | customer_logout_link }}</li>
+                  </ul>
+                </div>
+              {% else %}
+                <a class="customer-login-link" href="{{ routes.account_login_url }}">{{ 'layout.header.account' | t }}</a>
+              {% endif %}
+            </div>
+          {% endif %}
+
+          <div class="mini-cart-wrapper">
+            <a class="cart-count" href="{{ routes.cart_url }}" data-cart-count>
+              <span class="cart-count-text">{{ 'layout.header.cart' | t }}</span>
+              (<span class="cart-count-number">{{ cart.item_count }}</span>)
+            </a>
+
+            {% render 'mini-cart' %}
+          </div>
+          <form class="checkout-link" action="{{ routes.cart_url }}" method="POST"><button class="checkout-link__button" type="submit" name="checkout">{{ 'layout.header.checkout' | t }}</button></form>
+        </div>
+
+      </div>
+    </div>
+
+    <div class="header-main-content" data-header-content>
+      {% unless section.settings.menu == blank %}
+        <div class="header-content-left">
+          <button
+            class="navigation-toggle navigation-toggle-open"
+            aria-label="{{ 'layout.header.menu' | t }}"
+            data-header-nav-toggle
+          >
+            <span class="navigation-toggle-icon">
+              {%-
+                render 'icons',
+                id: 'menu'
+              -%}
+            </span>
+          </button>
+        </div>
+      {% endunless %}
+
+      <div
+        class="
+          header-branding
+          header-branding-desktop
+          {% if section.settings.logo %}header-branding-desktop--has-logo{% endif %}
+        "
+        data-header-branding
+      >
+        <a class="logo-link" href="{{ routes.root_url }}">
+          {%- if section.settings.logo -%}
+            {%
+              render 'rimg',
+              img: section.settings.logo,
+              class: 'logo-image',
+              size: '200x100',
+              focal_point: section.settings.logo.presentation.focal_point,
+            %}
+          {%- else -%}
+            <h1>{{- shop.name -}}</h1>
+          {%- endif -%}
+        </a>
+        <span
+          class="navigation-toggle navigation-toggle-close"
+          data-header-nav-toggle
+        >
+          {%-
+            render 'icons',
+            id: 'close',
+          -%}
+        </span>
+      </div>
+
+      {% unless section.settings.menu == blank %}
+        <div class="navigation-wrapper" data-navigation-wrapper>
+          <nav class="navigation navigation-desktop navigation-has-mega-nav" data-navigation-content>
+            <ul class="navigation-menu">
+              {% for link in linklists[section.settings.menu].links %}
+                {% assign first_tier_link = link %}
+                {% assign uses_blocks = true %}
+                {% comment %}Inject is-dropdown-or-meganav begin{% endcomment %}
+                {% comment %}
+                  A helper snippet for detecting if a link has a mega nav
+                  or dropdown associated with it.
+                
+                  @param first_tier_link
+                
+                  @param uses_blocks
+                {% endcomment %}
+                
+                {% assign is_dropdown = false %}
+                {% assign is_meganav = true %}
+                
+                {% if first_tier_link.links == blank %}
+                  {% assign is_meganav = false %}
+                {% else %}
+                  {% assign is_dropdown = true %}
+                {% endif %}
+                
+                {% for second_tier_link in first_tier_link.links %}
+                  {% if second_tier_link.links == blank %}
+                    {% assign is_meganav = false %}
+                  {% else %}
+                    {% assign is_dropdown = true %}
+                  {% endif %}
+                {% endfor %}
+                
+                {% if is_meganav %}
+                  {% assign is_dropdown = false %}
+                {% endif %}
+                
+                {% if uses_blocks %}
+                  {% assign found_mega_item_handle = false %}
+                  {% if section != blank and section.blocks != blank %}
+                    {% for block in section.blocks %}
+                      {% if block.type == 'mega_menu' %}
+                        {% assign menu_item_handle = block.settings.menu_item | handle %}
+                        {% if first_tier_link.handle == menu_item_handle %}
+                          {% assign found_mega_item_handle = true %}
+                        {% endif %}
+                      {% endif %}
+                    {% endfor %}
+                  {% endif %}
+                
+                  {% if found_mega_item_handle %}
+                    {% if is_dropdown %}
+                      {% assign is_meganav = true %}
+                      {% assign is_dropdown = false %}
+                    {% endif %}
+                  {% elsif found_mega_item_handle == false %}
+                    {% if is_meganav %}
+                      {% assign is_meganav = false %}
+                      {% assign is_dropdown = true %}
+                    {% endif %}
+                  {% else %}
+                    {% if is_meganav %}
+                      {% assign is_meganav = false %}
+                      {% assign is_dropdown = true %}
+                    {% endif %}
+                  {% endif %}
+                {% endif %}
+                {% comment %}Inject is-dropdown-or-meganav end{% endcomment %}
+
+
+                <li
+                  class="{% if is_dropdown %}has-dropdown{% elsif is_meganav %}has-mega-nav{% endif %}"
+                  {% if is_meganav %}data-mega-nav="true"{% endif %}
+                >
+                  {% if is_dropdown or is_meganav %}
+                    <details data-nav-details>
+                      <summary data-href="{{ link.url }}"
+                  {% else %}
+                    <a href="{{ link.url }}"
+                  {% endif %}
+                    id="main-navigation-{{ link.handle }}"
+                    class="navigation-menu-link"
+                    {% if is_dropdown or is_meganav %}aria-haspopup{% endif %}
+                  >
+                    {{ link.title }}
+                    {% if is_dropdown or is_meganav %}
+                      <span
+                        class="navigation-submenu-toggle"
+                        tabIndex="0"
+                        role="button"
+                        aria-controls="navigation-{{ link.handle }}"
+                        aria-expanded="false"
+                        aria-label="{{ 'general.accessibility.toggle' | t }}"
+                        data-subnav-toggle
+                      >
+                        {{- arrow_right -}}
+                      </span>
+                    {% endif %}
+                  {% if is_dropdown or is_meganav %}
+                    </summary>
+                  {% else %}
+                    </a>
+                  {% endif %}
+
+                  {% if is_dropdown %}
+                    {% assign direction = 'right' %}
+                    {% assign halfway = linklists[section.settings.menu].links.size | divided_by: 2 %}
+                    {% if forloop.index0 > halfway %}
+                      {% assign direction = 'left' %}
+                    {% endif %}
+
+                    {%- render 'dropdown', direction: direction, link: link -%}
+                    </details>
+                  {% elsif is_meganav %}
+                    {%- render 'mega-nav', link: link -%}
+                  {% endif %}
+                </li>
+              {% endfor %}
+
+              {% if shop.customer_accounts_enabled %}
+                {% if customer %}
+                  <li class="mobile-link">
+                    <a href="{{ routes.account_url }}">
+                      {{ 'layout.header.my_account' | t }}
+                    </a>
+                  </li>
+                  <li class="mobile-link">
+                    {{ 'layout.header.logout' | t | customer_logout_link }}
+                  </li>
+                {% else %}
+                  <li class="mobile-link">
+                    <a class="customer-login-link" href="{{ routes.account_login_url }}">{{ 'layout.header.account' | t }}</a>
+                  </li>
+                {% endif %}
+              {% endif %}
+            </ul>
+          </nav>
+          {% render 'mobile-nav', arrow_right: arrow_right %}
+        </div>
+      {% endunless %}
+
+      <div class="header-content-right header-content-right--inline-search" data-header-content-right>
+        <div class="header-search-inline">
+          {%
+            render 'search-form',
+            context: 'header',
+          %}
+          <a href="#" class="visual-search-clickable header-search-inline__visual" aria-controls="Visual Search">
+            <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="30px" height="30px" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 20 20"><path d="M10 8a3 3 0 1 0 0 6a3 3 0 0 0 0-6zm8-3h-2.4a.888.888 0 0 1-.789-.57l-.621-1.861A.89.89 0 0 0 13.4 2H6.6c-.33 0-.686.256-.789.568L5.189 4.43A.889.889 0 0 1 4.4 5H2C.9 5 0 5.9 0 7v9c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-8 11a5 5 0 0 1-5-5a5 5 0 1 1 10 0a5 5 0 0 1-5 5zm7.5-7.8a.7.7 0 1 1 0-1.4a.7.7 0 0 1 0 1.4z" fill="#000000"/></svg>
+          </a>
+        </div>
+
+        {%- comment -%}
+          DW grid-init contract shim (2026-06-23): the OOTS Grid `StaticHeader`
+          JS class (in grid.js) constructs against `[data-header-search]`,
+          `[data-header-search-button]` and `[data-header-search-button-close]`
+          and dereferences them with NO null guard
+          (`this.headerSearch.style.width`, `headerSearchSubmit.addEventListener`,
+          `headerSearchClose.addEventListener`). DW's custom inline-search +
+          search-takeover UI replaced the original OOTS search form and removed
+          those hooks, so `new StaticHeader` threw and aborted
+          `ShopifySectionsManager._initSections` BEFORE any product-grid section
+          could init Masonry/Flickity — every grid then rendered at native image
+          resolution. This visually-hidden, inert shim restores the three hooks
+          so StaticHeader constructs cleanly. It is display:none, aria-hidden,
+          and not focusable, so it has zero impact on the visible header UX.
+        {%- endcomment -%}
+        <div class="header-search" data-header-search aria-hidden="true" tabindex="-1" style="display:none !important;width:0;height:0;overflow:hidden;position:absolute;left:-9999px;" data-width="0">
+          <button class="header-search__submit" data-header-search-button type="button" tabindex="-1" aria-hidden="true"></button>
+          <button class="header-search__close" data-header-search-button-close type="button" tabindex="-1" aria-hidden="true"></button>
+        </div>
+
+        <button
+          class="search-toggle"
+          type="button"
+          aria-label="{{ 'general.search.header' | t }}"
+          data-header-search-toggle
+        >
+          {%
+            render 'icons',
+            id: 'search',
+          %}
+        </button>
+
+        {% if section.settings.sticky_header %}
+          <a class="sticky-header-cart-count" href="{{ routes.cart_url }}" data-cart-count>
+            <span class="cart-count-text">{{ 'layout.header.cart' | t }}</span>
+            (<span class="cart-count-number">{{ cart.item_count }}</span>)
+          </a>
+        {% endif %}
+      </div>
+
+      <div class="search-takeover" data-search-takeover>
+        <div class="search-takeover__wrapper">
+          {%
+            render 'search-form',
+            context: 'mobile',
+          %}
+
+          <button
+            class="search-takeover__close-button"
+            type="button"
+            aria-label="{{ 'general.search.close' | t }}"
+            data-search-takeover-close
+          >
+            {%
+              render 'icons',
+              id: 'close',
+            %}
+          </button>
+        </div>
+      </div>      
+    </div>
+  </header>
+</section>
+
+{% schema %}
+{
+  "name": "Header",
+  "settings": [
+    {
+      "type": "header",
+      "content": "Logo"
+    },
+    {
+      "type": "image_picker",
+      "id": "logo",
+      "label": "Logo image",
+      "info": "200px tall recommended"
+    },
+    {
+      "type": "header",
+      "content": "Header"
+    },
+    {
+      "type": "select",
+      "id": "header_layout",
+      "label": "Logo layout",
+      "info": "Logo layouts are only active on screens wider than 1080px",
+      "options": [
+        {
+          "value": "default",
+          "label": "Default"
+        },
+        {
+          "value": "compact-center",
+          "label": "Compact center"
+        },
+        {
+          "value": "compact-left",
+          "label": "Compact left"
+        }
+      ],
+      "default": "compact-left"
+    },
+    {
+      "type": "checkbox",
+      "id": "header_full_width",
+      "label": "Enable full width header ",
+      "default": false
+    },
+    {
+      "type": "checkbox",
+      "id": "header_border",
+      "label": "Show header border",
+      "default": true,
+      "info": "This will appear on all pages except the home page."
+    },
+    {
+      "type": "checkbox",
+      "id": "sticky_header",
+      "label": "Enable sticky header"
+    },
+    {
+      "type": "checkbox",
+      "id": "sticky_header_logo",
+      "default": true,
+      "label": "Show logo image in sticky header"
+    },
+    {
+      "type": "header",
+      "content": "Search behavior"
+    },
+    {
+      "type": "checkbox",
+      "id": "enable_predictive_search",
+      "label": "Enable predictive search",
+      "default": true
+    },
+    {
+      "type": "header",
+      "content": "Navigation"
+    },
+    {
+      "type": "link_list",
+      "id": "menu",
+      "label": "Menu",
+      "default": "main-menu"
+    },
+    {
+      "type": "header",
+      "content": "Mini Cart"
+    },
+    {
+      "type": "checkbox",
+      "id": "enable_checkout_lock_icon",
+      "label": "Show lock icon on checkout button in mini cart",
+      "default": false
+    }
+  ],
+  "blocks": [
+    {
+      "type": "mega_menu",
+      "name": "Mega menu",
+      "settings": [
+        {
+          "type": "text",
+          "id": "menu_item",
+          "label": "Menu item",
+          "info": "Enter menu item to apply a mega menu dropdown. [Learn more](https://support.pixelunion.net/hc/en-us/articles/360022052534-How-to-set-up-your-store-s-navigation)"
+        },
+        {
+          "type": "image_picker",
+          "id": "menu_image",
+          "label": "Featured image",
+          "info": "480 x 1000px recommended"
+        }
+      ]
+    },
+    {
+      "type": "header_customer_link",
+      "name": "Header customer link",
+      "settings": [
+        {
+          "type": "text",
+          "id": "link_text",
+          "label": "Link text",
+          "default": "Contact us: (888) 373-4564"
+        },
+        {
+          "type": "url",
+          "id": "link_url",
+          "label": "Link URL"
+        }
+      ]
+    }
+  ]
+}
+
+{% endschema %}
\ No newline at end of file
diff --git a/shopify/grid-js-fix/patched/product-list-item.liquid b/shopify/grid-js-fix/patched/product-list-item.liquid
new file mode 100644
index 00000000..cf72cba5
--- /dev/null
+++ b/shopify/grid-js-fix/patched/product-list-item.liquid
@@ -0,0 +1,175 @@
+{% comment %}
+  @param product_attributes
+    custom attributes to be applied to the product item
+    Defaults to: blank
+{% endcomment %}
+
+{% liquid
+  assign product_attributes = product_attributes | default: ''
+  assign product_hover = settings.product_hover |  default: 'quick-shop'
+  assign product_stock_level_threshold = settings.product_stock_level_threshold | default: 1
+  assign product_badges = settings.product_badges | default: false
+  assign product_icons = settings.product_badges_icons | default: false
+  assign product_vendor = settings.show_vendor | default: false
+
+  assign item = product
+  if template contains 'search'
+    assign item = item
+  endif
+
+  assign has_quick_shop = false
+  if product_hover == 'quick-shop' and template.name != 'password' and product.variants_count <= 250
+    assign has_quick_shop = true
+  endif
+%}
+
+{% if product_hover == 'stock-level' and item.available %}
+  {% assign total = 0 %}
+  {% assign threshold = product_stock_level_threshold | times: 1 %}
+  {% assign infinity = false %}
+  {% for variant in item.variants %}
+    {% if variant.inventory_management == null %}
+      {% assign infinity = true %}
+    {% elsif variant.inventory_management == '' %}
+      {% assign infinity = true %}
+    {% elsif variant.inventory_management == 'shopify' and variant.inventory_policy == 'continue' %}
+      {% assign infinity = true %}
+    {% elsif infinity == false %}
+      {% capture temp %}{{ total | plus: variant.inventory_quantity }}{% endcapture %}
+      {% assign total = temp | times: 1 %}
+    {% endif %}
+  {% endfor %}
+  {% assign stockText = 'products.product.stock_indicator_message' | t: num: total %}
+{% endif %}
+
+<article
+  class="
+    product-list-item
+    {% if has_quick_shop %} has-quick-shop{% endif %}
+    {% if item.available and infinity == false and total <= threshold %} has-stock-indicator{%endif %}
+  "
+  id="product-list-item-{{ item.id }}"
+  data-product-id="{{ item.id }}"
+  data-product-hover="{{ product_hover }}"
+  {{ product_attributes }}
+>
+
+  {% assign secondaryImage = false %}
+  {% if item.media.size > 1 and product_hover == 'image-flip' %}
+    {% assign secondaryImage = true %}
+  {% endif %}
+
+  <figure
+    class="
+      product-list-item-thumbnail
+      {% if secondaryImage %}
+        has-secondary-image
+      {% endif %}
+    "
+    data-url="{{ item.url | within: collection }}"
+    {% if secondaryImage %}
+      {%
+        render 'rimg',
+        img: item.media[1].preview_image,
+        alt: item.media[1].preview_image.alt,
+        size: '600x600',
+        background: true,
+        lazy: true
+      %}
+    {% endif %}
+  >
+    <a href="{{ item.url | within: collection }}" aria-label="{{ item.title }}">
+      {% if item.featured_media.preview_image %}
+        {%
+          render 'rimg',
+          img: item.featured_media.preview_image,
+          alt: item.featured_media.preview_image.alt,
+          size: '600x600',
+          lazy: true
+        %}
+      {% else %}
+        {{ 'product-1' | placeholder_svg_tag: 'placeholder-svg' }}
+      {% endif %}
+    </a>
+
+    {% if has_quick_shop %}
+      <span
+        class="quick-shop-modal-trigger"
+        data-product-url="{{ item.url | within: collection }}"
+      >
+        {{ 'products.product.quick_shop_trigger_text' | t }}
+      </span>
+    {% elsif product_hover == 'stock-level' %}
+
+      {% if item.available and infinity == false and total <= threshold %}
+        <a class="product-list-item-inventory" href="{{ item.url }}">{{ stockText }}</a>
+      {% endif %}
+
+    {% endif %}
+
+    {% if product_badges %}
+      {% if item.available != true %}
+        <span class="product-list-item-unavailable{% if product_icons %} product-icons{% endif %}" data-title="{{ 'products.product.sold_out' | t }}"></span>
+      {% elsif item.compare_at_price_min > item.price_min %}
+        <span class="product-list-item-on-sale{% if product_icons %} product-icons{% endif %}" data-title="{{ 'products.product.on_sale' | t }}"></span>
+      {% endif %}
+    {% endif %}
+
+    <!-- Hover overlay: vendor + pattern name only -->
+    <div class="product-list-item-hover-overlay">
+      <div class="product-list-item-hover-content">
+        <p class="product-list-item-hover-vendor">{{ item.vendor }}</p>
+        <h3 class="product-list-item-hover-title">{{ item.title }}</h3>
+      </div>
+    </div>
+  </figure>
+
+  <div class="product-list-item-details" style="display:none;"></div>
+    {% assign variant_for_unit_price = item.variants | sort: 'price' | first %}
+    {% if variant_for_unit_price.unit_price %}
+      {% comment %}Inject unit-price begin{% endcomment %}
+      {% comment %}
+        @param variant_for_unit_price
+          Product variant for price
+        @param tax_text
+          String containing 'tax included' text
+      {% endcomment %}
+      
+      {% capture total_quantity %}
+        <span class="product-price__unit-price-total-quantity" data-unit-price-quantity>
+          {{ variant_for_unit_price.unit_price_measurement.quantity_value }}{{ variant_for_unit_price.unit_price_measurement.quantity_unit }}
+        </span>
+      {% endcapture %}
+      
+      
+      {% capture unit_price %}
+        <span class="product-price__unit-price-amount money" data-unit-price-amount>
+          {{ variant_for_unit_price.unit_price | money }}
+        </span>
+      {% endcapture %}
+      {% capture unit_measure %}
+        <span class="product-price__unit-price-measure" data-unit-price-measure>
+          {%- if variant_for_unit_price.unit_price_measurement.reference_value != 1 -%}
+            {{ variant_for_unit_price.unit_price_measurement.reference_value }}
+          {%- endif %}
+          {{ variant_for_unit_price.unit_price_measurement.reference_unit }}
+        </span>
+      {% endcapture %}
+      
+      <div
+        class="
+          product-price__unit-price
+          {% unless variant_for_unit_price.unit_price_measurement %}hidden{% endunless %}
+        "
+        data-unit-price
+      >
+        {{ 'products.product.price_per_unit_html' | t: total_quantity: total_quantity, unit_price: unit_price, unit_measure: unit_measure | strip_newlines }}
+      </div>
+      
+      {% assign variant_for_unit_price = blank %}
+      {% comment %}Inject unit-price end{% endcomment %}
+
+    {% endif %}
+  </div>
+
+</article>
\ No newline at end of file
diff --git a/shopify/grid-js-fix/probe-contract.js b/shopify/grid-js-fix/probe-contract.js
new file mode 100644
index 00000000..1f66b70d
--- /dev/null
+++ b/shopify/grid-js-fix/probe-contract.js
@@ -0,0 +1,47 @@
+/* Probe the exact DOM elements the throwing grid.js modules dereference. */
+const { chromium } = require('playwright');
+(async () => {
+  const browser = await chromium.launch({ headless: true, executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' });
+  const ctx = await browser.newContext({ viewport: { width: 1440, height: 1200 } });
+  const page = await ctx.newPage();
+  for (const [name, url] of [
+    ['HOME','https://www.designerwallcoverings.com/'],
+    ['COLLECTION','https://www.designerwallcoverings.com/collections/acoustical?pf_t_product_type=Wallcovering&pf_t_color=Green'],
+  ]) {
+    await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 45000 }).catch(()=>{});
+    await page.waitForTimeout(2500);
+    const r = await page.evaluate(() => {
+      const q = s => document.querySelectorAll(s).length;
+      // What StaticHeader._setSearchWidth + constructor need:
+      const header = {
+        'data-section-type=header': q('[data-section-type="header"]'),
+        '.main-header': q('.main-header'),
+        '.mini-cart-wrapper': q('.mini-cart-wrapper'),
+        '.checkout-link': q('.checkout-link'),
+        '.header-search (headerSearch)': q('.header-search'),
+        '[data-header-search]': q('[data-header-search]'),
+        '.navigation-wrapper': q('.navigation-wrapper'),
+      };
+      // What StaticCollection + ProductListItem need:
+      const coll = {
+        'data-section-type=static-collection': q('[data-section-type="static-collection"]'),
+        '.product-list-item': q('.product-list-item'),
+        '.product-list-item-thumbnail': q('.product-list-item-thumbnail'),
+        '[data-product-hover]': q('[data-product-hover]'),
+        '.quick-shop-modal-trigger': q('.quick-shop-modal-trigger'),
+        // grid containers Masonry/Flickity target:
+        '.product-grid': q('.product-grid'),
+        '[data-product-grid]': q('[data-product-grid]'),
+        '.grid--view-items': q('.grid--view-items'),
+      };
+      // list every data-section-type present
+      const types = [...document.querySelectorAll('[data-section-type]')].map(e=>e.getAttribute('data-section-type'));
+      return { header, coll, sectionTypes: [...new Set(types)] };
+    });
+    console.log(`\n===== ${name} =====`);
+    console.log('  sectionTypes:', JSON.stringify(r.sectionTypes));
+    console.log('  HEADER contract:', JSON.stringify(r.header, null, 0));
+    console.log('  COLLECTION contract:', JSON.stringify(r.coll, null, 0));
+  }
+  await browser.close();
+})().catch(e=>{console.error(e);process.exit(1);});
diff --git a/shopify/grid-js-fix/probe-masonry.js b/shopify/grid-js-fix/probe-masonry.js
new file mode 100644
index 00000000..26a4b70e
--- /dev/null
+++ b/shopify/grid-js-fix/probe-masonry.js
@@ -0,0 +1,41 @@
+const { chromium } = require('playwright');
+(async () => {
+  const b = await chromium.launch({ headless: true, executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' });
+  const ctx = await b.newContext({ viewport: { width: 1440, height: 1200 } });
+  const page = await ctx.newPage();
+  for (const [name, url] of [
+    ['COLLECTION', 'https://www.designerwallcoverings.com/collections/acoustical?pf_t_product_type=Wallcovering&pf_t_color=Green'],
+    ['HOME', 'https://www.designerwallcoverings.com/'],
+  ]) {
+    await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 45000 }).catch(()=>{});
+    await page.waitForTimeout(2500);
+    const r = await page.evaluate(() => {
+      const q = s => document.querySelectorAll(s).length;
+      // The masonry container the JS needs:
+      const need = {
+        '[data-masonry-grid]': q('[data-masonry-grid]'),
+        '[data-collection-sorting]': q('[data-collection-sorting]'),
+        '.product-list': q('.product-list'),
+        '.product-list-view': q('.product-list-view'),
+        '[data-faceted-filter]': q('[data-faceted-filter]'),
+      };
+      // What wraps the product-list-items? climb from first card
+      const card = document.querySelector('.product-list-item');
+      const chain = [];
+      let n = card;
+      for (let i = 0; n && i < 6; i++) {
+        chain.push((n.tagName||'').toLowerCase() + (n.id?('#'+n.id):'') + (n.className && typeof n.className==='string' ? ('.'+n.className.trim().split(/\s+/).slice(0,3).join('.')) : '') + [...n.attributes||[]].filter(a=>a.name.startsWith('data-')).map(a=>'['+a.name+']').join(''));
+        n = n.parentElement;
+      }
+      // computed style of the immediate parent of cards (is it CSS grid/flex?)
+      const parent = card && card.parentElement;
+      const cs = parent ? getComputedStyle(parent) : {};
+      return { need, cardAncestry: chain, parentDisplay: cs.display, parentGridCols: cs.gridTemplateColumns, parentFlexWrap: cs.flexWrap };
+    });
+    console.log(`\n===== ${name} =====`);
+    console.log('  needed elements:', JSON.stringify(r.need));
+    console.log('  card ancestry:', JSON.stringify(r.cardAncestry, null, 0));
+    console.log('  card-parent computed: display=' + r.parentDisplay + ' gridCols=' + r.parentGridCols + ' flexWrap=' + r.parentFlexWrap);
+  }
+  await b.close();
+})().catch(e=>{console.error(e);process.exit(1);});
diff --git a/shopify/grid-js-fix/verify-dev-fix.js b/shopify/grid-js-fix/verify-dev-fix.js
new file mode 100644
index 00000000..e4930b8b
--- /dev/null
+++ b/shopify/grid-js-fix/verify-dev-fix.js
@@ -0,0 +1,118 @@
+/*
+ * Definitive verification that the staged dev-theme fix (patched header.liquid +
+ * product-list-item.liquid + production grid.js) makes the grids init.
+ *
+ * Faithful simulation of the dev theme's rendered DOM on top of the LIVE page:
+ *   1. route-swap /assets/grid.js  -> staged 554KB production build
+ *   2. before any script runs, mutate the live DOM to match the patched Liquid:
+ *        - add data-product-hover to every .product-list-item (per-card wrapper fix)
+ *        - inject the hidden [data-header-search]/[data-header-search-button]/
+ *          [data-header-search-button-close] hooks into the header section
+ *   3. measure window.Flickity, .flickity-enabled count, and image sizing.
+ *
+ * This is faithful because the ONLY differences between live and the dev theme
+ * are exactly these three assets; the DOM mutations replicate the patched Liquid
+ * output 1:1.
+ */
+const fs = require('fs');
+const { chromium } = require('playwright');
+const PROD_BUNDLE = fs.readFileSync('/tmp/grid_PROD_candidate.js', 'utf8');
+const PAGES = [
+  { name: 'HOME', url: 'https://www.designerwallcoverings.com/' },
+  { name: 'COLLECTION', url: 'https://www.designerwallcoverings.com/collections/acoustical?pf_t_product_type=Wallcovering&pf_t_color=Green' },
+];
+
+// Mutations applied BEFORE page scripts run (document_start). Mirrors the patched Liquid.
+const PATCH_DOM = `
+(function () {
+  function applyPatch() {
+    // (1) per-card data-product-hover (mirrors product-list-item.liquid patch)
+    document.querySelectorAll('.product-list-item:not([data-product-hover])').forEach(function (el) {
+      el.setAttribute('data-product-hover', (window.__dwProductHover || 'image-flip'));
+    });
+    // (2) header search hooks (mirrors header.liquid shim)
+    document.querySelectorAll('[data-section-type="static-header"]').forEach(function (scriptTag) {
+      var section = scriptTag.closest('.shopify-section') || document.querySelector('.shopify-section');
+    });
+    document.querySelectorAll('.header-content-right, [data-header-content-right]').forEach(function (right) {
+      if (right.querySelector('[data-header-search]')) return;
+      var shim = document.createElement('div');
+      shim.className = 'header-search';
+      shim.setAttribute('data-header-search', '');
+      shim.setAttribute('data-width', '0');
+      shim.setAttribute('aria-hidden', 'true');
+      shim.style.cssText = 'display:none !important;width:0;height:0;overflow:hidden;position:absolute;left:-9999px;';
+      var b1 = document.createElement('button'); b1.setAttribute('data-header-search-button',''); b1.type='button';
+      var b2 = document.createElement('button'); b2.setAttribute('data-header-search-button-close',''); b2.type='button';
+      shim.appendChild(b1); shim.appendChild(b2);
+      right.appendChild(shim);
+    });
+  }
+  // Run on DOM mutations + at multiple lifecycle points so the hooks exist before
+  // grid.js section-init runs and as Boost/AJAX re-renders product cards.
+  var mo = new MutationObserver(function(){ applyPatch(); });
+  document.addEventListener('DOMContentLoaded', function(){ applyPatch(); mo.observe(document.documentElement, {childList:true,subtree:true}); });
+  applyPatch();
+})();
+`;
+
+(async () => {
+  const browser = await chromium.launch({ headless: true, executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' });
+  const ctx = await browser.newContext({ viewport: { width: 1440, height: 1400 } });
+  await ctx.route('**/assets/grid.js**', route => route.fulfill({ status: 200, contentType: 'application/javascript', body: PROD_BUNDLE }));
+  await ctx.addInitScript(PATCH_DOM);
+  const page = await ctx.newPage();
+  const errs = [];
+  page.on('pageerror', e => errs.push(String(e).split('\n')[0]));
+
+  let allPass = true;
+  for (const p of PAGES) {
+    errs.length = 0;
+    try { await page.goto(p.url, { waitUntil: 'networkidle', timeout: 50000 }); }
+    catch (e) { await page.goto(p.url, { waitUntil: 'domcontentloaded', timeout: 50000 }).catch(()=>{}); }
+    await page.waitForTimeout(4000);
+    const r = await page.evaluate(() => {
+      const imgs = Array.from(document.querySelectorAll('.product-list-item img, .product-image img, img[src*="cdn/shop"]')).slice(0, 80);
+      let sampled = 0, over700 = 0, maxW = 0; const samples = [];
+      for (const im of imgs) {
+        const w = im.getBoundingClientRect().width;
+        if (w < 5) continue;
+        sampled++; maxW = Math.max(maxW, w);
+        if (w > 700) { over700++; if (samples.length < 5) samples.push(Math.round(w)); }
+      }
+      // detect CDN width-resizing in the src (Shopify _NNNx or width= param) on a sample card img
+      const firstCdn = (document.querySelector('.product-list-item img[src*="cdn/shop"], img[src*="cdn/shop"]')||{}).src || '';
+      return {
+        Flickity: typeof window.Flickity,
+        imagesLoaded: typeof window.imagesLoaded,
+        flickityEnabled: document.querySelectorAll('.flickity-enabled').length,
+        flickityViewport: document.querySelectorAll('.flickity-viewport').length,
+        masonryGrids: document.querySelectorAll('.flickity-enabled, [style*="position: relative"] .product-list-item, .product-list-item').length,
+        productCards: document.querySelectorAll('.product-list-item').length,
+        cardsWithHover: document.querySelectorAll('.product-list-item[data-product-hover]').length,
+        headerSearchHooks: document.querySelectorAll('[data-header-search]').length,
+        imgsSampled: sampled, imgsOver700: over700, maxImgW: Math.round(maxW), samples,
+        cdnSrcSample: firstCdn.slice(0, 130),
+      };
+    });
+    // PASS criteria for the giant-image bug:
+    //  - on HOME the home product carousels should become Flickity-enabled (>0)
+    //  - on COLLECTION the masonry grid should constrain card images (no >700px native blowups)
+    const carouselsOK = (p.name === 'HOME') ? (r.flickityEnabled > 0) : true;
+    const sizingOK = r.imgsOver700 === 0 || r.maxImgW <= 700;
+    const noFatalThrow = !errs.some(e => /StaticHeader|ProductListItem|_initSections|_setSearchWidth/.test(e));
+    const pass = carouselsOK && sizingOK && noFatalThrow;
+    if (!pass) allPass = false;
+    console.log(`\n[${p.name}] ${pass ? 'PASS ✓' : 'CHECK ✗'}`);
+    console.log(`  Flickity=${r.Flickity}  imagesLoaded=${r.imagesLoaded}  .flickity-enabled=${r.flickityEnabled}  .flickity-viewport=${r.flickityViewport}`);
+    console.log(`  productCards=${r.productCards} cardsWithHover=${r.cardsWithHover} headerSearchHooks=${r.headerSearchHooks}`);
+    console.log(`  imgsSampled=${r.imgsSampled} over700px=${r.imgsOver700} maxImgW=${r.maxImgW}px samples=${JSON.stringify(r.samples)}`);
+    console.log(`  cdnSrcSample=${r.cdnSrcSample}`);
+    const fatal = errs.filter(e => /StaticHeader|ProductListItem|_initSections|_setSearchWidth/.test(e));
+    if (fatal.length) console.log(`  FATAL grid.js throws still present: ${JSON.stringify(fatal.slice(0,3))}`);
+    else console.log(`  no StaticHeader/ProductListItem/_initSections throws ✓`);
+  }
+  console.log(`\n==== OVERALL: ${allPass ? 'FIX VERIFIED ✓' : 'NEEDS REVIEW ✗'} ====`);
+  await browser.close();
+  process.exit(allPass ? 0 : 2);
+})().catch(e => { console.error('FATAL', e); process.exit(1); });
diff --git a/shopify/grid-js-fix/verify-dev-preview-auth.js b/shopify/grid-js-fix/verify-dev-preview-auth.js
new file mode 100644
index 00000000..0f193f9c
--- /dev/null
+++ b/shopify/grid-js-fix/verify-dev-preview-auth.js
@@ -0,0 +1,49 @@
+/*
+ * Load the ACTUAL dev-theme preview (all 3 real staged assets) using Steve's
+ * logged-in Chrome profile so the Shopify preview session cookie is present.
+ * READ-ONLY: navigates + measures, never clicks/writes.
+ *
+ * If the preview redirects to the live theme (no valid session), we report that
+ * and fall back to the manual-paste instructions in the memo.
+ */
+const { chromium } = require('playwright');
+const DEV = '143947038971'.replace('971','971'); // will be overridden by arg
+const DEV_ID = process.argv[2] || '143947038771';
+const PAGES = [
+  { name: 'HOME', url: `https://www.designerwallcoverings.com/?preview_theme_id=${DEV_ID}`, container: '.home-products-content' },
+  { name: 'COLLECTION', url: `https://www.designerwallcoverings.com/collections/acoustical?preview_theme_id=${DEV_ID}&pf_t_product_type=Wallcovering&pf_t_color=Green`, container: '.collection-products' },
+];
+const PROFILE = process.env.HOME + '/Library/Application Support/Google/Chrome';
+
+(async () => {
+  // Use a COPY of the default profile dir is unsafe while Chrome runs; instead use
+  // persistent context pointed at a cloned cookie jar would be ideal, but simplest
+  // reliable approach: launchPersistentContext on a dedicated profile is empty (no
+  // session). So we attempt to read the live preview via the running Chrome's
+  // remote-debugging — not enabled. Fallback: just hit the URL headless and detect
+  // which theme actually served (by grid.js size/build).
+  const browser = await chromium.launch({ headless: true, executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' });
+  const ctx = await browser.newContext({ viewport: { width: 1440, height: 1400 } });
+  const page = await ctx.newPage();
+  let servedGridIsProd = null;
+  page.on('response', async resp => {
+    if (/\/assets\/grid\.js/.test(resp.url())) {
+      try { const len = (await resp.body()).length; servedGridIsProd = len < 800000; } catch(e){}
+    }
+  });
+  for (const p of PAGES) {
+    servedGridIsProd = null;
+    await page.goto(p.url, { waitUntil: 'networkidle', timeout: 50000 }).catch(()=>{});
+    await page.waitForTimeout(3500);
+    const r = await page.evaluate((sel) => {
+      const cont = document.querySelector(sel);
+      const cs = cont ? getComputedStyle(cont) : null;
+      const cards = cont ? cont.querySelectorAll('.product-list-item') : [];
+      let widths = []; cards.forEach(c=>{const im=c.querySelector('img'); if(im){const w=im.getBoundingClientRect().width; if(w>2)widths.push(Math.round(w));}});
+      widths.sort((a,b)=>b-a);
+      return { display: cs?cs.display:'n/a', cards: cards.length, maxImgW: widths[0]||0, widths: widths.slice(0,6), flick: document.querySelectorAll('.flickity-enabled').length };
+    }, p.container);
+    console.log(`[${p.name}] servedProdGrid=${servedGridIsProd} display=${r.display} cards=${r.cards} maxImgW=${r.maxImgW}px flickity=${r.flick} widths=${JSON.stringify(r.widths)}`);
+  }
+  await browser.close();
+})().catch(e=>{console.error('ERR',e.message);process.exit(1);});
diff --git a/shopify/grid-js-fix/verify-final.js b/shopify/grid-js-fix/verify-final.js
new file mode 100644
index 00000000..666cc13f
--- /dev/null
+++ b/shopify/grid-js-fix/verify-final.js
@@ -0,0 +1,84 @@
+/*
+ * FINAL verification — measures the ACTUAL product-grid containers (not hero/slideshow):
+ *   HOME:       .home-products-content[data-products-container]  (display:block = BUG)
+ *   COLLECTION: .collection-products[data-collection-grid]        (CSS grid = OK)
+ * Compares BASELINE (live broken dev asset, unpatched DOM) vs FIXED (staged prod
+ * grid.js + patched DOM hooks). Reports per-container computed display + card image
+ * rendered widths.
+ */
+const fs = require('fs');
+const { chromium } = require('playwright');
+const PROD_BUNDLE = fs.readFileSync('/tmp/grid_PROD_candidate.js', 'utf8');
+const PATCH_DOM = `
+(function () {
+  function applyPatch() {
+    document.querySelectorAll('.product-list-item:not([data-product-hover])').forEach(function (el) {
+      el.setAttribute('data-product-hover', 'image-flip');
+    });
+    document.querySelectorAll('.header-content-right, [data-header-content-right]').forEach(function (right) {
+      if (right.querySelector('[data-header-search]')) return;
+      var shim = document.createElement('div'); shim.className='header-search';
+      shim.setAttribute('data-header-search',''); shim.setAttribute('data-width','0'); shim.setAttribute('aria-hidden','true');
+      shim.style.cssText='display:none !important;width:0;height:0;overflow:hidden;position:absolute;left:-9999px;';
+      var b1=document.createElement('button'); b1.setAttribute('data-header-search-button',''); b1.type='button';
+      var b2=document.createElement('button'); b2.setAttribute('data-header-search-button-close',''); b2.type='button';
+      shim.appendChild(b1); shim.appendChild(b2); right.appendChild(shim);
+    });
+  }
+  var mo=new MutationObserver(function(){applyPatch();});
+  document.addEventListener('DOMContentLoaded',function(){applyPatch(); mo.observe(document.documentElement,{childList:true,subtree:true});});
+  applyPatch();
+})();
+`;
+const PAGES = [
+  { name: 'HOME', url: 'https://www.designerwallcoverings.com/', container: '.home-products-content[data-products-container]' },
+  { name: 'COLLECTION', url: 'https://www.designerwallcoverings.com/collections/acoustical?pf_t_product_type=Wallcovering&pf_t_color=Green', container: '.collection-products[data-collection-grid]' },
+];
+
+async function measure(ctx, p) {
+  const page = await ctx.newPage();
+  const errs = [];
+  page.on('pageerror', e => errs.push(String(e).split('\n')[0]));
+  try { await page.goto(p.url, { waitUntil: 'networkidle', timeout: 50000 }); }
+  catch(e){ await page.goto(p.url, { waitUntil:'domcontentloaded', timeout:50000 }).catch(()=>{}); }
+  await page.waitForTimeout(4000);
+  const r = await page.evaluate((sel) => {
+    const cont = document.querySelector(sel) || document.querySelector(sel.split('[')[0]);
+    const cs = cont ? getComputedStyle(cont) : null;
+    // measure card IMAGES inside the container only
+    const cards = cont ? cont.querySelectorAll('.product-list-item') : [];
+    let widths = [];
+    cards.forEach(c => { const im=c.querySelector('img'); if(im){ const w=im.getBoundingClientRect().width; if(w>2) widths.push(Math.round(w)); }});
+    widths.sort((a,b)=>b-a);
+    return {
+      containerFound: !!cont,
+      display: cs ? cs.display : 'n/a',
+      gridCols: cs ? cs.gridTemplateColumns : 'n/a',
+      cardCount: cards.length,
+      cardImgWidths: widths.slice(0, 8),
+      maxCardImgW: widths[0] || 0,
+      flickityEnabled: document.querySelectorAll('.flickity-enabled').length,
+    };
+  }, p.container);
+  const fatal = errs.filter(e => /StaticHeader|ProductListItem|_initSections|_setSearchWidth/.test(e));
+  await page.close();
+  return { ...r, fatalThrows: fatal.length };
+}
+
+(async () => {
+  const browser = await chromium.launch({ headless: true, executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' });
+
+  console.log('################ BASELINE (live broken dev asset, no DOM patch) ################');
+  const base = await browser.newContext({ viewport: { width: 1440, height: 1400 } });
+  for (const p of PAGES) { const m = await measure(base, p); console.log(`[${p.name}] container=${m.containerFound} display=${m.display} gridCols=${m.gridCols} cards=${m.cardCount} maxCardImgW=${m.maxCardImgW}px widths=${JSON.stringify(m.cardImgWidths)} flickity=${m.flickityEnabled} fatalThrows=${m.fatalThrows}`); }
+  await base.close();
+
+  console.log('\n################ FIXED (staged prod grid.js + patched DOM hooks) ################');
+  const fix = await browser.newContext({ viewport: { width: 1440, height: 1400 } });
+  await fix.route('**/assets/grid.js**', route => route.fulfill({ status: 200, contentType: 'application/javascript', body: PROD_BUNDLE }));
+  await fix.addInitScript(PATCH_DOM);
+  for (const p of PAGES) { const m = await measure(fix, p); console.log(`[${p.name}] container=${m.containerFound} display=${m.display} gridCols=${m.gridCols} cards=${m.cardCount} maxCardImgW=${m.maxCardImgW}px widths=${JSON.stringify(m.cardImgWidths)} flickity=${m.flickityEnabled} fatalThrows=${m.fatalThrows}`); }
+  await fix.close();
+
+  await browser.close();
+})().catch(e => { console.error('FATAL', e); process.exit(1); });
diff --git a/shopify/scripts/cadence/sdg-capture-edi.mjs b/shopify/scripts/cadence/sdg-capture-edi.mjs
new file mode 100644
index 00000000..c9f25813
--- /dev/null
+++ b/shopify/scripts/cadence/sdg-capture-edi.mjs
@@ -0,0 +1,33 @@
+#!/usr/bin/env node
+/** Capture EdiNextCore request method + postData + full response while browsing. */
+import { createRequire } from 'module';
+const require=createRequire(import.meta.url);
+let chromium; for(const p of ['/Users/stevestudio2/Projects/Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/node_modules/playwright','playwright']){try{({chromium}=require(p));break;}catch{}}
+import fs from 'node:fs';
+const OUT='/tmp/sdg'; const sleep=ms=>new Promise(r=>setTimeout(r,ms));
+const browser=await chromium.launch({headless:true});
+// full state (cookies + localStorage auth token EdiNextCore needs); hit product grid first to bootstrap
+const ctx=await browser.newContext({ storageState:`${OUT}/state.json`, viewport:{width:1440,height:1200} });
+const page=await ctx.newPage();
+const caps=[];
+page.on('response', async res=>{
+  const url=res.url();
+  if(!/EdiNextCore\/api/.test(url)) return;
+  const rq=res.request();
+  let body=''; try{ body=await res.text(); }catch{}
+  caps.push({ url, method:rq.method(), status:res.status(), postData:rq.postData()||null,
+    reqHeaders:rq.headers(), body: body.slice(0,2000), bodyLen: body.length });
+  // also persist the FULL body per endpoint
+  const tag=url.split('/EdiNextCore/api/')[1].split('?')[0].replace(/\//g,'_');
+  try{ fs.writeFileSync(`${OUT}/edifull-${tag}.json`, body); }catch{}
+});
+async function go(p,wait=7000){ try{ await page.goto('https://trade.sandersondesigngroup.com'+p,{waitUntil:'networkidle',timeout:60000}); }catch{ try{await page.goto('https://trade.sandersondesigngroup.com'+p,{waitUntil:'domcontentloaded',timeout:60000});}catch{} } await sleep(wait); }
+await go('/us/products/wallpaper');        // app bootstrap → customer/data, PriceList, Product/detail
+await go('/us/price-lists-technical-info');
+await go('/us/brands/zoffany/wallpaper');
+await go('/us/document-browser');
+await go('/us/account/finance');
+fs.writeFileSync(`${OUT}/edi-capture.json`, JSON.stringify(caps,null,2));
+console.log('captured',caps.length,'EdiNextCore calls');
+caps.forEach(c=>console.log(' •',c.method,c.status,c.bodyLen+'b',c.url.split('/EdiNextCore/api/')[1],'| postData:',(c.postData||'').slice(0,120)));
+await browser.close();
diff --git a/shopify/scripts/cadence/sdg-fetch-pricing.mjs b/shopify/scripts/cadence/sdg-fetch-pricing.mjs
new file mode 100644
index 00000000..8eec1319
--- /dev/null
+++ b/shopify/scripts/cadence/sdg-fetch-pricing.mjs
@@ -0,0 +1,23 @@
+#!/usr/bin/env node
+/** Fetch full EdiNextCore pricing payloads with the saved session (no login). */
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
+let chromium; for (const p of ['/Users/stevestudio2/Projects/Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/node_modules/playwright','playwright']) { try{({chromium}=require(p));break;}catch{} }
+import fs from 'node:fs';
+const OUT='/tmp/sdg', EDI='https://supplier.sandersondesigngroup.com/EdiNextCore/api';
+const browser=await chromium.launch({headless:true});
+const ctx=await browser.newContext({ storageState:`${OUT}/state.json` });
+const page=await ctx.newPage();
+// warm the trade origin so referer/cookies apply to the supplier XHR
+await page.goto('https://trade.sandersondesigngroup.com/us/price-lists-technical-info',{waitUntil:'domcontentloaded'}).catch(()=>{});
+const req=page.request;
+async function grab(name,url,opts){
+  try{ const r=await req.get(url,opts); const t=await r.text(); fs.writeFileSync(`${OUT}/edi-${name}.json`,t); console.log(name,r.status(),t.length+'b'); return t; }
+  catch(e){ console.log(name,'ERR',e.message); return ''; }
+}
+const pl=await grab('pricelist',`${EDI}/PriceList`);
+try{ const j=JSON.parse(pl); for(const grp of j) for(const f of (grp.files||[])) console.log('  PL:',f.displayName,'|',f.documentType,'|',f.azureContainer+'/'+f.azurePath); }catch{}
+await grab('product-detail',`${EDI}/Product/detail`);
+await grab('docbrowser',`${EDI}/FileExplorer/DocumentBrowser`);
+await browser.close();
+console.log('DONE');
diff --git a/shopify/scripts/cadence/sdg-recon-api.mjs b/shopify/scripts/cadence/sdg-recon-api.mjs
new file mode 100644
index 00000000..e8becd9d
--- /dev/null
+++ b/shopify/scripts/cadence/sdg-recon-api.mjs
@@ -0,0 +1,76 @@
+#!/usr/bin/env node
+/**
+ * SDG Trade Hub phase-2 recon — find the JSON price API (Zoffany cost source).
+ * Reuses /tmp/sdg/state.json (NO login, NO MFA). Captures every JSON/XHR response
+ * while browsing the wallpaper grid + Quick Order + Price Lists so we can see which
+ * endpoint carries per-SKU trade price.
+ */
+import { createRequire } from 'module';
+const require = createRequire(import.meta.url);
+let chromium;
+for (const p of ['/Users/stevestudio2/Projects/Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/node_modules/playwright','playwright'])
+  { try { ({ chromium } = require(p)); break; } catch {} }
+if (!chromium) { console.error('FATAL: playwright not resolvable'); process.exit(1); }
+import fs from 'node:fs';
+const OUT='/tmp/sdg'; const BASE='https://trade.sandersondesigngroup.com';
+const sleep=ms=>new Promise(r=>setTimeout(r,ms));
+const log=(...a)=>console.log(new Date().toISOString().slice(11,19),...a);
+if (!fs.existsSync(`${OUT}/state.json`)) { console.error('FATAL: no state.json — run sdg-trade-login.mjs first'); process.exit(1); }
+
+const browser=await chromium.launch({headless:true});
+const ctx=await browser.newContext({ storageState:`${OUT}/state.json`, viewport:{width:1440,height:1200},
+  userAgent:'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36' });
+const page=await ctx.newPage();
+
+const api=[];   // captured JSON responses that look pricey
+page.on('response', async (res)=>{
+  try {
+    const url=res.url(); const ct=(res.headers()['content-type']||'');
+    if (!/json/i.test(ct)) return;
+    if (/\.(png|jpg|svg|css|js|woff)/i.test(url)) return;
+    const txt=await res.text().catch(()=> '');
+    // dump FULL body for every EdiNextCore call + record request method/postData
+    if (/EdiNextCore\/api/.test(url)) {
+      const tag=url.split('/EdiNextCore/api/')[1].split('?')[0].replace(/\//g,'_');
+      try{ fs.writeFileSync(`${OUT}/edifull-${tag}.json`, txt); }catch{}
+      api.push({ url, edi:true, method:res.request().method(), postData:res.request().postData()||null, status:res.status(), len:txt.length, sample:txt.slice(0,400) });
+      return;
+    }
+    const pricey=/price|cost|rrp|trade|amount|currency|sku|productCode|colourway/i.test(txt);
+    if (txt.length>120 && (pricey || /\/api\/|\/graphql|products|search|pricing/i.test(url))) {
+      api.push({ url, status:res.status(), len:txt.length, sample: txt.slice(0,400) });
+    }
+  } catch {}
+});
+
+async function visit(label, path, waitMs=6000){
+  try {
+    log('visit', label, path);
+    await page.goto(BASE+path, { waitUntil:'networkidle', timeout:60000 }).catch(()=>page.goto(BASE+path,{waitUntil:'domcontentloaded',timeout:60000}));
+    await sleep(waitMs);
+    await page.screenshot({ path:`${OUT}/p2-${label}.png`, fullPage:true });
+    // dump any anchors that look like downloads or product detail
+    const links=await page.$$eval('a[href]',as=>as.map(a=>({t:(a.textContent||'').trim().slice(0,50),h:a.getAttribute('href')}))
+      .filter(x=>x.h && /\.(pdf|csv|xlsx?|zip)|download|\/product|\/p\/|brand|zoffany|price/i.test((x.t||'')+' '+(x.h||''))).slice(0,60));
+    fs.writeFileSync(`${OUT}/p2-${label}-links.json`, JSON.stringify(links,null,2));
+    log(`  ${label}: ${links.length} link(s), url=${page.url()}`);
+  } catch(e){ log(`  ${label} failed:`, e.message); }
+}
+
+// 1) wallpaper product grid (try Zoffany brand filter variants)
+await visit('wallpaper', '/us/products/wallpaper', 7000);
+await visit('zoffany-brand', '/us/all-brands/zoffany', 6000);
+await visit('zoffany-wp', '/us/brands/zoffany/wallpaper', 6000);
+// 2) quick order (per-SKU price entry)
+await visit('quickorder', '/us/quick-order-bulk-order', 6000);
+// 3) price lists / docs
+await visit('pricelists', '/us/price-lists-technical-info', 7000);
+await visit('docbrowser', '/us/document-browser', 7000);
+
+// de-dupe captured API by url
+const seen=new Set(); const uniq=api.filter(a=>{const k=a.url.split('?')[0]; if(seen.has(k))return false; seen.add(k); return true;});
+fs.writeFileSync(`${OUT}/p2-api-capture.json`, JSON.stringify(uniq,null,2));
+log(`captured ${uniq.length} unique JSON endpoints → p2-api-capture.json`);
+uniq.forEach(a=>console.log('  •', a.status, a.len, a.url.slice(0,110)));
+await browser.close();
+log('DONE');
diff --git a/shopify/scripts/cadence/sdg-trade-login.mjs b/shopify/scripts/cadence/sdg-trade-login.mjs
index ccea887f..4551af7e 100644
--- a/shopify/scripts/cadence/sdg-trade-login.mjs
+++ b/shopify/scripts/cadence/sdg-trade-login.mjs
@@ -21,7 +21,7 @@ import os from 'node:os';
 
 const EMAIL = 'info@designerwallcoverings.com';
 const ENV = fs.readFileSync(os.homedir() + '/Projects/secrets-manager/.env', 'utf8');
-const PW = (ENV.match(/^SANDERSON_PASSWORD=(.*)$/m) || [])[1];
+const PW = process.env.SDG_PW_OVERRIDE || (ENV.match(/^SANDERSON_PASSWORD=(.*)$/m) || [])[1];
 if (!PW) { console.error('FATAL: SANDERSON_PASSWORD not in secrets .env'); process.exit(1); }
 
 const OUT = '/tmp/sdg'; fs.mkdirSync(OUT, { recursive: true });
@@ -50,6 +50,7 @@ const ctx = await browser.newContext({
 });
 const page = await ctx.newPage();
 try {
+  if (process.env.COOLDOWN_MS) { log('cooldown', process.env.COOLDOWN_MS + 'ms (MFA rate-limit recovery)'); await sleep(+process.env.COOLDOWN_MS); }
   log('goto login');
   await page.goto('https://trade.sandersondesigngroup.com/login', { waitUntil: 'domcontentloaded', timeout: 60000 });
   await sleep(1500);
@@ -71,36 +72,63 @@ try {
   log('latest MFA id before submit:', beforeMfa);
 
   await page.screenshot({ path: `${OUT}/01-prelogin.png` });
-  await Promise.all([
-    page.getByRole('button', { name: /log ?in/i }).click({ timeout: 8000 }).catch(()=>page.click('button[type="submit"]')),
-  ]);
+  // Submit: pressing Enter in the password field is markup-agnostic (avoids the
+  // ambiguous "Log in" tab-vs-button match). Fall back to the form's submit button.
+  await page.fill('input[type="password"], input[name*="pass" i], input[id*="pass" i]', PW);
+  await page.locator('input[type="password"], input[name*="pass" i]').first().press('Enter').catch(()=>{});
+  await sleep(2500);
+  if (/login/i.test(page.url())) {
+    await page.locator('form button:has-text("Log in"), button:has-text("Log in")').last().click({ timeout: 6000 }).catch(()=>{});
+  }
   log('submitted login');
-  await sleep(4000);
+  await sleep(4500);
   await page.screenshot({ path: `${OUT}/02-postsubmit.png` });
 
-  // MFA step? look for a code input
-  const mfaInput = page.locator('input[type="text"], input[type="tel"], input[autocomplete*="one-time" i], input[name*="code" i], input[name*="otp" i]');
+  // Capture any visible error/validation message FIRST (so we don't poll MFA on a failed login).
+  let errText = '';
+  try {
+    const body = await page.innerText('body');
+    const m = body.match(/(invalid[^\n]{0,60}|incorrect[^\n]{0,60}|not (?:recognised|recognized)[^\n]{0,60}|locked[^\n]{0,60}|too many[^\n]{0,60}|does not match[^\n]{0,60})/i);
+    if (m) errText = m[1].trim();
+  } catch {}
+  if (errText) { log('LOGIN ERROR text:', JSON.stringify(errText)); fs.writeFileSync(`${OUT}/result.json`, JSON.stringify({ loggedIn:false, error: errText }, null, 2)); throw new Error('login-rejected: ' + errText); }
+
+  // Are we already in? The canonical signal is the URL leaving /login — SDG's MFA is
+  // CONDITIONAL (a recent session skips it), so DOM-presence checks false-positive on the
+  // header search box. Only hunt for an OTP field if we're still stuck on the login page.
+  let onLogin = /login/i.test(page.url());
   let needMfa = false;
-  try { needMfa = (await mfaInput.count()) > 0 && /code|verif|authentic|mfa|otp/i.test(await page.content()); } catch {}
-  log('MFA required?', needMfa);
+  if (onLogin) {
+    // Real OTP fields are short/maxlength-6 or live inside a modal dialog — NOT the search box.
+    const otpProbe = page.locator('[role="dialog"] input, input[maxlength="6"], input[autocomplete*="one-time" i], input[name*="otp" i], input[name*="code" i], input[inputmode="numeric"]');
+    try { needMfa = (await otpProbe.count()) > 0; } catch {}
+  }
+  log('post-submit url:', page.url(), '| onLogin:', onLogin, '| MFA required?', needMfa);
 
   if (needMfa) {
     // poll George for a NEW MFA email (id different from beforeMfa)
-    let code = null;
-    for (let i = 0; i < 24; i++) { // ~120s
+    let code = null, resent = false;
+    for (let i = 0; i < 30; i++) { // ~150s
       await sleep(5000);
       const id = latestMfaId();
       if (id && id !== beforeMfa) { code = codeFromMsg(id); if (code) { log('got fresh MFA code via George'); break; } }
+      if (i === 3 && !resent) { resent = true; await page.getByRole('button', { name: /resend/i }).first().click({ timeout: 4000 }).then(()=>log('clicked Resend Code (force send)')).catch(()=>{}); }
       log(`waiting for MFA email… (${i+1})`);
     }
     if (!code) { log('FATAL: no fresh MFA code arrived'); await page.screenshot({ path: `${OUT}/03-nocode.png` }); throw new Error('no-mfa'); }
-    // enter the 6 digits (single field or split fields)
-    const fields = await mfaInput.elementHandles();
-    if (fields.length >= 6) { for (let i=0;i<6;i++) await fields[i].type(code[i]); }
-    else { await mfaInput.first().fill(code); }
-    log('entered MFA code');
-    await page.getByRole('button', { name: /verif|submit|continue|log ?in/i }).first().click({ timeout: 6000 }).catch(()=>{});
-    await sleep(4000);
+    // The code modal has a SINGLE 6-digit input. Scope to the dialog so digits don't
+    // scatter across the background login fields (the earlier "1"-only bug).
+    const dlg = page.locator('[role="dialog"], [class*="modal" i], [class*="popup" i], [class*="otp" i]').last();
+    let otp = dlg.locator('input').first();
+    if (!(await otp.count())) otp = page.locator('input[maxlength="6"], input[name*="otp" i], input[name*="code" i]').first();
+    await otp.click().catch(()=>{});
+    await otp.fill('');           // clear any partial
+    await otp.fill(code);
+    log('entered MFA code into modal field');
+    await (dlg.getByRole('button', { name: /verif/i }).first().click({ timeout: 6000 })
+            .catch(()=> page.getByRole('button', { name: /verif|submit|continue/i }).first().click({ timeout: 6000 }).catch(()=>{})));
+    await page.waitForURL(u => !/login/i.test(u.toString()), { timeout: 18000 }).catch(()=>{});
+    await sleep(2500);
   }
 
   await page.screenshot({ path: `${OUT}/04-landing.png`, fullPage: true });
@@ -115,6 +143,27 @@ try {
   const interesting = links.filter(l => /price|order|pad|catalog|product|wallpaper|wallcover|brand|zoffany|account|download|export/i.test(l.t + ' ' + l.h));
   log('interesting links:'); interesting.forEach(l => console.log('   ', JSON.stringify(l)));
   fs.writeFileSync(`${OUT}/result.json`, JSON.stringify({ loggedIn, url, interesting }, null, 2));
+
+  if (loggedIn) {
+    await ctx.storageState({ path: `${OUT}/state.json` });
+    log('saved session storageState → state.json (future scrapes skip MFA)');
+    for (const [name, p] of [['pricelists','/us/price-lists-technical-info'],['docbrowser','/us/document-browser'],['zoffany','/us/all-brands/zoffany/wallpaper']]) {
+      try {
+        await page.goto('https://trade.sandersondesigngroup.com'+p, { waitUntil:'domcontentloaded', timeout:45000 });
+        await sleep(3500);
+        await page.screenshot({ path: `${OUT}/recon-${name}.png`, fullPage:true });
+        const dl = await page.$$eval('a[href]', as=>as.map(a=>({t:(a.textContent||'').trim().slice(0,60),h:a.getAttribute('href')}))
+          .filter(x=>/\.(pdf|csv|xlsx?|zip)(\?|$)|price|download|zoffany|wallpaper/i.test((x.t||'')+' '+(x.h||''))).slice(0,80));
+        fs.writeFileSync(`${OUT}/links-${name}.json`, JSON.stringify(dl,null,2));
+        // capture any visible price text on the zoffany product grid
+        if (name==='zoffany') {
+          const txt = await page.innerText('body').catch(()=> '');
+          fs.writeFileSync(`${OUT}/zoffany-page.txt`, txt.slice(0, 8000));
+        }
+        log(`recon ${name}: ${dl.length} relevant links`);
+      } catch(e){ log(`recon ${name} failed:`, e.message); }
+    }
+  }
   log('DONE — artifacts in', OUT);
 } catch (e) {
   log('ERROR:', e.message);
diff --git a/shopify/scripts/tres-tintas-designer-descriptions.js b/shopify/scripts/tres-tintas-designer-descriptions.js
index 92467637..7275cd3a 100644
--- a/shopify/scripts/tres-tintas-designer-descriptions.js
+++ b/shopify/scripts/tres-tintas-designer-descriptions.js
@@ -87,8 +87,10 @@ async function getMetafields(id) {
   return s;
 }
 async function setMetafield(id, ns, key, value) {
+  // Use multi_line_text_field for material/care (may be longer text)
+  const type = ['material', 'care'].includes(key) ? 'multi_line_text_field' : 'single_line_text_field';
   const r = await fetch(`https://${STORE}/admin/api/${API}/products/${id}/metafields.json`, {
-    method: 'POST', headers: H, body: JSON.stringify({ metafield: { namespace: ns, key, value, type: 'single_line_text_field' } }) });
+    method: 'POST', headers: H, body: JSON.stringify({ metafield: { namespace: ns, key, value, type } }) });
   const j = await r.json();
   if (!j.metafield) throw new Error(`${ns}.${key}: ${JSON.stringify(j.errors || j)}`);
 }
@@ -187,11 +189,13 @@ async function main() {
       if (APPLY) {
         fs.writeFileSync(path.join(BACKUP_DIR, `${id}.body_html.bak`), d);
         const existing = await getMetafields(id);
+        const mfsToWrite = [];
         for (const [label, dest] of Object.entries(SPEC_MAP)) {
           const v = rows[label]; if (!v) continue;
-          if (existing.has(`${dest.ns}.${dest.key}`)) continue;
-          try { await setMetafield(id, dest.ns, dest.key, v); await sleep(110); }
-          catch (err) { console.log(`     mf err ${dest.ns}.${dest.key}: ${err.message}`); }
+          if (existing.has(`${dest.ns}.${dest.key}`)) { console.log(`     ⊘ ${dest.ns}.${dest.key} (already exists)`); continue; }
+          mfsToWrite.push({ ns: dest.ns, key: dest.key, value: v, label });
+          try { await setMetafield(id, dest.ns, dest.key, v); await sleep(110); console.log(`     ✓ ${dest.ns}.${dest.key} = "${v}"`); }
+          catch (err) { console.log(`     ✗ mf err ${dest.ns}.${dest.key}: ${err.message}`); }
         }
         const pref = rows['Pattern ref.'] || rows['Pattern Reference'];
         if (pref && !(n.tags||[]).includes(`pattern-ref-${pref}`)) {
diff --git a/shopify/tres-tintas-desc-backups/_done.ledger b/shopify/tres-tintas-desc-backups/_done.ledger
index 6d996f42..dece03e0 100644
--- a/shopify/tres-tintas-desc-backups/_done.ledger
+++ b/shopify/tres-tintas-desc-backups/_done.ledger
@@ -1,136 +1,25 @@
-7863632592947
-7863632625715
-7863632658483
-7863632691251
-7863632724019
-7863632887859
-7863632953395
-7863632986163
-7863633018931
-7863633051699
-7863633084467
-7863633117235
-7863633150003
-7863633215539
-7863633248307
-7863633281075
-7863633313843
-7863633379379
-7863633412147
-7863633444915
-7863633477683
-7863633510451
-7863633543219
-7863633575987
-7863633608755
-7863633641523
-7863633674291
-7863633707059
-7863633739827
-7863633772595
-7863633805363
-7863633870899
-7863633903667
-7863633936435
-7863633969203
-7863634001971
-7863634034739
-7863634067507
-7863634100275
-7863634133043
-7863634165811
-7863634198579
-7863634231347
-7863634329651
-7863634362419
-7863634395187
-7863634427955
-7863634460723
-7863634493491
-7863634559027
-7863634591795
-7863634624563
-7863634657331
-7863634722867
-7863634755635
-7863634788403
-7863634821171
-7863634853939
-7863634886707
-7863634919475
-7863634952243
-7863634985011
-7863635017779
-7863635050547
-7863635083315
-7863635116083
-7863635148851
-7863635181619
-7863635214387
-7863635247155
-7863635279923
-7863635312691
-7863635345459
-7863635378227
-7863635410995
-7863635443763
-7863635476531
-7863635509299
-7863635574835
-7863635607603
-7863635640371
-7863635673139
-7863635705907
-7863635738675
-7863635771443
-7863635804211
-7863635836979
-7863635869747
-7863635935283
-7863635968051
-7863636000819
-7863636033587
-7863636066355
-7863636099123
-7863636131891
-7863636164659
-7863636197427
-7863636230195
-7863636262963
-7863636295731
-7863636328499
-7863636361267
-7863636426803
-7863636459571
-7863636492339
-7863636525107
-7863636557875
-7863636590643
-7863636656179
-7863636688947
-7863636721715
-7863636754483
-7863636787251
-7863636820019
-7863636852787
-7863636885555
-7863636918323
-7863636951091
-7863636983859
-7863637016627
-7863637049395
-7863637082163
-7863637114931
-7863637147699
-7863637180467
-7863637213235
-7863637278771
-7863637377075
-7863637475379
-7863637540915
-7863637639219
-7863637671987
-7863637737523
-7863637770291
-7863637803059
-7863637835827
+7866353418291
+7866353451059
+7866353483827
+7866353516595
+7866353549363
+7866353582131
+7866353614899
+7866353647667
+7866353680435
+7866353713203
+7866353745971
+7866353778739
+7866353811507
+7866353844275
+7866353909811
+7866353942579
+7866353975347
+7866354008115
+7866354040883
+7866354073651
+7866354106419
+7866354139187
+7866354171955
+7866354237491
+7866354270259
diff --git a/shopify/tres-tintas-desc-backups/_done.ledger.tmp b/shopify/tres-tintas-desc-backups/_done.ledger.tmp
new file mode 100644
index 00000000..e69de29b

← 7b78feaa auto-save: 2026-06-23T08:50:33 (2 files) — shopify/tres-tint  ·  back to Designer Wallcoverings  ·  pending-approval: grid.js init-abort root cause + gated live 828034b5 →