{
  "version": 3,
  "sources": ["/cdn/shop/t/1064/assets/names-and-numbers.js?auto=format%2Ccompress"],
  "sourcesContent": ["/* Names & Numbers configurator \u2014 v2\n *\n * State management, jersey mockup, 22\" gang-sheet packer, sq-in variant matcher,\n * presigned-S3 PNG upload at add-to-cart, and line-item-property handoff matching\n * the existing DTF property contract.\n */\n(function () {\n  'use strict';\n\n  const EXPORT_DPI = 300;\n\n  const PLACEHOLDER_NAME   = 'NAME';\n  const PLACEHOLDER_NUMBER = '00';\n\n  function bootAll() { document.querySelectorAll('.nn-page').forEach(boot); }\n\n  function boot(root) {\n    if (root.__nnInitialized) return;\n    root.__nnInitialized = true;\n\n    const fonts         = readJSON(root, '[data-nn-fonts]');\n    const colors        = readJSON(root, '[data-nn-colors]');\n    const nameHeights   = readJSON(root, '[data-nn-name-heights]');   // [{ id, label, inches }]\n    const numberHeights = readJSON(root, '[data-nn-number-heights]'); // [{ id, label, inches }]\n    const variants      = readJSON(root, '[data-nn-variants]') || [];\n\n    const cfg = {\n      // Sheet packing concept removed. Each entry stands alone; production\n      // rendering uses renderEntryPNG which emits one PNG per entry. vertGapIn\n      // is kept as the vertical-stack increment in packShelf for any consumers\n      // that walk packed.placements[].y.\n      vertGapIn:       numAttr(root, 'data-vert-gap-in', 0),\n      // Pair stacking gap (name above number) \u2014 preserved as a visual gap\n      // inside a single Names + Numbers entry so the printed jersey reads\n      // with proper white space between player name and player number.\n      nameNumGapIn:    numAttr(root, 'data-name-num-gap-in', 1.0),\n      maxEntries:      numAttr(root, 'data-max-entries', 200),\n      maxItemWidthIn:  numAttr(root, 'data-max-item-width-in', 12),\n      apiUrl:          root.getAttribute('data-api-url'),\n      imgixHost:       root.getAttribute('data-imgix-host'),\n    };\n\n    const state = {\n      scope: null,\n      fontId:   fonts[0]   ? fonts[0].id   : null,\n      colorId:  colors[0]  ? colors[0].id  : null,\n      customColorHex: '#ff00ff',\n      shirtColorHex: '#d8d8d8',\n      nameHeightId:   findClosestHeightId(nameHeights,   numAttr(root, 'data-default-name-height-in', 2)),\n      numberHeightId: findClosestHeightId(numberHeights, numAttr(root, 'data-default-number-height-in', 8)),\n      entries: [],\n      focusedEntryId: null,\n      activeTab: 'grid',\n      editLineKey: null, // populated when editing an existing cart line\n      cartTransferQty: 0, // cumulative DTF/UV/N&N/Puff qty already in cart (drives the volume-discount tier)\n    };\n\n    const dom = {\n      root,\n      scope:           root.querySelector('[data-nn-control=\"scope\"]'),\n      fontGrid:        root.querySelector('[data-nn-control=\"font\"]'),\n      swatchGrid:      root.querySelector('[data-nn-control=\"color\"]'),\n      heightName:      root.querySelector('[data-nn-control=\"height-name\"]'),\n      heightNumber:    root.querySelector('[data-nn-control=\"height-number\"]'),\n      heightNameGroup: root.querySelector('[data-nn-height-for=\"name\"]'),\n      heightNumGroup:  root.querySelector('[data-nn-height-for=\"number\"]'),\n      currentFont:     root.querySelector('[data-nn-current-font]'),\n      currentColor:    root.querySelector('[data-nn-current-color]'),\n      entries:         root.querySelector('[data-nn-entries]'),\n      entriesCount:    root.querySelector('[data-nn-entries-count]'),\n      addEntryBtn:     root.querySelector('[data-nn-add-entry]'),\n      tabs:            root.querySelector('[data-nn-tabs]'),\n      paneGrid:        root.querySelector('[data-pane=\"grid\"]'),\n      panePaste:       root.querySelector('[data-pane=\"paste\"]'),\n      paneCsv:         root.querySelector('[data-pane=\"csv\"]'),\n      bulkTextarea:    root.querySelector('[data-nn-bulk-text]'),\n      pastePreview:    root.querySelector('[data-nn-paste-preview]'),\n      pastePreviewRows:root.querySelector('[data-nn-paste-preview-rows]'),\n      pasteCount:      root.querySelector('[data-nn-paste-count]'),\n      pasteApplyBtn:   root.querySelector('[data-nn-paste-apply]'),\n      pasteApplyLabel: root.querySelector('[data-nn-paste-apply-label]'),\n      csvDrop:         root.querySelector('[data-nn-csv-drop]'),\n      csvInput:        root.querySelector('[data-nn-csv-input]'),\n      csvTemplate:     root.querySelector('[data-nn-csv-template]'),\n      csvResult:       root.querySelector('[data-nn-csv-result]'),\n      // Jersey preview lives in the LEFT column of the PDP (rendered by snippets/nn-preview-left.liquid),\n      // which is a different DOM subtree than the configurator (.nn-page). Query at document level.\n      chip:            document.querySelector('[data-nn-chip]'),\n      jerseyName:      document.querySelector('[data-nn-jersey-name]'),\n      jerseyNumber:    document.querySelector('[data-nn-jersey-number]'),\n      jerseyShirt:     document.querySelector('[data-nn-shirt]'),\n      dims:            root.querySelector('[data-nn-dimensions]'),\n      sqin:            root.querySelector('[data-nn-sqin]'),\n      price:           root.querySelector('[data-nn-price]'),\n      discount:        root.querySelector('[data-nn-discount]'),\n      ctaPrice:        root.querySelector('[data-nn-cta-price]'),\n      warning:         root.querySelector('[data-nn-warning]'),\n      submitBtn:       root.querySelector('[data-nn-submit]'),\n      submitLabel:     root.querySelector('[data-nn-submit-label]'),\n      variantInput:    root.querySelector('[data-nn-variant-id]'),\n      // The product form is rendered by bySizeNew's buy_buttons block \u2014 outside .nn-page.\n      // Query globally; tolerate it being missing while bySizeNew is mid-render.\n      form:            document.querySelector('form[data-type=\"add-to-cart-form\"]'),\n      props: {\n        scope:        root.querySelector('[data-nn-prop=\"scope\"]'),\n        font:         root.querySelector('[data-nn-prop=\"font\"]'),\n        color:        root.querySelector('[data-nn-prop=\"color\"]'),\n        nameHeight:   root.querySelector('[data-nn-prop=\"name-height\"]'),\n        numberHeight: root.querySelector('[data-nn-prop=\"number-height\"]'),\n        entriesCount: root.querySelector('[data-nn-prop=\"entries-count\"]'),\n        entries:      root.querySelector('[data-nn-prop=\"entries\"]'),\n        // width / height parent-form fields removed when the sheet concept\n        // was dropped. Per-line cart items still carry per-entry _width and\n        // _height via the cart-add payload (see line ~739 below).\n        uploadImgix:  root.querySelector('[data-nn-prop=\"upload-imgix\"]'),\n        uploadS3:     root.querySelector('[data-nn-prop=\"upload-s3\"]'),\n      },\n    };\n\n    // Hydrate state from URL (?nn=<base64>&edit_line=<key>) if the customer\n    // came back from the cart to edit an existing line item.\n    hydrateFromUrl();\n\n    renderFontGrid();\n    renderSwatches();\n    renderHeightPills(dom.heightName,   nameHeights,   state.nameHeightId);\n    renderHeightPills(dom.heightNumber, numberHeights, state.numberHeightId);\n    if (state.entries.length === 0) addEntry();\n    else renderEntries();\n\n    // Defensive ordering: wire the gallery-swap listener FIRST so any later wire\n    // crash doesn't prevent the carousel\u2192jersey swap from working.\n    wireFirstInteraction();\n    wireShirtColors();\n    wireShirtView();\n    wireScope();\n    wireFonts();\n    wireColors();\n    wireHeights();\n    wireEntries();\n    wireRosterTools();\n    wireTabs();\n    wireBulk();\n    wireCsv();\n    wireForm();\n\n    Promise.all(fonts.map(loadFont)).then(render);\n    render();\n\n    // Fetch cumulative cart qty of DTF-tier-eligible items so the volume\n    // discount tier highlights consider what's already in cart, not just\n    // the current roster. Matches bySize's already-in-cart behavior.\n    fetchCartTransferQty().then(qty => {\n      state.cartTransferQty = qty;\n      render();\n    });\n\n    // Edit-resume: when arriving via the cart's Edit Entries link\n    // (URL has ?nn=\u2026&edit_line=\u2026), bypass the \"first interaction\"\n    // gate that hides the live jersey behind the product gallery, jump\n    // the user to the roster (Step 5), and focus the row they came to\n    // edit. The customer has already chosen scope/font/color upstream\n    // \u2014 what they want here is the entries panel, ready to type into.\n    if (state.editLineKey) {\n      const previewLeft = document.querySelector('.nn-preview-left');\n      if (previewLeft) previewLeft.setAttribute('data-nn-left-mode', 'configuring');\n\n      // Defer one frame so renderEntries() has painted the rows and\n      // the layout/sticky positions have settled before we scroll.\n      requestAnimationFrame(() => {\n        const roster = root.querySelector('.nn-step--roster');\n        if (roster) roster.scrollIntoView({ behavior: 'smooth', block: 'start' });\n\n        const focusedRow = state.focusedEntryId\n          ? dom.entries.querySelector(`.nn-roster__row[data-id=\"${state.focusedEntryId}\"]`)\n          : dom.entries.querySelector('.nn-roster__row');\n        if (focusedRow) {\n          /* Prefer the name input on \"both\" / \"names\" scopes, fall back\n             to number on \"numbers only\". */\n          const target = state.scope === 'numbers'\n            ? focusedRow.querySelector('[data-nn-field=\"number\"]')\n            : focusedRow.querySelector('[data-nn-field=\"name\"]');\n          if (target) {\n            target.focus({ preventScroll: true });\n            try { target.setSelectionRange(target.value.length, target.value.length); } catch (e) {}\n          }\n        }\n      });\n    }\n\n    // ---- renderers ----\n\n    function renderFontGrid() {\n      dom.fontGrid.innerHTML = '';\n      fonts.forEach(f => {\n        const tile = el('div', 'nn-font-tile', { 'data-id': f.id, role: 'button', tabindex: '0', 'aria-label': `Choose font ${f.label}` });\n        const sample = el('div', 'nn-font-tile__sample');\n        sample.style.fontFamily = `\"${f.family}\", sans-serif`;\n        sample.style.fontWeight = String(f.weight || 700);\n        sample.innerHTML = `${escapeHTML(f.sample_letter || 'A')}<span class=\"nn-font-tile__sample-num\">${escapeHTML(String(f.sample_number ?? 7))}</span>`;\n        const label = el('span', 'nn-font-tile__label');\n        label.textContent = f.label;\n        tile.appendChild(sample);\n        tile.appendChild(label);\n        if (f.id === state.fontId) tile.classList.add('is-selected');\n        dom.fontGrid.appendChild(tile);\n      });\n    }\n\n    function renderSwatches() {\n      dom.swatchGrid.innerHTML = '';\n      colors.forEach(c => {\n        const sw = el('div', 'nn-swatch', { 'data-id': c.id, role: 'button', tabindex: '0', 'aria-label': `Choose color ${c.label}`, title: c.label });\n        sw.style.background = c.hex;\n        if (isLightHex(c.hex)) sw.setAttribute('data-light', 'true');\n        if (c.id === state.colorId) sw.classList.add('is-selected');\n        dom.swatchGrid.appendChild(sw);\n      });\n\n      // Custom color tile: clicking the <label> opens the native color picker\n      // attached to the hidden <input type=\"color\">.\n      const customTile = document.createElement('label');\n      customTile.className = 'nn-swatch nn-swatch--custom';\n      customTile.setAttribute('data-id', 'custom');\n      customTile.setAttribute('title', 'Custom color');\n      customTile.setAttribute('aria-label', 'Choose a custom color');\n      customTile.innerHTML = `\n        <span class=\"nn-swatch__plus\" aria-hidden=\"true\">+</span>\n        <input type=\"color\" data-nn-custom-color value=\"${state.customColorHex}\">\n      `;\n      if (state.colorId === 'custom') {\n        customTile.classList.add('is-selected');\n        customTile.style.background = state.customColorHex;\n      }\n      dom.swatchGrid.appendChild(customTile);\n    }\n\n    function renderHeightPills(container, heightList, selectedId) {\n      container.innerHTML = '';\n      heightList.forEach(h => {\n        const pill = el('div', 'nn-pill', { 'data-id': h.id, role: 'button', tabindex: '0', 'aria-label': `${h.inches} inch \u2014 ${h.label}` });\n        const inches = el('span', 'nn-pill__inches');\n        inches.textContent = `${h.inches}\"`;\n        const label = el('span', 'nn-pill__label');\n        label.textContent = h.label;\n        pill.appendChild(inches);\n        pill.appendChild(label);\n        if (h.id === selectedId) pill.classList.add('is-selected');\n        container.appendChild(pill);\n      });\n    }\n\n    function renderEntries() {\n      dom.entries.innerHTML = '';\n      state.entries.forEach((entry, idx) => {\n        const isFocused = entry.id === state.focusedEntryId;\n        const tr = el('tr', isFocused ? 'nn-roster__row is-focused' : 'nn-roster__row', { 'data-id': entry.id });\n        tr.innerHTML = `\n          <td><input type=\"text\" placeholder=\"${escapeHTML(PLACEHOLDER_NAME)}\" maxlength=\"40\" data-nn-field=\"name\"></td>\n          <td><input type=\"text\" placeholder=\"${escapeHTML(PLACEHOLDER_NUMBER)}\" maxlength=\"3\" data-nn-field=\"number\"></td>\n          <td>\n            <div class=\"nn-stepper\">\n              <button type=\"button\" class=\"nn-stepper__btn\" data-nn-qty-step=\"-1\" aria-label=\"Decrease qty\">\u2212</button>\n              <input type=\"number\" min=\"1\" max=\"999\" step=\"1\" value=\"${entry.qty || 1}\" data-nn-field=\"qty\" inputmode=\"numeric\">\n              <button type=\"button\" class=\"nn-stepper__btn\" data-nn-qty-step=\"1\" aria-label=\"Increase qty\">+</button>\n            </div>\n          </td>\n          <td><button type=\"button\" class=\"nn-roster__remove\" data-nn-remove aria-label=\"Remove row\">&times;</button></td>\n        `;\n        tr.querySelector('[data-nn-field=\"name\"]').value   = entry.name || '';\n        tr.querySelector('[data-nn-field=\"number\"]').value = entry.number || '';\n        tr.querySelector('[data-nn-field=\"qty\"]').value    = entry.qty || 1;\n        dom.entries.appendChild(tr);\n      });\n      applyScopeToRoster();\n    }\n\n    function applyScopeToRoster() {\n      const showName   = state.scope !== 'numbers';\n      const showNumber = state.scope !== 'names';\n      dom.entries.querySelectorAll('[data-nn-field=\"name\"]').forEach(i => i.style.display = showName   ? '' : 'none');\n      dom.entries.querySelectorAll('[data-nn-field=\"number\"]').forEach(i => i.style.display = showNumber ? '' : 'none');\n    }\n\n    // ---- wiring ----\n\n    function wireScope() {\n      dom.scope.addEventListener('click', e => {\n        const btn = e.target.closest('[data-value]');\n        if (!btn || !dom.scope.contains(btn)) return;\n        state.scope = btn.getAttribute('data-value');\n        dom.scope.querySelectorAll('[data-value]').forEach(b => {\n          const sel = b === btn;\n          b.classList.toggle('is-selected', sel);\n          b.setAttribute('aria-checked', sel ? 'true' : 'false');\n        });\n        /* Reveal the rest of the configurator (Step 2 onwards) the\n           first time the customer picks a scope. CSS keys on the\n           data-nn-scope-set attribute on .nn-page to gate visibility. */\n        root.setAttribute('data-nn-scope-set', '1');\n        applyScopeToRoster();\n        render();\n      });\n    }\n\n    function wireFonts() {\n      dom.fontGrid.addEventListener('click', e => {\n        const tile = e.target.closest('.nn-font-tile');\n        if (!tile) return;\n        state.fontId = tile.getAttribute('data-id');\n        dom.fontGrid.querySelectorAll('.nn-font-tile').forEach(t => t.classList.toggle('is-selected', t === tile));\n        render();\n      });\n    }\n\n    function wireColors() {\n      dom.swatchGrid.addEventListener('click', e => {\n        // The custom tile is a <label> wrapping a hidden color input \u2014 let the browser\n        // open the native picker. We update state on the input's change event below.\n        if (e.target.closest('input[type=\"color\"]')) return;\n        const sw = e.target.closest('.nn-swatch');\n        if (!sw) return;\n        if (sw.classList.contains('nn-swatch--custom')) {\n          state.colorId = 'custom';\n          dom.swatchGrid.querySelectorAll('.nn-swatch').forEach(s => s.classList.toggle('is-selected', s === sw));\n          render();\n          return;\n        }\n        state.colorId = sw.getAttribute('data-id');\n        dom.swatchGrid.querySelectorAll('.nn-swatch').forEach(s => s.classList.toggle('is-selected', s === sw));\n        render();\n      });\n\n      dom.swatchGrid.addEventListener('input', e => {\n        if (!e.target.matches('[data-nn-custom-color]')) return;\n        state.customColorHex = e.target.value;\n        state.colorId = 'custom';\n        const customTile = e.target.closest('.nn-swatch--custom');\n        if (customTile) {\n          customTile.style.background = state.customColorHex;\n          dom.swatchGrid.querySelectorAll('.nn-swatch').forEach(s => s.classList.toggle('is-selected', s === customTile));\n        }\n        render();\n      });\n    }\n\n    function wireHeights() {\n      dom.heightName.addEventListener('click', e => {\n        const pill = e.target.closest('.nn-pill');\n        if (!pill) return;\n        state.nameHeightId = pill.getAttribute('data-id');\n        dom.heightName.querySelectorAll('.nn-pill').forEach(p => p.classList.toggle('is-selected', p === pill));\n        render();\n      });\n      dom.heightNumber.addEventListener('click', e => {\n        const pill = e.target.closest('.nn-pill');\n        if (!pill) return;\n        state.numberHeightId = pill.getAttribute('data-id');\n        dom.heightNumber.querySelectorAll('.nn-pill').forEach(p => p.classList.toggle('is-selected', p === pill));\n        render();\n      });\n    }\n\n    function wireEntries() {\n      dom.addEntryBtn.addEventListener('click', () => {\n        if (state.entries.length >= cfg.maxEntries) return;\n        addEntry();\n        renderEntries();\n        render();\n      });\n\n      // Any focus or click on a row moves the jersey preview to that row.\n      const focusRow = id => {\n        if (state.focusedEntryId === id) return;\n        state.focusedEntryId = id;\n        dom.entries.querySelectorAll('.nn-roster__row').forEach(r => {\n          r.classList.toggle('is-focused', r.getAttribute('data-id') === id);\n        });\n        render();\n      };\n      dom.entries.addEventListener('focusin', e => {\n        const row = e.target.closest('.nn-roster__row');\n        if (row) focusRow(row.getAttribute('data-id'));\n      });\n\n      dom.entries.addEventListener('input', e => {\n        const inp = e.target.closest('input[data-nn-field]');\n        if (!inp) return;\n        const row = inp.closest('.nn-roster__row');\n        const id = row.getAttribute('data-id');\n        const entry = state.entries.find(x => x.id === id);\n        if (!entry) return;\n        focusRow(id);\n        const field = inp.getAttribute('data-nn-field');\n        if (field === 'number' && inp.value.length > 3) inp.value = inp.value.slice(0, 3);\n        entry[field] = field === 'qty' ? Math.max(1, parseInt(inp.value, 10) || 1) : inp.value;\n        render();\n      });\n      dom.entries.addEventListener('click', e => {\n        const row = e.target.closest('.nn-roster__row');\n        if (row) focusRow(row.getAttribute('data-id'));\n\n        const stepBtn = e.target.closest('[data-nn-qty-step]');\n        if (stepBtn) {\n          const id = stepBtn.closest('.nn-roster__row').getAttribute('data-id');\n          const entry = state.entries.find(x => x.id === id);\n          if (!entry) return;\n          const delta = parseInt(stepBtn.getAttribute('data-nn-qty-step'), 10);\n          entry.qty = Math.max(1, Math.min(999, (parseInt(entry.qty, 10) || 1) + delta));\n          stepBtn.closest('.nn-roster__row').querySelector('[data-nn-field=\"qty\"]').value = entry.qty;\n          render();\n          return;\n        }\n        const removeBtn = e.target.closest('[data-nn-remove]');\n        if (removeBtn) {\n          const id = removeBtn.closest('.nn-roster__row').getAttribute('data-id');\n          state.entries = state.entries.filter(x => x.id !== id);\n          if (state.entries.length === 0) addEntry();\n          // If we just removed the focused entry, focus the first remaining.\n          if (state.focusedEntryId === id) state.focusedEntryId = state.entries[0]?.id || null;\n          renderEntries();\n          render();\n        }\n      });\n    }\n\n    function wireRosterTools() { /* no-op for now */ }\n\n    function wireShirtColors() {\n      const grid = document.querySelector('[data-nn-shirt-colors]');\n      const bg   = document.querySelector('[data-nn-shirt-bg]');\n      if (!grid || !bg) return;\n      const apply = hex => {\n        state.shirtColorHex = hex;\n        bg.style.backgroundColor = hex;\n        grid.querySelectorAll('.nn-shirt-swatch').forEach(s => {\n          s.classList.toggle('is-selected', s.getAttribute('data-shirt-hex') === hex);\n        });\n      };\n      grid.addEventListener('click', e => {\n        if (e.target.closest('input[type=\"color\"]')) return;\n        const sw = e.target.closest('.nn-shirt-swatch');\n        if (!sw) return;\n        const hex = sw.getAttribute('data-shirt-hex');\n        if (hex) apply(hex);\n      });\n      const customInput = grid.querySelector('[data-nn-shirt-custom]');\n      if (customInput) {\n        customInput.addEventListener('input', e => {\n          apply(e.target.value);\n          const tile = e.target.closest('.nn-shirt-swatch--custom');\n          if (tile) tile.style.background = e.target.value;\n        });\n      }\n    }\n\n    function wireShirtView() {\n      const toggle = document.querySelector('[data-nn-shirt-view]');\n      const img    = document.querySelector('[data-nn-shirt-img]');\n      if (!toggle || !img) return;\n      toggle.addEventListener('click', e => {\n        const btn = e.target.closest('.nn-shirt-view__btn');\n        if (!btn) return;\n        const view = btn.getAttribute('data-shirt-view');\n        const src  = img.getAttribute(view === 'front' ? 'data-front-src' : 'data-back-src');\n        if (src) img.src = src;\n        toggle.querySelectorAll('.nn-shirt-view__btn').forEach(b => {\n          b.classList.toggle('is-selected', b === btn);\n        });\n      });\n    }\n\n    function wireFirstInteraction() {\n      // On the customer's first click or keystroke anywhere in the configurator,\n      // swap the left-column gallery out for the live jersey mockup.\n      const previewLeft = document.querySelector('.nn-preview-left');\n      if (!previewLeft) return;\n      const swap = () => {\n        if (previewLeft.getAttribute('data-nn-left-mode') !== 'configuring') {\n          previewLeft.setAttribute('data-nn-left-mode', 'configuring');\n        }\n      };\n      root.addEventListener('click', swap, { once: true });\n      root.addEventListener('input', swap, { once: true });\n    }\n\n    function wireTabs() {\n      dom.tabs.addEventListener('click', e => {\n        const btn = e.target.closest('.nn-tab');\n        if (!btn) return;\n        const tab = btn.getAttribute('data-tab');\n        state.activeTab = tab;\n        dom.tabs.querySelectorAll('.nn-tab').forEach(b => b.classList.toggle('is-active', b === btn));\n        dom.paneGrid.hidden  = tab !== 'grid';\n        dom.panePaste.hidden = tab !== 'paste';\n        if (dom.paneCsv) dom.paneCsv.hidden = tab !== 'csv';\n      });\n    }\n\n    function wireBulk() {\n      // Live preview: every keystroke re-parses and updates the preview table.\n      dom.bulkTextarea.addEventListener('input', () => {\n        const parsed = parseBulk(dom.bulkTextarea.value || '');\n        renderPastePreview(parsed);\n      });\n      // Apply: replace roster with parsed rows and switch to grid.\n      dom.pasteApplyBtn.addEventListener('click', () => {\n        const parsed = parseBulk(dom.bulkTextarea.value || '');\n        if (parsed.length === 0) return;\n        state.entries = parsed.map(p => ({ ...p, id: makeId() }));\n        state.focusedEntryId = state.entries[0]?.id || null;\n        state.activeTab = 'grid';\n        dom.tabs.querySelector('[data-tab=\"grid\"]').click();\n        renderEntries();\n        render();\n      });\n    }\n\n    function renderPastePreview(rows) {\n      if (rows.length === 0) {\n        dom.pastePreview.hidden = true;\n        dom.pasteApplyBtn.disabled = true;\n        dom.pasteApplyLabel.textContent = 'Apply 0 rows to roster';\n        return;\n      }\n      dom.pastePreview.hidden = false;\n      dom.pasteCount.textContent = String(rows.length);\n      dom.pasteApplyBtn.disabled = false;\n      dom.pasteApplyLabel.textContent = `Apply ${rows.length} row${rows.length === 1 ? '' : 's'} to roster`;\n      dom.pastePreviewRows.innerHTML = rows.slice(0, 50).map((r, i) => `\n        <tr>\n          <td class=\"nn-roster__idx\">${i + 1}</td>\n          <td>${escapeHTML(r.name || '')}</td>\n          <td>${escapeHTML(r.number || '')}</td>\n          <td>${r.qty || 1}</td>\n        </tr>\n      `).join('');\n      if (rows.length > 50) {\n        dom.pastePreviewRows.insertAdjacentHTML('beforeend',\n          `<tr><td colspan=\"4\" class=\"nn-roster__truncated\">+ ${rows.length - 50} more\u2026</td></tr>`);\n      }\n    }\n\n    function wireCsv() {\n      if (!dom.csvDrop) return;\n      const openPicker = () => dom.csvInput.click();\n      // Click anywhere on the drop label (except the template button) opens the file picker.\n      dom.csvDrop.addEventListener('click', e => {\n        if (e.target.closest('[data-nn-csv-template]')) return;\n        if (e.target === dom.csvInput) return;\n        e.preventDefault();\n        openPicker();\n      });\n      dom.csvDrop.addEventListener('dragover', e => { e.preventDefault(); dom.csvDrop.classList.add('is-dragover'); });\n      dom.csvDrop.addEventListener('dragleave', () => dom.csvDrop.classList.remove('is-dragover'));\n      dom.csvDrop.addEventListener('drop', e => {\n        e.preventDefault();\n        dom.csvDrop.classList.remove('is-dragover');\n        const file = e.dataTransfer.files && e.dataTransfer.files[0];\n        if (file) handleCsvFile(file);\n      });\n      dom.csvInput.addEventListener('change', e => {\n        const file = e.target.files && e.target.files[0];\n        if (file) handleCsvFile(file);\n      });\n      dom.csvTemplate.addEventListener('click', e => {\n        e.preventDefault();\n        e.stopPropagation();\n        downloadCsvTemplate();\n      });\n    }\n\n    function handleCsvFile(file) {\n      const reader = new FileReader();\n      reader.onload = () => {\n        const text = String(reader.result || '');\n        const rows = parseCsv(text);\n        if (rows.length === 0) {\n          showCsvResult(`Couldn't read any rows from <strong>${escapeHTML(file.name)}</strong>. Check the file is a CSV with name/number/qty columns.`, 'error');\n          return;\n        }\n        state.entries = rows.map(r => ({ ...r, id: makeId() }));\n        state.focusedEntryId = state.entries[0]?.id || null;\n        state.activeTab = 'grid';\n        dom.tabs.querySelector('[data-tab=\"grid\"]').click();\n        renderEntries();\n        render();\n        showCsvResult(`Imported <strong>${rows.length}</strong> row${rows.length === 1 ? '' : 's'} from <strong>${escapeHTML(file.name)}</strong>.`, 'success');\n      };\n      reader.readAsText(file);\n    }\n\n    function showCsvResult(html, kind) {\n      if (!dom.csvResult) return;\n      dom.csvResult.hidden = false;\n      dom.csvResult.className = `nn-csv-result nn-csv-result--${kind}`;\n      dom.csvResult.innerHTML = html;\n    }\n\n    function downloadCsvTemplate() {\n      const csv = 'name,number,qty\\nRivera,23,1\\nChen,7,1\\nOkafor,11,1\\n';\n      const blob = new Blob([csv], { type: 'text/csv' });\n      const url = URL.createObjectURL(blob);\n      const a = document.createElement('a');\n      a.href = url;\n      a.download = 'names-and-numbers-template.csv';\n      document.body.appendChild(a);\n      a.click();\n      document.body.removeChild(a);\n      setTimeout(() => URL.revokeObjectURL(url), 1000);\n    }\n\n    function wireForm() {\n      // The submit button intercepts and drives /cart/add.js (bulk) entirely\n      // via fetch \u2014 bypasses bySizeNew's product form to avoid double-id\n      // collisions from its <template> clone.\n      //\n      // Each roster entry becomes its own cart line item, sized & priced by\n      // the matched bySize-style variant. All lines from one configurator\n      // session share `_design name` + `_Upload (Vector Files Preferred)`\n      // so the cart UI groups them under one design header.\n      dom.submitBtn.addEventListener('click', async e => {\n        if (!dom.variantInput.value) return; // disabled state \u2014 let nothing happen\n        e.preventDefault();\n        const originalLabel = dom.submitLabel.textContent;\n        dom.submitBtn.disabled = true;\n        dom.submitLabel.textContent = 'Uploading artwork\u2026';\n        try {\n          const fontDef = currentFont();\n          const colorDef = currentColor();\n          const printables = buildPrintables(state, nameHeights, numberHeights);\n          measurePrintables(printables, cfg, fontDef);\n\n          // Per-entry artwork: each roster entry gets its own tight PNG so\n          // the cart can show individual line items each with their own\n          // preview thumbnail. Uploaded in parallel via Promise.all.\n          const entryPrintables = new Map();\n          printables.forEach(p => {\n            if (p.copyIdx === 0 && !entryPrintables.has(p.entryIdx)) {\n              entryPrintables.set(p.entryIdx, p);\n            }\n          });\n          const entryIdxs = [...entryPrintables.keys()];\n          dom.submitLabel.textContent = `Rendering ${entryIdxs.length} prints\u2026`;\n          const uploads = await Promise.all(entryIdxs.map(async (idx) => {\n            const p = entryPrintables.get(idx);\n            const blob = await renderEntryPNG(p, cfg, fontDef, colorDef.hex);\n            const urls = await uploadBlob(blob, cfg);\n            return { idx, urls };\n          }));\n          const urlByEntryIdx = new Map(uploads.map(u => [u.idx, u.urls]));\n          // The first entry's PNG also acts as the shared design thumbnail\n          // (used by the cart's nt-design-group-header).\n          const firstUrls = urlByEntryIdx.get(entryIdxs[0]);\n          dom.props.uploadImgix.value = firstUrls.imgixUrl;\n          dom.props.uploadS3.value    = firstUrls.sourceUrl;\n\n          dom.submitLabel.textContent = state.editLineKey ? 'Updating cart\u2026' : 'Adding to cart\u2026';\n\n          // File URL params.\n          //\n          // ORDER_PARAMS (production / Upload (Vector Files Preferred) / _Original Image):\n          //   Empty string \u2014 production downloads the PNG exactly as rendered,\n          //   including the PAD_IN=0.15\" transparent safety pad around the ink.\n          //   We previously used `trim=auto` here thinking it would crop to the\n          //   priced dimensions, but `trim=auto` strips ALL transparent pixels \u2014\n          //   including our safety pad \u2014 leaving the ink flush against every\n          //   edge of the canvas. That broke production: no bleed, anti-alias\n          //   artifacts at the cut line, and customer perception that the file\n          //   was \"cropped.\" Dropping the trim restores the pad.\n          //\n          // CART_PARAMS (cart / slide-out thumbnail):\n          //   Tight visual crop is fine for the 100px-tall cart thumbnail, so\n          //   trim=auto stays. fm=png preserves transparency, auto=compress + q=50\n          //   keep the thumbnail bytes small.\n          const ORDER_PARAMS = '';\n          const CART_PARAMS  = 'trim=auto&fm=png&auto=compress&q=50&h=150';\n\n          // Shared properties \u2014 every line carries _design name so the cart\n          // UI groups them under one design header even though each line has\n          // a different upload URL.\n          const designId = 'nn-' + Date.now() + '-' + Math.random().toString(36).slice(2, 8);\n          /* All N&N metadata keys are underscore-prefixed so Shopify's\n             default checkout property list hides them from the customer\n             while still passing the data through on the order record.\n             Snippets that need to render this metadata in the cart UI\n             read the underscore-prefixed keys (with a fallback to the\n             pre-underscore names for carts created before this change). */\n          const sharedProps = {\n            '_Style': 'Names & Numbers',\n            '_design name': designId,\n            '_Scope': scopeLabel(state.scope),\n            '_Font': fontDef.label,\n            '_Color': colorDef.id === 'custom' ? `Custom (${colorDef.hex})` : colorDef.label,\n            '_Name Height': state.scope === 'numbers' ? '' : `${currentNameH().inches} in (${currentNameH().label})`,\n            '_Number Height': state.scope === 'names' ? '' : `${currentNumH().inches} in (${currentNumH().label})`,\n            /* No _Entries / _Entries Count here: each entry is its own cart\n               line carrying its own _Name/_Number/qty, and the cart's Edit\n               Entries flow rebuilds the roster from those per-line props\n               (see snippets/nt-nn-design-group.liquid). Duplicating the full\n               roster onto every line only bloated the order record. */\n            '_render_version': 'client-v6',\n          };\n\n          // bySize tier-quantity discount ladder (mirrors the prices-table at\n          // snippets/portal-multiupload.liquid:498-540). Total qty across the\n          // whole submission determines the active tier. We attach the FULL\n          // ladder string + the active tier name to each line so the existing\n          // cart Liquid (snippets/arrange-properties.liquid:31) renders the\n          // discount badge correctly.\n          const TIER_DISCOUNT_LADDER = '_discount_input=min_1-off_50#min_15-off_20#min_50-off_30#min_100-off_40#min_250-off_50#';\n          // The ladder above is purely for cart display. The numbers actually\n          // applied per tier come from this table:\n          const TIER_TABLE = [\n            { min:   1, max: 14,      off: 0,  name: '0%_off_for_1_transfers'    },\n            { min:  15, max: 49,      off: 20, name: '20%_off_for_15_transfers'  },\n            { min:  50, max: 99,      off: 30, name: '30%_off_for_50_transfers'  },\n            { min: 100, max: 249,     off: 40, name: '40%_off_for_100_transfers' },\n            { min: 250, max: Infinity, off: 50, name: '50%_off_for_250_transfers' },\n          ];\n          const totalQty = state.entries.reduce((sum, e) => sum + Math.max(1, parseInt(e.qty, 10) || 1), 0);\n          const activeTier = TIER_TABLE.find(t => totalQty >= t.min && totalQty <= t.max) || TIER_TABLE[0];\n          const discountInputProp = `min_${activeTier.min}-off_${activeTier.off}`;\n          const discountNameProp  = activeTier.name;\n\n          const items = [];\n          state.entries.forEach((entry, i) => {\n            const p = entryPrintables.get(i);\n            if (!p) return;\n            const lineSqIn = Math.ceil((p.widthIn || 0) * (p.heightIn || 0));\n            const variant = matchVariantBySqIn(variants, lineSqIn);\n            if (!variant) return;\n            const lineUrls = urlByEntryIdx.get(i) || firstUrls;\n            const widthStr  = p.widthIn.toFixed(2);\n            const heightStr = p.heightIn.toFixed(2);\n            items.push({\n              id: variant.id,\n              quantity: Math.max(1, parseInt(entry.qty, 10) || 1),\n              properties: {\n                ...sharedProps,\n                // File URLs:\n                //   Upload (Vector Files Preferred) \u2192 imgix (raw \u2014 preserves PAD_IN safety pad)\n                //   _Original Image                 \u2192 imgix (same)\n                //   _cartImg                        \u2192 imgix?trim=auto&fm=png&auto=compress&q=50&h=150 (tight thumbnail)\n                'Upload (Vector Files Preferred)': ORDER_PARAMS ? lineUrls.imgixUrl + '?' + ORDER_PARAMS : lineUrls.imgixUrl,\n                '_Original Image':                 ORDER_PARAMS ? lineUrls.imgixUrl + '?' + ORDER_PARAMS : lineUrls.imgixUrl,\n                '_cartImg':                        lineUrls.imgixUrl + '?' + CART_PARAMS,\n                '_Name':   p.kind === 'pair' ? p.name : (p.kind === 'name' ? p.text : ''),\n                '_Number': p.kind === 'pair' ? p.number : (p.kind === 'number' ? p.text : ''),\n                '_entry_idx': String(i),\n                '_width':  widthStr,\n                '_height': heightStr,\n                '_Size':  `${widthStr}x${heightStr}`,\n                '_Total Sq In': String(lineSqIn),\n                '_discount_input': discountInputProp,\n                '_discount_name':  discountNameProp,\n              },\n            });\n          });\n          if (items.length === 0) throw new Error('No valid roster entries to add');\n\n          // Edit-line cleanup: if we came back from cart to edit, delete the\n          // ENTIRE previous design group before adding the new bulk. We look\n          // up the old design by either _design name (preferred) or the\n          // editLineKey directly.\n          let updatesPayload = null;\n          if (state.editLineKey) {\n            try {\n              const cartRes = await fetch('/cart.js', { credentials: 'include' });\n              const cart = await cartRes.json();\n              const editingLine = (cart.items || []).find(it => it.key === state.editLineKey);\n              const oldDesignId = editingLine && editingLine.properties && editingLine.properties['_design name'];\n              const updates = {};\n              (cart.items || []).forEach(it => {\n                if (it.key === state.editLineKey) {\n                  updates[it.key] = 0;\n                } else if (oldDesignId && it.properties && it.properties['_design name'] === oldDesignId) {\n                  updates[it.key] = 0;\n                }\n              });\n              if (Object.keys(updates).length > 0) updatesPayload = { updates };\n            } catch (cartErr) {\n              console.warn('N&N: failed to read cart for edit-cleanup; falling back to single-line delete', cartErr);\n              updatesPayload = { updates: { [state.editLineKey]: 0 } };\n            }\n          }\n\n          const addRes = await fetch('/cart/add.js', {\n            method: 'POST',\n            headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },\n            body: JSON.stringify({ items }),\n          });\n          if (!addRes.ok) {\n            const errText = await addRes.text().catch(() => '');\n            throw new Error(`/cart/add.js HTTP ${addRes.status}: ${errText.slice(0, 200)}`);\n          }\n          if (updatesPayload) {\n            const updRes = await fetch('/cart/update.js', {\n              method: 'POST',\n              headers: { 'Content-Type': 'application/json' },\n              body: JSON.stringify(updatesPayload),\n            });\n            if (!updRes.ok) console.warn('N&N: failed to remove old design group; cart may have duplicates');\n          }\n          window.location.href = '/cart';\n        } catch (err) {\n          console.error('N&N submit failed', err);\n          dom.submitLabel.textContent = 'Submit failed \u2014 try again';\n          dom.submitBtn.disabled = false;\n          setTimeout(() => { dom.submitLabel.textContent = originalLabel; }, 3000);\n        }\n      });\n    }\n\n    function hydrateFromUrl() {\n      const params = new URLSearchParams(location.search);\n      const nn = params.get('nn');\n      const editLine = params.get('edit_line');\n      if (!nn) return;\n      let data;\n      try {\n        data = JSON.parse(atob(nn));\n      } catch (e) {\n        console.warn('N&N: failed to decode `nn` param', e);\n        return;\n      }\n\n      if (data.scope === 'Names only')      state.scope = 'names';\n      else if (data.scope === 'Numbers only') state.scope = 'numbers';\n      else if (data.scope === 'Names + Numbers') state.scope = 'both';\n      /* When restoring a saved configurator state (via Edit Entries),\n         the customer has already chosen a scope \u2014 reveal Steps 2-5\n         and mark the corresponding card as selected. */\n      if (state.scope) {\n        root.setAttribute('data-nn-scope-set', '1');\n        const scopeWrap = root.querySelector('[data-nn-control=\"scope\"]');\n        if (scopeWrap) {\n          scopeWrap.querySelectorAll('[data-value]').forEach(b => {\n            const sel = b.getAttribute('data-value') === state.scope;\n            b.classList.toggle('is-selected', sel);\n            b.setAttribute('aria-checked', sel ? 'true' : 'false');\n          });\n        }\n      }\n\n      if (data.font) {\n        const f = fonts.find(x => x.label === data.font);\n        if (f) state.fontId = f.id;\n      }\n\n      if (data.color) {\n        const customMatch = /Custom\\s*\\(([^)]+)\\)/.exec(data.color);\n        if (customMatch) {\n          state.customColorHex = customMatch[1].trim();\n          state.colorId = 'custom';\n        } else {\n          const c = colors.find(x => x.label === data.color);\n          if (c) state.colorId = c.id;\n        }\n      }\n\n      const matchInches = s => {\n        const m = /^([\\d.]+)\\s*in/.exec(s || '');\n        return m ? parseFloat(m[1]) : null;\n      };\n      const nameIn = matchInches(data.nameHeight);\n      const numIn  = matchInches(data.numberHeight);\n      if (nameIn != null) state.nameHeightId   = findClosestHeightId(nameHeights, nameIn);\n      if (numIn  != null) state.numberHeightId = findClosestHeightId(numberHeights, numIn);\n\n      if (data.entries) {\n        const lines = String(data.entries).split('\\n').map(l => l.trim()).filter(Boolean);\n        state.entries = lines.map(line => {\n          const qm = /\\s+[\u00D7x]\\s*(\\d+)\\s*$/i.exec(line);\n          const qty = qm ? parseInt(qm[1], 10) : 1;\n          const cleaned = qm ? line.slice(0, qm.index).trim() : line;\n          if (state.scope === 'names')   return { id: makeId(), name: cleaned, number: '',                       qty };\n          if (state.scope === 'numbers') return { id: makeId(), name: '',     number: cleaned.slice(0, 3),       qty };\n          const parts = cleaned.split('|');\n          return { id: makeId(), name: (parts[0] || '').trim(), number: (parts[1] || '').trim().slice(0, 3), qty };\n        });\n        if (state.entries.length > 0) state.focusedEntryId = state.entries[0].id;\n      }\n\n      if (editLine) state.editLineKey = editLine;\n\n      // entry_idx focuses a specific roster row when the customer clicked\n      // a per-line Edit link in the cart. Resolves to focusedEntryId after\n      // entries have been parsed above.\n      const entryIdxParam = params.get('entry_idx');\n      if (entryIdxParam != null && state.entries.length > 0) {\n        const idx = parseInt(entryIdxParam, 10);\n        if (!isNaN(idx) && idx >= 0 && idx < state.entries.length) {\n          state.focusedEntryId = state.entries[idx].id;\n        }\n      }\n    }\n\n    function addEntry() {\n      if (state.entries.length >= cfg.maxEntries) return;\n      const entry = { id: makeId(), name: '', number: '', qty: 1 };\n      state.entries.push(entry);\n      state.focusedEntryId = entry.id;\n      renderEntries();\n    }\n\n    function currentFont()  { return fonts.find(f => f.id === state.fontId)   || fonts[0]  || { family: 'sans-serif', weight: 700, label: 'sans-serif' }; }\n    function currentColor() {\n      if (state.colorId === 'custom') return { id: 'custom', label: 'Custom', hex: state.customColorHex };\n      return colors.find(c => c.id === state.colorId) || colors[0] || { hex: '#ffffff', label: 'White' };\n    }\n    function currentNameH() { return nameHeights.find(h => h.id === state.nameHeightId)     || nameHeights[0]; }\n    function currentNumH()  { return numberHeights.find(h => h.id === state.numberHeightId) || numberHeights[0]; }\n\n    // Refresh the volume-discount tier strip + hint. Returns the active\n    // tier's % off so render() can use it for the Discount-row dollar\n    // savings + Subtotal final-price math. (The Discount row itself is\n    // written by render() once it has totalCents to multiply against.)\n    function updateTierUI() {\n      const tiersContainer = document.querySelector('[data-nn-tiers]');\n      if (!tiersContainer) return 0;\n      const rosterQty = state.entries.reduce((sum, e) => sum + Math.max(1, parseInt(e.qty, 10) || 1), 0);\n      const effectiveQty = rosterQty + (state.cartTransferQty || 0);\n      const cells = [...tiersContainer.querySelectorAll('.nn-tiers__cell')];\n      let activeIdx = -1;\n      cells.forEach((cell, i) => {\n        const min = parseInt(cell.getAttribute('data-tier-min'), 10);\n        const max = parseInt(cell.getAttribute('data-tier-max'), 10);\n        const hit = effectiveQty >= min && effectiveQty <= max;\n        cell.classList.toggle('is-active', hit);\n        if (hit) activeIdx = i;\n      });\n      const hint = tiersContainer.querySelector('[data-nn-tier-hint]');\n      if (hint) {\n        if (effectiveQty <= 0) {\n          hint.textContent = 'Add transfers to unlock discounts';\n          hint.classList.remove('nn-tiers__hint--maxed');\n        } else if (activeIdx >= 0 && activeIdx < cells.length - 1) {\n          const next = cells[activeIdx + 1];\n          const nextMin = parseInt(next.getAttribute('data-tier-min'), 10);\n          const nextOff = parseInt(next.getAttribute('data-tier-off'), 10);\n          const need = Math.max(1, nextMin - effectiveQty);\n          hint.textContent = `Add ${need} more transfer${need === 1 ? '' : 's'} to unlock ${nextOff}% off`;\n          hint.classList.remove('nn-tiers__hint--maxed');\n        } else {\n          hint.textContent = 'Maximum discount unlocked \u2014 50% off';\n          hint.classList.add('nn-tiers__hint--maxed');\n        }\n      }\n      return activeIdx >= 0 ? parseInt(cells[activeIdx].getAttribute('data-tier-off'), 10) || 0 : 0;\n    }\n\n    function render() {\n      const fontDef  = currentFont();\n      const colorDef = currentColor();\n      const nameH    = currentNameH();\n      const numH     = currentNumH();\n\n      // Show/hide height groups based on scope\n      dom.heightNameGroup.style.display = state.scope === 'numbers' ? 'none' : '';\n      dom.heightNumGroup.style.display  = state.scope === 'names'   ? 'none' : '';\n\n      // Step labels\n      dom.currentFont.textContent  = fontDef.label;\n      dom.currentColor.textContent = colorDef.label;\n\n      // (Chip + LIVE PREVIEW badge removed from the markup \u2014 skip writing.)\n\n      // Jersey mockup \u2014 follow whichever entry the user last interacted with,\n      // falling back to the first entry or placeholder samples.\n      const focused = state.entries.find(e => e.id === state.focusedEntryId) || state.entries[0] || { name: '', number: '' };\n      const previewEntry = {\n        name:   (focused.name   || '').trim() || PLACEHOLDER_NAME,\n        number: (focused.number || '').trim() || PLACEHOLDER_NUMBER,\n      };\n      updateJerseyMockup(previewEntry, fontDef, colorDef, nameH, numH);\n\n      dom.entriesCount.textContent = String(state.entries.length);\n\n      // Build printables, pack, price\n      const printables = buildPrintables(state, nameHeights, numberHeights);\n      const packed = packShelf(printables, cfg, fontDef);\n      const empty = packed.placements.length === 0;\n\n      // Refresh the volume-discount tier strip ALWAYS \u2014 even when the\n      // roster is empty \u2014 so a customer arriving with items already in\n      // their cart sees the active tier highlighted. Returns the active\n      // tier's % off for the savings math below.\n      const activeOff = updateTierUI() || 0;\n\n      if (empty) {\n        if (dom.dims)     dom.dims.textContent     = '\u2014';\n        if (dom.sqin)     dom.sqin.textContent     = '\u2014';\n        if (dom.price)    dom.price.textContent    = '\u2014';\n        if (dom.discount) dom.discount.textContent = '\u2014';\n        dom.submitBtn.disabled = true;\n        dom.submitLabel.textContent = 'Add a name or number to start';\n        dom.warning.hidden = true;\n        dom.variantInput.value = '';\n        return;\n      }\n\n      // Per-entry cart-line model: each roster entry becomes its own cart\n      // line item, sized by its bounding box (or pair box for scope=both).\n      // The configurator's displayed total is the sum of per-line prices so\n      // the customer sees what they'll pay before adding to cart.\n      const entryPrintables = new Map();   // entryIdx \u2192 representative printable (one copy)\n      printables.forEach(p => {\n        if (p.copyIdx === 0 && !entryPrintables.has(p.entryIdx)) {\n          entryPrintables.set(p.entryIdx, p);\n        }\n      });\n      let totalSqIn = 0;\n      let totalCents = 0;\n      let oversize = false;\n      let lastVariantId = '';\n      const lineSummary = [];\n      const perEntryCents = new Map();   // entryIdx \u2192 line total cents\n      state.entries.forEach((entry, i) => {\n        const p = entryPrintables.get(i);\n        if (!p) return;\n        const qty = Math.max(1, parseInt(entry.qty, 10) || 1);\n        const lineSqIn = Math.ceil((p.widthIn || 0) * (p.heightIn || 0));\n        totalSqIn += lineSqIn * qty;\n        const variant = matchVariantBySqIn(variants, lineSqIn);\n        if (!variant) { oversize = true; return; }\n        lastVariantId = variant.id;\n        const lineCents = (variant.price || 0) * qty;\n        totalCents += lineCents;\n        perEntryCents.set(i, lineCents);\n        lineSummary.push({ entryIdx: i, sqIn: lineSqIn, qty, variantId: variant.id, lineCents });\n      });\n      // Stash the per-line breakdown for the submit handler to consume.\n      state._lineSummary = lineSummary;\n\n      // Tier UI already updated above (runs in both empty + populated\n      // branches so the cart-aware tier reflects regardless of roster).\n\n      /* Sheet dimensions display removed with the sheet concept. The hidden\n         [data-nn-dimensions] element is left in the DOM as a vestige but no\n         longer written to. dom.sqin still shows the total square inches\n         (sum of per-entry width \u00D7 height) for the price summary. */\n      if (dom.sqin) dom.sqin.textContent = `${totalSqIn} sq in`;\n\n      if (oversize) {\n        dom.variantInput.value = '';\n        dom.price.textContent = '\u2014';\n        if (dom.discount) dom.discount.textContent = '\u2014';\n        dom.submitBtn.disabled = true;\n        dom.submitLabel.textContent = 'Item too large';\n        dom.warning.hidden = false;\n        dom.warning.textContent = `One of the names/numbers is larger than our biggest variant. Reduce its size or split into two orders.`;\n      } else if (lineSummary.length === 0) {\n        dom.variantInput.value = '';\n        dom.price.textContent = '\u2014';\n        if (dom.discount) dom.discount.textContent = '\u2014';\n        dom.submitBtn.disabled = true;\n        dom.submitLabel.textContent = 'Add a name or number to start';\n        dom.warning.hidden = true;\n      } else {\n        // Apply the active tier's % off to derive the dollar savings.\n        // Subtotal shows the FINAL price (matches what Shopify will charge\n        // at checkout once the line-level discount is applied); Discount\n        // row shows the savings as a negative dollar amount.\n        const savingsCents = Math.round(totalCents * activeOff / 100);\n        const finalCents   = totalCents - savingsCents;\n        dom.variantInput.value = lastVariantId;   // satisfies the wireForm gate\n        dom.price.textContent = '$' + (finalCents / 100).toFixed(2);\n        if (dom.discount) {\n          dom.discount.textContent = savingsCents > 0\n            ? '\u2212$' + (savingsCents / 100).toFixed(2) + ` (${activeOff}% off)`\n            : '\u2014';\n        }\n        dom.submitBtn.disabled = false;\n        dom.submitLabel.textContent = state.editLineKey ? 'Update item' : 'Add to cart';\n        dom.warning.hidden = true;\n      }\n\n      // Sync hidden properties\n      dom.props.scope.value        = scopeLabel(state.scope);\n      dom.props.font.value         = fontDef.label;\n      dom.props.color.value        = colorDef.id === 'custom' ? `Custom (${colorDef.hex})` : colorDef.label;\n      dom.props.nameHeight.value   = state.scope === 'numbers' ? '' : `${nameH.inches} in (${nameH.label})`;\n      dom.props.numberHeight.value = state.scope === 'names'   ? '' : `${numH.inches} in (${numH.label})`;\n      dom.props.entriesCount.value = String(state.entries.length);\n      dom.props.entries.value      = serializeEntries(state);\n      /* Parent-form _height write removed with the sheet concept. Each cart\n         line item carries its own _height (per-entry) set in cart-add. */\n    }\n\n    function updateJerseyMockup(entry, fontDef, colorDef, nameH, numH) {\n      const name   = (entry.name || '').trim();\n      const number = (entry.number || '').trim();\n      const showName   = state.scope !== 'numbers' && name;\n      const showNumber = state.scope !== 'names'   && number;\n\n      // Target scale: 1 inch \u2192 REAL_UNITS_PER_INCH svg units. The shirt\n      // back panel maps to ~320 svg units in the zone, so 16 units/inch\n      // keeps the printed art at the same relative size as the actual\n      // jersey. When a chosen stack overflows the zone we scale uniformly\n      // down to fit; the inter-entry ratio stays honest.\n      const REAL_UNITS_PER_INCH = 16;\n\n      const svg = dom.jerseyName && dom.jerseyName.ownerSVGElement;\n      const zoneX = svg ? parseFloat(svg.getAttribute('data-nn-zone-x') || '80')  : 80;\n      const zoneY = svg ? parseFloat(svg.getAttribute('data-nn-zone-y') || '140') : 140;\n      const zoneW = svg ? parseFloat(svg.getAttribute('data-nn-zone-w') || '320') : 320;\n      const zoneH = svg ? parseFloat(svg.getAttribute('data-nn-zone-h') || '340') : 340;\n      const centerX = zoneX + zoneW / 2;\n\n      // Per-text bbox sizing \u2014 mirrors measureInk() in renderExportPNG.\n      // We scale each text so its INK bbox height equals the requested\n      // heightU. Using a shared M-cap probe (previous behaviour) made\n      // numbers like \"00\" render taller than their requested height\n      // because '0' bbox > 'M' bbox in many fonts, especially italic\n      // display faces like Racing Sans One.\n      const PROBE_PX = 768;\n      const probeCtx = document.createElement('canvas').getContext('2d');\n      const family = fontDef.family;\n      const weight = fontDef.weight || 700;\n      const measureFor = (text, heightU) => {\n        if (!text || !heightU) return { fontSize: 0, ascent: 0, height: 0, bboxWidth: 0, advance: 0 };\n        probeCtx.font = `${weight} ${PROBE_PX}px \"${family}\", sans-serif`;\n        const m = probeCtx.measureText(text);\n        const ascentPx  = m.actualBoundingBoxAscent  || PROBE_PX * 0.78;\n        const descentPx = m.actualBoundingBoxDescent || 0;\n        // True visual ink width (left+right bbox extents). Stencil and\n        // italic display fonts have ink that overhangs the advance width,\n        // so using advance for fit-checks lets glyphs spill past the zone.\n        const leftPx    = m.actualBoundingBoxLeft    || 0;\n        const rightPx   = m.actualBoundingBoxRight   || m.width;\n        const inkPx     = ascentPx + descentPx;\n        if (!inkPx) return { fontSize: 0, ascent: 0, height: 0, bboxWidth: 0, advance: 0 };\n        const k = heightU / inkPx;\n        return {\n          fontSize:  PROBE_PX * k,\n          ascent:    ascentPx * k,            // baseline = capTop + ascent (alphabetic)\n          height:    heightU,                 // ink bbox height in svg units\n          bboxWidth: (leftPx + rightPx) * k,  // true visual width\n          advance:   m.width * k,             // text advance \u2014 what textLength manipulates\n        };\n      };\n\n      // HEIGHTS NEVER SCALE. A 2\" name always renders at 2\" tall \u00D7 16 = 32\n      // svg units, an 8\" number at 128 svg units, regardless of how long\n      // the text is. Owen wants real heights preserved so the customer\n      // sees an accurate sense of the printed size; if a name is too wide\n      // it compresses horizontally (textLength squish) instead of shrinking\n      // the whole stack. Mirrors renderExportPNG's scaleX behaviour.\n      const nameU = showName   ? nameH.inches      * REAL_UNITS_PER_INCH : 0;\n      const numU  = showNumber ? numH.inches       * REAL_UNITS_PER_INCH : 0;\n      const gapU  = (showName && showNumber) ? cfg.nameNumGapIn * REAL_UNITS_PER_INCH : 0;\n      const nameMeas = showName   ? measureFor(name.toUpperCase(), nameU) : null;\n      const numMeas  = showNumber ? measureFor(number,             numU)  : null;\n\n      // Horizontal squish target: the smaller of the zone bound (with a 5%\n      // safety margin so glyphs don't kiss the shirt edge) and the print's\n      // maxItemWidthIn \u2014 which the print pipeline clamps to via scaleX.\n      // Keeping these in lockstep makes the preview width = printed width.\n      const WIDTH_SAFETY = 0.95;\n      const maxBboxU = Math.min(zoneW * WIDTH_SAFETY, cfg.maxItemWidthIn * REAL_UNITS_PER_INCH);\n\n      // Anchor stack to the TOP of the zone \u2014 names sit high on the back\n      // panel like real jersey placement, with the number below.\n      const stackTop = zoneY;\n\n      const applyText = (node, text, meas) => {\n        node.textContent = text;\n        node.setAttribute('x', String(centerX));\n        node.setAttribute('font-size', String(Math.max(1, Math.round(meas.fontSize))));\n        node.style.fontFamily = `\"${family}\", sans-serif`;\n        node.setAttribute('font-weight', String(weight));\n        node.setAttribute('fill', colorDef.hex);\n        // alphabetic baseline \u2192 y is the baseline; ascent rises ABOVE it,\n        // descent drops BELOW it. Lets us place pieces by cap-top + ascent.\n        node.setAttribute('dominant-baseline', 'alphabetic');\n        // Squish horizontally if the bbox overflows. textLength controls\n        // ADVANCE width, but bbox scales proportionally with advance under\n        // spacingAndGlyphs, so target = maxBboxU \u00D7 advance/bbox makes the\n        // resulting visual bbox = maxBboxU exactly. Height stays untouched.\n        if (meas.bboxWidth > maxBboxU && meas.bboxWidth > 0) {\n          const ratio = meas.advance / meas.bboxWidth;\n          node.setAttribute('textLength', String(maxBboxU * ratio));\n          node.setAttribute('lengthAdjust', 'spacingAndGlyphs');\n        } else {\n          node.removeAttribute('textLength');\n          node.removeAttribute('lengthAdjust');\n        }\n        node.style.display = '';\n      };\n\n      // cursorY tracks the INK TOP of the next piece.\n      let cursorY = stackTop;\n      if (showName && nameMeas) {\n        applyText(dom.jerseyName, name.toUpperCase(), nameMeas);\n        dom.jerseyName.setAttribute('y', String(cursorY + nameMeas.ascent));\n        cursorY += nameMeas.height + gapU;\n      } else {\n        dom.jerseyName.style.display = 'none';\n      }\n\n      if (showNumber && numMeas) {\n        applyText(dom.jerseyNumber, number, numMeas);\n        dom.jerseyNumber.setAttribute('y', String(cursorY + numMeas.ascent));\n      } else {\n        dom.jerseyNumber.style.display = 'none';\n      }\n    }\n  }\n\n  // ---------- packer ----------\n\n  function buildPrintables(state, nameHeights, numberHeights) {\n    const nameH = nameHeights.find(h => h.id === state.nameHeightId)     || nameHeights[0];\n    const numH  = numberHeights.find(h => h.id === state.numberHeightId) || numberHeights[0];\n    const out = [];\n    state.entries.forEach((e, i) => {\n      const qty = Math.max(1, parseInt(e.qty, 10) || 1);\n      const name = (e.name   || '').trim().toUpperCase();\n      const num  = (e.number || '').trim();\n      const hasName = state.scope !== 'numbers' && name !== '';\n      const hasNum  = state.scope !== 'names'   && num  !== '';\n      if (!hasName && !hasNum) return;\n      for (let k = 0; k < qty; k++) {\n        if (state.scope === 'both' && hasName && hasNum) {\n          // Combined per-entry printable: name above number, stacked. The\n          // bounding box is what gets priced as a single cart line item.\n          out.push({\n            kind: 'pair',\n            name,\n            number: num,\n            text: `${name} ${num}`,         // used by cart-line display\n            nameHeightIn: nameH.inches,\n            numHeightIn:  numH.inches,\n            heightIn: nameH.inches + numH.inches,   // gap added in packShelf measurement\n            entryIdx: i,\n            copyIdx: k,\n          });\n        } else if (hasName) {\n          out.push({ kind: 'name',   text: name, heightIn: nameH.inches, entryIdx: i, copyIdx: k });\n        } else if (hasNum) {\n          out.push({ kind: 'number', text: num,  heightIn: numH.inches,  entryIdx: i, copyIdx: k });\n        }\n      }\n    });\n    return out;\n  }\n\n  // Measure each printable's TIGHT INK bounding box. Sets widthIn / heightIn\n  // to the visible glyph extent (not the em-square) so the canvas can be\n  // cropped to \"art only \u2014 nothing cut off, nothing added\". Customer's chosen\n  // letter height (e.g., \"8 in number\") is treated as the visible ink height.\n  function measurePrintables(printables, cfg, fontDef) {\n    if (printables.length === 0) return;\n    const family = fontDef.family;\n    const weight = fontDef.weight || 700;\n    const mctx = document.createElement('canvas').getContext('2d');\n    const maxWidth = cfg.maxItemWidthIn || Infinity;\n    const measureInk = (text, heightIn) => {\n      const PROBE_PX = heightIn * 96;\n      mctx.font = `${weight} ${PROBE_PX}px \"${family}\", sans-serif`;\n      const m = mctx.measureText(text);\n      // Honor a legitimate 0 (e.g. flat-bottomed glyphs like \"1\" have descent\n      // exactly 0); only fall back when a metric is genuinely missing. A bare\n      // `|| fallback` fired on descent=0 and rendered such glyphs ~20% short.\n      const num = (v, fb) => (typeof v === 'number' && isFinite(v)) ? v : fb;\n      const ascentPx  = num(m.actualBoundingBoxAscent,  PROBE_PX * 0.78);\n      const descentPx = num(m.actualBoundingBoxDescent, PROBE_PX * 0.22);\n      const leftPx    = num(m.actualBoundingBoxLeft,    0);\n      const rightPx   = num(m.actualBoundingBoxRight,   m.width);\n      // Scale so the actual ink height (ascent + descent) equals heightIn.\n      const scale = heightIn / (ascentPx + descentPx);\n      return {\n        widthIn:   (leftPx + rightPx) * scale,\n        ascentIn:  ascentPx  * scale,\n        descentIn: descentPx * scale,\n        leftIn:    leftPx    * scale,\n        _probePx:  PROBE_PX,\n        _scale:    scale,\n      };\n    };\n    printables.forEach(p => {\n      if (p.kind === 'pair') {\n        const nameInk = measureInk(p.name,   p.nameHeightIn);\n        const numInk  = measureInk(p.number, p.numHeightIn);\n        const contentW = Math.max(nameInk.widthIn, numInk.widthIn);\n        const contentH = (nameInk.ascentIn + nameInk.descentIn)\n                       + cfg.nameNumGapIn\n                       + (numInk.ascentIn  + numInk.descentIn);\n        p.naturalWidthIn = contentW;\n        p.widthIn  = Math.min(contentW, maxWidth);\n        p.heightIn = contentH;\n        p.scaleX   = contentW > maxWidth ? maxWidth / contentW : 1;\n        p._nameInk = nameInk;\n        p._numInk  = numInk;\n      } else {\n        const ink = measureInk(p.text, p.heightIn);\n        p.naturalWidthIn = ink.widthIn;\n        p.widthIn  = Math.min(ink.widthIn, maxWidth);\n        p.heightIn = ink.ascentIn + ink.descentIn;\n        p.scaleX   = ink.widthIn > maxWidth ? maxWidth / ink.widthIn : 1;\n        p._ink     = ink;\n      }\n    });\n  }\n\n  function packShelf(printables, cfg, fontDef) {\n    if (printables.length === 0) {\n      return { placements: [], totalHeightIn: 0 };\n    }\n    measurePrintables(printables, cfg, fontDef);\n\n    /* Sheet packing removed \u2014 each entry is its own row. Items keep their\n       insertion order (no decreasing-height sort) so the on-screen roster\n       order matches what production receives. Width is clamped to\n       cfg.maxItemWidthIn per item; height is each item's natural measured\n       height. Total height is the vertical extent of the stack. */\n    const placements = [];\n    let y = 0;\n    for (const item of printables) {\n      const widthIn = Math.min(item.widthIn, cfg.maxItemWidthIn);\n      placements.push({ item, x: 0, y, w: widthIn, h: item.heightIn });\n      y += item.heightIn + cfg.vertGapIn;\n    }\n    const totalHeightIn = y - cfg.vertGapIn;\n    return { placements, totalHeightIn };\n  }\n\n  /* Render to a canvas. Used directly by renderEntryPNG so it can pixel-scan\n     the rendered output to find the true ink bounds (canvas measureText\n     under-reports the visible extent of tall display bold fonts) and crop\n     to the actual ink + a small bleed pad. renderExportPNG is a thin wrapper\n     for any other caller that just wants a Blob. */\n  function renderExportToCanvas(packed, cfg, fontDef, colorHex) {\n    const widthIn  = packed.widthIn;\n    const heightIn = Math.max(packed.totalHeightIn, 1);\n    const canvas = document.createElement('canvas');\n    canvas.width  = Math.round(widthIn  * EXPORT_DPI);\n    canvas.height = Math.round(heightIn * EXPORT_DPI);\n    const ctx = canvas.getContext('2d');\n    ctx.setTransform(EXPORT_DPI, 0, 0, EXPORT_DPI, 0, 0);\n    ctx.fillStyle = colorHex || '#ffffff';\n    ctx.textBaseline = 'alphabetic';\n    const family = fontDef.family;\n    const weight = fontDef.weight || 700;\n    // Draw a measured text piece. (x, y) is the TOP-LEFT of the ink bounding\n    // box in context (inch) units. The glyph is shifted so its visible\n    // left edge lands at x and its visible top lands at y.\n    const drawInk = (text, x, y, ink, scaleX) => {\n      ctx.font = `${weight} ${ink._probePx}px \"${family}\", sans-serif`;\n      ctx.save();\n      ctx.translate(x, y + ink.ascentIn);\n      ctx.scale(ink._scale * scaleX, ink._scale);\n      // Shift the text origin RIGHT by the left side-bearing so the glyph's\n      // visible left edge lands at the box origin (x). measureText reports\n      // leftIn = actualBoundingBoxLeft*scale (negative when ink sits right of\n      // the origin). The negated form (-leftIn) shoved the glyph right by\n      // 2*|leftIn| \u2014 for large-bearing fonts (Bebas \"1\", Anton \"T\", Oswald)\n      // that pushed ink off the temp canvas and scanInkBounds cropped the\n      // right side. Browser-verified: +leftIn \u2192 0/720 clips; -leftIn \u2192 91/720.\n      ctx.fillText(text, ink.leftIn / ink._scale, 0);\n      ctx.restore();\n    };\n    packed.placements.forEach(pl => {\n      const scaleX = pl.item.scaleX || 1;\n      if (pl.item.kind === 'pair') {\n        const nameInk = pl.item._nameInk;\n        const numInk  = pl.item._numInk;\n        // Center each piece horizontally within the placement bounding box.\n        const nameW = nameInk.widthIn * scaleX;\n        const nameX = pl.x + (pl.w - nameW) / 2;\n        drawInk(pl.item.name, nameX, pl.y, nameInk, scaleX);\n        const numW = numInk.widthIn * scaleX;\n        const numX = pl.x + (pl.w - numW) / 2;\n        const numY = pl.y + nameInk.ascentIn + nameInk.descentIn + cfg.nameNumGapIn;\n        drawInk(pl.item.number, numX, numY, numInk, scaleX);\n        return;\n      }\n      const ink = pl.item._ink;\n      const w   = ink.widthIn * scaleX;\n      const tx  = pl.x + (pl.w - w) / 2;\n      drawInk(pl.item.text, tx, pl.y, ink, scaleX);\n    });\n    return canvas;\n  }\n\n  async function renderExportPNG(packed, cfg, fontDef, colorHex) {\n    const canvas = renderExportToCanvas(packed, cfg, fontDef, colorHex);\n    return new Promise(resolve => canvas.toBlob(b => resolve(b), 'image/png'));\n  }\n\n  // ---------- variant matching ----------\n\n  function matchVariantBySqIn(variants, sqIn) {\n    if (!Array.isArray(variants) || isNaN(sqIn)) return null;\n    sqIn = Number(sqIn);\n    for (const v of variants) {\n      if (!v.option1) continue;\n      if (v.available === false) continue;\n      const m = v.option1.match(/(\\d+)\\s*[\u2013-]\\s*(\\d+)/);\n      if (!m) continue;\n      const min = parseInt(m[1], 10);\n      const max = parseInt(m[2], 10);\n      if (sqIn >= min && sqIn <= max) return v;\n    }\n    return null;\n  }\n\n  // ---------- upload ----------\n\n  // Render a single printable (name / number / pair) to its own tight PNG.\n  // Adds a small safety pad around the ink so anti-aliasing at the canvas\n  // edges doesn't clip the last letter or shave the descender. The ink\n  // dimensions (printable.widthIn \u00D7 printable.heightIn) remain authoritative\n  // for pricing and variant matching \u2014 the pad only affects the PNG canvas.\n  /* Scan a canvas for the bounding box of every non-transparent pixel.\n     Returns { x, y, w, h } in canvas-pixel units, or null if the canvas\n     is entirely transparent. */\n  function scanInkBounds(canvas) {\n    const cw = canvas.width, ch = canvas.height;\n    const data = canvas.getContext('2d').getImageData(0, 0, cw, ch).data;\n    let minX = cw, maxX = -1, minY = ch, maxY = -1;\n    for (let y = 0; y < ch; y++) {\n      const rowStart = y * cw * 4;\n      for (let x = 0; x < cw; x++) {\n        if (data[rowStart + x * 4 + 3] > 0) {\n          if (x < minX) minX = x;\n          if (x > maxX) maxX = x;\n          if (y < minY) minY = y;\n          if (y > maxY) maxY = y;\n        }\n      }\n    }\n    if (maxX < 0) return null;\n    return { x: minX, y: minY, w: maxX - minX + 1, h: maxY - minY + 1 };\n  }\n\n  /* Render a single-ink printable (kind 'name' or 'number') to its own\n     temp canvas. Used by renderEntryPNG to render name and number\n     independently of each other, so pair items can be composited with\n     true-pixel-center alignment. */\n  function renderSingleInkToCanvas(text, kind, heightIn, ink, scaleX, cfg, fontDef, colorHex) {\n    const initialPad = Math.max(0.5, heightIn * 0.1);\n    const printable = { kind, text, heightIn, widthIn: ink.widthIn, scaleX, _ink: ink };\n    const packed = {\n      placements: [{ item: printable, x: initialPad, y: initialPad, w: ink.widthIn, h: heightIn }],\n      totalHeightIn: heightIn + initialPad * 2,\n      widthIn: ink.widthIn + initialPad * 2,\n    };\n    return renderExportToCanvas(packed, cfg, fontDef, colorHex);\n  }\n\n  async function renderEntryPNG(printable, cfg, fontDef, colorHex) {\n    /* Two-canvas pixel-scan render. The old single-canvas approach used\n       measureText().actualBoundingBox* to size the canvas and position\n       each ink piece, but that API under-reports the visible glyph extent\n       for tall display bold fonts (Anton, Oswald, Black Ops One) at 8\"+\n       heights \u2014 causing both clipping (glyphs flush against edges) and\n       miscentering (name + number not stacked centered on each other).\n\n       Fix: render each ink piece (name and number for pairs, or the one\n       text for singles) to its own generously-padded temp canvas, pixel-\n       scan each to find its TRUE rendered bounds, then composite into a\n       final canvas with each piece exactly center-aligned by actual\n       pixels. Output is tight to (ink + 0.15\" production bleed), never\n       larger, never clipped. */\n    const FINAL_PAD_PX = Math.round(0.15 * EXPORT_DPI);\n    const maxW = cfg.maxItemWidthIn || Infinity;\n\n    /* Each \"piece\" gets rendered + scanned independently. For pair items\n       this is the name on top and number below; for single-mode items\n       this is just the one text. Same code path for both \u2014 pieces are\n       stacked vertically in the order given.\n\n       Per-piece scaleX (rather than the pair-level scaleX from\n       measurePrintables) so a long name doesn't drag the number into the\n       same horizontal compression: customer expects an over-wide name to\n       squeeze on its own row while the number stays at natural aspect. */\n    const pieces = [];\n    if (printable.kind === 'pair') {\n      const nameSX = printable._nameInk.widthIn > maxW ? maxW / printable._nameInk.widthIn : 1;\n      const numSX  = printable._numInk.widthIn  > maxW ? maxW / printable._numInk.widthIn  : 1;\n      pieces.push({ text: printable.name,   kind: 'name',   heightIn: printable.nameHeightIn, ink: printable._nameInk, scaleX: nameSX });\n      pieces.push({ text: printable.number, kind: 'number', heightIn: printable.numHeightIn,  ink: printable._numInk,  scaleX: numSX });\n    } else {\n      const sx = printable._ink.widthIn > maxW ? maxW / printable._ink.widthIn : 1;\n      pieces.push({ text: printable.text, kind: printable.kind, heightIn: printable.heightIn, ink: printable._ink, scaleX: sx });\n    }\n\n    const rendered = pieces.map(p => {\n      const canvas = renderSingleInkToCanvas(p.text, p.kind, p.heightIn, p.ink, p.scaleX, cfg, fontDef, colorHex);\n      const bounds = scanInkBounds(canvas);\n      return { canvas, bounds };\n    }).filter(r => r.bounds);\n\n    /* All pieces empty (e.g., upstream wired a blank cart line). Return\n       a 1x1 transparent PNG so the cart-add upload still succeeds. */\n    if (rendered.length === 0) {\n      const empty = document.createElement('canvas');\n      empty.width = 1; empty.height = 1;\n      return new Promise(resolve => empty.toBlob(b => resolve(b), 'image/png'));\n    }\n\n    /* Compute final canvas dimensions from actual pixel bounds. For pairs\n       the inter-piece gap is the configured nameNumGapIn (1.0\" by default\n       \u2014 the visual breathing room between jersey name and number). */\n    const gapPx = (pieces.length > 1) ? Math.round(cfg.nameNumGapIn * EXPORT_DPI) : 0;\n    const maxInkW = Math.max(...rendered.map(r => r.bounds.w));\n    const totalInkH = rendered.reduce((s, r) => s + r.bounds.h, 0);\n    const totalGapH = gapPx * (rendered.length - 1);\n    const finalW = maxInkW + FINAL_PAD_PX * 2;\n    const finalH = totalInkH + totalGapH + FINAL_PAD_PX * 2;\n\n    const finalCanvas = document.createElement('canvas');\n    finalCanvas.width  = finalW;\n    finalCanvas.height = finalH;\n    const finalCtx = finalCanvas.getContext('2d');\n\n    /* Composite each piece into the final canvas: horizontally centered\n       (by ACTUAL pixel width \u2014 no measureText errors), stacked vertically\n       in pieces[] order with the gap between consecutive pieces. */\n    let yCursor = FINAL_PAD_PX;\n    for (let i = 0; i < rendered.length; i++) {\n      const r = rendered[i];\n      const xDest = Math.round((finalW - r.bounds.w) / 2);\n      finalCtx.drawImage(\n        r.canvas,\n        r.bounds.x, r.bounds.y, r.bounds.w, r.bounds.h,  // source rect (the scanned ink)\n        xDest,      yCursor,    r.bounds.w, r.bounds.h   // dest rect (centered in final)\n      );\n      yCursor += r.bounds.h + (i < rendered.length - 1 ? gapPx : 0);\n    }\n\n    return new Promise(resolve => finalCanvas.toBlob(b => resolve(b), 'image/png'));\n  }\n\n  // Count cart items whose product title qualifies them for the bySize\n  // cumulative volume-discount tier. Mirrors snippets/__alreadyItemsInCart.liquid\n  // (DTF Transfers By Size / UV DTF / Puff) and adds Names & Numbers since\n  // those are now first-class bySize-style lines too. Returns 0 on failure\n  // so the tier highlight degrades gracefully to roster-only.\n  async function fetchCartTransferQty() {\n    try {\n      const res = await fetch('/cart.js', { credentials: 'include' });\n      if (!res.ok) {\n        console.warn('[N&N] /cart.js HTTP', res.status);\n        return 0;\n      }\n      const cart = await res.json();\n      const QUALIFIERS = [\n        'dtf transfers',     // matches \"DTF Transfers By Size\", \"DTF Transfers Names & Numbers\", etc.\n        'uv dtf',\n        'puff',\n        'names & numbers',\n        'names &amp; numbers',\n      ];\n      let total = 0;\n      const trace = [];\n      (cart.items || []).forEach(item => {\n        const productTitle = String(item.product_title || '').toLowerCase();\n        const lineTitle    = String(item.title || '').toLowerCase();\n        const qualifies = QUALIFIERS.some(q =>\n          productTitle.indexOf(q) !== -1 || lineTitle.indexOf(q) !== -1\n        );\n        trace.push({ title: item.product_title, qty: item.quantity, qualifies });\n        if (qualifies) total += (item.quantity || 0);\n      });\n      console.log('[N&N] cart qualifier trace:', trace, '\u2192 total', total);\n      return total;\n    } catch (e) {\n      console.warn('[N&N] /cart.js fetch failed', e);\n      return 0;\n    }\n  }\n\n  async function uploadBlob(blob, cfg) {\n    const randomName = `names-and-numbers-${Date.now()}-${Math.floor(Math.random() * 1e6)}.png`;\n    const presignUrl = `${cfg.apiUrl}/uploads/getPresignedUploadUrl?file_name=${encodeURIComponent(randomName)}&file_type=${encodeURIComponent('image/png')}`;\n    const presignRes = await fetch(presignUrl);\n    if (!presignRes.ok) throw new Error(`presign HTTP ${presignRes.status}`);\n    const presign = await presignRes.json();\n    if (!presign.url) throw new Error('presign response missing url');\n    const put = await fetch(presign.url, { method: 'PUT', body: blob, headers: { 'Content-Type': 'image/png' } });\n    if (!put.ok) throw new Error(`S3 PUT HTTP ${put.status}`);\n    const imgixUrl = `https://${cfg.imgixHost}/${randomName}`;\n    return { imgixUrl, sourceUrl: presign.sourceUrl || imgixUrl };\n  }\n\n  // ---------- helpers ----------\n\n  function readJSON(root, sel) {\n    const node = root.querySelector(sel);\n    if (!node) return [];\n    try { return JSON.parse(node.textContent); } catch { return []; }\n  }\n  function numAttr(node, name, dflt) {\n    const v = parseFloat(node.getAttribute(name));\n    return Number.isFinite(v) ? v : dflt;\n  }\n  function el(tag, cls, attrs) {\n    const e = document.createElement(tag);\n    if (cls) e.className = cls;\n    if (attrs) Object.entries(attrs).forEach(([k, v]) => e.setAttribute(k, v));\n    return e;\n  }\n  function makeId() { return 'e' + Math.random().toString(36).slice(2, 9); }\n  function isLightHex(hex) {\n    if (!hex || hex[0] !== '#') return false;\n    const x = hex.length === 4\n      ? [parseInt(hex[1] + hex[1], 16), parseInt(hex[2] + hex[2], 16), parseInt(hex[3] + hex[3], 16)]\n      : [parseInt(hex.slice(1, 3), 16), parseInt(hex.slice(3, 5), 16), parseInt(hex.slice(5, 7), 16)];\n    return (0.299 * x[0] + 0.587 * x[1] + 0.114 * x[2]) / 255 > 0.85;\n  }\n  function loadFont(font) {\n    try {\n      return document.fonts.load(`${font.weight || 700} 48px \"${font.family}\"`).then(() => font);\n    } catch { return Promise.resolve(font); }\n  }\n  function scopeLabel(scope) {\n    return scope === 'both' ? 'Names + Numbers' : scope === 'names' ? 'Names only' : 'Numbers only';\n  }\n  function serializeEntries(state) {\n    return state.entries\n      .filter(e => (e.name || '').trim() || (e.number || '').trim())\n      .map(e => {\n        const qty = Math.max(1, parseInt(e.qty, 10) || 1);\n        const qtySuffix = qty > 1 ? ` \u00D7${qty}` : '';\n        if (state.scope === 'names')   return `${e.name || ''}${qtySuffix}`;\n        if (state.scope === 'numbers') return `${e.number || ''}${qtySuffix}`;\n        return `${e.name || ''}|${e.number || ''}${qtySuffix}`;\n      })\n      .join('\\n');\n  }\n  function findClosestHeightId(heights, targetInches) {\n    if (!heights || !heights.length) return null;\n    let best = heights[0];\n    let bestDiff = Math.abs(best.inches - targetInches);\n    heights.forEach(h => {\n      const diff = Math.abs(h.inches - targetInches);\n      if (diff < bestDiff) { best = h; bestDiff = diff; }\n    });\n    return best.id;\n  }\n  function escapeHTML(s) {\n    return String(s).replace(/[&<>\"']/g, c => ({ '&':'&amp;', '<':'&lt;', '>':'&gt;', '\"':'&quot;', \"'\":'&#39;' })[c]);\n  }\n  function parseCsv(text) {\n    const lines = text.split(/\\r?\\n/).map(l => l.trim()).filter(l => l.length > 0);\n    if (lines.length === 0) return [];\n    // Detect header row (case-insensitive match on \"name\" or \"number\")\n    const first = lines[0].toLowerCase();\n    const hasHeader = /name|number|qty/.test(first);\n    const dataLines = hasHeader ? lines.slice(1) : lines;\n    // Determine column order from header if present\n    let cols = ['name', 'number', 'qty'];\n    if (hasHeader) {\n      const headerCells = splitCsvLine(lines[0]).map(c => c.trim().toLowerCase());\n      cols = headerCells.map(c => (c === 'name' || c === 'number' || c === 'qty') ? c : c);\n    }\n    return dataLines.map(line => {\n      const cells = splitCsvLine(line);\n      const row = { name: '', number: '', qty: 1 };\n      cols.forEach((col, i) => {\n        const val = (cells[i] || '').trim();\n        if (col === 'name')   row.name = val;\n        if (col === 'number') row.number = val.slice(0, 3);\n        if (col === 'qty')    row.qty = Math.max(1, parseInt(val, 10) || 1);\n      });\n      return row;\n    }).filter(r => r.name || r.number);\n  }\n\n  function splitCsvLine(line) {\n    // Minimal RFC-4180 CSV splitter: supports quoted fields with embedded commas.\n    const out = [];\n    let cur = '';\n    let inQuotes = false;\n    for (let i = 0; i < line.length; i++) {\n      const ch = line[i];\n      if (inQuotes) {\n        if (ch === '\"' && line[i + 1] === '\"') { cur += '\"'; i++; }\n        else if (ch === '\"') { inQuotes = false; }\n        else { cur += ch; }\n      } else {\n        if (ch === '\"') inQuotes = true;\n        else if (ch === ',') { out.push(cur); cur = ''; }\n        else { cur += ch; }\n      }\n    }\n    out.push(cur);\n    return out;\n  }\n\n  function parseBulk(raw) {\n    const lines = raw.split(/\\r?\\n/).map(l => l.trim()).filter(l => l.length > 0);\n    return lines.map(line => {\n      // Accept formats: \"Smith, 23\" | \"Smith,23\" | \"Smith 23\" | \"Smith\\t23\"\n      const m = line.match(/^([^\\d,;\\t]+?)\\s*[,;\\t\\s]+\\s*(\\d+)\\s*(?:[x\u00D7]\\s*(\\d+))?$/i);\n      if (m) {\n        return { name: m[1].trim(), number: m[2].slice(0, 3), qty: m[3] ? parseInt(m[3], 10) : 1 };\n      }\n      // Number-only line\n      const justNum = line.match(/^(\\d+)\\s*(?:[x\u00D7]\\s*(\\d+))?$/);\n      if (justNum) {\n        return { name: '', number: justNum[1].slice(0, 3), qty: justNum[2] ? parseInt(justNum[2], 10) : 1 };\n      }\n      // Name-only line\n      return { name: line, number: '', qty: 1 };\n    });\n  }\n\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', bootAll);\n  } else {\n    bootAll();\n  }\n})();\n"],
  "mappings": "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",
  "names": ["e"]
}
