Play-Scrapper / templates /landing.html
WebashalarForML's picture
Upload 6 files
fee4aa3 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PlayPulse | Intelligence Platform</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Outfit:wght@400;600;800&display=swap" rel="stylesheet">
<style>
:root { --bg:#0b0e14; --surface:#151921; --surface2:#1c2333; --border:rgba(255,255,255,0.08); --accent:#3b82f6; --accent-gradient:linear-gradient(135deg,#3b82f6 0%,#2dd4bf 100%); --text:#f1f5f9; --muted:#94a3b8; }
*{box-sizing:border-box;margin:0;padding:0;}
::-webkit-scrollbar{width:6px;} ::-webkit-scrollbar-track{background:transparent;} ::-webkit-scrollbar-thumb{background:rgba(255,255,255,0.1);border-radius:10px;}
body{font-family:'Inter',sans-serif;background:var(--bg);color:var(--text);min-height:100vh;display:flex;flex-direction:column;overflow-x:hidden;}
.blob{position:fixed;width:500px;height:500px;background:var(--accent);filter:blur(120px);opacity:0.1;z-index:-1;border-radius:50%;}
.blob-1{top:-100px;right:-100px;} .blob-2{bottom:-100px;left:-100px;background:#2dd4bf;}
header{padding:30px 5%;display:flex;justify-content:space-between;align-items:center;}
.logo{font-family:'Outfit',sans-serif;font-weight:800;font-size:24px;letter-spacing:-1px;color:var(--text);display:flex;align-items:center;gap:10px;}
.logo-icon{width:32px;height:32px;background:var(--accent-gradient);border-radius:8px;display:flex;align-items:center;justify-content:center;}
main{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:60px 5%;max-width:1200px;margin:0 auto;text-align:center;width:100%;}
.hero-tag{background:rgba(59,130,246,0.1);border:1px solid rgba(59,130,246,0.2);color:var(--accent);padding:6px 16px;border-radius:100px;font-size:13px;font-weight:700;margin-bottom:24px;text-transform:uppercase;letter-spacing:1px;}
h1{font-family:'Outfit',sans-serif;font-size:clamp(40px,8vw,72px);font-weight:800;line-height:1.1;margin-bottom:20px;letter-spacing:-2px;}
h1 span{background:var(--accent-gradient);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;}
.sub-hero{color:var(--muted);font-size:clamp(16px,2vw,20px);max-width:600px;margin-bottom:60px;line-height:1.6;}
.cards-container{display:grid;grid-template-columns:repeat(auto-fit,minmax(340px,1fr));gap:30px;width:100%;}
.card{background:var(--surface);border:1px solid var(--border);padding:40px;border-radius:24px;text-align:left;transition:all 0.3s cubic-bezier(0.4,0,0.2,1);cursor:pointer;position:relative;overflow:hidden;display:flex;flex-direction:column;height:100%;}
.card:hover{border-color:rgba(59,130,246,0.5);transform:translateY(-8px);box-shadow:0 20px 40px rgba(0,0,0,0.4);}
.card-icon{width:56px;height:56px;background:rgba(255,255,255,0.03);border:1px solid var(--border);border-radius:16px;display:flex;align-items:center;justify-content:center;margin-bottom:24px;transition:0.3s;}
.card:hover .card-icon{background:var(--accent);color:white;border-color:var(--accent);}
.card h2{font-family:'Outfit',sans-serif;font-size:24px;margin-bottom:12px;font-weight:700;}
.card p{color:var(--muted);line-height:1.6;font-size:15px;margin-bottom:24px;flex:1;}
.badge{position:absolute;top:20px;right:20px;background:rgba(255,255,255,0.05);border:1px solid var(--border);padding:4px 12px;border-radius:100px;font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;}
.badge.active{background:rgba(59,130,246,0.1);color:var(--accent);border-color:rgba(59,130,246,0.2);}
.btn{display:inline-flex;align-items:center;gap:8px;font-weight:700;font-size:14px;color:var(--text);transition:0.3s;}
.card:hover .btn{color:var(--accent);}
footer{padding:40px;text-align:center;color:var(--muted);font-size:13px;}
@media(max-width:768px){h1{font-size:48px;}.cards-container{grid-template-columns:1fr;}}
/* ── Chat styles ── */
#chat-dialer{position:fixed;bottom:24px;right:24px;width:56px;height:56px;background:var(--accent);border-radius:50%;display:flex;align-items:center;justify-content:center;box-shadow:0 8px 32px rgba(59,130,246,0.4);cursor:pointer;z-index:1000;transition:0.3s cubic-bezier(0.175,0.885,0.32,1.275);border:2px solid rgba(255,255,255,0.1);}
#chat-dialer:hover{transform:scale(1.1) rotate(5deg);box-shadow:0 12px 40px rgba(59,130,246,0.6);}
#chat-dialer svg{width:24px;height:24px;color:white;fill:none;stroke:currentColor;stroke-width:2.5;}
#chat-window{position:fixed;bottom:90px;right:24px;width:420px;height:560px;background:var(--surface);border:1px solid rgba(255,255,255,0.1);border-radius:20px;display:flex;flex-direction:column;box-shadow:0 20px 50px rgba(0,0,0,0.5);z-index:1001;overflow:hidden;transform:translateY(20px) scale(0.95);opacity:0;pointer-events:none;transition:0.3s cubic-bezier(0.4,0,0.2,1);backdrop-filter:blur(20px);}
#chat-window.open{transform:translateY(0) scale(1);opacity:1;pointer-events:auto;}
.chat-header{padding:14px 18px;background:var(--accent);color:white;display:flex;align-items:center;gap:12px;flex-shrink:0;}
.chat-header-info{flex:1;} .chat-header-title{font-weight:800;font-size:15px;} .chat-header-status{font-size:10px;opacity:0.8;display:flex;align-items:center;gap:4px;} .status-dot{width:6px;height:6px;background:#22c55e;border-radius:50%;}
.chat-header-actions{display:flex;gap:8px;align-items:center;}
.chat-clear-btn{background:rgba(255,255,255,0.15);border:none;color:white;font-size:11px;padding:4px 10px;border-radius:8px;cursor:pointer;transition:0.2s;} .chat-clear-btn:hover{background:rgba(255,255,255,0.25);}
.chat-messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:12px;background-image:radial-gradient(rgba(255,255,255,0.05) 1px,transparent 1px);background-size:20px 20px;}
.msg-row{display:flex;flex-direction:column;gap:4px;} .msg-row.user{align-items:flex-end;} .msg-row.bot{align-items:flex-start;}
.message{max-width:88%;padding:11px 15px;border-radius:16px;font-size:13px;line-height:1.6;}
.message.user{background:var(--accent);color:white;border-bottom-right-radius:4px;}
.message.bot{background:var(--surface2);color:var(--text);border:1px solid rgba(255,255,255,0.08);border-bottom-left-radius:4px;white-space:pre-wrap;word-break:break-word;}
.msg-section{margin-top:10px;font-weight:700;font-size:11px;color:var(--accent);letter-spacing:0.05em;text-transform:uppercase;}
.msg-item{display:flex;gap:8px;margin-top:5px;} .msg-item-num{font-weight:700;color:var(--accent);min-width:16px;} .msg-bullet{color:var(--accent);min-width:14px;}
.typing-indicator{display:flex;gap:4px;padding:12px 16px;background:var(--surface2);border:1px solid rgba(255,255,255,0.08);border-radius:16px;width:fit-content;}
.dot{width:6px;height:6px;background:#64748b;border-radius:50%;animation:bounce 1.4s infinite;} .dot:nth-child(2){animation-delay:0.2s;} .dot:nth-child(3){animation-delay:0.4s;}
@keyframes bounce{0%,80%,100%{transform:translateY(0)}40%{transform:translateY(-6px)}}
.chat-input-area{padding:14px 16px;background:var(--surface);border-top:1px solid rgba(255,255,255,0.06);display:flex;gap:10px;flex-shrink:0;}
#chat-input{flex:1;background:var(--bg);border:1px solid rgba(255,255,255,0.08);color:white;padding:10px 14px;border-radius:12px;font-size:13px;outline:none;} #chat-input:focus{border-color:var(--accent);}
.btn-send{width:40px;height:40px;background:var(--accent);color:white;border:none;border-radius:10px;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:0.2s;flex-shrink:0;} .btn-send:hover{transform:scale(1.05);} .btn-send svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2.5;}
</style>
</head>
<body>
<div class="blob blob-1"></div>
<div class="blob blob-2"></div>
<header>
<div class="logo">
<div class="logo-icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="3"><path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg>
</div>
PLAYPULSE
</div>
</header>
<main>
<div class="hero-tag">Next-Gen Intelligence</div>
<h1>Extract Insights from <span>Global App Data</span></h1>
<p class="sub-hero">The most powerful tool for analyzing app reviews, sentiment, and developer responses in real-time. Powered by AI chat.</p>
<div class="cards-container">
<div class="card" onclick="location.href='/scraper'">
<div class="badge active">Live Now</div>
<div class="card-icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM21 21l-4.35-4.35"/></svg>
</div>
<h2>Single App Explorer</h2>
<p>Deep-dive into any Play Store app. Extract hundreds of reviews, analyze ratings, and chat with AI to get instant insights.</p>
<div class="btn">Explore Now <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><path d="M5 12h14M12 5l7 7-7 7"/></svg></div>
</div>
<div class="card" onclick="location.href='/batch'">
<div class="badge active">New Mode</div>
<div class="card-icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
</div>
<h2>Batch Intelligence</h2>
<p>Compare multiple apps side-by-side. Track competitor updates and aggregate sentiment across entire game categories.</p>
<div class="btn">Start Analysis <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><path d="M5 12h14M12 5l7 7-7 7"/></svg></div>
</div>
</div>
</main>
<footer>&copy; 2026 PlayPulse Intelligence. Powered by Google Play Scraper Engine.</footer>
<!-- Chat bubble -->
<div id="chat-dialer" onclick="toggleChat()">
<svg viewBox="0 0 24 24"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
</div>
<div id="chat-window">
<div class="chat-header">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5"><circle cx="12" cy="12" r="3"/><path d="M12 1v4M12 19v4M4.22 4.22l2.83 2.83M16.95 16.95l2.83 2.83M1 12h4M19 12h4M4.22 19.78l2.83-2.83M16.95 7.05l2.83-2.83"/></svg>
<div class="chat-header-info">
<div class="chat-header-title">PlayPulse Intelligence</div>
<div class="chat-header-status"><span class="status-dot"></span> Agent Online</div>
</div>
<div class="chat-header-actions">
<button class="chat-clear-btn" onclick="clearChat()">Clear</button>
<div style="cursor:pointer;opacity:0.7;" onclick="toggleChat()">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
</div>
</div>
</div>
<div class="chat-messages" id="chat-messages">
<div class="msg-row bot">
<div class="message bot">👋 Welcome to PlayPulse! I can help you understand what tools are available, or answer general app-store questions. Head to <strong>Single Explorer</strong> or <strong>Batch Intelligence</strong> to start analyzing reviews.</div>
</div>
</div>
<div class="chat-input-area">
<input type="text" id="chat-input" placeholder="Ask a question…" onkeydown="if(event.key==='Enter') sendChatMessage()">
<button class="btn-send" onclick="sendChatMessage()">
<svg viewBox="0 0 24 24"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
</button>
</div>
</div>
<script>
const SESSION_ID=(()=>{let id=sessionStorage.getItem('pp_sid');if(!id){id='sess_'+Math.random().toString(36).slice(2);sessionStorage.setItem('pp_sid',id);}return id;})();
function toggleChat(){document.getElementById('chat-window').classList.toggle('open');}
async function clearChat(){
document.getElementById('chat-messages').innerHTML=`<div class="msg-row bot"><div class="message bot">Chat cleared!</div></div>`;
await fetch('/chat/clear',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({session_id:SESSION_ID})});
}
async function sendChatMessage(){
const input=document.getElementById('chat-input');const msg=input.value.trim();if(!msg)return;
appendUserMsg(msg);input.value='';
const container=document.getElementById('chat-messages');
const typing=document.createElement('div');typing.className='typing-indicator';typing.innerHTML='<div class="dot"></div><div class="dot"></div><div class="dot"></div>';
container.appendChild(typing);container.scrollTop=container.scrollHeight;
try{
const res=await fetch('/chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:msg,session_id:SESSION_ID,reviews:[]})});
const data=await res.json();
if(typing.parentNode)container.removeChild(typing);
appendBotMsg(data.reply||data.error||'Something went wrong.',null);
}catch(e){if(typing.parentNode)container.removeChild(typing);appendBotMsg('Connection error.',null);}
}
function appendUserMsg(text){const c=document.getElementById('chat-messages');const row=document.createElement('div');row.className='msg-row user';row.innerHTML=`<div class="message user">${escHtml(text)}</div>`;c.appendChild(row);c.scrollTop=c.scrollHeight;}
function appendBotMsg(text,table){const c=document.getElementById('chat-messages');const row=document.createElement('div');row.className='msg-row bot';if(text&&text.trim()){const b=document.createElement('div');b.className='message bot';b.innerHTML=renderMD(text);row.appendChild(b);}c.appendChild(row);c.scrollTop=c.scrollHeight;}
function renderMD(text){
const lines=text.split('\n');let html='',inList=false;
for(let raw of lines){
if(/^\*\*[^*]+\*\*:?$/.test(raw.trim())){if(inList){html+='</div>';inList=false;}html+=`<div class="msg-section">${escHtml(raw.trim().replace(/^\*\*/,'').replace(/\*\*:?$/,''))}</div>`;continue;}
const nm=raw.match(/^(\d+)\.\s+(.+)/);if(nm){if(!inList){html+='<div style="margin-top:6px">';inList=true;}html+=`<div class="msg-item"><span class="msg-item-num">${nm[1]}.</span><span>${inlineFmt(nm[2])}</span></div>`;continue;}
const bm=raw.match(/^[•\-\*]\s+(.+)/);if(bm){if(!inList){html+='<div style="margin-top:6px">';inList=true;}html+=`<div class="msg-item"><span class="msg-bullet">•</span><span>${inlineFmt(bm[1])}</span></div>`;continue;}
if(inList&&raw.trim()===''){html+='</div>';inList=false;}
if(raw.trim()===''){html+='<br>';}else{html+=`<span>${inlineFmt(raw)}</span><br>`;}
}
if(inList)html+='</div>';return html;
}
function inlineFmt(t){return escHtml(t).replace(/\*\*(.+?)\*\*/g,'<strong>$1</strong>').replace(/_(.+?)_/g,'<em>$1</em>');}
function escHtml(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');}
</script>
</body>
</html>