{"product_id":"1-16-table-top-wound-tracker-choose-a-colour","title":"1-16 Table top Wound Tracker (Choose a Colour)","description":"\u003cstyle\u003e\n  .bundle-wrapper {\n    display: flex;\n    gap: 30px;\n    flex-wrap: wrap;\n    justify-content: space-between;\n    align-items: flex-start;\n  }\n\n  .bundle-table {\n    flex: 1;\n    min-width: 300px;\n  }\n\n  .discount-calculator {\n    flex: 0 0 350px;\n    border: 1px solid #ccc;\n    border-radius: 10px;\n    padding: 20px;\n    font-family: Arial, sans-serif;\n    background: #faad16;\n    color: white;\n    opacity: 0;\n    transform: translateY(20px);\n    animation: fadeInUp 1s ease forwards;\n    animation-delay: 0.3s;\n  }\n\n  @keyframes fadeInUp {\n    to {\n      opacity: 1;\n      transform: translateY(0);\n    }\n  }\n\n  .discount-calculator h3,\n  .discount-calculator label,\n  .discount-calculator p,\n  .discount-calculator span {\n    color: white;\n  }\n\n  .discount-calculator input {\n    background-color: white;\n    color: black;\n    border: none;\n    border-radius: 5px;\n    padding: 8px;\n    width: 100%;\n    margin: 10px 0;\n  }\n\n  @media (max-width: 768px) {\n    .discount-calculator {\n      display: none;\n    }\n  }\n\u003c\/style\u003e\n\n\u003cdiv class=\"bundle-wrapper\"\u003e\n  \u003c!-- Pricing Table --\u003e\n  \u003cdiv class=\"bundle-table\"\u003e\n    \u003ctable style=\"width: 100%; border-collapse: collapse; text-align: center; font-family: Arial, sans-serif;\"\u003e\n      \u003cthead\u003e\n        \u003ctr style=\"background-color: #f2f2f2;\"\u003e\n          \u003cth style=\"border: 1px solid #ddd; padding: 8px;\"\u003eQuantity\u003c\/th\u003e\n          \u003cth style=\"border: 1px solid #ddd; padding: 8px;\"\u003eTotal Each (£)\u003c\/th\u003e\n          \u003cth style=\"border: 1px solid #ddd; padding: 8px;\"\u003eNew Discounted Total (£)\u003c\/th\u003e\n          \u003cth style=\"border: 1px solid #ddd; padding: 8px;\"\u003eSaving (£)\u003c\/th\u003e\n        \u003c\/tr\u003e\n      \u003c\/thead\u003e\n      \u003ctbody\u003e\n        \u003ctr\u003e\n          \u003ctd style=\"border: 1px solid #ddd; padding: 8px;\"\u003e1\u003c\/td\u003e\n          \u003ctd style=\"border: 1px solid #ddd; padding: 8px;\"\u003e\u003cstrong\u003e£2.99\u003c\/strong\u003e\u003c\/td\u003e\n          \u003ctd style=\"border: 1px solid #ddd; padding: 8px;\"\u003e£2.99\u003c\/td\u003e\n          \u003ctd style=\"border: 1px solid #ddd; padding: 8px;\"\u003e–\u003c\/td\u003e\n        \u003c\/tr\u003e\n        \u003ctr\u003e\n          \u003ctd style=\"border: 1px solid #ddd; padding: 8px;\"\u003e3+\u003c\/td\u003e\n          \u003ctd style=\"border: 1px solid #ddd; padding: 8px;\"\u003e\u003cstrong\u003e£2.84\u003c\/strong\u003e\u003c\/td\u003e\n          \u003ctd style=\"border: 1px solid #ddd; padding: 8px;\"\u003e£8.52\u003c\/td\u003e\n          \u003ctd style=\"border: 1px solid #ddd; padding: 8px;\"\u003e£0.45 \/ \u003cstrong\u003e5% Off\u003c\/strong\u003e\n\u003c\/td\u003e\n        \u003c\/tr\u003e\n        \u003ctr\u003e\n          \u003ctd style=\"border: 1px solid #ddd; padding: 8px;\"\u003e5+\u003c\/td\u003e\n          \u003ctd style=\"border: 1px solid #ddd; padding: 8px;\"\u003e\u003cstrong\u003e£2.69\u003c\/strong\u003e\u003c\/td\u003e\n          \u003ctd style=\"border: 1px solid #ddd; padding: 8px;\"\u003e£13.46\u003c\/td\u003e\n          \u003ctd style=\"border: 1px solid #ddd; padding: 8px;\"\u003e£1.49 \/ \u003cstrong\u003e10% Off\u003c\/strong\u003e\n\u003c\/td\u003e\n        \u003c\/tr\u003e\n        \u003ctr\u003e\n          \u003ctd style=\"border: 1px solid #ddd; padding: 8px;\"\u003e10+\u003c\/td\u003e\n          \u003ctd style=\"border: 1px solid #ddd; padding: 8px;\"\u003e\u003cstrong\u003e£2.54\u003c\/strong\u003e\u003c\/td\u003e\n          \u003ctd style=\"border: 1px solid #ddd; padding: 8px;\"\u003e£25.42\u003c\/td\u003e\n          \u003ctd style=\"border: 1px solid #ddd; padding: 8px;\"\u003e£4.49 \/ \u003cstrong\u003e15% Off\u003c\/strong\u003e\n\u003c\/td\u003e\n        \u003c\/tr\u003e\n        \u003ctr\u003e\n          \u003ctd style=\"border: 1px solid #ddd; padding: 8px;\"\u003e15+\u003c\/td\u003e\n          \u003ctd style=\"border: 1px solid #ddd; padding: 8px;\"\u003e£2.39\u003c\/td\u003e\n          \u003ctd style=\"border: 1px solid #ddd; padding: 8px;\"\u003e£35.88\u003c\/td\u003e\n          \u003ctd style=\"border: 1px solid #ddd; padding: 8px;\"\u003e£8.97 \/ \u003cstrong\u003e20% Off\u003c\/strong\u003e\n\u003c\/td\u003e\n        \u003c\/tr\u003e\n      \u003c\/tbody\u003e\n    \u003c\/table\u003e\n  \u003c\/div\u003e\n\n  \u003c!-- Discount Calculator (Desktop only) --\u003e\n  \u003cdiv class=\"discount-calculator\"\u003e\n    \u003ch3 style=\"margin-top: 0;\"\u003eMulti-Buy Calculator\u003c\/h3\u003e\n    \u003clabel for=\"quantity\"\u003eEnter Quantity:\u003c\/label\u003e\n    \u003cinput type=\"number\" id=\"quantity\" min=\"1\" value=\"1\"\u003e\n\n    \u003cp\u003e\u003cstrong\u003eTotal Each:\u003c\/strong\u003e \u003cspan id=\"pricePerItem\"\u003e£2.99\u003c\/span\u003e\u003c\/p\u003e\n    \u003cp\u003e\u003cstrong\u003eNew Discounted Total:\u003c\/strong\u003e \u003cspan id=\"totalPrice\"\u003e£2.99\u003c\/span\u003e\u003c\/p\u003e\n    \u003cp\u003e\u003cstrong\u003eSavings:\u003c\/strong\u003e \u003cspan id=\"savings\"\u003e–\u003c\/span\u003e\u003c\/p\u003e\n  \u003c\/div\u003e\n\u003c\/div\u003e\n\n\u003c!-- Description Section --\u003e\n\u003cdiv style=\"margin-top: 40px; font-family: Arial, sans-serif;\"\u003e\n  \u003ch3\u003e🔥 The Only Clickable Wound Tracker for Warhammer 40K\u003c\/h3\u003e\n  \u003cp\u003eTired of losing track of wounds mid-game?\u003c\/p\u003e\n  \u003cp\u003eThis is the \u003cstrong\u003eONLY clickable wound tracker\u003c\/strong\u003e on the market – designed to \u003cstrong\u003elock each wound in place\u003c\/strong\u003e so nothing shifts when you pick it up or adjust your models.\u003c\/p\u003e\n\n  \u003ch4\u003e✅ Why You'll Love It:\u003c\/h4\u003e\n  \u003cul\u003e\n    \u003cli\u003e🔒 \u003cstrong\u003eSecure click-to-lock mechanism\u003c\/strong\u003e – no more knocked dials or lost wounds\u003c\/li\u003e\n    \u003cli\u003e🔢 \u003cstrong\u003eClear, bold numbers\u003c\/strong\u003e – easy to read at a glance\u003c\/li\u003e\n    \u003cli\u003e🎯 \u003cstrong\u003eSmooth \u0026amp; satisfying clicks\u003c\/strong\u003e – precise control over tracking\u003c\/li\u003e\n    \u003cli\u003e🧱 \u003cstrong\u003eDurable and battle-ready\u003c\/strong\u003e – built to last through heavy use\u003c\/li\u003e\n    \u003cli\u003e🎨 \u003cstrong\u003eMinimalist design\u003c\/strong\u003e – complements your painted armies perfectly\u003c\/li\u003e\n    \u003cli\u003e🛠️ \u003cstrong\u003eCompatible with Warhammer 40K, Kill Team, AoS \u0026amp; more\u003c\/strong\u003e\n\u003c\/li\u003e\n  \u003c\/ul\u003e\n\n  \u003cp\u003eWhether you're fighting for glory at a tournament or battling friends at home, this wound tracker gives you the \u003cstrong\u003eaccuracy and confidence\u003c\/strong\u003e every commander needs.\u003c\/p\u003e\n  \u003cp\u003e\u003cstrong\u003eNo more guesswork. No more accidental resets. Just reliable, click-by-click tracking.\u003c\/strong\u003e\u003c\/p\u003e\n\u003c\/div\u003e\n\n\u003cscript\u003e\n(() =\u003e {\n  \/\/ --- Config ---\n  const DISCOUNTS = [\n    { min: 15, rate: 0.20, label: \"20% Off\" },\n    { min: 10, rate: 0.15, label: \"15% Off\" },\n    { min: 5,  rate: 0.10, label: \"10% Off\" },\n    { min: 3,  rate: 0.05, label: \"5% Off\"  },\n    { min: 1,  rate: 0.00, label: \"\"        },\n  ];\n  const currencyFormatter = new Intl.NumberFormat('en-GB', {\n    style: 'currency', currency: 'GBP', minimumFractionDigits: 2\n  });\n\n  let currentVariantPrice = null; \/\/ in GBP (e.g., 12.99)\n\n  \/\/ --- Price helpers ---\n  function getDiscountForQty(qty) {\n    for (const tier of DISCOUNTS) {\n      if (qty \u003e= tier.min) return tier;\n    }\n    return { rate: 0, label: \"\" };\n  }\n\n  function readPriceFromDOM() {\n    \/\/ Try a few common selectors used by Shopify themes\n    const candidates = [\n      '[data-product-price]',\n      '.price .price-item--regular',\n      '.product__price .price-item--regular',\n      '.price__regular .price-item',\n      '[itemprop=\"price\"]',\n      '[data-price]'\n    ];\n    for (const sel of candidates) {\n      const el = document.querySelector(sel);\n      if (el \u0026\u0026 el.textContent) {\n        const raw = el.textContent.replace(\/[^\\d.,]\/g, '').replace(',', '.');\n        const val = parseFloat(raw);\n        if (!Number.isNaN(val)) return val;\n      }\n    }\n    return null;\n  }\n\n  function readVariantFromJSON() {\n    \/\/ Works on most OS 2.0 themes\n    const jsonEl = document.querySelector('script[type=\"application\/json\"][data-product], script[id*=\"ProductJson\"]');\n    if (!jsonEl) return null;\n    try {\n      const product = JSON.parse(jsonEl.textContent);\n      const qsId = new URLSearchParams(location.search).get('variant');\n      if (qsId \u0026\u0026 product.variants) {\n        const v = product.variants.find(v =\u003e String(v.id) === String(qsId));\n        if (v) return v;\n      }\n      \/\/ fallback: first available or first variant\n      return (product.variants || []).find(v =\u003e v.available) || (product.variants || [])[0] || null;\n    } catch { return null; }\n  }\n\n  function getBasePriceGBP() {\n    \/\/ 1) From live variant change listener\n    if (typeof currentVariantPrice === 'number') return currentVariantPrice;\n\n    \/\/ 2) From DOM price\n    const domVal = readPriceFromDOM();\n    if (typeof domVal === 'number') return domVal;\n\n    \/\/ 3) From product JSON\n    const v = readVariantFromJSON();\n    if (v \u0026\u0026 typeof v.price === 'number') return v.price \/ 100; \/\/ JSON is in cents\n\n    \/\/ 4) Fallback to your default\n    return 2.99;\n  }\n\n  \/\/ --- Calculator binding ---\n  function bindCalculator() {\n    const qtyInput = document.getElementById('quantity');\n    const pricePerItemEl = document.getElementById('pricePerItem');\n    const totalPriceEl    = document.getElementById('totalPrice');\n    const savingsEl       = document.getElementById('savings');\n\n    if (!qtyInput || !pricePerItemEl || !totalPriceEl || !savingsEl) return;\n\n    \/\/ Avoid double-binding after re-renders\n    if (qtyInput.dataset.bound === '1') return;\n    qtyInput.dataset.bound = '1';\n\n    const recalc = () =\u003e {\n      const qty = Math.max(1, parseInt(qtyInput.value, 10) || 1);\n      const { rate, label } = getDiscountForQty(qty);\n      const base = getBasePriceGBP();\n\n      const totalFull = base * qty;\n      const discountAmount = totalFull * rate;\n      const totalDiscounted = totalFull - discountAmount;\n      const perItem = totalDiscounted \/ qty;\n\n      pricePerItemEl.textContent = currencyFormatter.format(perItem);\n      totalPriceEl.textContent   = currencyFormatter.format(totalDiscounted);\n      savingsEl.textContent      = rate \u003e 0\n        ? `${currencyFormatter.format(discountAmount)} \/ ${label}`\n        : '–';\n    };\n\n    qtyInput.addEventListener('input', recalc);\n    recalc(); \/\/ initial\n  }\n\n  \/\/ --- React to variant changes (modern Shopify themes dispatch this) ---\n  document.addEventListener('variant:change', (e) =\u003e {\n    if (e \u0026\u0026 e.detail \u0026\u0026 e.detail.variant \u0026\u0026 typeof e.detail.variant.price === 'number') {\n      currentVariantPrice = e.detail.variant.price \/ 100; \/\/ convert cents-\u003eGBP\n      bindCalculator();\n      \/\/ Recalculate immediately if qty exists\n      const qtyInput = document.getElementById('quantity');\n      if (qtyInput) qtyInput.dispatchEvent(new Event('input', { bubbles: true }));\n    }\n  });\n\n  \/\/ --- Re-bind after any DOM updates\/section re-renders ---\n  const mo = new MutationObserver(() =\u003e { bindCalculator(); });\n  mo.observe(document.body, { childList: true, subtree: true });\n\n  \/\/ --- Initial bind on page load ---\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', bindCalculator, { once: true });\n  } else {\n    bindCalculator();\n  }\n\n  \/\/ --- Also handle URL param changes (?variant=...) without full reload ---\n  window.addEventListener('popstate', bindCalculator);\n})();\n\u003c\/script\u003e\n","brand":"Fat Dwarf","offers":[{"title":"Red","offer_id":55446630269304,"sku":"15047844200835","price":2.99,"currency_code":"GBP","in_stock":true},{"title":"Yellow","offer_id":55446630302072,"sku":"15047844200836","price":2.99,"currency_code":"GBP","in_stock":true},{"title":"Black","offer_id":55446630334840,"sku":"15047844200837","price":2.99,"currency_code":"GBP","in_stock":true},{"title":"Blue","offer_id":55446630367608,"sku":"15047844200838","price":2.99,"currency_code":"GBP","in_stock":true},{"title":"Green","offer_id":55446630400376,"sku":"15047844200839","price":2.99,"currency_code":"GBP","in_stock":true},{"title":"Silver","offer_id":55446630433144,"sku":"15047844200840","price":2.99,"currency_code":"GBP","in_stock":true},{"title":"Gold","offer_id":55446630465912,"sku":"15047844200841","price":2.99,"currency_code":"GBP","in_stock":true},{"title":"White","offer_id":55446630498680,"sku":"sku-55091019481475","price":2.99,"currency_code":"GBP","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0775\/9439\/4944\/files\/1-16-table-top-wound-tracker-choose-a-colour-fireside-fat-dwarf-2449181.png?v=1770478637","url":"https:\/\/nightspiregaming.co.uk\/de\/products\/1-16-table-top-wound-tracker-choose-a-colour","provider":"Night Spire Gaming","version":"1.0","type":"link"}