@liveloop · 5/18/2026, 8:14:56 AM
Initial version — all lines are new.
+<style>+ html,body{height:100%;margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;background:linear-gradient(160deg,#1e1b4b,#0f172a);color:#f1f5f9;overflow:hidden;}+ .wrap{display:flex;flex-direction:column;height:100%;box-sizing:border-box;padding:1.2rem;gap:.85rem;align-items:center;justify-content:center;}+ h1{margin:0;font-size:1.35rem;text-align:center;}+ .hint{margin:0;font-size:.78rem;color:#94a3b8;text-align:center;min-height:1.1em;}+ #card{position:relative;width:min(86vw,330px);aspect-ratio:5/3;border-radius:1rem;overflow:hidden;box-shadow:0 14px 36px rgba(0,0,0,.55);}+ #prize{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.35rem;text-align:center;padding:1rem;box-sizing:border-box;}+ #prize .big{font-size:1.45rem;font-weight:800;}+ #prize .code{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:1.5rem;font-weight:800;letter-spacing:.14em;background:rgba(0,0,0,.28);padding:.3rem .75rem;border-radius:.5rem;}+ #prize .note{font-size:.68rem;opacity:.8;max-width:26ch;}+ #scratch{position:absolute;inset:0;width:100%;height:100%;touch-action:none;cursor:grab;}+ button{font:inherit;font-weight:700;border:0;border-radius:9999px;padding:.6rem 1.5rem;background:#fbbf24;color:#0b1020;cursor:pointer;touch-action:manipulation;}+</style>+<div class="wrap">+ <h1>🎟️ Scratch & Win</h1>+ <div id="card">+ <div id="prize"></div>+ <canvas id="scratch"></canvas>+ </div>+ <p class="hint" id="hint">Scratch the silver area with your finger.</p>+ <button id="again" type="button" style="display:none">New card</button>+</div>+<script>+(function(){+ // Remixing this into a real promo? Set your odds + prize text here.+ var WIN_RATE = 0.5;+ var PRIZE = {+ label: 'You won a coupon!',+ note: 'Demo code — remix this card and set your own real one.'+ };++ var prize=document.getElementById('prize'), cv=document.getElementById('scratch'),+ ctx=cv.getContext('2d'), hint=document.getElementById('hint'),+ again=document.getElementById('again');+ var W=0,H=0,won=false,revealed=false,scratching=false,lastX=0,lastY=0;++ function onTap(el,fn){+ var sx=0,sy=0,armed=false;+ el.addEventListener('pointerdown',function(e){ armed=true;sx=e.clientX;sy=e.clientY; });+ el.addEventListener('pointercancel',function(){ armed=false; });+ el.addEventListener('pointerup',function(e){+ if(!armed) return; armed=false;+ if(Math.abs(e.clientX-sx)<16&&Math.abs(e.clientY-sy)<16) fn();+ });+ }+ function mk(cls,txt){ var e=document.createElement('div'); e.className=cls; e.textContent=txt; return e; }+ function genCode(){+ var A='ABCDEFGHJKLMNPQRSTUVWXYZ23456789', s='';+ for(var i=0;i<4;i++) s+=A.charAt(Math.floor(Math.random()*A.length));+ return 'LL-'+s;+ }+ function setPrize(){+ won = Math.random() < WIN_RATE;+ prize.innerHTML='';+ if(won){+ prize.style.background='linear-gradient(160deg,#16a34a,#065f46)';+ prize.appendChild(mk('big','🎉 '+PRIZE.label));+ prize.appendChild(mk('code',genCode()));+ prize.appendChild(mk('note',PRIZE.note));+ } else {+ prize.style.background='linear-gradient(160deg,#475569,#1e293b)';+ prize.appendChild(mk('big','😬 Not this time'));+ prize.appendChild(mk('note','Tap “New card” for another go.'));+ }+ }+ function coverFoil(){+ ctx.globalCompositeOperation='source-over';+ var g=ctx.createLinearGradient(0,0,W,H);+ g.addColorStop(0,'#cbd5e1'); g.addColorStop(0.5,'#94a3b8'); g.addColorStop(1,'#e2e8f0');+ ctx.fillStyle=g; ctx.fillRect(0,0,W,H);+ ctx.fillStyle='rgba(15,23,42,.5)';+ ctx.textAlign='center'; ctx.textBaseline='middle';+ ctx.font='800 '+Math.round(H*0.15)+'px system-ui';+ ctx.fillText('SCRATCH HERE',W/2,H*0.42);+ ctx.font='600 '+Math.round(H*0.16)+'px system-ui';+ ctx.fillText('👆',W/2,H*0.7);+ }+ function fit(){+ var r=cv.getBoundingClientRect(); if(r.width<=0) return;+ var dpr=Math.min(window.devicePixelRatio||1,2);+ W=r.width; H=r.height;+ cv.width=Math.round(W*dpr); cv.height=Math.round(H*dpr);+ ctx.setTransform(dpr,0,0,dpr,0,0);+ if(!revealed) coverFoil();+ }+ function newCard(){+ revealed=false; scratching=false;+ again.style.display='none';+ hint.textContent='Scratch the silver area with your finger.';+ cv.style.display='block';+ setPrize(); coverFoil();+ }+ function eraseTo(x,y){+ ctx.globalCompositeOperation='destination-out';+ ctx.lineCap='round'; ctx.lineJoin='round';+ ctx.lineWidth=Math.max(24,W*0.11);+ ctx.beginPath(); ctx.moveTo(lastX,lastY); ctx.lineTo(x,y); ctx.stroke();+ ctx.beginPath(); ctx.arc(x,y,ctx.lineWidth/2,0,Math.PI*2); ctx.fill();+ lastX=x; lastY=y;+ }+ function scratchedPct(){+ try{+ var d=ctx.getImageData(0,0,cv.width,cv.height).data, clear=0, n=0;+ for(var i=3;i<d.length;i+=40){ n++; if(d[i]<40) clear++; }+ return n? clear/n : 0;+ }catch(e){ return 0; }+ }+ function revealAll(){+ revealed=true;+ ctx.globalCompositeOperation='destination-out';+ ctx.fillRect(0,0,W,H);+ cv.style.display='none';+ hint.textContent = won ? 'Lucky you — use the code above!' : 'No prize this time.';+ again.style.display='inline-block';+ }+ function ptr(e){+ var r=cv.getBoundingClientRect();+ return { x:(e.clientX-r.left)/r.width*W, y:(e.clientY-r.top)/r.height*H };+ }+ cv.addEventListener('pointerdown',function(e){+ if(revealed) return;+ scratching=true;+ try{ cv.setPointerCapture(e.pointerId); }catch(_){}+ var p=ptr(e); lastX=p.x; lastY=p.y; eraseTo(p.x,p.y);+ e.preventDefault();+ });+ cv.addEventListener('pointermove',function(e){+ if(!scratching||revealed) return;+ var p=ptr(e); eraseTo(p.x,p.y);+ e.preventDefault();+ });+ function endScratch(){+ if(!scratching) return;+ scratching=false;+ if(!revealed && scratchedPct()>0.55) revealAll();+ }+ cv.addEventListener('pointerup',endScratch);+ cv.addEventListener('pointercancel',endScratch);++ onTap(again,newCard);+ window.addEventListener('resize',fit);+ setPrize();+ fit();+ requestAnimationFrame(fit);+})();+</script>