{"product_id":"garage-portrait","title":"Cut My Ride - UV Printed Steel Garage Portrait","description":"\u003cstyle data-test-hide-2026-11-05\u003e#shopify-section-product-template \u003e div:has(svg[viewBox=\"0 0 300 160\"]){display:none!important}\u003c\/style\u003e\r\n\u003cp\u003eYour own vehicle in full color on steel, cut to its shape. Add a name or plate cut through a steel band under it, or leave it off.\u003c\/p\u003e\r\n\u003ch3\u003eWhat you send\u003c\/h3\u003e\r\n\u003cp\u003eOne photo of your whole vehicle. We send you a proof before we cut.\u003c\/p\u003e\r\n\u003ch3\u003eWhat you get\u003c\/h3\u003e\r\n\u003cp\u003eYour portrait and a template. Hangs on two screws.\u003c\/p\u003e\r\n\u003cp\u003eCut and printed in our Tennessee shop. Made to order. Cut after you order.\u003c\/p\u003e\r\n\u003ch3\u003eSizes\u003c\/h3\u003e\r\n\u003cp\u003e24 in $179.95.\u003c\/p\u003e\r\n\u003cp\u003e36 in $259.95. 48 in $359.95.\u003c\/p\u003e\r\n\u003ch3\u003eCare\u003c\/h3\u003e\r\n\u003cp\u003eHang in the garage or shop, or on a covered porch.\u003c\/p\u003e\r\n\u003c!-- File route: archived NMA ProductForm FormData guard in before-b1live\/10359662673968.json. --\u003e\r\n\u003cstyle\u003e\n#pz-fields{width:100%;margin:8px 0 22px;color:inherit}#pz-fields *{box-sizing:border-box}\n#pz-fields label{display:block;font-size:14px;margin-bottom:6px}#pz-fields .pz-field{margin:18px 0 0}\n#pz-fields input{width:100%;border:1px solid #b9b0ad;border-radius:0;background:#fff;color:#292421;padding:12px;font:inherit;font-size:16px}\n#pz-fields input:focus-visible{outline:2px solid #51372d;outline-offset:3px}\n#pz-fields .pz-meta{display:flex;justify-content:space-between;gap:12px;margin-top:5px;font-size:12px}\n#pz-name{text-transform:uppercase}\n#pz-message{color:#a01818}\n\u003c\/style\u003e\r\n\u003cdiv id=\"pz-fields\"\u003e\r\n\u003cdiv class=\"pz-field\"\u003e\n\u003clabel for=\"pz-name\"\u003eName or plate to cut (optional)\u003c\/label\u003e\r\n\u003cinput id=\"pz-name\" type=\"text\" name=\"properties[Name or plate to cut]\" form=\"product_form_10367341264944\" maxlength=\"14\" pattern=\"[A-Za-z0-9 ]{1,14}\" autocomplete=\"off\" aria-describedby=\"pz-help pz-name-count pz-message\"\u003e\r\n\u003cdiv class=\"pz-meta\"\u003e\n\u003cspan id=\"pz-help\"\u003eOptional. Up to 14 letters or numbers. Leave it blank for the vehicle only.\u003c\/span\u003e\u003cspan id=\"pz-name-count\"\u003e0\/14\u003c\/span\u003e\n\u003c\/div\u003e\n\u003c\/div\u003e\r\n\u003cdiv class=\"pz-field\"\u003e\n\u003clabel for=\"pz-photo\"\u003eVehicle photo\u003c\/label\u003e\r\n\u003cinput id=\"pz-photo\" type=\"file\" name=\"properties[Vehicle photo]\" form=\"product_form_10367341264944\" accept=\"image\/jpeg,image\/png\" required aria-describedby=\"pz-photo-help pz-message\"\u003e\r\n\u003csmall id=\"pz-photo-help\"\u003eOne photo of the whole vehicle from the side. Nothing cut off. Send it full size from your phone.\u003c\/small\u003e\n\u003c\/div\u003e\r\n\u003cp id=\"pz-message\" role=\"alert\" aria-live=\"polite\" hidden\u003e\u003c\/p\u003e\r\n\u003c\/div\u003e\r\n\u003cscript\u003e\r\n(function(){\r\n  var FORM_ID=\"product_form_10367341264944\",busy=false;\r\n  function productForm(){\r\n    var select=document.getElementById('product-select-'+FORM_ID.replace('product_form_',''));\r\n    return (select\u0026\u0026select.closest('form.ProductForm'))||document.getElementById(FORM_ID);\r\n  }\r\n  function mount(){\r\n    var form=productForm(),fields=document.getElementById('pz-fields');\r\n    var button=form\u0026\u0026form.querySelector('.ProductForm__AddToCart,button[name=\"add\"],input[name=\"add\"],[data-add-to-cart],.add_to_cart,.add-to-cart');\r\n    if(!form||!fields||!button)return;\r\n    if(!form.id)form.id=FORM_ID;\r\n    fields.querySelectorAll('input').forEach(function(input){input.setAttribute('form',form.id);});\r\n    form.enctype='multipart\/form-data';\r\n    if(fields.parentNode!==button.parentNode||fields.nextElementSibling!==button)button.parentNode.insertBefore(fields,button);\r\n  }\r\n  function stop(event){event.preventDefault();event.stopImmediatePropagation();}\r\n  function check(event){\r\n    mount();\r\n    var form=productForm(),name=document.getElementById('pz-name'),photo=document.getElementById('pz-photo');\r\n    var message=document.getElementById('pz-message');\r\n    if(!form||!name||!photo||!message){stop(event);return;}\r\n    var text=name.value.trim()\u0026\u0026!\/^[A-Za-z0-9 ]{1,14}$\/.test(name.value)?'Use up to 14 letters, numbers and spaces.':'';\r\n    name.setCustomValidity(text);name.setAttribute('aria-invalid',text?'true':'false');\r\n    var invalid=text?name:!photo.files||photo.files.length!==1?photo:null;\r\n    if(!text\u0026\u0026invalid)text='One photo of the whole vehicle from the side. Nothing cut off. Send it full size from your phone.';\r\n    photo.setAttribute('aria-invalid',invalid===photo?'true':'false');\r\n    message.textContent=text;message.hidden=!text;\r\n    if(invalid){stop(event);invalid.reportValidity();invalid.focus();return;}\r\n    stop(event);\r\n    if(busy||!form.reportValidity())return;\r\n    var button=form.querySelector('.ProductForm__AddToCart,button[name=\"add\"],input[name=\"add\"],[data-add-to-cart],.add_to_cart,.add-to-cart');\r\n    if(!button||button.disabled)return;\r\n    var data=new FormData(form);\r\n    \/* Set explicitly too: the field may sit outside the form in this theme. *\/\r\n    data.set(name.name,name.value.trim()||'None (vehicle only)');data.set(photo.name,photo.files[0]);\r\n    busy=true;button.disabled=true;form.setAttribute('aria-busy','true');\r\n    fetch('\/cart\/add.js',{method:'POST',body:data,headers:{Accept:'application\/json'}})\r\n      .then(function(response){if(!response.ok)throw new Error('cart');return response.json();})\r\n      .then(function(){window.location.assign('\/cart');})\r\n      .catch(function(){\r\n        busy=false;button.disabled=false;form.removeAttribute('aria-busy');\r\n        message.textContent='Photo upload failed. Try again.';message.hidden=false;photo.focus();\r\n      });\r\n  }\r\n  window.addEventListener('click',function(event){\r\n    var button=event.target.closest\u0026\u0026event.target.closest('button,input[type=\"submit\"],input[type=\"image\"]');\r\n    if(!button||button.form!==productForm())return;\r\n    if(button.name!=='add'\u0026\u0026!button.classList.contains('ProductForm__AddToCart')\u0026\u0026!button.classList.contains('add_to_cart')\u0026\u0026!button.classList.contains('add-to-cart')\u0026\u0026!button.hasAttribute('data-add-to-cart'))return;\r\n    check(event);\r\n  },true);\r\n  window.addEventListener('submit',function(event){if(event.target===productForm())check(event);},true);\r\n  function initialize(){\r\n    mount();\r\n    var name=document.getElementById('pz-name'),photo=document.getElementById('pz-photo');\r\n    name.addEventListener('input',function(){\r\n      name.setCustomValidity('');name.setAttribute('aria-invalid','false');\r\n      document.getElementById('pz-name-count').textContent=name.value.length+'\/14';\r\n      document.getElementById('pz-message').hidden=true;\r\n    });\r\n    photo.addEventListener('change',function(){photo.setAttribute('aria-invalid','false');document.getElementById('pz-message').hidden=true;});\r\n  }\r\n  if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',initialize,{once:true});\r\n  else initialize();\r\n})();\r\n\u003c\/script\u003e\r\n\u003cscript\u003e(function(){\"use strict\";const API_BASE=\"https:\/\/nma-preview.vercel.app\";const STROKE=3\/8,MARGIN=1.25,SIDE_MARGIN=0.5;const WALL=\"#b9b3ab\",STEEL=\"#141414\";const COPY={proof:\"Made from your photo. Your emailed proof shows the exact cut.\",privacy:\"Your photo is used for this preview and your proof.\",fallback:\"Preview is unavailable right now. You still get a proof before we cut.\",small:\"This photo is small. A full size photo from your phone prints sharper.\",edge:\"Your whole vehicle must be inside the photo.\",fit:\"Shorter text fits this size.\"};function bandDepth(size){return Math.max(3.5,Math.round(0.14*size*4)\/4);}function capHeight(size){return 0.6*bandDepth(size);}function normalizeName(text){const value=String(text||\"\").trim();return value.toUpperCase()===\"NONE (VEHICLE ONLY)\"?\"\":value.replace(\/[^A-Za-z0-9 ]\/g,\"\").toUpperCase().trim();}function bandMode(text,components=1){return normalizeName(text)?\"lettered\":components\u003e1?\"plain\":\"none\";}function parseSize(value){const size=parseInt(value,10);return[24,36,48].includes(size)?size:null;}function fitText(text,size,bandWidth,atlas){text=normalizeName(text);const cells=Array.from(text,char=\u003eatlas.glyphs[char]);if(cells.some(cell=\u003e!cell))return{fits:false,capHeight:0,width:0,cells:[]};const units=cells.reduce((sum,cell)=\u003esum+cell.advance_px,0)+Math.max(0,cells.length-1)*atlas.tracking_px;const baseWidth=units\/atlas.grid_px_per_in;const cap=Math.min(capHeight(size),baseWidth?Math.max(0,bandWidth-2*SIDE_MARGIN)\/baseWidth*atlas.cap_height_in:capHeight(size));return{fits:cap\u003e=1.5,capHeight:cap,width:baseWidth*cap\/atlas.cap_height_in,scale:cap\/atlas.cap_height_in\/atlas.grid_px_per_in,cells};}function regions(bits,width,height,value=1){const seen=new Uint8Array(bits.length),queue=new Int32Array(bits.length),result=[];for(let start=0;start\u003cbits.length;start++){if(seen[start]||bits[start]!==value)continue;let head=0,tail=1,left=width,right=0,top=height,bottom=0,edge=false;queue[0]=start;seen[start]=1;while(head\u003ctail){const index=queue[head++],x=index%width,y=Math.floor(index\/width);left=Math.min(left,x);right=Math.max(right,x+1);top=Math.min(top,y);bottom=Math.max(bottom,y+1);if(!x||!y||x===width-1||y===height-1)edge=true;for(const next of[x?index-1:-1,x\u003cwidth-1?index+1:-1,y?index-width:-1,y\u003cheight-1?index+width:-1]){if(next\u003e=0\u0026\u0026!seen[next]\u0026\u0026bits[next]===value){seen[next]=1;queue[tail++]=next;}}}result.push({left,right,top,bottom,edge,pixels:queue.slice(0,tail)});}return result;}function fillHoles(bits,width,height,pixelsPerInch){const filled=bits.slice();for(const region of regions(bits,width,height,0)){if(!region.edge\u0026\u0026Math.max(region.right-region.left,region.bottom-region.top)\u003cpixelsPerInch){for(const index of region.pixels)filled[index]=1;}}return filled;}function filterSpecks(bits,width,height){const minimum=bits.reduce((sum,value)=\u003esum+value,0)*0.02;const filtered=bits.slice();for(const region of regions(bits,width,height)){if(region.pixels.length\u003cminimum)for(const index of region.pixels)filtered[index]=0;}return filtered;}function touchesEdge(bits,width,height){for(let x=0;x\u003cwidth;x++)if(bits[x]||bits[(height-1)*width+x])return true;for(let y=0;y\u003cheight;y++)if(bits[y*width]||bits[y*width+width-1])return true;return false;}function distanceTransform(bits,width,height){const distances=new Float64Array(bits.length),length=Math.max(width,height);const input=new Float64Array(length),output=new Float64Array(length);const sites=new Int32Array(length),boundaries=new Float64Array(length+1);function line(count){let k=0;sites[0]=0;boundaries[0]=-Infinity;boundaries[1]=Infinity;for(let q=1;q\u003ccount;q++){let intersection;do{const v=sites[k];intersection=((input[q]+q*q)-(input[v]+v*v))\/(2*q-2*v);if(intersection\u003eboundaries[k])break;k--;}while(k\u003e=0);k++;sites[k]=q;boundaries[k]=intersection;boundaries[k+1]=Infinity;}k=0;for(let q=0;q\u003ccount;q++){while(boundaries[k+1]\u003cq)k++;output[q]=(q-sites[k])**2+input[sites[k]];}}for(let y=0;y\u003cheight;y++){for(let x=0;x\u003cwidth;x++)input[x]=bits[y*width+x]?0:1e12;line(width);for(let x=0;x\u003cwidth;x++)distances[y*width+x]=output[x];}for(let x=0;x\u003cwidth;x++){for(let y=0;y\u003cheight;y++)input[y]=distances[y*width+x];line(height);for(let y=0;y\u003cheight;y++)distances[y*width+x]=output[y];}return distances;}function dilate(bits,width,height,radius){const distances=distanceTransform(bits,width,height);return Uint8Array.from(distances,distance=\u003edistance\u003c=radius*radius?1:0);}function erode(bits,width,height,radius){const outside=dilate(Uint8Array.from(bits,bit=\u003e1-bit),width,height,radius);return Uint8Array.from(outside,bit=\u003e1-bit);}const core={bandDepth,capHeight,normalizeName,bandMode,fitText,parseSize,regions,fillHoles,filterSpecks,touchesEdge,dilate,erode,distanceTransform,SIDE_MARGIN,STROKE,MARGIN,COPY};if(typeof module!==\"undefined\"\u0026\u0026module.exports)module.exports=core;if(typeof document===\"undefined\")return;let atlasPromise;async function glyphAtlas(){if(!atlasPromise){atlasPromise=(async function(){const controller=new AbortController();const timer=setTimeout(()=\u003econtroller.abort(),20000);try{const response=await fetch(API_BASE+\"\/gp-glyphs.json\",{credentials:\"omit\",signal:controller.signal});if(!response.ok)throw Error(\"glyphs\");const atlas=await response.json();if(Object.keys(atlas.glyphs||{}).length!==37)throw Error(\"glyphs\");await Promise.all(Object.values(atlas.glyphs).map(async glyph=\u003e{const image=new Image();image.src=\"data:image\/png;base64,\"+glyph.png;await image.decode();glyph.image=image;}));return atlas;}finally{clearTimeout(timer);}}()).catch(error=\u003e{atlasPromise=null;throw error;});}return atlasPromise;}function canvasOf(width,height){const element=document.createElement(\"canvas\");element.width=width;element.height=height;return element;}function bitmapCanvas(bits,width,height,color){const element=canvasOf(width,height),context=element.getContext(\"2d\");const image=context.createImageData(width,height);for(let index=0;index\u003cbits.length;index++)if(bits[index]){image.data.set([color,color,color,255],index*4);}context.putImageData(image,0,0);return element;}function productForm(fields){const selector='form[action*=\"\/cart\/add\"]',associated=document.getElementById(\"pz-photo\")?.form;return fields.closest(selector)||(associated?.matches(selector)?associated:null)||document.querySelector(selector);}function selectedSize(fields){const form=productForm(fields);if(!form)return 36;const selected=form.querySelectorAll('select option:checked,input[type=\"radio\"]:checked');for(const option of selected){const size=parseSize(option.value)||parseSize(option.textContent)||parseSize(option.getAttribute(\"data-option-value\"))||parseSize(option.labels?.[0]?.textContent?.trim());if(size)return size;}return 36;}function mount(fields){if(document.getElementById(\"pz-preview\"))return;const photo=document.getElementById(\"pz-photo\"),name=document.getElementById(\"pz-name\");const photoField=photo?.closest(\".pz-field\");if(!photoField||!name)return;const figure=document.createElement(\"figure\");figure.id=\"pz-preview\";figure.hidden=true;figure.style.cssText=\"margin:20px 0;width:100%;max-width:600px\";const heading=document.createElement(\"h3\");heading.textContent=\"Preview\";const canvas=document.createElement(\"canvas\");canvas.setAttribute(\"aria-label\",\"Vehicle preview\");canvas.style.cssText=\"display:block;width:100%;max-width:600px;height:auto\";const state=document.createElement(\"p\");state.setAttribute(\"role\",\"status\");state.setAttribute(\"aria-live\",\"polite\");const proof=document.createElement(\"span\");proof.textContent=COPY.proof;proof.style.display=\"block\";const privacy=document.createElement(\"small\");privacy.textContent=COPY.privacy;privacy.style.display=\"block\";const notice=document.createElement(\"span\");notice.style.display=\"block\";state.append(proof,privacy,notice);figure.append(heading,canvas,state);photoField.after(figure);let current,geometry,generation=0,controller,scheduled=false;function fallback(){canvas.hidden=true;canvas.style.display=\"none\";notice.textContent=COPY.fallback;figure.hidden=false;}function guarded(action){return function(...args){try{return action(...args);}catch(_){fallback();}};}function prepare(){if(!current||current.edge)return;const size=selectedSize(fields),cssWidth=Math.min(600,figure.clientWidth||600);const ratio=window.devicePixelRatio||1,width=Math.max(1,Math.round(cssWidth*ratio));const ppi=width\/(size+2*MARGIN),margin=MARGIN*ppi;const box=current.box,vehicleWidth=size*ppi,vehicleHeight=(box.bottom-box.top)\/(box.right-box.left)*vehicleWidth;const height=Math.ceil(vehicleHeight+2*margin+bandDepth(size)*ppi);const alpha=canvasOf(width,height),a=alpha.getContext(\"2d\",{willReadFrequently:true});a.drawImage(current.mask,box.left,box.top,box.right-box.left,box.bottom-box.top,margin,margin,vehicleWidth,vehicleHeight);const pixels=a.getImageData(0,0,width,height).data;const photoBits=Uint8Array.from({length:width*height},(_,i)=\u003epixels[i*4]\u003e=128?1:0);const bits=fillHoles(photoBits,width,height,ppi);const halo=fillHoles(dilate(bits,width,height,STROKE*ppi),width,height,ppi);const parts=regions(halo,width,height);const left=Math.min(...parts.map(part=\u003epart.left)),right=Math.max(...parts.map(part=\u003epart.right));a.clearRect(0,0,width,height);a.drawImage(current.photoMask,box.left,box.top,box.right-box.left,box.bottom-box.top,margin,margin,vehicleWidth,vehicleHeight);const clipPixels=a.getImageData(0,0,width,height).data;const clipBits=fillHoles(Uint8Array.from({length:width*height},(_,i)=\u003ephotoBits[i]\u0026\u0026clipPixels[i*4]\u003e=160?1:0),width,height,ppi);const photoClip=erode(clipBits,width,height,Math.max(2,ppi\/32));const clipped=canvasOf(width,height),c=clipped.getContext(\"2d\");c.drawImage(current.photo,box.left,box.top,box.right-box.left,box.bottom-box.top,margin,margin,vehicleWidth,vehicleHeight);c.globalCompositeOperation=\"destination-in\";c.filter=\"blur(1px)\";c.drawImage(bitmapCanvas(photoClip,width,height,255),0,0);c.filter=\"none\";const steel=bitmapCanvas(halo,width,height,20),layer=canvasOf(width,height);const top=margin+vehicleHeight-ppi\/64;geometry={size,ppi,width,height,margin,vehicleHeight,left,right,top,parts,steel,clipped,layer,ratio};canvas.width=width;canvas.height=height;}function draw(){if(!current||!geometry||current.edge)return;const g=geometry,text=normalizeName(name.value),mode=bandMode(text,g.parts.length);const context=g.layer.getContext(\"2d\"),fit=fitText(text,g.size,(g.right-g.left)\/g.ppi,current.atlas);context.clearRect(0,0,g.width,g.height);context.drawImage(g.steel,0,0);let top=g.top,depth=bandDepth(g.size)*g.ppi;if(mode===\"lettered\"\u0026\u0026g.parts.some(part=\u003epart.bottom\u003c=top)){fallback();return;}if(mode===\"plain\"){top=Math.min(...g.parts.map(part=\u003epart.bottom))-STROKE*g.ppi;depth+=g.top-top;}if(mode!==\"none\"){context.fillStyle=STEEL;context.fillRect(g.left,top,g.right-g.left,depth);}if(mode===\"lettered\"\u0026\u0026fit.fits){let x=(g.left+g.right-fit.width*g.ppi)\/2;context.globalCompositeOperation=\"destination-out\";for(const cell of fit.cells){const scale=fit.scale*g.ppi,h=cell.height_px*scale;context.drawImage(cell.image,x,top+(depth-h)\/2,cell.width_px*scale,h);x+=(cell.advance_px+current.atlas.tracking_px)*scale;}context.globalCompositeOperation=\"source-over\";}context.drawImage(g.clipped,0,0);const visibleHeight=mode===\"none\"?Math.ceil(g.vehicleHeight+2*g.margin):g.height;if(canvas.height!==visibleHeight)canvas.height=visibleHeight;const output=canvas.getContext(\"2d\");output.fillStyle=WALL;output.fillRect(0,0,g.width,canvas.height);output.save();output.shadowColor=\"rgba(0,0,0,0.35)\";output.shadowBlur=0.5*g.ppi;output.shadowOffsetX=0.25*g.ppi;output.shadowOffsetY=0.5*g.ppi;output.drawImage(g.layer,0,0);output.restore();canvas.hidden=false;canvas.style.display=\"block\";notice.textContent=[current.small?COPY.small:\"\",mode===\"lettered\"\u0026\u0026!fit.fits?COPY.fit:\"\"].filter(Boolean).join(\" \");canvas.dataset.ppi=String(g.ppi);canvas.dataset.bandTop=String(top);canvas.dataset.bandDepth=String(mode===\"none\"?0:depth);canvas.dataset.bandLeft=String(g.left);canvas.dataset.bandRight=String(g.right);canvas.dataset.bandMode=mode;}const redraw=guarded(function(rebuild){if(rebuild)prepare();draw();});name.addEventListener(\"input\",guarded(function(){const start=name.selectionStart,end=name.selectionEnd,upper=name.value.replace(\/[a-z]\/g,char=\u003echar.toUpperCase());if(name.value!==upper){name.value=upper;name.setSelectionRange(start,end);}redraw(false);}));const form=productForm(fields),variants='select,input[type=\"radio\"],input[type=\"hidden\"][name=\"id\"]';if(form){form.addEventListener(\"change\",guarded(event=\u003e{if(event.target.matches(variants))redraw(true);}));if(typeof MutationObserver!==\"undefined\")new MutationObserver(guarded(records=\u003e{if(records.some(record=\u003erecord.target.matches('input[type=\"hidden\"][name=\"id\"]')))redraw(true);})).observe(form,{subtree:true,attributes:true,attributeFilter:[\"value\"]});}let observedWidth=-1;if(typeof ResizeObserver!==\"undefined\")new ResizeObserver(guarded(()=\u003e{const width=figure.clientWidth;if(width===observedWidth)return;observedWidth=width;if(!scheduled){scheduled=true;requestAnimationFrame(()=\u003e{scheduled=false;redraw(true);});}})).observe(figure);photo.addEventListener(\"change\",async function(){const token=++generation;if(controller)controller.abort();if(current)current.photo.close();current=null;geometry=null;const file=photo.files?.[0];figure.hidden=true;if(!file)return;controller=new AbortController();const requestController=controller;let timer,bitmap;const timeout=new Promise((_,reject)=\u003e{timer=setTimeout(()=\u003e{requestController.abort();reject(Error(\"timeout\"));},20000);});try{const job=(async function(){const atlasJob=glyphAtlas();const photoJob=(async function(){bitmap=await createImageBitmap(file,{imageOrientation:\"from-image\"});if(requestController.signal.aborted){bitmap.close();throw Error(\"cancelled\");}const small=Math.max(bitmap.width,bitmap.height)\u003c3000;const scale=Math.min(1,1024\/Math.max(bitmap.width,bitmap.height));const upload=canvasOf(Math.max(1,Math.round(bitmap.width*scale)),Math.max(1,Math.round(bitmap.height*scale)));upload.getContext(\"2d\").drawImage(bitmap,0,0,upload.width,upload.height);const blob=await new Promise(resolve=\u003eupload.toBlob(resolve,\"image\/jpeg\",0.85));if(!blob||blob.size\u003e4000000)throw Error(\"encode\");const response=await fetch(API_BASE+\"\/api\/mask\",{method:\"POST\",body:blob,headers:{\"Content-Type\":\"image\/jpeg\"},credentials:\"omit\",signal:requestController.signal});if(!response.ok)throw Error(\"mask\");const maskImage=await createImageBitmap(await response.blob());try{if(maskImage.width!==upload.width||maskImage.height!==upload.height)throw Error(\"mask size\");const maskCanvas=canvasOf(maskImage.width,maskImage.height),m=maskCanvas.getContext(\"2d\",{willReadFrequently:true});m.drawImage(maskImage,0,0);const pixels=m.getImageData(0,0,maskImage.width,maskImage.height).data;const bits=filterSpecks(Uint8Array.from({length:maskImage.width*maskImage.height},(_,i)=\u003epixels[i*4]\u003e=128?1:0),maskImage.width,maskImage.height);const parts=regions(bits,maskImage.width,maskImage.height);if(!parts.length)throw Error(\"empty mask\");const box={left:Math.min(...parts.map(p=\u003ep.left)),right:Math.max(...parts.map(p=\u003ep.right)),top:Math.min(...parts.map(p=\u003ep.top)),bottom:Math.max(...parts.map(p=\u003ep.bottom))};return{photo:await createImageBitmap(upload),mask:bitmapCanvas(bits,maskImage.width,maskImage.height,255),photoMask:maskCanvas,box,small,edge:touchesEdge(bits,maskImage.width,maskImage.height)};}finally{maskImage.close();}}());let result;try{const values=await Promise.all([atlasJob,photoJob]);result=values[1];if(requestController.signal.aborted){result.photo.close();throw Error(\"cancelled\");}return{...result,atlas:values[0]};}catch(error){if(!result)photoJob.then(value=\u003evalue.photo.close(),()=\u003e{});throw error;}}());const result=await Promise.race([job,timeout]);if(token!==generation||requestController.signal.aborted){result.photo.close();return;}current=result;figure.hidden=false;if(current.edge){canvas.width=current.photo.width;canvas.height=current.photo.height;canvas.getContext(\"2d\").drawImage(current.photo,0,0);canvas.hidden=false;canvas.style.display=\"block\";notice.textContent=[current.small?COPY.small:\"\",COPY.edge].filter(Boolean).join(\" \");}else redraw(true);}catch(_){requestController.abort();if(token===generation)fallback();}finally{clearTimeout(timer);if(bitmap)bitmap.close();}});}const deadline=Date.now()+5000;function poll(){const fields=document.getElementById(\"pz-fields\");if(fields)mount(fields);else if(Date.now()\u003cdeadline)setTimeout(poll,50);}poll();}());\n\u003c\/script\u003e\r\n","brand":"Nashville Metal Art","offers":[{"title":"36 in","offer_id":51043367092272,"sku":null,"price":259.95,"currency_code":"USD","in_stock":true},{"title":"24 in","offer_id":51043367125040,"sku":null,"price":179.95,"currency_code":"USD","in_stock":true},{"title":"48 in","offer_id":51043367157808,"sku":null,"price":359.95,"currency_code":"USD","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0072\/9412\/4089\/files\/GP-M-r.png?v=1794259813","url":"https:\/\/nashvillemetalart.com\/products\/garage-portrait","provider":"Nashville Metal Art","version":"1.0","type":"link"}