Site Builder
Editing:
index.php
writable 0666
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover" /> <title>BestDealOn — Price Guess (App v3 Demo)</title> <meta name="theme-color" content="#0f172a"> <style> :root{ --bg:#0f172a; --bg2:#0b1220; --ink:#0b1220; --fg:#0f172a; --muted:#64748b; --card:#ffffff; --ring:#e5e7eb; --ringBold:#cbd5e1; --ink2:#111827; --accent:#ff73c6; --accent2:#a78bfa; --good:#16a34a; --bad:#ef4444; --warm:#f59e0b; --radius:18px; --shadow:0 18px 60px rgba(2,6,23,.35); } *{box-sizing:border-box} html,body{height:100%} body{ margin:0; color:#fff; background: radial-gradient(1200px 800px at 10% 0%, rgba(167,139,250,.35), transparent 60%), radial-gradient(1000px 600px at 100% 20%, rgba(255,115,198,.28), transparent 60%), linear-gradient(180deg,var(--bg2),var(--bg) 40%, var(--bg2)); font-family: ui-rounded, system-ui, -apple-system, "Segoe UI", Roboto, Arial; -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility; padding-bottom: 100px; /* room for dock */ } .appbar{ position:sticky; top:0; z-index:50; padding: calc(10px + env(safe-area-inset-top)) 14px 12px; background:linear-gradient(180deg, rgba(167,139,250,.28), rgba(255,115,198,.22)); border-bottom:1px solid #1f2a44; backdrop-filter:saturate(1.2) blur(6px); box-shadow:0 8px 30px rgba(0,0,0,.25); } .apphead{ display:flex; align-items:center; gap:12px; } .logo{width:40px;height:40px;border-radius:12px;display:grid;place-items:center;background: radial-gradient(220% 160% at 20% 10%, #fff3, transparent 40%), linear-gradient(135deg, var(--accent), var(--accent2)); box-shadow:0 10px 30px rgba(167,139,250,.4)} .app-title{ margin:0; font-size:clamp(18px,3.6vw,22px); letter-spacing:.2px } .app-sub{ margin:2px 0 0; color:#e5e7eb; font-size:13px; line-height:1.25 } .wrap{max-width:900px; margin:0 auto; padding:16px 14px 56px} .scorebar{ display:flex; gap:10px; flex-wrap:wrap; align-items:center; justify-content:space-between; margin:10px 0 12px } .scorebar span{ background:#0b1327; color:#e2e8f0; border:1px solid #223154; padding:8px 12px; border-radius:999px; font-weight:800; font-size:12px } .card{background:var(--card); color:var(--fg); border-radius:var(--radius); box-shadow:var(--shadow); border:1px solid var(--ringBold)} .pad{padding:16px} .title{ font-size:clamp(18px,2.2vw,24px); margin:0 0 8px; color:var(--ink2) } .meta{ margin:0 0 12px; color:#6b7280; font-size:14px } .input-row{ display:flex; gap:10px; align-items:center; } .money{ position:relative; flex:1; } .money input{ width:100%; font:700 20px/1.2 ui-rounded,system-ui,-apple-system,"Segoe UI",Roboto,Arial; padding:16px 16px 16px 40px; border-radius:14px; border:1px solid var(--ringBold); background:#fff; outline:none; box-shadow: 0 2px 0 rgba(255,255,255,.6) inset, 0 6px 18px rgba(0,0,0,.05); } .money input:focus{ border-color:#a78bfa; box-shadow:0 0 0 4px rgba(167,139,250,.2) } .prefix{ position:absolute; left:12px; top:50%; transform:translateY(-50%); color:#64748b; font-weight:800 } .btn{ appearance:none; border:none; cursor:pointer; padding:14px 18px; border-radius:14px; font-weight:800; font-size:16px; color:#fff; background:linear-gradient(135deg,var(--accent),var(--accent2)); box-shadow:0 14px 30px rgba(167,139,250,.35); transition:transform .06s ease, filter .2s ease; } .btn:hover{ filter:brightness(1.05) } .btn:active{ transform:translateY(1px) } .btn.secondary{ color:#0f172a; background:#fff; border:1px solid var(--ringBold); box-shadow:0 8px 18px rgba(2,6,23,.08) } .feedback{ display:grid; grid-template-columns:auto 1fr; gap:14px; align-items:center; margin:12px 0 2px } .heat-ring{ --pct:0; --col:#94a3b8; width:72px; height:72px; border-radius:50%; background: conic-gradient(var(--col) calc(var(--pct)*1%), #e5e7eb 0); position:relative; border:2px solid #f1f5f9; box-shadow:0 6px 16px rgba(0,0,0,.08) inset; } .heat-ring::after{ content:""; position:absolute; inset:8px; background:#fff; border-radius:50%; box-shadow:0 6px 18px rgba(0,0,0,.06) inset } .heat-label{ font-weight:900; letter-spacing:.3px; color:#111827 } .heat-detail{ margin:2px 0 0; color:#6b7280; font-size:14px } .thermo{ margin:8px 0 0 } .thermo-track{ position:relative; height:12px; border-radius:999px; background:#e5e7eb; overflow:hidden; border:1px solid #e2e8f0 } .thermo-fill{ height:100%; width:0%; transition:width .35s ease; background:linear-gradient(90deg,#60a5fa,#f59e0b,#ef4444,#dc2626) } .thermo-legend{ margin-top:6px; color:#6b7280; font-size:13px; font-weight:800 } .guesses{ display:flex; gap:8px; flex-wrap:wrap; margin-top:8px } .chip{ display:inline-flex; align-items:center; gap:6px; padding:7px 10px; border-radius:999px; font-weight:900; font-size:12px; border:1px solid var(--ringBold); background:#fff; color:#0f172a; box-shadow:0 2px 8px rgba(0,0,0,.06) } .chip.cold{ background:#eff6ff } .chip.warm{ background:#fff7ed } .chip.hot{ background:#fee2e2 } .tries{ margin:8px 0 0; color:#6b7280; font-size:13px } .hint{ margin:8px 0 0; color:#0f172a; background:#f8fafc; border:1px dashed #e2e8f0; border-radius:12px; padding:10px 12px; display:none } .cta{ margin-top:12px } .amazon-btn{ display:inline-block; text-decoration:none; font-weight:900; color:#0f172a; background:#fff; border:1px solid var(--ringBold); border-radius:999px; padding:12px 16px; box-shadow:0 8px 18px rgba(2,6,23,.06) } details.settings{ margin-top:16px } details.settings > summary{ cursor:pointer; list-style:none; user-select:none; color:#e2e8f0; font-weight:900; margin-bottom:10px } details.settings .panel{ background:#0b1327; border:1px solid #1f2a44; border-radius:14px; padding:14px; color:#cbd5e1 } .settings .row{ display:flex; gap:18px; flex-wrap:wrap } .settings label{ display:flex; align-items:center; gap:8px; font-size:14px } .settings select, .settings input[type="checkbox"]{ accent-color:#a78bfa } .foot{ margin-top:16px; color:#94a3b8; font-size:12px } @media (max-width:560px){ .input-row{ flex-direction:column; align-items:stretch } .btn{ width:100% } } /* Dock + toast + confetti */ .dock{ position:fixed; left:0; right:0; bottom:0; z-index:60; padding:10px 12px calc(12px + env(safe-area-inset-bottom)); display:flex; gap:10px; justify-content:space-between; background:linear-gradient(180deg, transparent, rgba(2,6,23,.6)); backdrop-filter: blur(10px); } .dock-btn{ flex:1; display:flex; align-items:center; justify-content:center; gap:8px; padding:14px; border-radius:14px; border:1px solid var(--ringBold); background:#fff; color:#0f172a; font-weight:900; box-shadow:0 10px 24px rgba(0,0,0,.1)} .dock-btn.primary{ background:linear-gradient(135deg,var(--accent),var(--accent2)); color:#fff; border-color:transparent } .toast{ position:fixed; left:50%; transform:translate(-50%, 30px); bottom: calc(72px + env(safe-area-inset-bottom)); z-index:70; background:#111827; color:#fff; padding:10px 14px; border-radius:12px; border:1px solid #334155; opacity:0; pointer-events:none; transition: opacity .2s ease, transform .2s ease } .toast.show{ opacity:1; transform:translate(-50%, 0) } .confetti{ position:fixed; top:-10px; width:8px; height:12px; border-radius:2px; opacity:.9; pointer-events:none; animation: confetti-fall 1.2s linear forwards; z-index:70 } @keyframes confetti-fall { to { transform: translateY(110vh) rotate(720deg); } } @keyframes shake{ 10%,90%{transform:translateX(-1px)} 20%,80%{transform:translateX(2px)} 30%,50%,70%{transform:translateX(-4px)} 40%,60%{transform:translateX(4px)} } .shake{ animation:shake .4s ease both } .pulse-hot{ animation:pulseHot .8s ease } @keyframes pulseHot{ 0%{ box-shadow:0 0 0 0 rgba(220,38,38,.35)} 100%{ box-shadow:0 0 0 16px rgba(220,38,38,0)} } </style> </head> <body> <div class="appbar"> <div class="apphead"> <div class="logo" aria-hidden="true"> <svg width="22" height="22" viewBox="0 0 24 24" fill="#fff"><path d="M12 2l2.39 4.84L20 8l-4 3.9.94 5.48L12 15.77 7.06 17.38 8 13 4 9l5.61-.16L12 2z"/></svg> </div> <div> <h1 class="app-title">Test your shopping knowledge</h1> <p class="app-sub">See how many tries it takes you to guess the price exactly.</p> </div> </div> </div> <div class="wrap"> <nav class="scorebar" id="scorebar"> <span>🔥 Streak <strong id="streak">0</strong></span> <span>🏆 Best <strong id="best">—</strong></span> <span>📊 Avg <strong id="avg">—</strong></span> </nav> <section class="game"> <div class="card pad" id="cardGame"> <h2 class="title" id="title">Wireless ANC over‑ear headphones</h2> <p class="meta" id="meta">Category · <span id="category">Electronics</span> · ASIN <code id="asin">B0DEMO001</code></p> <div class="input-row"> <label class="money" for="guess"> <span class="prefix">$</span> <input id="guess" type="number" inputmode="decimal" step="0.01" placeholder="Enter your guess" aria-label="Enter your price guess" /> </label> <button class="btn" id="btnGuess">Guess</button> </div> <div class="feedback"> <div class="heat-ring" id="heatRing" aria-hidden="true"></div> <div> <div class="heat-label" id="heatLabel">I'll tell you if you're hot or cold.</div> <div class="heat-detail" id="heatDetail">Try a number and press Guess.</div> </div> </div> <div class="thermo" aria-hidden="true"> <div class="thermo-track"> <div class="thermo-fill" id="thermoFill" style="width:0%"></div> </div> <div class="thermo-legend" id="thermoLegend">Getting started…</div> </div> <div class="guesses" id="guesses"></div> <div class="tries" id="tries">Tries: <strong>0</strong> / <span id="maxTries">6</span></div> <div class="hint" id="hintBox" role="status" aria-live="polite"></div> <div style="display:flex; gap:10px; margin-top:12px; flex-wrap:wrap"> <button class="btn secondary" id="btnHint">Hint</button> <button class="btn secondary" id="btnReveal">Reveal</button> <button class="btn" id="btnNext" hidden>Next item</button> </div> <div class="cta"> <a class="amazon-btn" id="linkAmazon" href="#" target="_blank" rel="nofollow sponsored noopener">View images and price at Amazon</a> </div> <p class="foot">Game target price is for gameplay only and is <em>not</em> Amazon’s price. For current price and availability, check Amazon.</p> </div> </section> <details class="settings"> <summary>Settings</summary> <div class="panel settings"> <div class="row"> <label>Difficulty <select id="difficulty"> <option value="relaxed">Relaxed (±15%)</option> <option value="normal" selected>Normal (±10%)</option> <option value="hard">Hard (±6%)</option> </select> </label> <label><input type="checkbox" id="dirToggle" checked> Tell me “higher / lower”</label> <label><input type="checkbox" id="allowHints" checked> Allow hints</label> </div> </div> </details> <p class="foot">As an Amazon Associate I earn from qualifying purchases.</p> </div> <div class="dock" id="dock"> <a class="dock-btn" id="linkAmazonDock" href="#" target="_blank" rel="nofollow sponsored noopener">🛒 View images & price</a> <button class="dock-btn primary" id="btnShareDock">📣 Share</button> </div> <div id="toast" class="toast" role="status" aria-live="polite" hidden>Copied!</div> <script> (function(){ const $ = sel => document.querySelector(sel); const products = [ { asin:'B0DEMO001', title:'Wireless ANC over‑ear headphones', category:'Electronics', target:199.00 }, { asin:'B0DEMO002', title:'Cordless stick vac with HEPA + pet brush', category:'Home & Kitchen', target:149.99 }, { asin:'B0DEMO003', title:'Insulated stainless bottle · 32 oz', category:'Outdoors', target:24.95 }, { asin:'B0DEMO004', title:'Smart LED desk lamp with dimmer', category:'Lighting', target:39.00 }, ]; const el = { title: $('#title'), category: $('#category'), asin: $('#asin'), guess: $('#guess'), btnGuess: $('#btnGuess'), heatRing: $('#heatRing'), heatLabel: $('#heatLabel'), heatDetail: $('#heatDetail'), tries: $('#tries'), maxTries: $('#maxTries'), btnHint: $('#btnHint'), hintBox: $('#hintBox'), btnReveal: $('#btnReveal'), btnNext: $('#btnNext'), linkAmazon: $('#linkAmazon'), linkAmazonDock: $('#linkAmazonDock'), btnShareDock: $('#btnShareDock'), streak: $('#streak'), best: $('#best'), avg: $('#avg'), toast: $('#toast'), difficulty: $('#difficulty'), dirToggle: $('#dirToggle'), allowHints: $('#allowHints'), thermoFill: $('#thermoFill'), thermoLegend: $('#thermoLegend'), guesses: $('#guesses'), cardGame: $('#cardGame') }; const MAX_TRIES = 6; el.maxTries.textContent = MAX_TRIES; let idx = 0, target = 0, tries = 0, ended = false, guessHistory = []; function amazonLink(asin, tag='YOURTAG-20'){ return `https://www.amazon.com/dp/${encodeURIComponent(asin)}?tag=${encodeURIComponent(tag)}`; } function load(i){ const p = products[i % products.length]; idx = i % products.length; target = p.target; tries = 0; ended = false; guessHistory = []; el.title.textContent = p.title; el.category.textContent = p.category; el.asin.textContent = p.asin; el.tries.innerHTML = `Tries: <strong>${tries}</strong> / <span id="maxTries">${MAX_TRIES}</span>`; el.heatRing.style.setProperty('--pct', 0); el.heatRing.style.setProperty('--col', '#94a3b8'); if (el.thermoFill) el.thermoFill.style.width = '0%'; if (el.thermoLegend) el.thermoLegend.textContent = 'Getting started…'; if (el.guesses) el.guesses.innerHTML = ''; el.heatLabel.textContent = "I'll tell you if you're hot or cold."; el.heatDetail.textContent = "Try a number and press Guess."; el.hintBox.style.display = 'none'; el.hintBox.textContent = ''; el.btnNext.hidden = true; el.btnGuess.disabled = false; el.guess.disabled = false; el.btnReveal.disabled = false; el.guess.value = ''; const link = amazonLink(p.asin); el.linkAmazon.href = link; if (el.linkAmazonDock) el.linkAmazonDock.href = link; el.guess.focus(); } function fmt(n){ return n.toLocaleString(undefined,{style:'currency', currency:'USD'}); } function heat(guess, target){ const diff = Math.abs(guess - target); const ratio = diff / Math.max(target, 1); let label = 'Cold', col = '#60a5fa'; if (ratio > .5) { label = 'Ice cold'; col = '#93c5fd'; } else if (ratio > .25) { label = 'Cold'; col = '#60a5fa'; } else if (ratio > .10) { label = 'Warm'; col = '#f59e0b'; } else if (ratio > .05) { label = 'Hot'; col = '#ef4444'; } else { label = 'Scorching!'; col = '#dc2626'; } const closeness = Math.max(0, 1 - ratio); return { label, col, pct: Math.round(closeness * 100), ratio }; } function applyHeat(h){ el.heatRing.style.setProperty('--pct', h.pct); el.heatRing.style.setProperty('--col', h.col); const emoji = h.label.includes('Ice') ? '❄️' : h.label === 'Cold' ? '🥶' : h.label === 'Warm' ? '🌤️' : h.label.includes('Hot') ? '🔥' : '🔥🔥'; el.heatLabel.textContent = `${emoji} ${h.label}`; if (el.thermoFill) el.thermoFill.style.width = h.pct + '%'; if (el.thermoLegend) el.thermoLegend.textContent = h.pct < 25 ? 'Cold start' : h.pct < 50 ? 'Warming up' : h.pct < 80 ? 'Heating up' : 'Almost there!'; } function renderGuesses(){ if (!el.guesses) return; el.guesses.innerHTML = guessHistory.map(({g,h})=>{ const cls = h.pct < 40 ? 'cold' : h.pct < 75 ? 'warm' : 'hot'; const arrow = g < target ? '↑' : (g > target ? '↓' : '✓'); return `<span class="chip ${cls}">${arrow} ${fmt(g)}</span>`; }).join(''); } function difficultyBand(){ switch(el.difficulty.value){ case 'relaxed': return .15; case 'hard': return .06; default: return .10; } } function guessOnce(){ if (ended) return; const g = parseFloat(el.guess.value); if (!Number.isFinite(g)){ el.heatDetail.textContent = 'Enter a number like 49.99'; return; } tries++; el.tries.innerHTML = `Tries: <strong>${tries}</strong> / <span id=\"maxTries\">${MAX_TRIES}</span>`; const h = heat(g, target); applyHeat(h); guessHistory.push({ g, h }); renderGuesses(); const dir = g < target ? 'Higher' : (g > target ? 'Lower' : 'Bang on!'); const showDir = el.dirToggle.checked; if (g === target || Math.abs(g - target) <= target * 0){ // exact only el.heatDetail.textContent = `Perfect! Target was ${fmt(target)}.`; if (el.heatRing) { el.heatRing.classList.add('pulse-hot'); setTimeout(()=> el.heatRing.classList.remove('pulse-hot'), 800); } endRound(true); return; } const dirMsg = showDir ? ` — try ${dir.toLowerCase()}.` : ''; const lead = h.pct >= 90 ? 'So close' : h.pct >= 60 ? 'Getting there' : h.pct >= 30 ? 'Not quite' : 'Way off'; el.heatDetail.textContent = `${lead}${dirMsg} Guess again.`; if (navigator.vibrate){ const dur = h.ratio > .25 ? 15 : h.ratio > .10 ? 25 : 35; navigator.vibrate(dur); } if (el.cardGame){ el.cardGame.classList.add('shake'); setTimeout(()=> el.cardGame.classList.remove('shake'), 400); } if (tries >= MAX_TRIES){ el.heatDetail.textContent = `Out of tries. Target was ${fmt(target)}.`; endRound(false); } } function showHint(){ if (!el.allowHints.checked) return; const span = Math.max(3, Math.round(target * .12)); const lo = Math.max(1, target - span), hi = target + span; el.hintBox.textContent = `Hint: It\'s between ${fmt(lo)} and ${fmt(hi)}.`; el.hintBox.style.display = 'block'; } function reveal(){ el.heatLabel.textContent = 'Reveal'; el.heatDetail.textContent = `Target was ${fmt(target)}.`; endRound(false); } function endRound(won){ ended = true; if (won){ saveWin(tries); confettiBurst(); } else { saveLoss(); } el.btnNext.hidden = false; el.btnGuess.disabled = true; el.guess.disabled = true; el.btnReveal.disabled = true; } // Helpers — stats, sharing, toast, confetti function getN(key, def=0){ const v = Number(localStorage.getItem(key)); return Number.isFinite(v) ? v : def; } function setN(key, val){ localStorage.setItem(key, String(val)); } function updateStatsUI(){ if (!el.streak || !el.best || !el.avg) return; const games = getN('pg_games', 0); const total = getN('pg_total', 0); const avg = games ? (total/games).toFixed(1) : '—'; const best = getN('pg_best', 0); const streak = getN('pg_streak', 0); el.avg.textContent = avg; el.best.textContent = best>0 ? best : '—'; el.streak.textContent = streak; } function saveWin(guesses){ setN('pg_games', getN('pg_games') + 1); setN('pg_total', getN('pg_total') + guesses); const best = getN('pg_best', 0); if (best === 0 || guesses < best) setN('pg_best', guesses); setN('pg_streak', getN('pg_streak') + 1); updateStatsUI(); } function saveLoss(){ setN('pg_games', getN('pg_games') + 1); setN('pg_total', getN('pg_total') + tries); setN('pg_streak', 0); updateStatsUI(); } async function shareScore(){ const title = el.title ? el.title.textContent.trim() : 'this item'; const url = 'https://bestdealon.com/unless'; const text = ended ? `It took me ${tries} ${tries===1?'guess':'guesses'} to nail the price of ${title}. See if you can do better at ${url}` : `Guessing the price of ${title} — play with me at ${url}`; if (navigator.share){ try { await navigator.share({ text, url }); } catch(e){} } else if (navigator.clipboard){ try{ await navigator.clipboard.writeText(text); showToast('Copied share text!'); }catch(e){ showToast('Copy failed'); } } else { showToast('Sharing not supported'); } } function showToast(msg){ if (!el.toast) return; el.toast.textContent = msg; el.toast.hidden = false; el.toast.classList.add('show'); setTimeout(()=>{ el.toast.classList.remove('show'); el.toast.hidden = true; }, 1600); } function confettiBurst(){ const n = 28; for (let i=0;i<n;i++){ const d=document.createElement('i'); d.className='confetti'; d.style.left=Math.random()*100+'vw'; d.style.background=`hsl(${Math.floor(Math.random()*360)},90%,60%)`; d.style.animationDuration=(0.9+Math.random()*0.8)+'s'; document.body.appendChild(d); setTimeout(()=>d.remove(),1800);} } // Events el.btnGuess.addEventListener('click', guessOnce); el.guess.addEventListener('keydown', e=>{ if (e.key === 'Enter') guessOnce(); }); el.btnHint.addEventListener('click', showHint); el.btnReveal.addEventListener('click', reveal); el.btnNext.addEventListener('click', ()=> load(idx+1)); if (el.btnShareDock) el.btnShareDock.addEventListener('click', shareScore); // Init updateStatsUI(); load(0); })(); </script> </body> </html>
Save changes
Create folder
writable 0777
Create
Cancel