← back to Dw Pairs Well

public/preview.html

466 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Design Coordinate — local preview</title>
<style>
  :root {
    --gutter: 40px; --text: #1a1a1a; --muted: #777; --line: #e7e7e7;
    --cols: 6;    /* density slider sets this */
  }
  * { box-sizing: border-box; }
  body { margin: 0; color: var(--text); background: #fafafa;
    font: 16px/1.45 -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif; }
  header.shop { border-bottom: 1px solid var(--line); background: #fff;
    padding: 14px var(--gutter); display: flex; justify-content: space-between; align-items: center; }
  header.shop .logo { font-weight: 600; letter-spacing: 0.04em; }
  header.shop .preview-tag { font-size: 11px; color: #b54708; background: #fff5d6;
    padding: 4px 10px; border-radius: 999px; letter-spacing: 0.06em; text-transform: uppercase; }
  main { max-width: 1480px; margin: 0 auto; padding: 32px var(--gutter); }

  .source-picker { background: #fff; border: 1px solid var(--line); border-radius: 6px;
    padding: 14px 18px; margin-bottom: 24px; display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
  .source-picker label { font-size: 12px; color: var(--muted); letter-spacing: 0.06em; text-transform: uppercase; }
  .source-picker input { font: inherit; padding: 8px 12px; border: 1px solid var(--line); border-radius: 4px; min-width: 180px; }
  .source-picker button { font: inherit; padding: 8px 14px; background: #1a1a1a; color: #fff; border: 0; border-radius: 4px; cursor: pointer; }
  .source-picker .quick { display: flex; gap: 6px; flex-wrap: wrap; }
  .source-picker .quick button { background: #fff; color: #333; border: 1px solid var(--line); padding: 6px 10px; font-size: 12px; }
  .source-picker .quick button:hover { background: #f4ede4; border-color: #c9b48a; }

  .product { display: grid; gap: 36px; grid-template-columns: 1.2fr 0.8fr;
    background: #fff; border: 1px solid var(--line); border-radius: 6px; padding: 32px; margin-bottom: 24px; }
  @media (max-width: 900px) { .product { grid-template-columns: 1fr; } }
  .product .photo { background: #f4f4f4; border-radius: 4px; overflow: hidden; aspect-ratio: 1/1; }
  .product .photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
  .product .meta h1 { margin: 0 0 6px; font-size: 24px; font-weight: 500; line-height: 1.2; }
  .product .meta .vendor { color: var(--muted); font-size: 13px; letter-spacing: 0.08em; text-transform: uppercase; margin-bottom: 20px; }
  .product .meta .price { font-size: 20px; margin-bottom: 18px; }
  .product .meta .add { display: block; width: 100%; padding: 14px; background: #111; color: #fff; border: 0; font-size: 12px; letter-spacing: 0.12em; text-transform: uppercase; cursor: pointer; border-radius: 4px; margin-bottom: 22px; }

  .src-palette { margin-top: 6px; }
  .src-palette h4 { margin: 0 0 8px; font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
  .src-palette table { border-collapse: collapse; width: 100%; font-size: 12px; }
  .src-palette th, .src-palette td { text-align: left; padding: 6px 8px; border-bottom: 1px solid #f0ebe2; }
  .src-palette th { font-weight: 500; color: var(--muted); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; }
  .src-palette td.swatch { width: 28px; }
  .swatch-dot { display: inline-block; width: 20px; height: 20px; border-radius: 50%; border: 1px solid rgba(0,0,0,0.12); vertical-align: middle; }
  .swatch-square { display: inline-block; width: 14px; height: 14px; border-radius: 3px; border: 1px solid rgba(0,0,0,0.12); vertical-align: middle; margin-right: 4px; }
  .paint-code { color: var(--muted); font-variant-numeric: tabular-nums; font-size: 11px; }

  .vendor-picker { margin-top: 6px; margin-bottom: 14px; }
  .vendor-picker h4 { margin: 0 0 8px; font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
  .vendor-picker .chips { display: flex; flex-wrap: wrap; gap: 6px; }
  .vendor-picker label {
    font-size: 12px; padding: 5px 10px; border: 1px solid var(--line); border-radius: 999px;
    cursor: pointer; background: #fff; user-select: none; display: inline-flex; gap: 6px; align-items: center;
  }
  .vendor-picker label:has(input:checked) { background: #1a1a1a; color: #fff; border-color: #1a1a1a; }
  .vendor-picker input { accent-color: #fff; }

  /* === Design Coordinate block === */
  .dc-section { margin-top: 32px; }
  .dc-section .dc-header {
    display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
    gap: 12px; margin-bottom: 14px;
  }
  .dc-section h2 {
    margin: 0; font: 500 16px/1.2 ui-serif, Georgia, serif;
  }
  .dc-section h2 .count { color: var(--muted); font-size: 13px; margin-left: 6px; }
  .dc-controls {
    display: flex; align-items: center; gap: 12px; font-size: 12px; color: var(--muted);
    text-transform: uppercase; letter-spacing: 0.06em;
  }
  .dc-controls input[type=range] { width: 180px; accent-color: #1a1a1a; }
  .dc-controls .colcount {
    font-variant-numeric: tabular-nums; color: var(--text);
    min-width: 1.5em; text-align: right; font-weight: 600;
  }

  .pairs-well__cta {
    display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    width: auto; max-width: 100%; padding: 10px 18px; margin: 0;
    background: #111; color: #fff; border: 0; border-radius: 999px;
    font-size: 12px; font-weight: 500; letter-spacing: 0.06em;
    text-transform: uppercase; cursor: pointer;
    transition: background 160ms ease, transform 120ms ease;
  }
  .pairs-well__cta:hover  { background: #2a2a2a; }
  .pairs-well__cta[aria-expanded="true"] .pairs-well__cta-icon { transform: rotate(45deg); }
  .pairs-well__cta-icon { display: inline-block; font-size: 22px; line-height: 1; transition: transform 200ms ease; }

  .pairs-well__grid {
    display: grid; gap: 14px; margin-top: 18px;
    grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
  }
  /* On narrow screens the slider value is clamped */
  @media (max-width: 1200px) { .pairs-well__grid { grid-template-columns: repeat(min(6, var(--cols)), minmax(0, 1fr)); } }
  @media (max-width: 900px)  { .pairs-well__grid { grid-template-columns: repeat(min(4, var(--cols)), minmax(0, 1fr)); } }
  @media (max-width: 600px)  { .pairs-well__grid { grid-template-columns: repeat(min(2, var(--cols)), minmax(0, 1fr)); } }

  .pairs-well__card { display: block; text-decoration: none; color: inherit;
    background: #fff; border: 1px solid #e7e7e7; border-radius: 6px;
    overflow: hidden; transition: box-shadow 160ms ease, transform 160ms ease; }
  .pairs-well__card:hover { box-shadow: 0 6px 20px rgba(0,0,0,0.08); transform: translateY(-2px); }
  .pairs-well__img-wrap { aspect-ratio: 1 / 1; background: #f4f4f4; overflow: hidden; }
  .pairs-well__img { width: 100%; height: 100%; object-fit: cover; display: block; }
  .pairs-well__meta { padding: 10px 12px 12px; }
  .pairs-well__title { margin: 0 0 4px; font-size: 13px; font-weight: 600; line-height: 1.3;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
  .pairs-well__vendor { margin: 0 0 6px; font-size: 10px; color: #777; letter-spacing: 0.04em; text-transform: uppercase; }
  .pairs-well__why { display: inline-block; padding: 2px 7px; background: #f4ede4;
    color: #5a4628; border-radius: 999px; font-size: 10px; line-height: 1.4; margin-bottom: 8px; }
  .pairs-well__palette { display: flex; gap: 3px; margin: 0 0 8px; flex-wrap: wrap; }
  .pairs-well__palette .swatch-dot { width: 12px; height: 12px; }
  .pairs-well__paints { border-top: 1px dashed #ebe4d8; margin-top: 4px; padding-top: 6px;
    font-size: 10px; line-height: 1.5; color: #444; }
  .pairs-well__paints .label { color: #888; font-weight: 600; margin-right: 4px; letter-spacing: 0.04em; }
  .pairs-well__paints .row { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

  /* AI-generated section variants */
  .ai-coords .pairs-well__card { border-style: dashed; border-color: #d9c7a8; }
  .ai-coords .pairs-well__why  { background: #efe6f5; color: #5b3478; }
  .ai-badge {
    display: inline-block; padding: 3px 8px; background: #2a2a2a; color: #fff;
    border-radius: 999px; font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase;
    margin-left: 8px;
  }

  .pairs-well__skeleton {
    background: linear-gradient(90deg, #eee 0%, #f6f6f6 50%, #eee 100%);
    background-size: 200% 100%; animation: pwShim 1.2s linear infinite;
    aspect-ratio: 1 / 1; border-radius: 6px;
  }
  @keyframes pwShim { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
  .pairs-well__empty { padding: 18px; color: #777; font-size: 14px; grid-column: 1 / -1; }
</style>
</head>
<body>
<header class="shop">
  <div class="logo">DESIGNER WALLCOVERINGS</div>
  <div class="preview-tag">Local preview · service :9813</div>
</header>

<main>
  <div class="source-picker">
    <label for="sku">SKU or handle</label>
    <input id="sku" type="text" value="DWRW-74991" autocomplete="off" placeholder="DWA-91351 or izara-beige-..." style="min-width:340px">
    <button id="load">Load</button>
    <div class="quick">
      <button data-sku="izara-beige-residential-wallpaper-jeffrey-stevens">Izara Beige (repro 2026-05-13)</button>
      <button data-sku="izara-light-green-residential-wallpaper-jeffrey-stevens">Izara Light Green</button>
      <button data-sku="DWRW-74991">DWRW-74991 (Rebel Walls mural)</button>
      <button data-sku="DWPT-200315">DWPT-200315 (1838 Floribunda Midnight)</button>
      <button data-sku="DWPT-200312">DWPT-200312 (1838 Lavender Dream)</button>
      <button data-sku="DWWC-508630">DWWC-508630 (Phillipe Romano Bongol)</button>
    </div>
  </div>

  <article class="product">
    <div class="photo"><img id="product-photo" alt="" src=""></div>
    <div class="meta">
      <h1 id="product-title">—</h1>
      <p class="vendor" id="product-vendor">—</p>
      <p class="price">From $4.25 / sample</p>
      <button class="add">Add Sample to Cart</button>

      <div class="vendor-picker" id="vendor-picker">
        <h4>Paint brands</h4>
        <div class="chips">
          <label><input type="checkbox" value="sw"   checked> Sherwin-Williams</label>
          <label><input type="checkbox" value="de"   checked> Dunn-Edwards</label>
          <label><input type="checkbox" value="bm"   checked> Benjamin Moore</label>
          <label><input type="checkbox" value="fb"   checked> Farrow &amp; Ball</label>
          <label><input type="checkbox" value="behr"        > Behr</label>
          <label><input type="checkbox" value="ppg"         > PPG</label>
          <label><input type="checkbox" value="ral"         > RAL</label>
        </div>
      </div>

      <div class="src-palette" id="src-palette" hidden>
        <h4>Color story</h4>
        <table>
          <thead><tr id="src-palette-head"></tr></thead>
          <tbody id="src-palette-body"></tbody>
        </table>
      </div>
    </div>
  </article>

  <!-- CATALOG COORDINATES -->
  <section id="pairs-well-with" class="dc-section"
           data-pairs-endpoint="/api/pairs"
           data-product-sku="DWRW-74991">
    <button type="button" class="pairs-well__cta" aria-expanded="false">
      <span class="pairs-well__cta-text">Design Coordinate</span>
      <span class="pairs-well__cta-icon">+</span>
    </button>
    <div class="dc-header" id="dc-header-catalog" hidden>
      <h2>Stripes &amp; Plaids from our catalog <span class="count" id="catalog-count"></span></h2>
      <div class="dc-controls">
        <label for="catalog-cols">Columns</label>
        <input id="catalog-cols" type="range" min="3" max="12" value="6" step="1">
        <span class="colcount" id="catalog-cols-val">6</span>
      </div>
    </div>
    <div id="catalog-grid" class="pairs-well__grid" hidden></div>
  </section>

  <!-- AI-GENERATED COORDINATES -->
  <section id="ai-coords-section" class="dc-section ai-coords" hidden>
    <div class="dc-header">
      <h2>Auto Generated <span class="ai-badge">Preview · 6 options</span></h2>
      <div class="dc-controls">
        <label for="ai-cols">Columns</label>
        <input id="ai-cols" type="range" min="3" max="12" value="6" step="1">
        <span class="colcount" id="ai-cols-val">6</span>
      </div>
    </div>
    <div id="ai-grid" class="pairs-well__grid"></div>
  </section>
</main>

<script>
  function esc(s) {
    return String(s == null ? '' : s)
      .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;').replace(/'/g, '&#39;');
  }

  // Relative paths — work whether served from localhost:9813 or pairs.designerwallcoverings.com
  var endpointPairs = '/api/pairs';
  var endpointAi    = '/api/ai-coordinates';

  var picker         = document.getElementById('sku');
  var loadBtn        = document.getElementById('load');
  var photoEl        = document.getElementById('product-photo');
  var titleEl        = document.getElementById('product-title');
  var vendorEl       = document.getElementById('product-vendor');
  var quickBtns      = document.querySelectorAll('.quick button');
  var section        = document.getElementById('pairs-well-with');
  var cta            = section.querySelector('.pairs-well__cta');
  var catalogHeader  = document.getElementById('dc-header-catalog');
  var catalogGrid    = document.getElementById('catalog-grid');
  var catalogCols    = document.getElementById('catalog-cols');
  var catalogColsVal = document.getElementById('catalog-cols-val');
  var catalogCount   = document.getElementById('catalog-count');
  var aiSection      = document.getElementById('ai-coords-section');
  var aiGrid         = document.getElementById('ai-grid');
  var aiCols         = document.getElementById('ai-cols');
  var aiColsVal      = document.getElementById('ai-cols-val');
  var srcPalette     = document.getElementById('src-palette');
  var srcPaletteBody = document.getElementById('src-palette-body');

  // ---- density sliders ----
  function wireSlider(input, valEl, gridEl) {
    function apply() {
      var n = Math.max(3, Math.min(12, parseInt(input.value, 10) || 8));
      valEl.textContent = n;
      gridEl.style.setProperty('--cols', n);
      try { localStorage.setItem('dc.cols.' + input.id, n); } catch (_) {}
    }
    try {
      var stored = parseInt(localStorage.getItem('dc.cols.' + input.id), 10);
      if (!Number.isNaN(stored) && stored >= 3 && stored <= 12) input.value = stored;
    } catch (_) {}
    input.addEventListener('input', apply);
    apply();
  }
  wireSlider(catalogCols, catalogColsVal, catalogGrid);
  wireSlider(aiCols, aiColsVal, aiGrid);

  // ---- source palette render ----
  // ===== paint vendor selection =====
  var VENDOR_LABEL = { sw:'Sherwin-Williams', de:'Dunn-Edwards', bm:'Benjamin Moore', fb:'Farrow & Ball', behr:'Behr', ppg:'PPG', ral:'RAL' };
  var VENDOR_DEFAULT = ['sw','de','bm','fb'];
  var vendorChecks = document.querySelectorAll('#vendor-picker input[type=checkbox]');
  function selectedVendors() {
    var out = [];
    vendorChecks.forEach(function (cb) { if (cb.checked) out.push(cb.value); });
    return out.length ? out : VENDOR_DEFAULT.slice();
  }
  try {
    var stored = JSON.parse(localStorage.getItem('dc.vendors') || 'null');
    if (Array.isArray(stored) && stored.length) {
      vendorChecks.forEach(function (cb) { cb.checked = stored.indexOf(cb.value) !== -1; });
    }
  } catch (_) {}
  function persistVendors() {
    try { localStorage.setItem('dc.vendors', JSON.stringify(selectedVendors())); } catch (_) {}
  }
  vendorChecks.forEach(function (cb) {
    cb.addEventListener('change', function () {
      persistVendors();
      // Re-render with the new vendor set.
      if (section._catalogPairs && !document.getElementById('catalog-grid').hidden) {
        renderGrid(catalogGrid, section._catalogPairs);
      }
      if (section._sourcePalette) renderSourcePalette(section._sourcePalette);
      // AI grid doesn't carry paint matches yet (placeholder posters)
    });
  });

  function renderSourcePalette(palette) {
    if (!palette || !palette.length) { srcPalette.hidden = true; return; }
    section._sourcePalette = palette;
    var vendors = selectedVendors();
    document.getElementById('src-palette-head').innerHTML =
      '<th></th>' + vendors.map(function (v) { return '<th>' + esc(VENDOR_LABEL[v]) + '</th>'; }).join('');
    srcPaletteBody.innerHTML = palette.map(function (c) {
      var cells = '<td class="swatch"><span class="swatch-dot" style="background:' + esc(c.hex) + '"></span></td>';
      vendors.forEach(function (v) {
        var p = c.paints && c.paints[v];
        cells += '<td>' + (p
          ? '<span class="swatch-square" style="background:' + esc(p.hex) + '"></span>' + esc(p.name) + ' <span class="paint-code">' + esc(p.code) + '</span>'
          : '—') + '</td>';
      });
      return '<tr>' + cells + '</tr>';
    }).join('');
    srcPalette.hidden = false;
  }

  function renderCard(p, opts) {
    opts = opts || {};
    var img = p.image_url ? esc(p.image_url) : '';
    var why = (p.why && p.why[0]) ? esc(p.why[0]) : (p.motif ? esc(p.motif) : '');
    var palette = (p.palette || []).map(function (c) {
      var hex = typeof c === 'string' ? c : c.hex;
      var sw = c && c.paints && c.paints.sw, de = c && c.paints && c.paints.de;
      var tip = (sw ? 'SW ' + sw.name + ' ' + sw.code : '') + (de ? ' · DE ' + de.name + ' ' + de.code : '');
      return '<span class="swatch-dot" style="background:' + esc(hex) + '" title="' + esc(tip) + '"></span>';
    }).join('');
    var dom = (p.palette && p.palette[0]) || null;
    var paintsBlock = '';
    if (dom && dom.paints) {
      var vendors = selectedVendors();
      var rows = vendors.map(function (v) {
        var paint = dom.paints[v];
        if (!paint) return '';
        var lbl = ({ sw:'SW', de:'DE', bm:'BM', fb:'F&B', behr:'Behr', ppg:'PPG', ral:'RAL' })[v] || v.toUpperCase();
        return '<span class="row"><span class="label">' + lbl + '</span>'
          + '<span class="swatch-square" style="background:' + esc(paint.hex) + '"></span>'
          + esc(paint.name) + ' <span class="paint-code">' + esc(paint.code) + '</span></span>';
      }).filter(Boolean).join('');
      if (rows) paintsBlock = '<div class="pairs-well__paints">' + rows + '</div>';
    }
    var href = p.url ? esc(p.url) : (p.handle ? '#' + esc(p.handle) : '#');
    return '<a class="pairs-well__card" href="' + href + '" title="' + esc((p.why || []).join(' · ')) + '">'
      +   '<div class="pairs-well__img-wrap">' + (img ? '<img class="pairs-well__img" src="' + img + '" alt="' + esc(p.title) + '" loading="lazy">' : '') + '</div>'
      +   '<div class="pairs-well__meta">'
      +     '<p class="pairs-well__title">' + esc(p.title || '') + '</p>'
      +     '<p class="pairs-well__vendor">' + esc(p.vendor || (opts.aiBadge || '')) + '</p>'
      +     (why ? '<span class="pairs-well__why">' + why + '</span>' : '')
      +     (palette ? '<div class="pairs-well__palette">' + palette + '</div>' : '')
      +     paintsBlock
      +   '</div>'
      + '</a>';
  }

  function renderGrid(grid, items, opts) {
    if (!items || !items.length) {
      grid.innerHTML = '<div class="pairs-well__empty">No coordinates found yet for this pattern.</div>';
      return;
    }
    grid.innerHTML = items.map(function (p) { return renderCard(p, opts); }).join('');
  }

  function showSkeletons(grid, n) {
    var html = '';
    for (var i = 0; i < n; i++) html += '<div class="pairs-well__skeleton"></div>';
    grid.innerHTML = html;
  }

  // ---- load source product + catalog pairs (deferred until CTA click) + AI ----
  // `key` can be a dw_sku (e.g. DWA-91351) OR a Shopify handle (e.g. izara-beige-...).
  // We auto-detect: if it contains a hyphen + non-uppercase letters, treat as handle.
  function buildQuery(key) {
    var isHandle = /[a-z]/.test(key) && key.indexOf('-') !== -1 && !/^DW[A-Z]?-/.test(key);
    return (isHandle ? 'handle=' : 'dw_sku=') + encodeURIComponent(key);
  }

  async function loadSource(key) {
    picker.value = key;
    section.setAttribute('data-product-sku', key);
    cta.setAttribute('aria-expanded', 'false');
    catalogHeader.hidden = true;
    catalogGrid.hidden = true;
    catalogGrid.innerHTML = '';
    srcPalette.hidden = true;
    aiSection.hidden = true;
    aiGrid.innerHTML = '';

    var qs = buildQuery(key);
    try {
      var r = await fetch(endpointPairs + '?' + qs + '&limit=6');
      if (!r.ok) throw new Error('http ' + r.status);
      var d = await r.json();
      if (d && d.ok === false) throw new Error(d.error || 'bad payload');
      if (d && d.source) {
        photoEl.src = d.source.image_url || '';
        photoEl.alt = d.source.title || '';
        titleEl.textContent = d.source.title || '—';
        vendorEl.textContent = d.source.vendor || '—';
        renderSourcePalette(d.source.palette || []);
        section._catalogPairs = d.pairs || [];
        catalogCount.textContent = '(' + (d.pairs || []).length + ')';
      } else {
        titleEl.textContent = 'Not found: ' + key;
        vendorEl.textContent = '';
        photoEl.removeAttribute('src');
      }
    } catch (e) {
      console.error('[preview] catalog fetch failed:', e, 'url=', endpointPairs + '?' + qs);
      titleEl.textContent = 'Catalog fetch failed: ' + (e && e.message ? e.message : 'unknown') + ' (key: ' + key + ')';
      vendorEl.textContent = '';
    }

    // Kick off AI generation in parallel — show its section as soon as it loads
    showSkeletons(aiGrid, 6);
    aiSection.hidden = false;
    try {
      var ar = await fetch(endpointAi + '?' + qs + '&limit=6');
      if (!ar.ok) throw new Error('http ' + ar.status);
      var ad = await ar.json();
      if (ad && ad.ok === false) throw new Error(ad.error || 'bad payload');
      if (ad && ad.items) renderGrid(aiGrid, ad.items, { aiBadge: 'Auto Generated' });
      else aiGrid.innerHTML = '<div class="pairs-well__empty">Auto Generated coordinates not ready yet.</div>';
    } catch (e) {
      console.error('[preview] ai fetch failed:', e, 'url=', endpointAi + '?' + qs);
      aiGrid.innerHTML = '<div class="pairs-well__empty">AI fetch failed: ' + esc(e && e.message ? e.message : 'unknown') + ' (key: ' + esc(key) + ')</div>';
    }
  }

  loadBtn.addEventListener('click', function () { loadSource(picker.value.trim()); });
  picker.addEventListener('keydown', function (e) { if (e.key === 'Enter') loadSource(picker.value.trim()); });
  quickBtns.forEach(function (b) { b.addEventListener('click', function () { loadSource(b.getAttribute('data-sku')); }); });

  // Deep-link: ?handle=... or ?sku=... or ?key=... auto-loads on page open.
  // Sets `window.__deepLinkLoaded` so the default loadSource init at the bottom
  // doesn't race + overwrite this load (caught 2026-05-13 — deep-link Izara
  // fetched correctly but was clobbered by the trailing DWRW-74991 default).
  (function () {
    var params = new URLSearchParams(window.location.search);
    var k = params.get('handle') || params.get('sku') || params.get('key');
    if (k) { window.__deepLinkLoaded = true; loadSource(k.trim()); }
  })();

  cta.addEventListener('click', function () {
    var open = cta.getAttribute('aria-expanded') === 'true';
    if (open) {
      cta.setAttribute('aria-expanded', 'false');
      catalogHeader.hidden = true;
      catalogGrid.hidden = true;
      return;
    }
    cta.setAttribute('aria-expanded', 'true');
    catalogHeader.hidden = false;
    catalogGrid.hidden = false;
    if (section._catalogPairs) renderGrid(catalogGrid, section._catalogPairs);
  });

  if (!window.__deepLinkLoaded) loadSource('DWRW-74991');
</script>
</body>
</html>