A powerful, client-side JavaScript bookmarklet that transforms standard Shopify-based medical cannabis dispensary pages into a fully-featured, personalized analytics dashboard and pharmacy management system.
Use at https://can-doc.de/collections/medizinische-cannabisblueten
- π Smart Sorting & Heatmaps: Automatically extracts THC, CBD, and Price to calculate a "Value Score" (THC per β¬). Uses dynamic HSL color heatmaps for quick visual scanning of potency and price.
- π Rx Stash Builder & 1-Click Cart: Build your monthly prescription with
+ / -buttons (5g increments). Automatically tracks your budget and total grams, and pushes your entire stash directly into the Shopify cart with a single click. - βοΈπ Day / Night Tracking: Set separate monthly gram limits for Sativas (Day) and Indicas (Night). The script automatically categorizes strains and warns you if you exceed your prescription limits.
- π€ "Magic Match" Engine: Click the πͺ wand icon on any strain to turn the table into a recommendation engine. It calculates a 0-100% Similarity Score for every other strain based on genetics, THC/CBD proximity, and shared aroma profiles.
- π§ Persistent Memory: Uses
localStorageto remember your Dark/Light mode preference, pinned strains, current stash, and budget across sessions. - π Price History Tracker: Remembers the prices of strains from your last visit and flags any price drops (β) or hikes (β).
- π¨οΈ Doctor Printout & Export: Instantly generate a clean, white-background PDF/Printout of your selected stash for your doctor, or copy a formatted text list to your clipboard.
- π Hover Magnifier: Hover over any tiny thumbnail to instantly see a high-res preview of the flower.
- π External Review Integration: Auto-generates clean search links to Flowzz (π©πͺ) and Leafly (π) for quick patient reviews.
- Show your browser's Bookmarks Bar (
Ctrl+Shift+BorCmd+Shift+B). - Right-click the Bookmarks Bar and select Add Page (or Add Bookmark).
- Name it something recognizable:
πΏ Strain Analyzer - In the URL (or Location) field, paste the minified code provided below.
- Click Save.
- Navigate to the dispensary page containing the product list.
- Click the
πΏ Strain Analyzerbookmark in your Bookmarks Bar. - The custom dashboard will instantly overlay the page.
Copy this entire block and paste it into the URL field of your new bookmark.
javascript:(function(){function safeParse(k,d){try{return JSON.parse(localStorage.getItem(k))||d;}catch(e){return d;}}let isDark=localStorage.getItem('cpto_isDark')==='false'?false:true;let pinnedIds=new Set(safeParse('cpto_pinned',[]));let stash=safeParse('cpto_stash',{});let rxDay=parseFloat(localStorage.getItem('cpto_rx_day')||'10');let rxNight=parseFloat(localStorage.getItem('cpto_rx_night')||'10');let budgetTarget=parseFloat(localStorage.getItem('cpto_budget')||'0');let priceHistory=safeParse('cpto_prices',{});function saveState(){localStorage.setItem('cpto_isDark',isDark);localStorage.setItem('cpto_pinned',JSON.stringify([...pinnedIds]));localStorage.setItem('cpto_stash',JSON.stringify(stash));localStorage.setItem('cpto_rx_day',rxDay);localStorage.setItem('cpto_rx_night',rxNight);localStorage.setItem('cpto_budget',budgetTarget);localStorage.setItem('cpto_prices',JSON.stringify(priceHistory));}const cards=document.querySelectorAll('product-card');let maxThc=0,minPrice=Infinity,maxValue=0;let allBrands=new Set();const products=Array.from(cards).map((card,index)=>{const titleEl=card.querySelector('.product-card__title a');const name=titleEl?titleEl.innerText.trim():'Unknown';const link=titleEl?titleEl.href:'#';const uniqueId=encodeURIComponent(name.replace(/[^a-zA-Z0-9]/g,''));const idInput=card.querySelector('input[name="id"]');const variantId=idInput?idInput.value:null;let brand=name.split(/(?=\s\d)|:/)[0].trim();if(brand)allBrands.add(brand);const imgEl=card.querySelector('img.product-card__image');const image=imgEl?imgEl.src:'';const effectEl=card.querySelector('.product-card__badge-list .badge');const effect=effectEl?effectEl.innerText.trim():'';const aromaEl=card.querySelector('.product-card__aroma');const aroma=aromaEl?aromaEl.innerText.trim():'';const badges=card.querySelectorAll('.product-card__badges .badge');const genetics=badges[0]?badges[0].innerText.trim():'';const radiation=badges[1]?badges[1].innerText.trim():'';const thcText=badges[2]?badges[2].innerText.replace(/[^0-9.,]/g,'').replace(',','.'):'0';const cbdText=badges[3]?badges[3].innerText.replace(/[^0-9.,]/g,'').replace(',','.'):'0';const thc=parseFloat(thcText)||0;const cbd=parseFloat(cbdText)||0;const priceEl=card.querySelector('sale-price');let priceStr=priceEl?priceEl.innerText:'0';let match=priceStr.match(/(\d+[,.]\d+)/);let price=match?parseFloat(match[1].replace(',','.')):0;let priceTrend='';if(priceHistory[uniqueId]){let oldPrice=priceHistory[uniqueId];if(price<oldPrice)priceTrend=`<span style="color:#2ecc71;font-size:12px;margin-left:4px;">β -β¬${(oldPrice-price).toFixed(2)}</span>`;else if(price>oldPrice)priceTrend=`<span style="color:#e74c3c;font-size:12px;margin-left:4px;">β +β¬${(price-oldPrice).toFixed(2)}</span>`;}priceHistory[uniqueId]=price;const comparePriceEl=card.querySelector('compare-at-price');const isOnSale=comparePriceEl&&!comparePriceEl.hasAttribute('hidden')&&price>0;const saleTag=isOnSale?' π¨':'';const valueScore=price>0?parseFloat((thc/price).toFixed(2)):0;const searchString=`${name} ${effect} ${aroma} ${genetics} ${radiation} ${brand}`.toLowerCase();let cleanStrainName=name.includes(':')?name.split(':')[1].trim():name;const encodedStrain=encodeURIComponent(cleanStrainName);const flowzzLink=`https://flowzz.com/search?q=${encodedStrain}`;const leaflyLink=`https://www.leafly.com/search?q=${encodedStrain}`;if(thc>maxThc)maxThc=thc;if(price>0&&price<minPrice)minPrice=price;if(valueScore>maxValue)maxValue=valueScore;return{uniqueId,variantId,brand,name:name+saleTag,cleanStrainName,link,image,effect,aroma,genetics,radiation,thc,cbd,price,priceTrend,valueScore,searchString,flowzzLink,leaflyLink,trophies:[],similarity:0};});if(products.length===0)return alert("No products found on this page.");saveState();products.forEach(p=>{if(p.thc===maxThc)p.trophies.push('π₯');if(p.price===minPrice)p.trophies.push('πΈ');if(p.valueScore===maxValue)p.trophies.push('π');});let currentSort={key:'valueScore',asc:false};let searchTerm='';let selectedBrand='';let magicMatchTarget=null;let activeFilters={cheap:false,sativa:false,unbestrahlt:false,highThc:false};function calculateSimilarity(target,cand){if(target.uniqueId===cand.uniqueId)return 100;let score=0;const tG=target.genetics.toLowerCase(),cG=cand.genetics.toLowerCase();if(tG===cG&&tG!=='')score+=30;else if(tG.includes('sativa')&&cG.includes('sativa'))score+=20;else if(tG.includes('indica')&&cG.includes('indica'))score+=20;else if(tG.includes('hybrid')&&cG.includes('hybrid'))score+=15;const diffThc=Math.abs(target.thc-cand.thc);score+=Math.max(0,30-(diffThc*3));const diffCbd=Math.abs(target.cbd-cand.cbd);score+=Math.max(0,10-(diffCbd*5));const tAro=target.aroma.toLowerCase().split(',').map(s=>s.trim()).filter(s=>s);const cAro=cand.aroma.toLowerCase().split(',').map(s=>s.trim()).filter(s=>s);if(tAro.length===0)score+=15;else{let matches=tAro.filter(a=>cAro.includes(a)).length;score+=(matches/tAro.length)*30;}return Math.round(score);}const themes={light:{bg:'#f9f9f9',text:'#111',panel:'#fff',border:'#ddd',row1:'#fdfdfd',row2:'#f1f1f1',th:'#2c3e50',thText:'#fff',link:'#2980b9',inputBg:'#fff',pinBg:'#fff3cd',pinBorder:'#f39c12',pinText:'#000',foot:'#fff'},dark:{bg:'#121212',text:'#eee',panel:'#1e1e1e',border:'#444',row1:'#1a1a1a',row2:'#242424',th:'#000',thText:'#ddd',link:'#63b3ed',inputBg:'#2c2c2c',pinBg:'#332b00',pinBorder:'#b8860b',pinText:'#ffd700',foot:'#1a1a1a'}};function getTheme(){return isDark?themes.dark:themes.light;}function getThcColor(thc){const h=120-((Math.max(10,Math.min(30,thc))-10)/20)*120;return`hsl(${h}, 80%, ${isDark?'25%':'80%'})`;}function getPriceColor(price){const h=120-(((Math.max(5,Math.min(15,price))-5)/10)*120);return`hsl(${h}, 100%, ${isDark?'65%':'35%'})`;}function getAromaIcons(text){let i='';if(text.includes('sΓΌΓ'))i+='π¬ ';if(text.includes('zitronig'))i+='π ';if(text.includes('erdig'))i+='π² ';if(text.includes('moschus'))i+='𦨠';if(text.includes('wΓΌrzig')||text.includes('pfeffrig'))i+='πΆοΈ ';return i+text;}function getGeneticsIcon(text){if(text.includes('sativa'))return'βοΈ '+text;if(text.includes('indica'))return'π '+text;return'βοΈ '+text;}const existing=document.getElementById('custom-product-table-overlay');if(existing)existing.remove();const tooltip=document.createElement('img');tooltip.id='cpto-hover-image';tooltip.style.cssText='position:fixed;display:none;max-width:350px;max-height:350px;z-index:9999999;border-radius:8px;box-shadow:0 10px 30px rgba(0,0,0,0.8);pointer-events:none;object-fit:contain;background:#fff;border:2px solid #555;';document.body.appendChild(tooltip);let brandOptions=`<option value="">π All Brands</option>`;[...allBrands].sort().forEach(b=>brandOptions+=`<option value="${b}">${b}</option>`);const container=document.createElement('div');container.id='custom-product-table-overlay';const topBar=document.createElement('div');topBar.innerHTML=`<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:10px;"><h2 style="margin:0;">πΏ Strain Analyzer <span id="cpto-count" style="font-size:16px;font-weight:normal;opacity:0.8;"></span></h2><div><button id="cpto-print" style="padding:8px 12px; background:#7f8c8d; color:white; border:none; border-radius:4px; cursor:pointer; margin-right:5px; font-weight:bold;">π¨οΈ Print Rx</button><button id="cpto-copy" style="padding:8px 12px; background:#2980b9; color:white; border:none; border-radius:4px; cursor:pointer; margin-right:15px; font-weight:bold;">π Copy List</button><button id="cpto-dark" style="padding:8px 12px; cursor:pointer; border-radius:4px; border:none; margin-right:5px;">${isDark?'βοΈ':'π'}</button><button id="cpto-close" style="padding:8px 15px; background:#e74c3c; color:#fff; cursor:pointer; border-radius:4px; border:none; font-weight:bold;">Close</button></div></div><div style="display:flex; flex-wrap:wrap; gap:10px; align-items:center;"><input type="text" id="cpto-search" placeholder="Search..." style="padding:8px; border-radius:4px; outline:none; width:150px;"><select id="cpto-brand" style="padding:8px; border-radius:4px; outline:none; cursor:pointer;">${brandOptions}</select><label>βοΈ Day Rx: <input type="number" id="cpto-rx-day" value="${rxDay}" style="width:50px;padding:6px;border-radius:4px; outline:none;" step="5">g</label><label>π Night Rx: <input type="number" id="cpto-rx-night" value="${rxNight}" style="width:50px;padding:6px;border-radius:4px; outline:none;" step="5">g</label><label>Budget: β¬<input type="number" id="cpto-budget" value="${budgetTarget}" style="width:60px;padding:6px;border-radius:4px; outline:none;" step="10"></label><div style="border-left:2px solid #ccc; height:20px; margin:0 5px;"></div><button class="cpto-filter" data-filter="cheap" style="padding:5px 10px; cursor:pointer; border-radius:15px;">π° < β¬7/g</button><button class="cpto-filter" data-filter="sativa" style="padding:5px 10px; cursor:pointer; border-radius:15px;">βοΈ Sativas</button><button class="cpto-filter" data-filter="unbestrahlt" style="padding:5px 10px; cursor:pointer; border-radius:15px;">π± Unbestrahlt</button><button class="cpto-filter" data-filter="highThc" style="padding:5px 10px; cursor:pointer; border-radius:15px;">π₯ 25%+ THC</button><span id="cpto-magic-clear" style="display:none; cursor:pointer; background:#9b59b6; color:white; padding:5px 10px; border-radius:15px;">β Clear Match</span></div>`;const tableContainer=document.createElement('div');const footerBar=document.createElement('div');container.appendChild(topBar);container.appendChild(tableContainer);container.appendChild(footerBar);document.body.appendChild(container);document.getElementById('cpto-close').onclick=()=>{container.remove();tooltip.remove();};document.getElementById('cpto-search').addEventListener('input',(e)=>{searchTerm=e.target.value.toLowerCase();renderTable();});document.getElementById('cpto-brand').addEventListener('change',(e)=>{selectedBrand=e.target.value;renderTable();});document.getElementById('cpto-rx-day').addEventListener('input',(e)=>{rxDay=parseFloat(e.target.value)||0;saveState();renderTable();});document.getElementById('cpto-rx-night').addEventListener('input',(e)=>{rxNight=parseFloat(e.target.value)||0;saveState();renderTable();});document.getElementById('cpto-budget').addEventListener('input',(e)=>{budgetTarget=parseFloat(e.target.value)||0;saveState();renderTable();});document.getElementById('cpto-dark').addEventListener('click',()=>{isDark=!isDark;saveState();renderTable();});document.getElementById('cpto-magic-clear').addEventListener('click',()=>{magicMatchTarget=null;renderTable();});document.getElementById('cpto-print').addEventListener('click',()=>{let stashed=products.filter(p=>stash[p.uniqueId]);if(stashed.length===0)return alert('Your Stash is empty! Add items first.');let win=window.open('','','width=800,height=600');let html=`<html lang="en"><head><title>Medical Rx Printout</title><style>body{font-family:Arial,sans-serif;padding:30px;color:#000;} table{width:100%;border-collapse:collapse;margin-top:20px;} th,td{border:1px solid #000;padding:12px;text-align:left;} th{background:#eee;} h1{margin-bottom:5px;}</style></head><body><h1>Requested Prescription</h1><p>Generated: ${new Date().toLocaleDateString()}</p><table><tr><th>Amount</th><th>Strain Name</th><th>Brand</th><th>Genetics</th><th>THC / CBD</th></tr>`;stashed.forEach(p=>{html+=`<tr><td><b>${stash[p.uniqueId]}g</b></td><td>${p.cleanStrainName}</td><td>${p.brand}</td><td>${p.genetics}</td><td>${p.thc}% / ${p.cbd}%</td></tr>`;});html+=`</table></body></html>`;win.document.write(html);win.document.close();win.focus();setTimeout(()=>{win.print();win.close();},500);});document.getElementById('cpto-copy').addEventListener('click',()=>{let txt="πΏ My Rx Stash:\n\n";let totG=0,totP=0;products.forEach(p=>{if(stash[p.uniqueId]){txt+=`${stash[p.uniqueId]}g x ${p.name.replace(' π¨','')} (${p.thc}% / ${p.cbd}% CBD) - β¬${(p.price*stash[p.uniqueId]).toFixed(2)}\n`;totG+=stash[p.uniqueId];totP+=p.price*stash[p.uniqueId];}});txt+=`\nTotal: ${totG}g | β¬${totP.toFixed(2)}`;navigator.clipboard.writeText(txt).then(()=>alert('List copied to clipboard!'));});footerBar.addEventListener('click',async(e)=>{if(e.target.id!=='cpto-checkout-btn')return;const btn=e.target;const items=products.filter(p=>stash[p.uniqueId]&&p.variantId).map(p=>({id:p.variantId,quantity:stash[p.uniqueId]}));if(items.length===0)return alert("Your Stash is empty!");btn.innerText="Adding to Cart β³...";btn.style.background="#f39c12";try{await fetch('/cart/add.js',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({items})});btn.innerText="β
Successfully Added!";btn.style.background="#27ae60";setTimeout(()=>{window.location.href='/cart';},1000);}catch(err){alert("Error adding to cart. Please try manually.");btn.innerText="ποΈ Add Stash to Cart";btn.style.background="#9b59b6";}});document.querySelectorAll('.cpto-filter').forEach(btn=>{btn.addEventListener('click',(e)=>{const f=e.target.getAttribute('data-filter');activeFilters[f]=!activeFilters[f];renderTable();});});tableContainer.addEventListener('click',(e)=>{const t=e.target;const id=t.getAttribute('data-id');if(!id)return;if(t.classList.contains('pin-btn')){pinnedIds.has(id)?pinnedIds.delete(id):pinnedIds.add(id);}else if(t.classList.contains('stash-inc')){stash[id]=(stash[id]||0)+5;}else if(t.classList.contains('stash-dec')){if(stash[id]>0){stash[id]-=5;if(stash[id]<=0)delete stash[id];}}else if(t.classList.contains('magic-btn')){magicMatchTarget=(magicMatchTarget===id)?null:id;}saveState();renderTable();});function renderTable(){const t=getTheme();container.style.cssText=`position:fixed;top:0;left:0;width:100vw;height:100vh;background:${t.bg};color:${t.text};z-index:999999;overflow:auto;padding:20px;padding-bottom:100px;font-family:sans-serif;box-sizing:border-box;`;topBar.style.cssText=`margin-bottom:20px;background:${t.panel};padding:15px;border-radius:8px;border: 1px solid ${t.border}; box-shadow:0 2px 10px rgba(0,0,0,0.1);`;['cpto-search','cpto-rx-day','cpto-rx-night','cpto-budget','cpto-brand','cpto-dark'].forEach(id=>{document.getElementById(id).style.cssText+=`background:${t.inputBg};color:${t.text};border:1px solid ${t.border};`;});document.getElementById('cpto-dark').innerText=isDark?'βοΈ Light':'π Dark';document.querySelectorAll('.cpto-filter').forEach(btn=>{const f=btn.getAttribute('data-filter');btn.style.background=activeFilters[f]?'#3498db':t.inputBg;btn.style.color=activeFilters[f]?'#fff':t.text;btn.style.border=`1px solid ${activeFilters[f]?'#3498db':t.border}`;});const mTarget=magicMatchTarget?products.find(p=>p.uniqueId===magicMatchTarget):null;document.getElementById('cpto-magic-clear').style.display=mTarget?'inline-block':'none';if(mTarget)products.forEach(p=>p.similarity=calculateSimilarity(mTarget,p));let filtered=products.filter(p=>{if(searchTerm&&!p.searchString.includes(searchTerm))return false;if(selectedBrand&&p.brand!==selectedBrand)return false;if(activeFilters.cheap&&p.price>=7)return false;if(activeFilters.sativa&&!p.genetics.toLowerCase().includes('sativa'))return false;if(activeFilters.unbestrahlt&&!p.radiation.toLowerCase().includes('unbestrahlt'))return false;if(activeFilters.highThc&&p.thc<25)return false;return true;});document.getElementById('cpto-count').innerText=`(${filtered.length} showing)`;let dayG=0,nightG=0,otherG=0,totalP=0;products.forEach(p=>{let q=stash[p.uniqueId];if(q){totalP+=(q*p.price);const g=p.genetics.toLowerCase();if(g.includes('sativa'))dayG+=q;else if(g.includes('indica'))nightG+=q;else otherG+=q;}});let dayWarn=(rxDay>0&&dayG>rxDay)?'color:#e74c3c;':'';let nightWarn=(rxNight>0&&nightG>rxNight)?'color:#e74c3c;':'';let budgetWarn=(budgetTarget>0&&totalP>budgetTarget)?`<span style="color:#e74c3c;font-size:14px;margin-left:5px;">(Over Budget!)</span>`:'';let totalColor=(budgetTarget>0&&totalP>budgetTarget)?'#e74c3c':'#2ecc71';footerBar.style.cssText=`position:fixed;bottom:0;left:0;width:100%;background:${t.foot};border-top:2px solid ${t.border};padding:15px 30px;display:flex;justify-content:space-between;align-items:center;font-size:16px;box-shadow:0 -5px 15px rgba(0,0,0,0.2);box-sizing:border-box;`;footerBar.innerHTML=`<div style="display:flex;align-items:center;gap:15px;"><div>π <b style="margin-left:4px;">Stash:</b></div><div style="${dayWarn}">βοΈ Day: <b>${dayG}</b>${rxDay>0?`/${rxDay}g`:''}</div><div style="${nightWarn}">π Night: <b>${nightG}</b>${rxNight>0?`/${rxNight}g`:''}</div>${otherG>0?`<div>βοΈ Other: <b>${otherG}g</b></div>`:''}<div style="border-left:1px solid ${t.border};height:20px;margin:0 10px;"></div><div style="font-size:22px;color:${totalColor};font-weight:bold;display:flex;align-items:center;">Total: β¬${totalP.toFixed(2)} ${budgetTarget>0?`<span style="font-size:16px;font-weight:normal;color:${t.text};margin-left:5px;margin-right:5px;">/ β¬${budgetTarget.toFixed(2)}</span>`:''} ${budgetWarn}</div></div><button id="cpto-checkout-btn" style="background:#9b59b6;color:white;border:none;padding:12px 25px;font-size:18px;border-radius:6px;font-weight:bold;cursor:pointer;box-shadow:0 4px 6px rgba(0,0,0,0.3);transition:all 0.2s;">ποΈ Add Stash to Cart</button>`;filtered.sort((a,b)=>{const aPin=pinnedIds.has(a.uniqueId)||stash[a.uniqueId]?1:0;const bPin=pinnedIds.has(b.uniqueId)||stash[b.uniqueId]?1:0;if(aPin!==bPin)return bPin-aPin;if(mTarget){return b.similarity-a.similarity;}let valA=a[currentSort.key],valB=b[currentSort.key];if(typeof valA==='string')valA=valA.toLowerCase();if(typeof valB==='string')valB=valB.toLowerCase();if(valA<valB)return currentSort.asc?-1:1;if(valA>valB)return currentSort.asc?1:-1;return 0;});const table=document.createElement('table');table.style.cssText=`width:100%;border-collapse:collapse;text-align:left;background:${t.panel};box-shadow:0 0 10px rgba(0,0,0,0.1);`;const thead=document.createElement('thead');const headerRow=document.createElement('tr');const cols=[{label:'π',key:'pin',sortable:false},{label:'Stash',key:'stash',sortable:false},{label:'Img',key:'image',sortable:false},{label:'Name',key:'name',sortable:true},{label:'Aroma',key:'aroma',sortable:true},{label:'Genetics',key:'genetics',sortable:true},{label:'THC %',key:'thc',sortable:true},{label:'CBD %',key:'cbd',sortable:true},{label:'Price/g',key:'price',sortable:true},{label:'Val π₯',key:'valueScore',sortable:true},{label:'Out',key:'reviews',sortable:false}];cols.forEach(c=>{const th=document.createElement('th');th.style.cssText=`padding:12px;background:${t.th};color:${t.thText};position:sticky;top:0;border:1px solid ${t.border};white-space:nowrap;z-index:10;`;if(c.sortable){th.style.cursor='pointer';th.innerText=c.label+(currentSort.key===c.key?(currentSort.asc?' β²':' βΌ'):'');th.onclick=()=>{if(currentSort.key===c.key)currentSort.asc=!currentSort.asc;else{currentSort.key=c.key;currentSort.asc=['thc','cbd','price','valueScore'].includes(c.key)?false:true;}renderTable();};}else{th.innerText=c.label;}headerRow.appendChild(th);});thead.appendChild(headerRow);table.appendChild(thead);const tbody=document.createElement('tbody');filtered.forEach((p,index)=>{const tr=document.createElement('tr');const qty=stash[p.uniqueId]||0;const isPinned=pinnedIds.has(p.uniqueId)||qty>0;const isMagic=magicMatchTarget===p.uniqueId;tr.style.cssText=isPinned?`background:${t.pinBg};color:${t.pinText};border:2px solid ${t.pinBorder};`:(index%2===0?`background:${t.row1};`:`background:${t.row2};`);const hoverLogic=`onmouseover="const tp=document.getElementById('cpto-hover-image'); tp.src=this.src; tp.style.display='block'; tp.style.left=(event.clientX+20)+'px'; tp.style.top=(event.clientY+20)+'px';" onmousemove="const tp=document.getElementById('cpto-hover-image'); tp.style.left=(event.clientX+20)+'px'; tp.style.top=(event.clientY+20)+'px';" onmouseout="document.getElementById('cpto-hover-image').style.display='none';"`;const imgTag=p.image?`<img src="${p.image}" style="width:40px;height:40px;object-fit:cover;border-radius:4px;cursor:zoom-in;" ${hoverLogic}>`:'';const btnStyle="width:24px;height:24px;border:none;color:#fff;border-radius:4px;cursor:pointer;font-weight:bold;display:flex;align-items:center;justify-content:center;padding:0;line-height:1;";const stashUI=`<div style="display:flex;align-items:center;justify-content:center;gap:8px;"><button class="stash-dec" data-id="${p.uniqueId}" style="${btnStyle}background:#e74c3c;">-</button><span style="font-weight:bold;width:20px;text-align:center;">${qty}</span><button class="stash-inc" data-id="${p.uniqueId}" style="${btnStyle}background:#2ecc71;">+</button></div>`;let magicBadge='';if(mTarget){const badgeColor=p.similarity>80?'#27ae60':(p.similarity>50?'#f39c12':'#7f8c8d');magicBadge=`<div style="margin-top:4px;"><span style="background:${badgeColor};color:#fff;padding:2px 6px;border-radius:12px;font-size:11px;font-weight:bold;">π€ ${p.similarity}% Match</span></div>`;}tr.innerHTML=`<td style="padding:10px;border:1px solid ${t.border};text-align:center;cursor:pointer;font-size:20px;" class="pin-btn" data-id="${p.uniqueId}">${pinnedIds.has(p.uniqueId)?'π':'π'}</td><td style="padding:10px;border:1px solid ${t.border};">${stashUI}</td><td style="padding:5px;border:1px solid ${t.border};text-align:center;">${imgTag}</td><td style="padding:10px;border:1px solid ${t.border};"><span class="magic-btn" data-id="${p.uniqueId}" style="cursor:pointer;font-size:18px;margin-right:5px;filter:grayscale(${mTarget?'1':'0'});" title="Magic Match (Find Similar)">πͺ</span><a href="${p.link}" target="_blank" style="color:${isPinned?t.pinText:t.link};font-weight:bold;text-decoration:none;">${p.trophies.join('')} ${p.name}</a><br><small style="opacity:0.7;">${p.brand}</small>${magicBadge}</td><td style="padding:10px;border:1px solid ${t.border};"><div>${p.effect?'π§ '+p.effect:''}</div><div style="opacity:0.8;">π
${p.aroma}</div></td><td style="padding:10px;border:1px solid ${t.border};white-space:nowrap;"><div>${p.genetics.includes('Sativa')?'βοΈ':p.genetics.includes('Indica')?'π':'βοΈ'} ${p.genetics}</div><div style="opacity:0.8;">${p.radiation.includes('unbestrahlt')?'π±':'β’οΈ'} ${p.radiation}</div></td><td style="padding:10px;border:1px solid ${t.border};font-weight:bold;background-color:${getThcColor(p.thc)};color:${isDark?'#fff':'#000'};text-shadow:0px 1px 2px rgba(0,0,0,0.2);text-align:center;">${p.thc}%</td><td style="padding:10px;border:1px solid ${t.border};font-weight:bold;text-align:center;">${p.cbd}%</td><td style="padding:10px;border:1px solid ${t.border};font-weight:bold;color:${getPriceColor(p.price)};white-space:nowrap;">β¬${p.price.toFixed(2)}${p.priceTrend}</td><td style="padding:10px;border:1px solid ${t.border};text-align:center;${p.valueScore>4?`font-weight:bold;color:${isDark?'#4ade80':'#27ae60'};`:''}">${p.valueScore}</td><td style="padding:10px;border:1px solid ${t.border};text-align:center;white-space:nowrap;"><a href="${p.flowzzLink}" target="_blank" title="Flowzz" style="text-decoration:none;font-size:18px;margin-right:4px;">π©πͺ</a><a href="${p.leaflyLink}" target="_blank" title="Leafly" style="text-decoration:none;font-size:18px;">π</a></td>`;tbody.appendChild(tr);});table.appendChild(tbody);tableContainer.innerHTML='';tableContainer.appendChild(table);}renderTable();})();Use this version if you want to read, audit, or modify the code.