{"product_id":"c1410001","title":"8Mm Excel 4V Brewery Oak - 2.131M2 Pk - Ac4 - Wg Surface Finish - 8 X 193 X 1380Mm","description":"\u003c!-- FLOORING CALCULATOR WITH UNDERLAY CHOICE (compat hotfix) --\u003e\n\u003cdiv id=\"flooring-calc-simple\" data-underlay-standard-variant-id=\"51593326395734\" data-underlay-premium-variant-id=\"51593338683734\" style=\"margin:16px 0;\"\u003e\n  \u003cdiv class=\"fc-wrap\"\u003e\n    \u003ch3 class=\"fc-h\"\u003eLaminate Flooring Calculator\u003c\/h3\u003e\n    \u003cp class=\"fc-lead\"\u003eEnter your room size in \u003cstrong\u003emetres\u003c\/strong\u003e or \u003cstrong\u003efeet\u003c\/strong\u003e. We'll add waste and work out how many packs and underlay rolls you need.\u003c\/p\u003e\n\n    \u003cdiv class=\"fc-card fc-meta\"\u003e\n      \u003cdiv class=\"fc-field\"\u003e\n        \u003clabel\u003eCoverage per pack\u003c\/label\u003e\n        \u003cdiv class=\"fc-static\"\u003e2.131 m² (fixed)\u003c\/div\u003e\n      \u003c\/div\u003e\n      \u003cdiv class=\"fc-field\"\u003e\n        \u003clabel\u003eWaste \/ offcuts (%)\u003c\/label\u003e\n        \u003cinput type=\"number\" step=\"1\" min=\"0\" max=\"30\" value=\"10\" data-fc-waste\u003e\n      \u003c\/div\u003e\n      \u003cdiv class=\"fc-field\"\u003e\n        \u003clabel\u003eRounding\u003c\/label\u003e\n        \u003cselect data-fc-rounding\u003e\n          \u003coption value=\"ceil\" selected\u003eAlways round up\u003c\/option\u003e\n          \u003coption value=\"nearest\"\u003eNearest whole\u003c\/option\u003e\n        \u003c\/select\u003e\n      \u003c\/div\u003e\n    \u003c\/div\u003e\n\n    \u003cdiv class=\"fc-card\"\u003e\n      \u003cdiv class=\"fc-underlay\"\u003e\n        \u003cdiv class=\"fc-field\"\u003e\n          \u003clabel\u003eChoose Premium ProVent underlay or Standard White Foam underlay\u003c\/label\u003e\n          \u003cselect data-fc-underlay-type\u003e\n            \u003coption value=\"none\"\u003eNo underlay\u003c\/option\u003e\n            \u003coption value=\"std\"\u003eStandard White Foam (25 m² roll)\u003c\/option\u003e\n            \u003coption value=\"pro\" selected\u003ePremium ProVent (15 m² roll)\u003c\/option\u003e\n          \u003c\/select\u003e\n        \u003c\/div\u003e\n        \u003cdiv class=\"fc-field fc-field--checkbox\"\u003e\n          \u003clabel class=\"fc-check\"\u003e\n            \u003cinput type=\"checkbox\" data-fc-include-underlay checked\u003e\n            \u003cspan\u003eInclude selected underlay in Add to Cart\u003c\/span\u003e\n          \u003c\/label\u003e\n        \u003c\/div\u003e\n      \u003c\/div\u003e\n    \u003c\/div\u003e\n\n    \u003c!-- Rooms --\u003e\n    \u003cdiv class=\"fc-rooms\" data-fc-rooms\u003e\u003c\/div\u003e\n    \u003cdiv class=\"fc-actions-inline\"\u003e\n      \u003cbutton type=\"button\" class=\"fc-btn fc-btn--primary\" data-fc-add-room\u003e+ Add additional room\u003c\/button\u003e\n    \u003c\/div\u003e\n\n    \u003c!-- Totals --\u003e\n    \u003cdiv class=\"fc-totals\"\u003e\n      \u003cdiv class=\"fc-stat\"\u003e\n\u003cdiv class=\"fc-label\"\u003eTotal (m²) before waste\u003c\/div\u003e\n\u003cdiv class=\"fc-value\" data-fc-total\u003e0.00 m²\u003c\/div\u003e\n\u003c\/div\u003e\n      \u003cdiv class=\"fc-stat\"\u003e\n\u003cdiv class=\"fc-label\"\u003eWaste added\u003c\/div\u003e\n\u003cdiv class=\"fc-value\" data-fc-waste-added\u003e0.00 m²\u003c\/div\u003e\n\u003c\/div\u003e\n      \u003cdiv class=\"fc-stat\"\u003e\n\u003cdiv class=\"fc-label\"\u003eGrand total\u003c\/div\u003e\n\u003cdiv class=\"fc-value\" data-fc-grand\u003e0.00 m²\u003c\/div\u003e\n\u003c\/div\u003e\n      \u003cdiv class=\"fc-stat\"\u003e\n\u003cdiv class=\"fc-label\"\u003ePacks required\u003c\/div\u003e\n\u003cdiv class=\"fc-value\" data-fc-packs\u003e0\u003c\/div\u003e\n\u003c\/div\u003e\n      \u003cdiv class=\"fc-stat\"\u003e\n\u003cdiv class=\"fc-label\"\u003eUnderlay (rolls)\u003c\/div\u003e\n\u003cdiv class=\"fc-value\" data-fc-underlay\u003e0 (—)\u003c\/div\u003e\n\u003c\/div\u003e\n    \u003c\/div\u003e\n    \u003cp class=\"fc-note\" data-fc-surplus-wrap style=\"display:none;\"\u003eEstimated leftover after rounding: \u003cspan data-fc-surplus\u003e0.00\u003c\/span\u003e m².\u003c\/p\u003e\n\n    \u003cdiv class=\"fc-actions\"\u003e\n      \u003cbutton type=\"button\" class=\"fc-btn\" data-fc-reset\u003eReset\u003c\/button\u003e\n      \u003cbutton type=\"button\" class=\"fc-btn fc-btn--primary\" data-fc-add-to-cart\u003eAdd to cart\u003c\/button\u003e\n    \u003c\/div\u003e\n    \u003cp class=\"fc-small\"\u003e1 ft = 0.3048 m,  1 ft² = 0.092903 m²\u003c\/p\u003e\n  \u003c\/div\u003e\n\n  \u003ctemplate data-fc-room-tpl\u003e\n    \u003cdiv class=\"fc-room\"\u003e\n      \u003cdiv class=\"fc-row\"\u003e\n        \u003cdiv\u003e\n\u003clabel\u003eRoom name\u003c\/label\u003e\u003cinput type=\"text\" placeholder=\"e.g. Living room\" data-fc-name\u003e\n\u003c\/div\u003e\n        \u003cdiv\u003e\n\u003clabel\u003eLength\u003c\/label\u003e\u003cinput type=\"number\" step=\"0.01\" min=\"0\" placeholder=\"0.00\" data-fc-length\u003e\n\u003c\/div\u003e\n        \u003cdiv\u003e\n\u003clabel\u003eWidth\u003c\/label\u003e\u003cinput type=\"number\" step=\"0.01\" min=\"0\" placeholder=\"0.00\" data-fc-width\u003e\n\u003c\/div\u003e\n        \u003cdiv\u003e\n\u003clabel\u003eUnits\u003c\/label\u003e\n          \u003cselect data-fc-unit\u003e\u003coption value=\"m\" selected\u003emetres\u003c\/option\u003e\n\u003coption value=\"ft\"\u003efeet\u003c\/option\u003e\u003c\/select\u003e\n        \u003c\/div\u003e\n        \u003cdiv\u003e\u003cbutton type=\"button\" class=\"fc-btn\" data-fc-remove\u003e×\u003c\/button\u003e\u003c\/div\u003e\n      \u003c\/div\u003e\n      \u003cdiv class=\"fc-note\" data-fc-room-summary\u003eArea: 0.00 m²\u003c\/div\u003e\n    \u003c\/div\u003e\n  \u003c\/template\u003e\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n  #flooring-calc-simple, #flooring-calc-simple * { box-sizing: border-box; }\n  #flooring-calc-simple label { float:none!important; }\n  #flooring-calc-simple input, #flooring-calc-simple select { max-width:100%; }\n  #flooring-calc-simple{font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,Arial}\n  .fc-wrap{background:#f8fafc;border:1px solid #e5e7eb;border-radius:14px;padding:14px; width:100%; max-width:100%;}\n  .fc-h{margin:0 0 6px}\n  .fc-lead{margin:0 0 12px;color:#4b5563}\n  .fc-card{background:#fff;border:1px solid #e5e7eb;border-radius:12px;padding:10px;margin-bottom:10px}\n  .fc-meta{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}\n  .fc-field label{display:block;font-size:12px;color:#6b7280;margin-bottom:6px}\n  .fc-field input,.fc-field select{width:100%;padding:10px 12px;border:1px solid #e5e7eb;border-radius:10px;background:#fff}\n  .fc-static{padding:10px 12px;border:1px dashed #e5e7eb;border-radius:10px;background:#f9fafb}\n  .fc-btn{appearance:none;border:1px solid #e5e7eb;background:#fff;border-radius:10px;padding:10px 12px;color:#111827;font-weight:600;cursor:pointer}\n  .fc-btn--primary{background:#22c55e;color:#fff;border:0}\n  .fc-rooms{display:grid;gap:10px}\n  .fc-room{border:1px dashed #e5e7eb;border-radius:12px;padding:10px}\n  .fc-row{display:grid;grid-template-columns:1.2fr 1fr 1fr .9fr auto;gap:10px;align-items:end}\n  .fc-underlay{display:grid;grid-template-columns:1fr 1fr;gap:10px;align-items:end}\n  .fc-check{display:flex;align-items:center;gap:8px}\n  .fc-totals{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin:10px 0}\n  .fc-stat{background:#fff;border:1px solid #e5e7eb;border-radius:12px;padding:10px}\n  .fc-label{font-size:12px;color:#6b7280}\n  .fc-value{font-size:18px;font-weight:700;margin-top:4px}\n  .fc-actions,.fc-actions-inline{display:flex;gap:10px;align-items:center;margin-top:6px;flex-wrap:wrap}\n  .fc-small{color:#6b7280;font-size:12px;margin-top:6px}\n  @media(max-width: 640px){\n    .fc-wrap{padding:12px}\n    .fc-meta{grid-template-columns:1fr;}\n    .fc-underlay{grid-template-columns:1fr;}\n    .fc-row{grid-template-columns:1fr;}\n    .fc-row \u003e div:last-child{justify-self:flex-start}\n    .fc-totals{grid-template-columns:1fr 1fr;}\n    .fc-actions,.fc-actions-inline{justify-content:stretch}\n    .fc-actions .fc-btn,.fc-actions-inline .fc-btn{width:100%}\n  }\n  @media(min-width:641px) and (max-width:900px){\n    .fc-meta{grid-template-columns:1fr 1fr;}\n    .fc-underlay{grid-template-columns:1fr 1fr;}\n    .fc-row{grid-template-columns:1fr 1fr 1fr 1fr auto}\n    .fc-totals{grid-template-columns:1fr 1fr 1fr}\n  }\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function(){\n  const root=document.getElementById('flooring-calc-simple');\n  if(!root) return;\n  const $=(s,r=root)=\u003er.querySelector(s);\n  const $$=(s,r=root)=\u003eArray.from(r.querySelectorAll(s));\n\n  const FT2_TO_M2=0.09290304, COVERAGE_M2=2.131, ROLL_STD_M2=25, ROLL_PRO_M2=15;\n  const state={ grand:0 };\n\n  const roomsWrap=$('[data-fc-rooms]');\n  const tpl=$('[data-fc-room-tpl]');\n  const wasteInput=$('[data-fc-waste]');\n  const roundingSel=$('[data-fc-rounding]');\n  const addBtn=$('[data-fc-add-room]');\n  const resetBtn=$('[data-fc-reset]');\n  const addToCartBtn=$('[data-fc-add-to-cart]');\n  const underlayTypeSel=$('[data-fc-underlay-type]');\n  const includeUnderlayChk=$('[data-fc-include-underlay]');\n  const totalEl=$('[data-fc-total]');\n  const wasteAddedEl=$('[data-fc-waste-added]');\n  const grandEl=$('[data-fc-grand]');\n  const packsEl=$('[data-fc-packs]');\n  const surplusWrap=$('[data-fc-surplus-wrap]');\n  const surplusEl=$('[data-fc-surplus]');\n  const underlayEl=$('[data-fc-underlay]');\n\n  function toNumber(v){const n=parseFloat(v);return isFinite(n)?n:0;}\n  function format(n,d=2){return (Math.round((n+Number.EPSILON)*(10**d))\/(10**d)).toFixed(d);}\n\n  function createRoom(){\n    const html=(tpl\u0026\u0026tpl.content\u0026\u0026tpl.content.firstElementChild)?tpl.innerHTML.trim():`\u003cdiv class=\"fc-room\"\u003e\u003cdiv class=\"fc-row\"\u003e\u003cdiv\u003e\u003clabel\u003eRoom name\u003c\/label\u003e\u003cinput type=\"text\" data-fc-name\u003e\u003c\/div\u003e\u003cdiv\u003e\u003clabel\u003eLength\u003c\/label\u003e\u003cinput type=\"number\" step=\"0.01\" min=\"0\" data-fc-length\u003e\u003c\/div\u003e\u003cdiv\u003e\u003clabel\u003eWidth\u003c\/label\u003e\u003cinput type=\"number\" step=\"0.01\" min=\"0\" data-fc-width\u003e\u003c\/div\u003e\u003cdiv\u003e\u003clabel\u003eUnits\u003c\/label\u003e\u003cselect data-fc-unit\u003e\u003coption value=\"m\"\u003emetres\u003c\/option\u003e\u003coption value=\"ft\"\u003efeet\u003c\/option\u003e\u003c\/select\u003e\u003c\/div\u003e\u003cdiv\u003e\u003cbutton type=\"button\" class=\"fc-btn\" data-fc-remove\u003e\u0026times;\u003c\/button\u003e\u003c\/div\u003e\u003c\/div\u003e\u003cdiv class=\"fc-note\" data-fc-room-summary\u003eArea: 0.00 m²\u003c\/div\u003e\u003c\/div\u003e`;\n    const wrap=document.createElement('div');wrap.innerHTML=html;\n    const node=wrap.firstElementChild;\n    node.addEventListener('input',update);\n    const rem=$('[data-fc-remove]',node); if(rem) rem.addEventListener('click',()=\u003e{node.remove();update();});\n    return node;\n  }\n\n  function roomAreaM2(room){\n    const L=toNumber($('[data-fc-length]',room).value);\n    const W=toNumber($('[data-fc-width]',room).value);\n    const unit=$('[data-fc-unit]',room).value;\n    if(L\u003c=0||W\u003c=0)return 0;\n    let area=L*W; if(unit==='ft')area*=FT2_TO_M2; return area;\n  }\n\n  function currentUnderlayRollM2(){\n    const t=underlayTypeSel?.value||'none';\n    if(t==='std')return ROLL_STD_M2;\n    if(t==='pro')return ROLL_PRO_M2;\n    return 0;\n  }\n\n  function update(){\n    const wastePct=Math.max(0,Math.min(100,toNumber(wasteInput.value)));\n    let sum=0;\n    $$('.fc-room').forEach(r=\u003e{const a=roomAreaM2(r);sum+=a;$('[data-fc-room-summary]',r).textContent=`Area: ${format(a)} m²`;});\n    const wasteAdded=sum*(wastePct\/100);\n    const grand=sum+wasteAdded;\n    state.grand=grand;\n\n    totalEl.textContent=`${format(sum)} m²`;\n    wasteAddedEl.textContent=`${format(wasteAdded)} m²`;\n    grandEl.textContent=`${format(grand)} m²`;\n\n    const packsRaw=grand\/COVERAGE_M2;\n    const rounding=roundingSel.value;\n    const packs=rounding==='ceil'?Math.ceil(packsRaw):Math.round(packsRaw);\n    packsEl.textContent=isFinite(packs)?packs:0;\n\n    const leftover=Math.max(0,packs*COVERAGE_M2-grand);\n    surplusWrap.style.display=packs\u003e0?'block':'none';\n    surplusEl.textContent=format(leftover);\n\n    const rollSize=currentUnderlayRollM2();\n    const rolls=rollSize\u003e0?Math.ceil(grand\/rollSize):0;\n    const label=underlayTypeSel.value==='std'?`Standard White Foam (${ROLL_STD_M2} m²)`:underlayTypeSel.value==='pro'?`Premium ProVent (${ROLL_PRO_M2} m²)`:'—';\n    underlayEl.textContent=`${rolls} (${label})`;\n  }\n\n  function findCurrentVariantId(){\n    const idInput=document.querySelector('form[action*=\"\/cart\/add\"] input[name=\"id\"][value]');\n    if(idInput\u0026\u0026idInput.value)return idInput.value;\n    const selected=document.querySelector('[data-product-id] [name=\"id\"]');\n    if(selected\u0026\u0026selected.value)return selected.value;\n    try{\n      const pd=window?.ShopifyAnalytics?.meta?.product||null;\n      if(pd\u0026\u0026pd.variants\u0026\u0026pd.variants.length){\n        const v=pd.variants.find(v=\u003ev.available)||pd.variants[0];\n        if(v\u0026\u0026v.id)return String(v.id);\n      }\n    }catch(e){}\n    return null;\n  }\n\n  \/\/ MOST COMPATIBLE multi-add (form), then JSON, then sequential\n  async function addMultipleItems(items){\n    \/\/ A) Classic form-encoded multi-add to \/cart\/add\n    try{\n      const formParams=new URLSearchParams();\n      items.forEach((it,i)=\u003e{\n        formParams.append(`items[${i}][id]`, String(it.id));\n        formParams.append(`items[${i}][quantity]`, String(it.quantity));\n      });\n      const res=await fetch('\/cart\/add',{method:'POST',headers:{'Content-Type':'application\/x-www-form-urlencoded; charset=UTF-8'},body:formParams});\n      if(res.ok) return true;\n      throw new Error('form multi-add failed');\n    }catch(e){\n      \/\/ B) JSON multi-add to \/cart\/add.js\n      try{\n        const res=await fetch('\/cart\/add.js',{method:'POST',headers:{'Content-Type':'application\/json'},body:JSON.stringify({items})});\n        if(!res.ok) throw new Error('json multi-add failed');\n        return true;\n      }catch(e2){\n        \/\/ C) Sequential adds to \/cart\/add.js\n        for(const it of items){\n          const body=new URLSearchParams({id:String(it.id),quantity:String(it.quantity)});\n          const res=await fetch('\/cart\/add.js',{method:'POST',headers:{'Content-Type':'application\/x-www-form-urlencoded; charset=UTF-8'},body});\n          if(!res.ok) return false;\n        }\n        return true;\n      }\n    }\n  }\n\n  function getUnderlayVariantId(){\n    const t=underlayTypeSel?.value||'none';\n    if(t==='std')return root.getAttribute('data-underlay-standard-variant-id');\n    if(t==='pro')return root.getAttribute('data-underlay-premium-variant-id');\n    return null;\n  }\n\n  \/\/ Init\n  roomsWrap.appendChild(createRoom());\n  update();\n\n  \/\/ Events\n  addBtn.addEventListener('click',()=\u003e{roomsWrap.appendChild(createRoom());update();});\n  wasteInput.addEventListener('input',update);\n  roundingSel.addEventListener('change',update);\n  underlayTypeSel.addEventListener('change',update);\n  if(resetBtn) resetBtn.addEventListener('click',()=\u003e{roomsWrap.innerHTML='';roomsWrap.appendChild(createRoom());update();});\n\n  if(addToCartBtn) addToCartBtn.addEventListener('click',async()=\u003e{\n    const packsQty=parseInt(packsEl.textContent,10)||0;\n    const rollSize=currentUnderlayRollM2();\n    const underlayQty=rollSize\u003e0?Math.ceil(state.grand\/rollSize):0;\n\n    const includeUnderlay=includeUnderlayChk?.checked;\n    const floorVar=findCurrentVariantId();\n    const underVar=getUnderlayVariantId();\n\n    if(!floorVar||packsQty\u003c=0){ alert('Enter room sizes before adding to cart.'); return; }\n\n    const items=[{id:String(floorVar),quantity:packsQty}];\n    if(includeUnderlay \u0026\u0026 underVar \u0026\u0026 underlayQty\u003e0){ items.push({id:String(underVar),quantity:underlayQty}); }\n\n    try{\n      const ok=await addMultipleItems(items);\n      if(!ok) throw new Error('add failed');\n      document.dispatchEvent(new CustomEvent('cart:refresh'));\n      const drawer=document.querySelector('[data-cart-drawer], cart-drawer, #CartDrawer');\n      if(drawer){ drawer.dispatchEvent(new CustomEvent('cart:open')); }\n      else { window.location.href='\/cart'; }\n    }catch(e){ window.location.href='\/cart'; }\n  });\n})();\n\u003c\/script\u003e\n\n\u003cp\u003e\u003cmeta charset=\"UTF-8\"\u003e\u003cspan\u003eThe EXCEL 4V range will win you over with its unique range of decors. 8mm boards with AC4 abrasion class rating can be used throughout the home, bringing a natural and durable wood look to your home’s spaces. Floors that are made to be enjoyed for a long time. EXCEL 4V features a 4V four way bevel on all edges.\u003c\/span\u003e\u003c\/p\u003e","brand":"Whiteriver Group","offers":[{"title":"Default Title","offer_id":50330842267990,"sku":"C1410001","price":25.55,"currency_code":"EUR","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0302\/0446\/1193\/files\/BREWERY_OAK_2.13M2_SPECIAL.png?v=1761145046","url":"https:\/\/www.dynamitehardware.com\/products\/c1410001","provider":"Dynamite Hardware","version":"1.0","type":"link"}