← back to Beverly Hills Slots
index.html
303 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Beverly Hills Slots — Pink Palace Jackpot</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700;900&family=Inter:wght@500;600;700;800&display=swap');
*{margin:0;padding:0;box-sizing:border-box;}
html{-webkit-text-size-adjust:100%;text-size-adjust:100%;height:100%;}
body{
touch-action:manipulation;-webkit-tap-highlight-color:transparent;
-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;
}
button{touch-action:manipulation;-webkit-tap-highlight-color:transparent;}
:root{
--ivory:#efe3c8; --ivory-dk:#c9b07a; --gold:#e9c46a; --gold-hi:#f6de9b;
--peach:#f4a261; --burnt:#e76f51; --teal:#2a9d8f; --ink:#1a1410;
--green:#41c98a; --font-disp:"Playfair Display",Georgia,serif;
--font-body:"Inter",-apple-system,"Segoe UI",Helvetica,Arial,sans-serif;
}
html,body{height:100%;}
body{
font-family:var(--font-body); color:var(--ivory);
background:
radial-gradient(circle at 20% 0%, rgba(233,196,106,.22), transparent 44%),
radial-gradient(circle at 100% 100%, rgba(231,111,81,.20), transparent 46%),
linear-gradient(160deg,#241a12 0%,#17110c 55%,#0d0907 100%);
min-height:100%; display:flex; align-items:center; justify-content:center; padding:clamp(12px,3vw,26px); overflow:hidden;
}
.machine{ width:min(440px,100%); }
/* Header */
.head{ text-align:center; margin-bottom:14px; }
.kicker{ font-size:11px; letter-spacing:.34em; text-transform:uppercase; color:var(--ivory-dk); }
.wordmark{
font-family:var(--font-disp); font-weight:900; line-height:.98;
font-size:clamp(28px,8vw,44px); margin:2px 0;
background:linear-gradient(180deg,var(--gold-hi),var(--peach) 72%,var(--burnt));
-webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent;
filter:drop-shadow(0 3px 8px rgba(0,0,0,.4));
}
.palms{ font-size:18px; letter-spacing:5px; }
/* Cabinet */
.cabinet{
position:relative; border-radius:22px; padding:clamp(14px,4vw,22px);
background:linear-gradient(180deg, rgba(255,255,255,.05), rgba(0,0,0,.2)), linear-gradient(155deg,#2c2118,#1b140e);
border:1px solid rgba(233,196,106,.4);
box-shadow:0 30px 70px rgba(0,0,0,.6), inset 0 1px 0 rgba(255,255,255,.06);
}
.cabinet::before{ content:""; position:absolute; inset:7px; border:1px solid rgba(233,196,106,.2); border-radius:16px; pointer-events:none; }
/* Reels */
.reels{
display:grid; grid-template-columns:repeat(3,1fr); gap:10px;
background:linear-gradient(180deg,#0f0b08,#161009); border-radius:16px; padding:12px;
border:1px solid rgba(233,196,106,.3); position:relative;
}
.reels::after{ /* payline */
content:""; position:absolute; left:8px; right:8px; top:50%; height:2px; transform:translateY(-50%);
background:linear-gradient(90deg,transparent,rgba(231,111,81,.85),transparent);
}
.reel{
height:clamp(84px,22vw,108px); border-radius:12px; overflow:hidden; position:relative;
background:linear-gradient(180deg,#efe3c8,#d8c290); border:1px solid rgba(0,0,0,.3);
box-shadow:inset 0 8px 14px rgba(0,0,0,.25), inset 0 -8px 14px rgba(0,0,0,.25);
}
.reel .sym{
position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
font-size:clamp(40px,11vw,58px); line-height:1;
filter:drop-shadow(0 2px 3px rgba(0,0,0,.3));
}
.reel.spin .sym{ animation:blur .28s linear infinite; }
@keyframes blur{ 0%{transform:translateY(-8%);} 100%{transform:translateY(8%);} }
.reel.win{ box-shadow:0 0 0 2px var(--gold), 0 0 22px rgba(233,196,106,.7), inset 0 8px 14px rgba(0,0,0,.2); }
/* Meters */
.meters{ display:grid; grid-template-columns:1fr 1fr 1fr; gap:8px; margin:14px 0 4px; }
.meter{ text-align:center; background:linear-gradient(160deg,rgba(255,255,255,.05),rgba(0,0,0,.2)); border:1px solid rgba(233,196,106,.28); border-radius:12px; padding:9px 4px; }
.meter .n{ font-family:var(--font-disp); font-weight:900; font-size:clamp(18px,5vw,24px); color:var(--gold); }
.meter .l{ font-size:9px; letter-spacing:.14em; text-transform:uppercase; color:var(--ivory-dk); margin-top:2px; }
.meter.win .n{ color:var(--green); }
.msg{ text-align:center; min-height:24px; margin:12px 0 4px; font-family:var(--font-disp); font-weight:700; font-size:clamp(15px,4vw,20px); letter-spacing:.01em; }
.msg.jackpot{ background:linear-gradient(90deg,var(--gold-hi),var(--peach),var(--gold-hi)); background-size:200% 100%; -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent; animation:sh 1s linear infinite; }
@keyframes sh{ from{background-position:200% 0;} to{background-position:-200% 0;} }
/* Controls */
.controls{ display:flex; align-items:center; gap:10px; margin-top:12px; }
.bet{ display:flex; align-items:center; gap:6px; }
.chip-btn{
width:38px; height:38px; border-radius:50%; border:1px solid rgba(233,196,106,.5);
background:linear-gradient(180deg,rgba(255,255,255,.06),rgba(0,0,0,.3)); color:var(--ivory);
font-size:20px; font-weight:800; cursor:pointer; display:flex; align-items:center; justify-content:center;
transition:transform .12s, border-color .15s;
}
.chip-btn:hover{ border-color:var(--gold); transform:translateY(-1px); }
.chip-btn:active{ transform:translateY(0); }
.bet-val{ min-width:64px; text-align:center; }
.bet-val .b{ font-family:var(--font-disp); font-weight:900; font-size:20px; color:var(--gold); }
.bet-val .l{ font-size:9px; letter-spacing:.14em; text-transform:uppercase; color:var(--ivory-dk); }
.spin-btn{
flex:1; font-weight:800; font-size:15px; letter-spacing:.1em; text-transform:uppercase; color:var(--ink);
cursor:pointer; border:none; padding:15px 10px; border-radius:999px;
background:linear-gradient(180deg,var(--gold-hi),var(--peach)); box-shadow:0 12px 26px rgba(0,0,0,.45);
transition:transform .12s, filter .12s;
}
.spin-btn:hover{ transform:translateY(-2px); filter:brightness(1.06); }
.spin-btn:active{ transform:translateY(0); }
.spin-btn:disabled{ filter:grayscale(.5) brightness(.8); cursor:default; transform:none; }
.maxbet{
font-weight:700; font-size:12px; letter-spacing:.08em; text-transform:uppercase; color:var(--ivory);
background:transparent; border:1px solid rgba(233,196,106,.4); border-radius:999px; padding:0 14px; height:44px; cursor:pointer;
}
.maxbet:hover{ border-color:var(--gold); }
.foot{ text-align:center; font-size:11px; color:var(--ivory-dk); margin-top:12px; letter-spacing:.04em; }
.foot .gold{ color:var(--gold); }
.comp{ display:none; margin:10px auto 0; }
.comp.show{ display:block; }
/* Paytable */
.pay{ margin-top:10px; font-size:10.5px; color:var(--ivory-dk); text-align:center; line-height:1.7; }
.pay b{ color:var(--gold); }
</style>
</head>
<body>
<div class="machine">
<div class="head">
<div class="kicker">Beverly Hills · 90210</div>
<div class="wordmark">Pink Palace Slots</div>
<div class="palms">🌴 🎰 🌴</div>
</div>
<div class="cabinet">
<div class="reels" id="reels">
<div class="reel" id="r0"><div class="sym">💎</div></div>
<div class="reel" id="r1"><div class="sym">🌴</div></div>
<div class="reel" id="r2"><div class="sym">🥂</div></div>
</div>
<div class="msg" id="msg">Pull for the Pink Palace jackpot.</div>
<div class="meters">
<div class="meter"><div class="n" id="mCredits">500</div><div class="l">Chips</div></div>
<div class="meter win"><div class="n" id="mWin">0</div><div class="l">Last Win</div></div>
<div class="meter"><div class="n" id="mBest">500</div><div class="l">Best</div></div>
</div>
<div class="controls">
<div class="bet">
<button class="chip-btn" id="betDown" aria-label="Lower bet">−</button>
<div class="bet-val"><div class="b" id="betVal">25</div><div class="l">Bet</div></div>
<button class="chip-btn" id="betUp" aria-label="Raise bet">+</button>
</div>
<button class="spin-btn" id="spinBtn">Spin</button>
<button class="maxbet" id="maxBtn">Max</button>
</div>
<button class="spin-btn comp" id="compBtn">🍸 Comp Me 300 Chips</button>
<div class="pay">
<b>💎💎💎</b> 50× · <b>👑👑👑</b> 25× · <b>🏎️🏎️🏎️</b> 15×<br>
<b>🥂🥂🥂</b> 10× · <b>🌴🌴🌴</b> 8× · <b>🌹🌹🌹</b> 5× · two 💎 3×
</div>
</div>
<div class="foot">← → set bet · Space to spin · <span class="gold">chase the diamonds</span></div>
</div>
<script>
(function(){
"use strict";
var LS_BEST="beverlySlots.best";
// Weighted reel strip — diamonds rarest.
var SYMBOLS=[
{s:"💎",w:2, pay:50},
{s:"👑",w:3, pay:25},
{s:"🏎️",w:4, pay:15},
{s:"🥂",w:5, pay:10},
{s:"🌴",w:6, pay:8},
{s:"🌹",w:7, pay:5}
];
var BAG=[]; SYMBOLS.forEach(function(o){ for(var i=0;i<o.w;i++) BAG.push(o); });
var PAY={}; SYMBOLS.forEach(function(o){ PAY[o.s]=o.pay; });
var BETS=[25,50,100,150,200];
var betIdx=0;
var credits=500, best=parseInt(localStorage.getItem(LS_BEST)||"500",10)||500;
var spinning=false, rigged=null;
var $=function(id){return document.getElementById(id);};
var reels=[$("r0"),$("r1"),$("r2")];
var symEls=reels.map(function(r){return r.querySelector(".sym");});
function bet(){ return BETS[betIdx]; }
function pick(){ return BAG[(Math.random()*BAG.length)|0]; }
function render(){
$("mCredits").textContent=credits.toLocaleString();
$("mBest").textContent=best.toLocaleString();
$("betVal").textContent=bet();
var broke = credits < BETS[0];
$("spinBtn").disabled = spinning || broke;
$("betUp").disabled=spinning; $("betDown").disabled=spinning; $("maxBtn").disabled=spinning;
$("compBtn").classList.toggle("show", broke && !spinning);
}
function setBet(dir){
if(spinning) return;
betIdx=Math.max(0, Math.min(BETS.length-1, betIdx+dir));
// don't allow a bet larger than the bank
while(BETS[betIdx] > credits && betIdx>0) betIdx--;
render();
}
function maxBet(){
if(spinning) return;
for(var i=BETS.length-1;i>=0;i--){ if(BETS[i]<=credits){ betIdx=i; break; } }
render();
}
function spin(){
if(spinning || credits < bet()) return;
spinning=true;
credits -= bet();
reels.forEach(function(r){ r.classList.remove("win"); });
$("mWin").textContent="0";
$("msg").textContent="Spinning…"; $("msg").className="msg";
render();
var result = rigged || [pick().s, pick().s, pick().s];
rigged=null;
// animate: each reel cycles symbols, stops staggered
var stops=[650, 900, 1180];
reels.forEach(function(r,i){
r.classList.add("spin");
var cyc=setInterval(function(){ symEls[i].textContent=pick().s; }, 70);
setTimeout(function(){
clearInterval(cyc);
r.classList.remove("spin");
symEls[i].textContent=result[i];
if(i===2) setTimeout(function(){ resolve(result); }, 120);
}, stops[i]);
});
}
function resolve(res){
var win=0, msg="", cls="msg", winReels=[];
if(res[0]===res[1] && res[1]===res[2]){
win = bet() * PAY[res[0]];
winReels=[0,1,2];
if(res[0]==="💎"){ msg="💎 PINK PALACE JACKPOT! +"+win.toLocaleString(); cls="msg jackpot"; }
else msg="Triple "+res[0]+" — +"+win.toLocaleString();
} else {
// two diamonds anywhere pays 3x
var dia = res.filter(function(s){return s==="💎";}).length;
if(dia===2){ win=bet()*3; msg="Two 💎 — +"+win.toLocaleString();
res.forEach(function(s,i){ if(s==="💎") winReels.push(i); });
} else { msg = pickTaunt(); }
}
if(win>0){
credits += win;
$("mWin").textContent=win.toLocaleString();
winReels.forEach(function(i){ reels[i].classList.add("win"); });
if(credits>best){ best=credits; localStorage.setItem(LS_BEST,String(best)); }
}
$("msg").textContent=msg; $("msg").className=cls;
spinning=false;
render();
}
var TAUNTS=["Valet's still parking your luck.","Not this pull — try Rodeo again.","So close. Spin it back.","The Palace keeps the house edge.","Almost A-list. Again?"];
function pickTaunt(){ return TAUNTS[(Math.random()*TAUNTS.length)|0]; }
// wire
$("spinBtn").addEventListener("click", spin);
$("betUp").addEventListener("click", function(){ setBet(1); });
$("betDown").addEventListener("click", function(){ setBet(-1); });
$("maxBtn").addEventListener("click", maxBet);
$("compBtn").addEventListener("click", function(){ credits+=300; if(betIdx>0&&BETS[betIdx]>credits) betIdx=0; $("msg").textContent="🍸 On the house — 300 chips. Good luck."; $("msg").className="msg"; render(); });
document.addEventListener("keydown", function(e){
if(e.key===" "||e.key==="Enter"){ e.preventDefault(); spin(); }
else if(e.key==="ArrowUp"||e.key==="ArrowRight"){ setBet(1); }
else if(e.key==="ArrowDown"||e.key==="ArrowLeft"){ setBet(-1); }
});
render();
// Automation/test hook (mirrors other hub games).
window.__slots={
spin:spin,
rig:function(arr){ rigged=arr.slice(0,3); }, // force next result, e.g. ["💎","💎","💎"]
state:function(){ return { credits:credits, bet:bet(), spinning:spinning, best:best, lastWin:parseInt($("mWin").textContent.replace(/,/g,""),10)||0, reels:symEls.map(function(e){return e.textContent;}), msg:$("msg").textContent }; }
};
})();
</script>
</body>
</html>